diff --git a/HSS/HSS.xcodeproj/project.pbxproj b/HSS/HSS.xcodeproj/project.pbxproj new file mode 100644 index 0000000..48aad76 --- /dev/null +++ b/HSS/HSS.xcodeproj/project.pbxproj @@ -0,0 +1,476 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 59F3430F14448535004904DE /* OsiriXAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59F3430E14448535004904DE /* OsiriXAPI.framework */; }; + 7110084514BB489E003F0C67 /* HSSMedcaseCreation.h in Headers */ = {isa = PBXBuildFile; fileRef = 7110084314BB489D003F0C67 /* HSSMedcaseCreation.h */; }; + 7110084614BB489E003F0C67 /* HSSMedcaseCreation.m in Sources */ = {isa = PBXBuildFile; fileRef = 7110084414BB489D003F0C67 /* HSSMedcaseCreation.m */; }; + 711FBBA814B5EE6500802A87 /* HSSAPISession.h in Headers */ = {isa = PBXBuildFile; fileRef = 711FBBA614B5EE6500802A87 /* HSSAPISession.h */; }; + 711FBBA914B5EE6500802A87 /* HSSAPISession.m in Sources */ = {isa = PBXBuildFile; fileRef = 711FBBA714B5EE6500802A87 /* HSSAPISession.m */; }; + 7120A65B14B4A87100682E31 /* HSSAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 7120A65914B4A87100682E31 /* HSSAPI.h */; }; + 7120A65C14B4A87100682E31 /* HSSAPI.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7120A65A14B4A87100682E31 /* HSSAPI.mm */; }; + 7125AEF714A230F900D068F3 /* HSSExportWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7125AEF514A230F900D068F3 /* HSSExportWindowController.h */; }; + 7125AEF814A230F900D068F3 /* HSSExportWindowController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7125AEF614A230F900D068F3 /* HSSExportWindowController.mm */; }; + 7125AF0414A23E7700D068F3 /* HSSExportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7125AF0214A23E7700D068F3 /* HSSExportWindow.xib */; }; + 7125AF0714A24B4800D068F3 /* HSSAuthenticationWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7125AF0514A24B4800D068F3 /* HSSAuthenticationWindowController.h */; }; + 7125AF0814A24B4800D068F3 /* HSSAuthenticationWindowController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7125AF0614A24B4800D068F3 /* HSSAuthenticationWindowController.mm */; }; + 712749361484F13A00B5E5DF /* HSS.h in Headers */ = {isa = PBXBuildFile; fileRef = 712749341484F13A00B5E5DF /* HSS.h */; }; + 712749371484F13A00B5E5DF /* HSS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 712749351484F13A00B5E5DF /* HSS.mm */; }; + 712749391484F14300B5E5DF /* HSS.png in Resources */ = {isa = PBXBuildFile; fileRef = 712749381484F14300B5E5DF /* HSS.png */; }; + 7128B26E14BC9B9F000B029E /* HSSIsNonEmptyString.h in Headers */ = {isa = PBXBuildFile; fileRef = 7128B26C14BC9B9F000B029E /* HSSIsNonEmptyString.h */; }; + 7128B26F14BC9B9F000B029E /* HSSIsNonEmptyString.m in Sources */ = {isa = PBXBuildFile; fileRef = 7128B26D14BC9B9F000B029E /* HSSIsNonEmptyString.m */; }; + 712A968D14B751EB00B6D69C /* HSSFolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 712A968B14B751EB00B6D69C /* HSSFolder.h */; }; + 712A968E14B751EB00B6D69C /* HSSFolder.m in Sources */ = {isa = PBXBuildFile; fileRef = 712A968C14B751EB00B6D69C /* HSSFolder.m */; }; + 712EC11E14A4B66B00299D0F /* HSSAuthenticationWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 712EC11C14A4B66B00299D0F /* HSSAuthenticationWindow.xib */; }; + 7175E64F14BF0F8C00F85692 /* HSSItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 7175E64E14BF0F8C00F85692 /* HSSItem.m */; }; + 71B415D514BD7C27007DACFA /* HSSMedcase.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B415D314BD7C27007DACFA /* HSSMedcase.h */; }; + 71B415D614BD7C27007DACFA /* HSSMedcase.m in Sources */ = {isa = PBXBuildFile; fileRef = 71B415D414BD7C27007DACFA /* HSSMedcase.m */; }; + 71B415D814BD7C43007DACFA /* HSSItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B415D714BD7C43007DACFA /* HSSItem.h */; }; + 71B415DB14BD8448007DACFA /* HSSCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B415D914BD8447007DACFA /* HSSCell.h */; }; + 71B415DC14BD8448007DACFA /* HSSCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 71B415DA14BD8447007DACFA /* HSSCell.m */; }; + 71EC989915F4C42700E97101 /* NSString+HSS.h in Headers */ = {isa = PBXBuildFile; fileRef = 71EC989715F4C42700E97101 /* NSString+HSS.h */; }; + 71EC989A15F4C42700E97101 /* NSString+HSS.m in Sources */ = {isa = PBXBuildFile; fileRef = 71EC989815F4C42700E97101 /* NSString+HSS.m */; }; + 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 599BEEFB1429C47600B45236 /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 59F3430E14448535004904DE /* OsiriXAPI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OsiriXAPI.framework; path = "../../../osirix-trunk/build/Development/OsiriXAPI.framework"; sourceTree = ""; }; + 7110084314BB489D003F0C67 /* HSSMedcaseCreation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSMedcaseCreation.h; path = Sources/HSSMedcaseCreation.h; sourceTree = ""; }; + 7110084414BB489D003F0C67 /* HSSMedcaseCreation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSSMedcaseCreation.m; path = Sources/HSSMedcaseCreation.m; sourceTree = ""; }; + 711FBBA614B5EE6500802A87 /* HSSAPISession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSAPISession.h; path = Sources/HSSAPISession.h; sourceTree = ""; }; + 711FBBA714B5EE6500802A87 /* HSSAPISession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSSAPISession.m; path = Sources/HSSAPISession.m; sourceTree = ""; }; + 7120A65914B4A87100682E31 /* HSSAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSAPI.h; path = Sources/HSSAPI.h; sourceTree = ""; }; + 7120A65A14B4A87100682E31 /* HSSAPI.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = HSSAPI.mm; path = Sources/HSSAPI.mm; sourceTree = ""; }; + 7125AEF514A230F900D068F3 /* HSSExportWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSExportWindowController.h; path = Sources/HSSExportWindowController.h; sourceTree = ""; }; + 7125AEF614A230F900D068F3 /* HSSExportWindowController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = HSSExportWindowController.mm; path = Sources/HSSExportWindowController.mm; sourceTree = ""; }; + 7125AF0314A23E7700D068F3 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = HSSExportWindow.xib; sourceTree = ""; }; + 7125AF0514A24B4800D068F3 /* HSSAuthenticationWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSAuthenticationWindowController.h; path = Sources/HSSAuthenticationWindowController.h; sourceTree = ""; }; + 7125AF0614A24B4800D068F3 /* HSSAuthenticationWindowController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = HSSAuthenticationWindowController.mm; path = Sources/HSSAuthenticationWindowController.mm; sourceTree = ""; }; + 712749341484F13A00B5E5DF /* HSS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSS.h; path = Sources/HSS.h; sourceTree = ""; }; + 712749351484F13A00B5E5DF /* HSS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = HSS.mm; path = Sources/HSS.mm; sourceTree = ""; }; + 712749381484F14300B5E5DF /* HSS.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = HSS.png; path = Resources/HSS.png; sourceTree = ""; }; + 7128B26C14BC9B9F000B029E /* HSSIsNonEmptyString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSIsNonEmptyString.h; path = Sources/HSSIsNonEmptyString.h; sourceTree = ""; }; + 7128B26D14BC9B9F000B029E /* HSSIsNonEmptyString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSSIsNonEmptyString.m; path = Sources/HSSIsNonEmptyString.m; sourceTree = ""; }; + 712A968B14B751EB00B6D69C /* HSSFolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSFolder.h; path = Sources/HSSFolder.h; sourceTree = ""; }; + 712A968C14B751EB00B6D69C /* HSSFolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSSFolder.m; path = Sources/HSSFolder.m; sourceTree = ""; }; + 712EC11D14A4B66B00299D0F /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Resources/English.lproj/HSSAuthenticationWindow.xib; sourceTree = ""; }; + 7175E64E14BF0F8C00F85692 /* HSSItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSSItem.m; path = Sources/HSSItem.m; sourceTree = ""; }; + 71B415D314BD7C27007DACFA /* HSSMedcase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSMedcase.h; path = Sources/HSSMedcase.h; sourceTree = ""; }; + 71B415D414BD7C27007DACFA /* HSSMedcase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSSMedcase.m; path = Sources/HSSMedcase.m; sourceTree = ""; }; + 71B415D714BD7C43007DACFA /* HSSItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSItem.h; path = Sources/HSSItem.h; sourceTree = ""; }; + 71B415D914BD8447007DACFA /* HSSCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSSCell.h; path = Sources/HSSCell.h; sourceTree = ""; }; + 71B415DA14BD8447007DACFA /* HSSCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSSCell.m; path = Sources/HSSCell.m; sourceTree = ""; }; + 71EC989715F4C42700E97101 /* NSString+HSS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+HSS.h"; path = "Sources/NSString+HSS.h"; sourceTree = ""; }; + 71EC989815F4C42700E97101 /* NSString+HSS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+HSS.m"; path = "Sources/NSString+HSS.m"; sourceTree = ""; }; + 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AB5D36050680E57E00F4007A /* HSS.osirixplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HSS.osirixplugin; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D5B49B3048680CD000E48DA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, + 59F3430F14448535004904DE /* OsiriXAPI.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* ExportSeriesAsNifti */ = { + isa = PBXGroup; + children = ( + 8D5B49B7048680CD000E48DA /* Info.plist */, + 08FB77AFFE84173DC02AAC07 /* Classes */, + 089C167CFE841241C02AAC07 /* Resources */, + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, + 19C28FB8FE9D52D311CA2CBB /* Products */, + ); + name = ExportSeriesAsNifti; + sourceTree = ""; + }; + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 59F3430E14448535004904DE /* OsiriXAPI.framework */, + 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, + 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, + ); + name = "Frameworks and Libraries"; + sourceTree = ""; + }; + 089C167CFE841241C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + 712749381484F14300B5E5DF /* HSS.png */, + ); + name = Resources; + sourceTree = ""; + }; + 08FB77AFFE84173DC02AAC07 /* Classes */ = { + isa = PBXGroup; + children = ( + 712749341484F13A00B5E5DF /* HSS.h */, + 712749351484F13A00B5E5DF /* HSS.mm */, + 7120A65914B4A87100682E31 /* HSSAPI.h */, + 7120A65A14B4A87100682E31 /* HSSAPI.mm */, + 711FBBA614B5EE6500802A87 /* HSSAPISession.h */, + 711FBBA714B5EE6500802A87 /* HSSAPISession.m */, + 7110084314BB489D003F0C67 /* HSSMedcaseCreation.h */, + 7110084414BB489D003F0C67 /* HSSMedcaseCreation.m */, + 711FBBA514B5ED2100802A87 /* GUI */, + 711FBBA314B5ED0C00802A87 /* Authentication */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 089C1672FE841209C02AAC07 /* Foundation.framework */, + 089C167FFE841241C02AAC07 /* AppKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FB8FE9D52D311CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + AB5D36050680E57E00F4007A /* HSS.osirixplugin */, + ); + name = Products; + sourceTree = ""; + }; + 711FBBA314B5ED0C00802A87 /* Authentication */ = { + isa = PBXGroup; + children = ( + 712EC11C14A4B66B00299D0F /* HSSAuthenticationWindow.xib */, + 7125AF0514A24B4800D068F3 /* HSSAuthenticationWindowController.h */, + 7125AF0614A24B4800D068F3 /* HSSAuthenticationWindowController.mm */, + ); + name = Authentication; + sourceTree = ""; + }; + 711FBBA514B5ED2100802A87 /* GUI */ = { + isa = PBXGroup; + children = ( + 71B415D114BD7B83007DACFA /* Tree */, + 7125AF0214A23E7700D068F3 /* HSSExportWindow.xib */, + 7125AEF514A230F900D068F3 /* HSSExportWindowController.h */, + 7125AEF614A230F900D068F3 /* HSSExportWindowController.mm */, + 7128B26C14BC9B9F000B029E /* HSSIsNonEmptyString.h */, + 7128B26D14BC9B9F000B029E /* HSSIsNonEmptyString.m */, + 71B415D914BD8447007DACFA /* HSSCell.h */, + 71B415DA14BD8447007DACFA /* HSSCell.m */, + 71EC989715F4C42700E97101 /* NSString+HSS.h */, + 71EC989815F4C42700E97101 /* NSString+HSS.m */, + ); + name = GUI; + sourceTree = ""; + }; + 71B415D114BD7B83007DACFA /* Tree */ = { + isa = PBXGroup; + children = ( + 71B415D714BD7C43007DACFA /* HSSItem.h */, + 7175E64E14BF0F8C00F85692 /* HSSItem.m */, + 712A968B14B751EB00B6D69C /* HSSFolder.h */, + 712A968C14B751EB00B6D69C /* HSSFolder.m */, + 71B415D314BD7C27007DACFA /* HSSMedcase.h */, + 71B415D414BD7C27007DACFA /* HSSMedcase.m */, + ); + name = Tree; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8D5B49AD048680CD000E48DA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 712749361484F13A00B5E5DF /* HSS.h in Headers */, + 7125AEF714A230F900D068F3 /* HSSExportWindowController.h in Headers */, + 7125AF0714A24B4800D068F3 /* HSSAuthenticationWindowController.h in Headers */, + 7120A65B14B4A87100682E31 /* HSSAPI.h in Headers */, + 711FBBA814B5EE6500802A87 /* HSSAPISession.h in Headers */, + 712A968D14B751EB00B6D69C /* HSSFolder.h in Headers */, + 7110084514BB489E003F0C67 /* HSSMedcaseCreation.h in Headers */, + 7128B26E14BC9B9F000B029E /* HSSIsNonEmptyString.h in Headers */, + 71B415D514BD7C27007DACFA /* HSSMedcase.h in Headers */, + 71B415D814BD7C43007DACFA /* HSSItem.h in Headers */, + 71B415DB14BD8448007DACFA /* HSSCell.h in Headers */, + 71EC989915F4C42700E97101 /* NSString+HSS.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8D5B49AC048680CD000E48DA /* HSS */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE6A9A9008AC841300D0C1EE /* Build configuration list for PBXNativeTarget "HSS" */; + buildPhases = ( + 8D5B49AD048680CD000E48DA /* Headers */, + 8D5B49AF048680CD000E48DA /* Resources */, + 599BEEFB1429C47600B45236 /* Copy Frameworks */, + 8D5B49B1048680CD000E48DA /* Sources */, + 8D5B49B3048680CD000E48DA /* Frameworks */, + 8D5B49B5048680CD000E48DA /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HSS; + productInstallPath = "$(HOME)/Library/Bundles"; + productName = Invert; + productReference = AB5D36050680E57E00F4007A /* HSS.osirixplugin */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0430; + }; + buildConfigurationList = CE6A9A9408AC841300D0C1EE /* Build configuration list for PBXProject "HSS" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 089C166AFE841209C02AAC07 /* ExportSeriesAsNifti */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D5B49AC048680CD000E48DA /* HSS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D5B49AF048680CD000E48DA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 712749391484F14300B5E5DF /* HSS.png in Resources */, + 7125AF0414A23E7700D068F3 /* HSSExportWindow.xib in Resources */, + 712EC11E14A4B66B00299D0F /* HSSAuthenticationWindow.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + 8D5B49B5048680CD000E48DA /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D5B49B1048680CD000E48DA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 712749371484F13A00B5E5DF /* HSS.mm in Sources */, + 7125AEF814A230F900D068F3 /* HSSExportWindowController.mm in Sources */, + 7125AF0814A24B4800D068F3 /* HSSAuthenticationWindowController.mm in Sources */, + 7120A65C14B4A87100682E31 /* HSSAPI.mm in Sources */, + 711FBBA914B5EE6500802A87 /* HSSAPISession.m in Sources */, + 712A968E14B751EB00B6D69C /* HSSFolder.m in Sources */, + 7110084614BB489E003F0C67 /* HSSMedcaseCreation.m in Sources */, + 7128B26F14BC9B9F000B029E /* HSSIsNonEmptyString.m in Sources */, + 71B415D614BD7C27007DACFA /* HSSMedcase.m in Sources */, + 71B415DC14BD8448007DACFA /* HSSCell.m in Sources */, + 7175E64F14BF0F8C00F85692 /* HSSItem.m in Sources */, + 71EC989A15F4C42700E97101 /* NSString+HSS.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 7125AF0214A23E7700D068F3 /* HSSExportWindow.xib */ = { + isa = PBXVariantGroup; + children = ( + 7125AF0314A23E7700D068F3 /* English */, + ); + name = HSSExportWindow.xib; + path = Resources/English.lproj; + sourceTree = ""; + }; + 712EC11C14A4B66B00299D0F /* HSSAuthenticationWindow.xib */ = { + isa = PBXVariantGroup; + children = ( + 712EC11D14A4B66B00299D0F /* English */, + ); + name = HSSAuthenticationWindow.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + CE6A9A9108AC841300D0C1EE /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "../../../osirix-trunk/build/Development", + "$(inherited)", + "\"$(SRCROOT)/../../_Frameworks_and_Libraries\"", + "\"$(SRCROOT)/../../HUG Framework/build/Development\"", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_TRIGRAPHS = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Sources/HSS_Prefix.pch; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "@executable_path/../Plug-ins"; + LIBRARY_SEARCH_PATHS = ""; + LIBRARY_STYLE = Bundle; + MACOSX_DEPLOYMENT_TARGET = 10.5; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ( + "-undefined", + dynamic_lookup, + ); + OTHER_REZFLAGS = ""; + PRODUCT_NAME = HSS; + SECTORDER_FLAGS = ""; + USER_HEADER_SEARCH_PATHS = "../../../osirix-trunk/Binaries/dcmtk-source/dcmimgle ../../../osirix-trunk/Binaries/dcmtk-source/config ../../../osirix-trunk/Binaries/dcmtk-source/ofstd ../../../osirix-trunk/Binaries/dcmtk-source/dcmdata"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + WRAPPER_EXTENSION = osirixplugin; + ZERO_LINK = NO; + }; + name = Development; + }; + CE6A9A9208AC841300D0C1EE /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + FRAMEWORK_SEARCH_PATHS = ( + "../../../osirix-trunk/build/Development", + "$(inherited)", + "\"$(SRCROOT)/../../_Frameworks_and_Libraries\"", + "\"$(SRCROOT)/../../HUG Framework/build/Development\"", + ); + GCC_ENABLE_TRIGRAPHS = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Sources/HSS_Prefix.pch; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "@executable_path/../Plug-ins"; + LIBRARY_SEARCH_PATHS = ""; + LIBRARY_STYLE = Bundle; + MACOSX_DEPLOYMENT_TARGET = 10.5; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ( + "-undefined", + dynamic_lookup, + ); + OTHER_REZFLAGS = ""; + PRODUCT_NAME = HSS; + SECTORDER_FLAGS = ""; + USER_HEADER_SEARCH_PATHS = "../../../osirix-trunk/Binaries/dcmtk-source/dcmimgle ../../../osirix-trunk/Binaries/dcmtk-source/config ../../../osirix-trunk/Binaries/dcmtk-source/ofstd ../../../osirix-trunk/Binaries/dcmtk-source/dcmdata"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + WRAPPER_EXTENSION = osirixplugin; + ZERO_LINK = NO; + }; + name = Deployment; + }; + CE6A9A9508AC841300D0C1EE /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + SDKROOT = macosx10.7; + }; + name = Development; + }; + CE6A9A9608AC841300D0C1EE /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + SDKROOT = macosx10.7; + }; + name = Deployment; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE6A9A9008AC841300D0C1EE /* Build configuration list for PBXNativeTarget "HSS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE6A9A9108AC841300D0C1EE /* Development */, + CE6A9A9208AC841300D0C1EE /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + CE6A9A9408AC841300D0C1EE /* Build configuration list for PBXProject "HSS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE6A9A9508AC841300D0C1EE /* Development */, + CE6A9A9608AC841300D0C1EE /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/HSS/HSS.xcodeproj/xcshareddata/xcschemes/HSS.xcscheme b/HSS/HSS.xcodeproj/xcshareddata/xcschemes/HSS.xcscheme new file mode 100644 index 0000000..e233cea --- /dev/null +++ b/HSS/HSS.xcodeproj/xcshareddata/xcschemes/HSS.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HSS/Info.plist b/HSS/Info.plist new file mode 100644 index 0000000..5f39d1c --- /dev/null +++ b/HSS/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + HSS + CFBundleIconFile + HSS + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleShortVersionString + 1.1.3 + NSPrincipalClass + HSS + MenuTitles + + Export to HSS + + pluginType + Database + HSS base URL - TEST + https://vmlouvre.hcuge.ch/studyshare + HSS base URL + https://collimage.hcuge.ch/studyshare + Accept all HTTPS certificates + + + diff --git a/HSS/Resources/English.lproj/HSSAuthenticationWindow.xib b/HSS/Resources/English.lproj/HSSAuthenticationWindow.xib new file mode 100644 index 0000000..9336f80 --- /dev/null +++ b/HSS/Resources/English.lproj/HSSAuthenticationWindow.xib @@ -0,0 +1,983 @@ + + + + 1070 + 12A269 + 2549 + 1187 + 624.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2549 + + + YES + NSButton + NSButtonCell + NSCustomObject + NSImageCell + NSImageView + NSScrollView + NSScroller + NSSecureTextField + NSSecureTextFieldCell + NSTextField + NSTextFieldCell + NSTextView + NSView + NSWindowTemplate + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + HSSAuthenticationWindowController + + + FirstResponder + + + NSApplication + + + 1 + 2 + {{196, 369}, {453, 142}} + 544736256 + Authentication + NSWindow + + + + + 256 + + YES + + + 274 + + YES + + + 2304 + + YES + + + 2322 + {348, 20} + + + + _NS:1480 + + + + + + + + + + YES + + + 38 + + + + 348 + 1 + + + 67110913 + 0 + + + 3 + MQA + + + YES + + YES + NSBackgroundColor + NSColor + + + YES + + 6 + System + selectedTextBackgroundColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + selectedTextColor + + 3 + MAA + + + + + + + YES + + YES + NSColor + NSCursor + NSUnderline + + + YES + + 1 + MCAwIDEAA + + + {8, -8} + 13 + + + + + + + 0 + + 6 + {348, 10000000} + + + + {348, 20} + + + + _NS:1478 + + + 3 + MSAwAA + + + {4, 5} + + 12582912 + + YES + + YES + + + + TU0AKgAAAHCAFUqgBVKsAAAAwdVQUqwaEQeIRGJRGFlYqwWLQ+JxuOQpVRmEx2RROKwOQyOUQSPyaUym +SxqWyKXyeYxyZzWbSuJTScRCbz2Nz+gRKhUOfTqeUai0OSxiWTiBQSHSGFquGwekxyAgAAAOAQAAAwAA +AAEAEAAAAQEAAwAAAAEAEAAAAQIAAwAAAAIACAAIAQMAAwAAAAEABQAAAQYAAwAAAAEAAQAAAREABAAA +AAEAAAAIARIAAwAAAAEAAQAAARUAAwAAAAEAAgAAARYAAwAAAAEAEAAAARcABAAAAAEAAABnARwAAwAA +AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA + + + + + + 3 + MCAwAA + + + + 2 + + + + -2147483392 + {{-100, -100}, {15, 19}} + + + + _NS:1494 + NO + + _doScroller: + 1 + 0.85256409645080566 + + + + -2147483392 + {{-100, -100}, {87, 18}} + + + + _NS:1482 + NO + 1 + + _doScroller: + 1 + 0.94565218687057495 + + + {{85, 20}, {348, 20}} + + + + _NS:940 + 133120 + + + + 0.25 + 4 + 1 + + + + 265 + {{187, 75}, {246, 22}} + + + + YES + + -1804599231 + 272630784 + + + LucidaGrande + 13 + 1044 + + + YES + + 6 + System + textBackgroundColor + + + + 6 + System + textColor + + + + NO + + + + 266 + {{82, 78}, {100, 17}} + + + + YES + + 68157504 + 71304192 + Username: + + + + 6 + System + controlColor + + + + 6 + System + controlTextColor + + + + NO + + + + 266 + {{82, 51}, {100, 17}} + + + + YES + + 68157504 + 71304192 + Password: + + + + + + NO + + + + 265 + {{187, 48}, {246, 22}} + + + + YES + + 342884416 + 272630784 + + + + YES + + + + YES + NSAllRomanInputSourcesLocaleIdentifier + + + NO + + + + 289 + {{339, 12}, {100, 32}} + + + YES + + 67108864 + 134217728 + Ok + + + -2038284288 + 129 + + DQ + 200 + 25 + + NO + + + + 289 + {{239, 12}, {100, 32}} + + + + YES + + 67108864 + 134217728 + Annuler + + + -2038284288 + 129 + + Gw + 200 + 25 + + NO + + + + 266 + {{82, 105}, {354, 17}} + + + + YES + + 68157504 + 272630784 + Horizon Study Share™ authentication: + + + + + + NO + + + + 268 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 72}, {47, 50}} + + + + YES + + 134217728 + 33554432 + + NSImage + HSS + + 0 + 0 + 0 + NO + + NO + YES + + + {453, 142} + + + + + {{0, 0}, {1920, 1178}} + {10000000000000, 10000000000000} + YES + + + + + YES + + + window + + + + 23 + + + + cancelAction: + + + + 24 + + + + okAction: + + + + 25 + + + + updateOkButton: + + + + 40 + + + + infoLabel + + + + 47 + + + + okButton + + + + 50 + + + + passwordField + + + + 51 + + + + messageLabel + + + + 60 + + + + loginField + + + + 61 + + + + delegate + + + + 42 + + + + initialFirstResponder + + + + 68 + + + + nextKeyView + + + + 63 + + + + nextKeyView + + + + 65 + + + + nextKeyView + + + + 66 + + + + nextKeyView + + + + 67 + + + + + YES + + 0 + + YES + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 1 + + + YES + + + + + + 2 + + + YES + + + + + + + + + + + + + + 7 + + + YES + + + + + + 8 + + + + + 9 + + + YES + + + + + + 10 + + + + + 11 + + + YES + + + + + + 12 + + + + + 13 + + + YES + + + + + + 14 + + + + + 15 + + + YES + + + + + + 16 + + + + + 17 + + + YES + + + + + + 18 + + + + + 19 + + + YES + + + + + + 20 + + + + + 35 + + + YES + + + + + + 36 + + + + + 56 + + + YES + + + + + + + + 57 + + + + + 58 + + + + + 59 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 1.IBPluginDependency + 1.IBWindowTemplateEditedContentRect + 1.NSWindowTemplate.visibleAtLaunch + 10.IBPluginDependency + 11.IBPluginDependency + 12.IBPluginDependency + 13.IBPluginDependency + 14.IBPluginDependency + 15.IBPluginDependency + 16.IBPluginDependency + 17.IBPluginDependency + 18.IBPluginDependency + 19.IBPluginDependency + 2.IBPluginDependency + 20.IBPluginDependency + 35.IBPluginDependency + 36.IBPluginDependency + 56.IBPluginDependency + 57.IBPluginDependency + 58.IBPluginDependency + 59.IBPluginDependency + 7.IBPluginDependency + 8.IBPluginDependency + 9.IBPluginDependency + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{745, 25}, {453, 181}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + YES + + + + + + YES + + + + + 68 + + + + YES + + HSSAuthenticationWindowController + NSWindowController + + YES + + YES + cancelAction: + okAction: + updateOkButton: + + + YES + id + id + id + + + + YES + + YES + cancelAction: + okAction: + updateOkButton: + + + YES + + cancelAction: + id + + + okAction: + id + + + updateOkButton: + id + + + + + YES + + YES + infoLabel + loginField + messageLabel + okButton + passwordField + + + YES + NSTextField + NSTextField + NSTextView + NSButton + NSTextField + + + + YES + + YES + infoLabel + loginField + messageLabel + okButton + passwordField + + + YES + + infoLabel + NSTextField + + + loginField + NSTextField + + + messageLabel + NSTextView + + + okButton + NSButton + + + passwordField + NSTextField + + + + + IBProjectSource + ./Classes/HSSAuthenticationWindowController.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + HSS + {61.435906522748574, 61.435906522748574} + + + diff --git a/HSS/Resources/English.lproj/HSSCreateSubfolderWindow.xib b/HSS/Resources/English.lproj/HSSCreateSubfolderWindow.xib new file mode 100644 index 0000000..ca4cb2f --- /dev/null +++ b/HSS/Resources/English.lproj/HSSCreateSubfolderWindow.xib @@ -0,0 +1,719 @@ + + + + 1060 + 11C74 + 1938 + 1138.23 + 567.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 1938 + + + YES + NSTextField + NSView + NSWindowTemplate + NSTextFieldCell + NSMenu + NSMenuItem + NSPopUpButton + NSButtonCell + NSPopUpButtonCell + NSButton + NSCustomObject + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + HSSCreateSubfolderWindowController + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{196, 240}, {480, 149}} + 544735232 + Hidden Title Bar + NSWindow + + + + + 256 + + YES + + + 289 + {{384, 13}, {81, 28}} + + + + _NS:687 + YES + + 67239424 + 134348800 + Create + + LucidaGrande + 11 + 3100 + + _NS:687 + + -2038284033 + 129 + + DQ + 200 + 25 + + + + + 289 + {{304, 13}, {82, 28}} + + + + _NS:687 + YES + + 67239424 + 134348800 + Cancel + + _NS:687 + + -2038284033 + 129 + + Gw + 200 + 25 + + + + + 266 + {{17, 115}, {446, 14}} + + + + _NS:3944 + YES + + 68288064 + 272761856 + Create new folder in %@: + + _NS:3944 + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + + + + 268 + {{17, 93}, {69, 14}} + + + + _NS:3944 + YES + + 68288064 + 71435264 + Name: + + _NS:3944 + + + + + + + + 268 + {{17, 72}, {69, 14}} + + + + _NS:3944 + YES + + 68288064 + 71435264 + Description: + + _NS:3944 + + + + + + + + 292 + {{17, 46}, {69, 14}} + + + + _NS:3944 + YES + + 68288064 + 71435264 + Privacy: + + _NS:3944 + + + + + + + + 266 + {{91, 91}, {369, 19}} + + + + _NS:903 + YES + + -1804468671 + 272761856 + + + Empty + _NS:903 + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + + + + + + 290 + {{88, 41}, {375, 22}} + + + + _NS:868 + YES + + -2076049856 + 133120 + + _NS:868 + + 109199615 + 129 + Test 333 + + 400 + 75 + + YES + + OtherViews + + YES + + + Readable by any user + + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + _popUpItemAction: + + + + + Writable by any user + + 2147483647 + + + _popUpItemAction: + + + + + LucidaGrande + 13 + 1044 + + + -1 + 1 + YES + YES + 2 + + + + + 274 + {{91, 70}, {369, 19}} + + + + _NS:354 + {250, 750} + YES + + -1805517311 + 272760832 + + + Empty + _NS:354 + + YES + + + + + + {480, 149} + + + + + {{0, 0}, {1920, 1178}} + {10000000000000, 10000000000000} + HSS Create Subfolder Window + NO + + + + + YES + + + messageField + + + + 27 + + + + window + + + + 28 + + + + delegate + + + + 29 + + + + + YES + + 0 + + YES + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 1 + + + YES + + + + + + 2 + + + YES + + + + + + + + + + + + + + 3 + + + YES + + + + + + 4 + + + + + 5 + + + YES + + + + + + 6 + + + + + 9 + + + YES + + + + + + 10 + + + + + 11 + + + YES + + + + + + 12 + + + + + 13 + + + YES + + + + + + 14 + + + + + 15 + + + YES + + + + + + 16 + + + + + 17 + + + YES + + + + + + 18 + + + + + 19 + + + YES + + + + + + 20 + + + YES + + + + + + 21 + + + YES + + + + + + + 22 + + + + + 23 + + + + + 25 + + + YES + + + + + + 26 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 1.IBPluginDependency + 1.IBWindowTemplateEditedContentRect + 1.NSWindowTemplate.visibleAtLaunch + 10.IBPluginDependency + 11.IBPluginDependency + 12.IBPluginDependency + 13.IBPluginDependency + 14.IBPluginDependency + 15.IBPluginDependency + 16.IBPluginDependency + 17.IBPluginDependency + 18.IBPluginDependency + 19.IBPluginDependency + 2.IBPluginDependency + 20.CustomClassName + 20.IBPluginDependency + 21.IBPluginDependency + 22.IBPluginDependency + 23.IBPluginDependency + 25.IBPluginDependency + 26.IBPluginDependency + 3.IBPluginDependency + 4.IBPluginDependency + 5.IBPluginDependency + 6.IBPluginDependency + 9.IBPluginDependency + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{357, 418}, {480, 270}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + HSSPopUpButtonCell + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + YES + + + + + + YES + + + + + 29 + + + + YES + + HSSCreateSubfolderWindowController + NSWindowController + + messageField + NSTextField + + + messageField + + messageField + NSTextField + + + + IBProjectSource + ./Classes/HSSCreateSubfolderWindowController.h + + + + HSSPopUpButtonCell + NSPopUpButtonCell + + IBProjectSource + ./Classes/HSSPopUpButtonCell.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + YES + + YES + NSMenuCheckmark + NSMenuMixedState + + + YES + {9, 8} + {7, 2} + + + + diff --git a/HSS/Resources/English.lproj/HSSExportWindow.xib b/HSS/Resources/English.lproj/HSSExportWindow.xib new file mode 100644 index 0000000..83fdc0b --- /dev/null +++ b/HSS/Resources/English.lproj/HSSExportWindow.xib @@ -0,0 +1,1913 @@ + + + + 1060 + 12C60 + 2843 + 1187.34 + 625.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2843 + + + NSArrayController + NSButton + NSButtonCell + NSCustomObject + NSCustomView + NSMatrix + NSOutlineView + NSProgressIndicator + NSScrollView + NSScroller + NSSplitView + NSTableColumn + NSTableHeaderView + NSTextField + NSTextFieldCell + NSTreeController + NSView + NSWindowTemplate + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + HSSExportWindowController + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{196, 240}, {500, 500}} + 1618477056 + Hidden Title Bar + NSWindow + + + {483, 500} + + + 256 + + + + 274 + + + + 256 + + + + 292 + {{-3, 67}, {89, 14}} + + + + _NS:3944 + YES + + 68157504 + 71435264 + Medcase name: + + LucidaGrande + 11 + 3100 + + _NS:3944 + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + NO + + + + 290 + {{91, 65}, {369, 19}} + + + + _NS:903 + YES + + -1804074943 + 272761856 + + + _NS:903 + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + + + NO + + + + 290 + {{91, 9}, {369, 48}} + + + + _NS:797 + YES + NO + 3 + 1 + + + -2080374784 + 131072 + All images (%d) + + + 1211912448 + 0 + + NSRadioButton + + + + 200 + 25 + + + 67108864 + 131072 + Key images (%d) + + + 1 + 1211912448 + 0 + + 549453824 + {18, 18} + + + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFugEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + + + + + 3 + MCAwAA + + + + 400 + 75 + + + 67108864 + 131072 + Current image + + + 2 + 1211912448 + 0 + + 12779520 + + + + + + regular + normal + + radiobutton + + {18, 18} + 0 + YES + NSCalibratedRGBColorSpace + + + + + + + 400 + 75 + + + {369, 16} + {0, 0} + 1151868928 + NSActionCell + + 67108864 + 131072 + Radio + + 1211912448 + 0 + + 549453824 + {18, 18} + + + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFugEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + + + + + + + 400 + 75 + + + + + + LucidaGrande + 13 + 1044 + + + + + 292 + {{-3, 43}, {89, 14}} + + + + _NS:3944 + YES + + 68157504 + 71435264 + Images to send: + + _NS:3944 + + + + + NO + + + + 301 + {{222, 160}, {16, 16}} + + + + _NS:3891 + 28938 + 100 + + + + 274 + + + + 2304 + + + + 256 + {458, 131} + + + + _NS:1703 + YES + NO + YES + + + 256 + {458, 17} + + + + _NS:1705 + + + + + -2147483392 + {{224, 0}, {16, 17}} + _NS:1709 + + + + name + 155.80859375 + 120 + 1000 + + 75497536 + 2048 + Name + + + 3 + MC4zMzMzMzI5ODU2AA + + + 6 + System + headerTextColor + + + + + 338690112 + 132160 + Text Cell + + + + 6 + System + controlBackgroundColor + + + + + 3 + YES + + + name + YES + caseDiacriticInsensitiveNumericCompare: + + + + description + 296 + 200 + 10000 + + 75497536 + 2048 + Description + + + + + + 338690112 + 132160 + Text Cell + + + + + + 3 + YES + + + desc.string + YES + caseDiacriticInsensitiveNumericCompare: + + + + 3 + 2 + + + 6 + System + gridColor + + 3 + MC41AA + + + 14 + -1019183104 + + + HSS Export Tree + 1 + 15 + 0 + YES + 0 + 1 + NO + 10 + + + {{1, 17}, {458, 131}} + + + + _NS:1701 + + + 4 + + + + -2147483392 + {{224, 17}, {15, 102}} + + + + _NS:1726 + NO + + _doScroller: + 1 + 0.98969072164948457 + + + + -2147483392 + {{1, 386}, {458, 15}} + + + + _NS:1728 + NO + 1 + + _doScroller: + 0.81639928698752229 + + + + 2304 + + + + {{1, 0}, {458, 17}} + + + + _NS:1706 + + + 4 + + + {{0, 89}, {460, 149}} + + + + _NS:1699 + 133682 + + + + + QSAAAEEgAABBgAAAQYAAAA + 0.25 + 4 + 1 + + + + 266 + {{-3, 238}, {466, 14}} + + + + _NS:3944 + YES + + 68157504 + 71435264 + Horizon Study Share + + _NS:3944 + + + + 6 + System + disabledControlTextColor + + 3 + MC4zMzMzMzMzMzMzAA + + + + NO + + + + 266 + {{-3, 238}, {466, 14}} + + + + _NS:3944 + YES + + 68157504 + 272761856 + Destination folders and Medcases: + + _NS:3944 + + + + + NO + + + {460, 253} + + + + _NS:1113 + NSView + + + + 256 + + + + 266 + {{-3, 76}, {466, 14}} + + + + _NS:3944 + YES + + 68157504 + 71435264 + (optional) + + _NS:3944 + + + + + NO + + + + 266 + {{-3, 76}, {466, 14}} + + + + _NS:3944 + YES + + 68157504 + 272761856 + Diagnosis: + + _NS:3944 + + + + + NO + + + + 274 + {{0, 10}, {460, 64}} + + + + _NS:354 + {250, 750} + YES + + -1805647871 + 272760832 + + + Empty + _NS:354 + + YES + + + + NO + + + {{0, 254}, {460, 95}} + + + + _NS:1116 + NSView + + + + 256 + + + + 266 + {{-3, 66}, {466, 14}} + + + + _NS:3944 + YES + + 68157504 + 71435264 + (optional) + + _NS:3944 + + + + + NO + + + + 266 + {{-3, 66}, {466, 14}} + + + + _NS:3944 + YES + + 68157504 + 272761856 + History: + + _NS:3944 + + + + + NO + + + + 274 + {460, 64} + + + + _NS:354 + {250, 750} + YES + + -1805647871 + 272760832 + + + Empty + _NS:354 + + YES + + + + NO + + + {{0, 350}, {460, 85}} + + + + _NS:1116 + NSView + + + {{20, 45}, {460, 435}} + + + + _NS:1111 + 2 + HSS Export Split + + + + 289 + {{403, 13}, {82, 28}} + + + + _NS:687 + YES + + 67108864 + 134348800 + Send + + _NS:687 + + -2038284288 + 129 + + DQ + 200 + 25 + + NO + + + + 289 + {{323, 13}, {82, 28}} + + + + _NS:687 + YES + + 67108864 + 134348800 + Cancel + + _NS:687 + + -2038022144 + 129 + + Gw + 200 + 25 + + NO + + + + 290 + {{17, 18}, {305, 18}} + + + + _NS:771 + YES + + -2080374784 + 131072 + When done, open the created medcase with HSS + + _NS:771 + + 1211912448 + 2 + + NSImage + NSSwitch + + + NSSwitch + + + + 200 + 25 + + NO + + + {500, 500} + + + + + {{0, 0}, {2560, 1418}} + {483, 522} + {10000000000000, 10000000000000} + HSS Window + YES + + + HSSItem + + YES + YES + YES + YES + + + HSSFolder + + YES + YES + arrangedObjects + isLeaf + + + + + + + window + + + + 12 + + + + sendAction: + + + + 13 + + + + cancelAction: + + + + 14 + + + + treeController + + + + 79 + + + + folder + + + + 94 + + + + diagnosisField + + + + 176 + + + + foldersOutline + + + + 177 + + + + historyField + + + + 178 + + + + imagesMatrix + + + + 179 + + + + openCheckbox + + + + 180 + + + + usernameField + + + + 181 + + + + nameField + + + + 192 + + + + progressIndicator + + + + 221 + + + + foldersOutlineScroll + + + + 225 + + + + sendButton + + + + 243 + + + + delegate + + + + 11 + + + + initialFirstResponder + + + + 193 + + + + nextKeyView + + + + 200 + + + + enabled: medcase.caseName + + + + + + enabled: medcase.caseName + enabled + medcase.caseName + + NSValueTransformerName + HSSIsNonEmptyString + + 2 + + + 207 + + + + nextKeyView + + + + 199 + + + + contentArray: arrangedObjects + + + + + + contentArray: arrangedObjects + contentArray + arrangedObjects + 2 + + + 241 + + + + nextKeyView + + + + 198 + + + + value: medcase.openFlag + + + + + + value: medcase.openFlag + value + medcase.openFlag + 2 + + + 213 + + + + delegate + + + + 175 + + + + nextKeyView + + + + 201 + + + + nextKeyView + + + + 195 + + + + content: arrangedObjects + + + + + + content: arrangedObjects + content + arrangedObjects + 2 + + + 144 + + + + selectionIndexPaths: selectionIndexPaths + + + + + + selectionIndexPaths: selectionIndexPaths + selectionIndexPaths + selectionIndexPaths + + 2 + + + 145 + + + + sortDescriptors: sortDescriptors + + + + + + sortDescriptors: sortDescriptors + sortDescriptors + sortDescriptors + + 2 + + + 146 + + + + delegate + + + + 149 + + + + value: arrangedObjects.desc + + + + + + value: arrangedObjects.desc + value + arrangedObjects.desc + + NSConditionallySetsEditable + + + 2 + + + 148 + + + + value: arrangedObjects.name + + + + + + value: arrangedObjects.name + value + arrangedObjects.name + + NSConditionallySetsEditable + + + 2 + + + 147 + + + + enabled: arrangedObjects.assignable + + + + + + enabled: arrangedObjects.assignable + enabled + arrangedObjects.assignable + 2 + + + 223 + + + + nextKeyView + + + + 196 + + + + value: medcase.diagnosis + + + + + + value: medcase.diagnosis + value + medcase.diagnosis + 2 + + + 209 + + + + nextKeyView + + + + 197 + + + + value: medcase.history + + + + + + value: medcase.history + value + medcase.history + 2 + + + 212 + + + + nextKeyView + + + + 194 + + + + value: medcase.caseName + + + + + + value: medcase.caseName + value + medcase.caseName + + NSContinuouslyUpdatesValue + + + 2 + + + 208 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 1 + + + + + + + + 2 + + + + + + + + + + + 7 + + + + + + + + 8 + + + + + 9 + + + + + + + + 10 + + + + + 15 + + + User Folders Tree + + + 85 + + + + + 107 + + + + + + + + 108 + + + + + 121 + + + + + + + + + + 122 + + + + + + + + + + + + + + + 123 + + + + + + + + + + 124 + + + + + + + + + + + 125 + + + + + + + + 126 + + + + + + + + 127 + + + + + + + + 128 + + + + + + + + + + + 129 + + + + + 130 + + + + + 131 + + + + + 132 + + + + + 133 + + + + + 134 + + + + + 135 + + + + + 136 + + + + + 137 + + + + + 138 + + + + + 139 + + + + + + + + + 140 + + + + + + + + 141 + + + + + + + + 142 + + + + + 143 + + + + + 152 + + + + + + + + 153 + + + + + + + + 154 + + + + + + + + 157 + + + + + 158 + + + + + 159 + + + + + 162 + + + + + + + + + + 164 + + + + + + + + 165 + + + + + + + + 166 + + + + + + + + 171 + + + + + 172 + + + + + 173 + + + + + 190 + + + + + + + + 191 + + + + + 184 + + + + + + + + 185 + + + + + 220 + + + Progress Indicator + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{357, 418}, {480, 270}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + HSS Export Window + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + HSS Export Outline + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + AccessibilityDescription + + AccessibilityDescription + + Progress Indicator + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + HSSFolder + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 243 + + + + + HSSExportWindowController + NSWindowController + + id + id + + + + cancelAction: + id + + + sendAction: + id + + + + NSTextField + HSSFolder + NSOutlineView + NSScrollView + NSTextField + NSMatrix + NSTextField + NSButton + NSProgressIndicator + NSButton + NSTreeController + NSTextField + + + + diagnosisField + NSTextField + + + folder + HSSFolder + + + foldersOutline + NSOutlineView + + + foldersOutlineScroll + NSScrollView + + + historyField + NSTextField + + + imagesMatrix + NSMatrix + + + nameField + NSTextField + + + openCheckbox + NSButton + + + progressIndicator + NSProgressIndicator + + + sendButton + NSButton + + + treeController + NSTreeController + + + usernameField + NSTextField + + + + IBProjectSource + ./Classes/HSSExportWindowController.h + + + + HSSFolder + HSSItem + + IBProjectSource + ./Classes/HSSFolder.h + + + + HSSItem + NSArrayController + + IBProjectSource + ./Classes/HSSItem.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + YES + 3 + + NSSwitch + {15, 15} + + + diff --git a/HSS/Resources/HSS.png b/HSS/Resources/HSS.png new file mode 100644 index 0000000..a1b7f3a Binary files /dev/null and b/HSS/Resources/HSS.png differ diff --git a/HSS/Sources/HSS.h b/HSS/Sources/HSS.h new file mode 100644 index 0000000..24c90f5 --- /dev/null +++ b/HSS/Sources/HSS.h @@ -0,0 +1,23 @@ +// +// HSS.h +// HSS +// +// Created by Alessandro Volz on 29.11.11. +// Copyright 2011 HUG. All rights reserved. +// + +#import +#import + +@interface HSS : PluginFilter { +} + +extern NSString* const HSSErrorDomain; + +- (long)filterImage:(NSString*)menuName; + ++ (NSURL*)baseURL; + ++ (Class)enterpriseClass; + +@end diff --git a/HSS/Sources/HSS.mm b/HSS/Sources/HSS.mm new file mode 100644 index 0000000..11a08e4 --- /dev/null +++ b/HSS/Sources/HSS.mm @@ -0,0 +1,278 @@ +// +// HSS.m +// HSS +// +// Created by Alessandro Volz on 29.11.11. +// Copyright 2011 HUG. All rights reserved. +// + +#import "HSS.h" +#import +#import +#import +#import +#import +//#import +//#import +#import +#import +#import +//#include +//#import "HSSAuthenticationWindowController.h" +#import "HSSExportWindowController.h" + + +@interface HSS () + +- (void)_initToolbarItems; +- (void)_processWithWindowController:(NSWindowController*)controller; + +@end + +@implementation HSS + +extern NSString* const HSSErrorDomain = @"HSSErrorDomain"; + +- (void)initPlugin { + [self _initToolbarItems]; +} + +- (long)filterImage:(NSString*)name { // jamais utilisé + NSWindowController* wc = [[NSApp keyWindow] windowController]; + if ([wc isKindOfClass:[NSWindowController class]]) + [self _processWithWindowController:wc]; + return 0; +} + ++ (NSURL*)baseURL { + NSString* str = [[[NSBundle bundleForClass:[self class]] infoDictionary] objectForKey:@"HSS base URL"]; + if (!str) return nil; + return [NSURL URLWithString:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; +} + ++ (Class)enterpriseClass { + static Class enterprise = nil; + if (!enterprise) { + enterprise = NSClassFromString(@"Enterprise"); + if (!enterprise) + enterprise = [NSNull class]; + } + + if (enterprise != [NSNull class]) + return enterprise; + + return nil; +} + +#pragma mark Toolbars + +HSS* HssInstance = nil; + +- (void)_initToolbarItems { + HssInstance = self; + + Method method; + IMP imp; + + // BrowserController + + Class BrowserControllerClass = NSClassFromString(@"BrowserController"); + + method = class_getInstanceMethod(BrowserControllerClass, @selector(toolbarAllowedItemIdentifiers:)); + imp = method_getImplementation(method); + class_addMethod(BrowserControllerClass, @selector(_HssBrowserToolbarAllowedItemIdentifiers:), imp, method_getTypeEncoding(method)); + method_setImplementation(method, class_getMethodImplementation([self class], @selector(_HssBrowserToolbarAllowedItemIdentifiers:))); + + method = class_getInstanceMethod(BrowserControllerClass, @selector(toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:)); + imp = method_getImplementation(method); + class_addMethod(BrowserControllerClass, @selector(_HssBrowserToolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:), imp, method_getTypeEncoding(method)); + method_setImplementation(method, class_getMethodImplementation([self class], @selector(_HssBrowserToolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:))); + + // ViewerController + + Class ViewerControllerClass = NSClassFromString(@"ViewerController"); + + method = class_getInstanceMethod(ViewerControllerClass, @selector(toolbarAllowedItemIdentifiers:)); + imp = method_getImplementation(method); + class_addMethod(ViewerControllerClass, @selector(_HssViewerToolbarAllowedItemIdentifiers:), imp, method_getTypeEncoding(method)); + method_setImplementation(method, class_getMethodImplementation([self class], @selector(_HssViewerToolbarAllowedItemIdentifiers:))); + + method = class_getInstanceMethod(ViewerControllerClass, @selector(toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:)); + imp = method_getImplementation(method); + class_addMethod(ViewerControllerClass, @selector(_HssViewerToolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:), imp, method_getTypeEncoding(method)); + method_setImplementation(method, class_getMethodImplementation([self class], @selector(_HssViewerToolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:))); +} + +NSString* HssToolbarItemIdentifier = @"HssToolbarItem"; + +- (NSArray*)_HssBrowserToolbarAllowedItemIdentifiers:(NSToolbar*)toolbar { + return [[self _HssBrowserToolbarAllowedItemIdentifiers:toolbar] arrayByAddingObject:HssToolbarItemIdentifier]; +} + +- (NSArray*)_HssViewerToolbarAllowedItemIdentifiers:(NSToolbar*)toolbar { + return [[self _HssViewerToolbarAllowedItemIdentifiers:toolbar] arrayByAddingObject:HssToolbarItemIdentifier]; +} + +- (NSToolbarItem*)_HssToolbar:(NSToolbar*)toolbar itemForItemIdentifier:(NSString*)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag { + static NSString* HssIconFilePath = [[[NSBundle bundleForClass:[HSS class]] pathForImageResource:@"HSS"] retain]; + + if ([itemIdentifier isEqualToString:HssToolbarItemIdentifier]) { + NSToolbarItem* item = [[[NSToolbarItem alloc] initWithItemIdentifier:HssToolbarItemIdentifier] autorelease]; + item.image = [[NSImage alloc] initWithContentsOfFile:HssIconFilePath]; + item.minSize = item.image.size; + item.label = item.paletteLabel = NSLocalizedString(@"HSS", @"Name of toolbar item"); + item.target = HssInstance; + item.action = @selector(_toolbarItemAction:); + return item; + } + + return nil; +} + +- (NSToolbarItem*)_HssViewerToolbar:(NSToolbar*)toolbar itemForItemIdentifier:(NSString*)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag { + NSToolbarItem* item = [HssInstance _HssToolbar:toolbar itemForItemIdentifier:itemIdentifier willBeInsertedIntoToolbar:flag]; + if (item) return item; + return [self _HssViewerToolbar:toolbar itemForItemIdentifier:itemIdentifier willBeInsertedIntoToolbar:flag]; +} + +- (NSToolbarItem*)_HssBrowserToolbar:(NSToolbar*)toolbar itemForItemIdentifier:(NSString*)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag { + NSToolbarItem* item = [HssInstance _HssToolbar:toolbar itemForItemIdentifier:itemIdentifier willBeInsertedIntoToolbar:flag]; + if (item) return item; + return [self _HssBrowserToolbar:toolbar itemForItemIdentifier:itemIdentifier willBeInsertedIntoToolbar:flag]; +} + +- (BOOL)validateToolbarItem:(NSToolbarItem*)item { + if ([item.toolbar.delegate isKindOfClass:[BrowserController class]]) + return [[(BrowserController*)item.toolbar.delegate databaseOutline] numberOfSelectedRows] >= 1; + return YES; +} + +- (void)_toolbarItemAction:(NSToolbarItem*)sender { + if ([sender.toolbar.delegate isKindOfClass:[NSWindowController class]]) + [self _processWithWindowController:(NSWindowController*)sender.toolbar.delegate]; + else NSLog(@"Warning: the toolbar delegate is not of type NSWindowController as expected, so the HSS plugin cannot proceed."); +} + +#pragma mark Processing + ++ (NSArray*)_uniqueObjectsInArray:(NSArray*)objects { + NSMutableArray* r = [NSMutableArray array]; + + for (id o in objects) + if (![r containsObject:o]) + [r addObject:o]; + + return r; +} + +- (void)_seriesIn:(id)obj into:(NSMutableArray*)collection { + if ([obj isKindOfClass:[DicomSeries class]]) + if (![collection containsObject:obj]) + [collection addObject:obj]; + + if ([obj isKindOfClass:[NSArray class]]) + for (id i in obj) + [self _seriesIn:i into:collection]; + + if ([obj isKindOfClass:[DicomStudy class]]) + [self _seriesIn:[[(DicomStudy*)obj series] allObjects] into:collection]; + + if ([obj isKindOfClass:[DicomImage class]]) + [self _seriesIn:[(DicomImage*)obj series] into:collection]; +} + +- (void)_processWithWindowController:(NSWindowController*)controller { + if (!controller) + controller = [BrowserController currentBrowser]; + + NSMutableArray* images = [NSMutableArray array]; + NSMutableArray* series = [NSMutableArray array]; + + if ([controller isKindOfClass:[ViewerController class]]) { + ViewerController* vc = (ViewerController*)controller; + [images addObject:vc.currentImage]; + [series addObject:vc.currentSeries]; + } + + if ([controller isKindOfClass:[BrowserController class]]) { + BrowserController* bc = (BrowserController*)controller; + + NSArray* selection = nil; + id responder = bc.window.firstResponder; + if (responder == bc.databaseOutline) + selection = [(BrowserController*)controller databaseSelection]; + if (responder == bc.oMatrix) { + NSMutableArray* s = [NSMutableArray array]; + for (NSCell* cell in [responder selectedCells]) + [s addObject:[bc.matrixViewArray objectAtIndex:[cell tag]]]; + selection = s; + } + + for (id obj in selection) + if ([obj isKindOfClass:[DicomImage class]]) + [images addObject:obj]; + else if ([obj isKindOfClass:[DicomSeries class]]) + [series addObject:obj]; + + [self _seriesIn:selection into:series]; + } + + // remove OsiriX private series + + for (NSUInteger i = 0; i < series.count; ++i) { + DicomSeries* s = [series objectAtIndex:i]; + if (![DicomStudy displaySeriesWithSOPClassUID:s.seriesSOPClassUID andSeriesDescription:s.name]) + [series removeObjectAtIndex:i--]; + } + + // is selection valid? + + if (series.count < 1 && images.count < 1) { + NSBeginAlertSheet(nil, nil, nil, nil, controller.window, nil, nil, nil, nil, NSLocalizedString(@"The current selection is invalid.", nil)); + return; + } + + // oook.... + +// Class enterprise = [HSS enterpriseClass]; +// NSString* enterpriseName = nil; +// if ([enterprise respondsToSelector:@selector(Name)]) +// enterpriseName = [enterprise performSelector:@selector(Name)]; + + HSSExportWindowController* hex = [[HSSExportWindowController alloc] initWithSeries:series images:images]; + [hex beginSheetOnWindow:controller.window]; + +// // TODO: set up window etc etc +// NSSavePanel* panel = nil; +// +// [panel beginSheetModalForWindow:controller.window completionHandler:^(NSInteger result) { +// [panel orderOut:self]; +// +// if (!result) +// return; +// +// NSThread* thread = [[[NSThread alloc] initWithTarget:self selector:@selector(_processInBackground:) object:[NSArray arrayWithObjects: nil]] autorelease]; +// thread.name = [NSString stringWithFormat:NSLocalizedString(@"Posting %d %@...", nil), 123, (series.count == 1 ? NSLocalizedString(@"image", @"singular") : NSLocalizedString(@"images", @"plural"))]; +// [thread startModalForWindow:controller.window]; +// [thread start]; +// }]; +} + +//- (void)_processInBackground:(NSArray*)params { +// NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; +// @try { +// NSThread* thread = [NSThread currentThread]; +// +// +// +// thread.status = @"Done."; +// thread.progress = -1; +// } @catch (NSException* e) { +// NSLog(@"HSS exception: %@", e.reason); +// } @finally { +// [pool release]; +// } +//} + +@end + diff --git a/HSS/Sources/HSSAPI.h b/HSS/Sources/HSSAPI.h new file mode 100644 index 0000000..d692ead --- /dev/null +++ b/HSS/Sources/HSSAPI.h @@ -0,0 +1,43 @@ +// +// HSSAPI.h +// HSS +// +// Created by Alessandro Volz on 04.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import + +@class HSSAPISession; + +@interface HSSAPI : NSObject { + NSURL* _url; + NSTimeInterval _timeoutInterval; + BOOL _acceptAllCertificates; +} + +@property NSTimeInterval timeoutInterval; +@property BOOL acceptAllCertificates; + ++ (HSSAPI*)defaultAPI; + +- (id)initWithBaseURL:(NSURL*)url; + +//extern NSString* const HSSAPIPOSTFilePathKey; + +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint parameters:(NSDictionary*)params error:(NSError**)error; +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint cookies:(NSDictionary*)cookies parameters:(NSDictionary*)params error:(NSError**)error; +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint cookies:(NSDictionary*)cookies parameters:(NSDictionary*)params progressDelegate:(id)delegate error:(NSError**)error; +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint cookies:(NSDictionary*)cookies parameters:(NSDictionary*)params timeout:(NSTimeInterval)timeout error:(NSError**)error; +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint cookies:(NSDictionary*)cookies parameters:(NSDictionary*)params timeout:(NSTimeInterval)timeout progressDelegate:(id)progressDelegate error:(NSError**)error; + +- (HSSAPISession*)newSessionWithLogin:(NSString*)login password:(NSString*)password timeout:(NSTimeInterval)timeout error:(NSError**)error; + +@end + +@interface NSObject (HSS) + +- (id)valueForKeyPath:(NSString*)keyPath ofClass:(Class)c; +- (NSString*)stringForKeyPath:(NSString*)keyPath; + +@end \ No newline at end of file diff --git a/HSS/Sources/HSSAPI.mm b/HSS/Sources/HSSAPI.mm new file mode 100644 index 0000000..0c845f6 --- /dev/null +++ b/HSS/Sources/HSSAPI.mm @@ -0,0 +1,320 @@ +// +// HSSAPI.m +// HSS +// +// Created by Alessandro Volz on 04.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSAPI.h" +#import "HSS.h" +#import "HSSAPISession.h" +#import +#import +#import +#import + +@interface NSURLRequest () + ++ (void)setAllowsAnyHTTPSCertificate:(BOOL)flag forHost:(NSString*)host; + +@end + +/*@interface _HSSConnector : NSObject { + NSConditionLock* _lock; + HSSAPI* _api; + NSMutableData* _data; + NSError* _err; +} + ++ (NSData*)dataWithAPI:(HSSAPI*)api request:(NSURLRequest*)request; + +@end*/ + +@interface HSSAPIURLConnectionDelegate : NSObject { + id _delegate; + NSMutableData* _data; + NSHTTPURLResponse* _response; + BOOL _done; + NSError* _error; +} + +@property(readonly) NSData* data; +@property(readonly) NSHTTPURLResponse* response; +@property(readonly) BOOL done; +@property(readonly) NSError* error; + +- (id)initWithProgressDelegate:(id)delegate; + +@end + +@implementation HSSAPI + +@synthesize timeoutInterval = _timeoutInterval; +@synthesize acceptAllCertificates = _acceptAllCertificates; + ++ (void)load { + Method originalMethod = class_getClassMethod([NSURLRequest class], @selector(allowsAnyHTTPSCertificateForHost:)); + Method hssapiMethod = class_getClassMethod([self class], @selector(allowsAnyHTTPSCertificateForHost:)); + method_exchangeImplementations(originalMethod, hssapiMethod); +} + +static NSMutableArray* HostsAllowedAnyHTTPSCertificate = [[NSMutableArray alloc] init]; + ++ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host { + @synchronized (HostsAllowedAnyHTTPSCertificate) { + if ([HostsAllowedAnyHTTPSCertificate containsObject:host]) + return YES; + } + + return [HSSAPI allowsAnyHTTPSCertificateForHost:host]; // this actually calls [NSURLRequest allowsAnyHTTPSCertificateForHost:] +} + ++ (HSSAPI*)defaultAPI { + static HSSAPI* api = [[HSSAPI alloc] initWithBaseURL:HSS.baseURL]; + return api; +} + +- (id)initWithBaseURL:(NSURL*)url { + if ((self = [super init])) { + _url = [[url URLByAppendingPathComponent:@"hss/api"] retain]; + _timeoutInterval = 60; + _acceptAllCertificates = [[[[NSBundle bundleForClass:[self class]] infoDictionary] objectForKey:@"Accept all HTTPS certificates"] boolValue]; + } + + return self; +} + +- (void)dealloc { + [_url release]; + [super dealloc]; +} + +//NSString* const HSSAPIPOSTFilePathKey = @"HSSAPIPOSTFilePath"; + +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint parameters:(NSDictionary*)params error:(NSError**)error { + return [self requestWithMethod:method endpoint:endpoint cookies:nil parameters:params timeout:_timeoutInterval progressDelegate:nil error:error]; +} + +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint cookies:(NSDictionary*)cookies parameters:(NSDictionary*)params error:(NSError**)error { + return [self requestWithMethod:method endpoint:endpoint cookies:cookies parameters:params timeout:_timeoutInterval progressDelegate:nil error:error]; +} + +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint cookies:(NSDictionary*)cookies parameters:(NSDictionary*)params progressDelegate:(id)delegate error:(NSError**)error { + return [self requestWithMethod:method endpoint:endpoint cookies:cookies parameters:params timeout:_timeoutInterval progressDelegate:delegate error:error]; +} + +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint cookies:(NSDictionary*)cookies parameters:(NSDictionary*)params timeout:(NSTimeInterval)timeout error:(NSError**)error { + return [self requestWithMethod:method endpoint:endpoint cookies:cookies parameters:params timeout:timeout progressDelegate:nil error:error]; +} + ++ (NSString*)boundaryForParts:(NSArray*)parts { + NSString* boundary = @"Boundary"; + + while (true) { + BOOL boundaryOk = YES; + NSString* bb = [NSString stringWithFormat:@"--%@", boundary]; + NSData* bbd = [bb dataUsingEncoding:NSUTF8StringEncoding]; + for (NSData* part in parts) + if ([part rangeOfData:bbd options:0 range:NSMakeRange(0, part.length)].location != NSNotFound) { + boundaryOk = NO; + break; + } + + if (boundaryOk) + break; + + NSData* randomData = [[NSString stringWithFormat:@"%ld", random()] dataUsingEncoding:NSUTF8StringEncoding]; + boundary = [[randomData md5] hex]; + } + + return boundary; +} + +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint cookies:(NSDictionary*)cookies parameters:(NSDictionary*)params timeout:(NSTimeInterval)timeout progressDelegate:(id)delegate error:(NSError**)error { + if (error) *error = nil; + + if ([NSThread isMainThread] && delegate) { + NSLog(@"Warning: HSSAPI progress delegate is only supported in background threads, ignored."); + delegate = nil; + } + + NSURL* url = [_url URLByAppendingPathComponent:endpoint]; + + NSMutableURLRequest* request = [[[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:timeout] autorelease]; + request.HTTPMethod = method; + + if (cookies.count) { + NSMutableArray* cookiesArray = [NSMutableArray array]; + for (NSString* key in cookies) + [cookiesArray addObject:[NSHTTPCookie cookieWithProperties:[NSDictionary dictionaryWithObjectsAndKeys: + url.host, NSHTTPCookieDomain, + @"/", NSHTTPCookiePath, + key, NSHTTPCookieName, + [cookies objectForKey:key], NSHTTPCookieValue, nil]]]; + [request setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:cookiesArray]]; + } + + if (params.count) { + BOOL multipartFormData = NO; + for (NSString* key in params) + if ([[params objectForKey:key] isKindOfClass:[NSArray class]]) + multipartFormData = YES; + + if (!multipartFormData) { + NSMutableArray* paramsArray = [NSMutableArray array]; + for (NSString* key in params) + [paramsArray addObject:[NSString stringWithFormat:@"%@=%@", key, [[params objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; + request.HTTPBody = [[paramsArray componentsJoinedByString:@";"] dataUsingEncoding:NSUTF8StringEncoding]; + } else { + NSMutableArray* parts = [NSMutableArray array]; + for (NSString* key in params) { + id value = [params objectForKey:key]; + NSMutableString* part = [NSMutableString string]; + if ([value isKindOfClass:[NSArray class]]) { + NSString* filename = [value objectAtIndex:0]; + NSData* data = [value objectAtIndex:1]; + [part appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, filename]; + [part appendFormat:@"Content-Type: application/octet-stream\r\n\r\n"]; + // [part appendString:@"Content-Transfer-Encoding: base64\r\n\r\n"]; + NSMutableData* dpart = [NSMutableData dataWithData:[part dataUsingEncoding:NSUTF8StringEncoding]]; + [dpart appendData:data]; + [parts addObject:dpart]; + } else { + [part appendFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key]; + [part appendString:value]; + [parts addObject:[part dataUsingEncoding:NSUTF8StringEncoding]]; + } + } + + NSString* boundary = [[self class] boundaryForParts:parts]; + + NSMutableData* body = [NSMutableData data]; + for (NSData* part in parts) { + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:part]; + } + [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + + request.HTTPBody = body; + [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"]; + } + } + + if (_acceptAllCertificates) + @synchronized (HostsAllowedAnyHTTPSCertificate) { + [HostsAllowedAnyHTTPSCertificate addObject:url.host]; + } + NSData* data = nil; + NSHTTPURLResponse* response; + @try { + if (delegate) { + HSSAPIURLConnectionDelegate* ucd = [[[HSSAPIURLConnectionDelegate alloc] initWithProgressDelegate:delegate] autorelease]; + [NSURLConnection connectionWithRequest:request delegate:ucd]; + while (!ucd.done) + [NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]; + response = ucd.response; + data = ucd.data; + } else { + data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error]; + } + } @catch (NSException* e) { + if (error) *error = [NSError errorWithDomain:HSSErrorDomain code:-1 userInfo:[NSDictionary dictionaryWithObject:e.reason forKey:NSLocalizedDescriptionKey]]; + } @finally { + if (_acceptAllCertificates) + @synchronized (HostsAllowedAnyHTTPSCertificate) { + [HostsAllowedAnyHTTPSCertificate removeObjectAtIndex:[HostsAllowedAnyHTTPSCertificate indexOfObject:url.host]]; // don't use removeObject: because that method removes ALL occurrences of the object + } + } + + id r = nil; + + if (data.length) { + NSString* s = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; +// NSLog(@"response: %@", string); + r = [s JSONValue]; +// NSLog(@"HSS API Response for %@ is %@", endpoint, response); + } + + if (response.statusCode < 200 || response.statusCode >= 300) { + if (error && [r isKindOfClass:[NSDictionary class]] && [[r objectForKey:@"error"] isKindOfClass:[NSString class]]) + *error = [NSError errorWithDomain:HSSErrorDomain code:response.statusCode userInfo:[NSDictionary dictionaryWithObject:[r objectForKey:@"error"] forKey:NSLocalizedDescriptionKey]]; + if (error && !*error) + *error = [NSError errorWithDomain:HSSErrorDomain code:response.statusCode userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat: r? [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] : NSLocalizedString(@"HTTP error: %@", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode]] forKey:NSLocalizedDescriptionKey]]; + return nil; + } + + return r; +} + +- (HSSAPISession*)newSessionWithLogin:(NSString*)login password:(NSString*)password timeout:(NSTimeInterval)timeout error:(NSError**)error { + HSSAPISession* session = [[[HSSAPISession alloc] initWithAPI:self login:login password:password] autorelease]; + BOOL ok = [session openWithTimeout:timeout error:error]; + return ok? session : nil; +} + +@end + +@implementation HSSAPIURLConnectionDelegate + +@synthesize data = _data; +@synthesize response = _response; +@synthesize done = _done; +@synthesize error = _error; + +- (id)initWithProgressDelegate:(id)delegate { + if ((self = [super init])) { + _delegate = [delegate retain]; + _data = [[NSMutableData alloc] init]; + } + + return self; +} + +- (void)dealloc { + [_error release]; + [_data release]; + [_response release]; + [_delegate release]; + [super dealloc]; +} + +- (void)connection:(NSURLConnection*)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { + if (_delegate) + [_delegate performSelector:@selector(HSSAPIProgress:) withObject:[NSNumber numberWithFloat:float(totalBytesWritten)/totalBytesExpectedToWrite]]; +} + +- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSHTTPURLResponse*)response { + _response = [response retain]; +} + +- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data { + [_data appendData:data]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection*)connection { + _done = YES; +} + +- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error { + _error = [error retain]; + _done = YES; +} + +@end + +@implementation NSObject (HSS) + +- (id)valueForKeyPath:(NSString*)keyPath ofClass:(Class)c { + id value = [self valueForKeyPath:keyPath]; + if ([value isKindOfClass:c]) + return value; + return nil; +} + +- (NSString*)stringForKeyPath:(NSString*)keyPath { + return [self valueForKeyPath:keyPath ofClass:[NSString class]]; +} + +@end + diff --git a/HSS/Sources/HSSAPISession.h b/HSS/Sources/HSSAPISession.h new file mode 100644 index 0000000..f0db579 --- /dev/null +++ b/HSS/Sources/HSSAPISession.h @@ -0,0 +1,48 @@ +// +// HSSAPISession.h +// HSS +// +// Created by Alessandro Volz on 05.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import + +@class HSSAPI; + +@interface HSSAPISession : NSObject { + HSSAPI* _api; + NSString* _apiSessionId; + + NSString* _userLogin; + NSString* _userPassword; + NSString* _userHomeFolderOid; + NSString* _userOid; + NSString* _userName; +} + +@property(readonly,retain) HSSAPI* api; +@property(readonly,retain) NSString* apiSessionId; + +@property(readonly,retain) NSString* userLogin; +@property(readonly,retain) NSString* userPassword; +@property(readonly,retain) NSString* userHomeFolderOid; +@property(readonly,retain) NSString* userOid; +@property(readonly,retain) NSString* userName; + +- (id)initWithAPI:(HSSAPI*)api login:(NSString*)login password:(NSString*)password; + +- (BOOL)openWithTimeout:(NSTimeInterval)timeout error:(NSError**)error; + +- (NSArray*)getHomeFolderTreeWithError:(NSError**)error; +- (NSDictionary*)getFolderWithOid:(NSString*)folderOid error:(NSError**)error; +- (NSDictionary*)getFolderTreeWithOid:(NSString*)folderOid error:(NSError**)error; + +- (NSDictionary*)getMedcaseWithOid:(NSString*)oid error:(NSError**)error; + +- (NSArray*)getMedcasesRelatedToPatientId:(NSString*)patientId error:(NSError**)error; + +- (NSDictionary*)postMedcaseWithZipFileAtPath:(NSString*)zipFilePath folderOid:(NSString*)folderOid progressDelegate:(id)delegate error:(NSError**)error; +- (NSDictionary*)putFileAtPath:(NSString*)filePath intoMedcaseWithOid:(NSString*)medcaseOid error:(NSError**)error; + +@end diff --git a/HSS/Sources/HSSAPISession.m b/HSS/Sources/HSSAPISession.m new file mode 100644 index 0000000..82c6ed8 --- /dev/null +++ b/HSS/Sources/HSSAPISession.m @@ -0,0 +1,208 @@ +// +// HSSAPISession.m +// HSS +// +// Created by Alessandro Volz on 05.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSS.h" +#import "HSSAPISession.h" +#import "HSSAPI.h" +#import + +@interface HSSAPISession () + +@property(readwrite,retain) HSSAPI* api; +@property(readwrite,retain) NSString* apiSessionId; + +@property(readwrite,retain) NSString* userLogin; +@property(readwrite,retain) NSString* userPassword; +@property(readwrite,retain) NSString* userHomeFolderOid; +@property(readwrite,retain) NSString* userOid; +@property(readwrite,retain) NSString* userName; + +@end + +@implementation HSSAPISession + +@synthesize api = _api; +@synthesize apiSessionId = _apiSessionId; +@synthesize userLogin = _userLogin; +@synthesize userPassword = _userPassword; +@synthesize userHomeFolderOid = _userHomeFolderOid; +@synthesize userOid = _userOid; +@synthesize userName = _userName; + +- (id)initWithAPI:(HSSAPI*)api login:(NSString*)login password:(NSString*)password { + if ((self = [super init])) { + self.api = api; + self.userLogin = login; + self.userPassword = password; + } + + return self; +} + +- (void)dealloc { + self.api = nil; + + self.apiSessionId = nil; + self.userHomeFolderOid = nil; + self.userOid = nil; + + self.userLogin = nil; + self.userPassword = nil; + [super dealloc]; +} + +- (BOOL)openWithTimeout:(NSTimeInterval)timeout error:(NSError**)error { + NSError* localError; + if (!error) error = &localError; + + NSDictionary* response = [self.api requestWithMethod:@"POST" + endpoint:@"session" + cookies:nil + parameters:[NSDictionary dictionaryWithObjectsAndKeys: + self.userLogin, @"login", + self.userPassword, @"password", + @"1", @"fixed", nil] + timeout:timeout + error:error]; + + if (*error || !response) + return NO; + + self.userHomeFolderOid = [response stringForKeyPath:@"user.home_folder.oid"]; + self.userOid = [response stringForKeyPath:@"user.oid"]; + self.userName = [NSString stringWithFormat:@"%@ %@", [response stringForKeyPath:@"user.first_name"], [response stringForKeyPath:@"user.last_name"]]; + NSString* userLogin = [response stringForKeyPath:@"user.login"]; + if (userLogin) self.userLogin = userLogin; + + self.apiSessionId = [response stringForKeyPath:@"api_session.id"]; + if (self.apiSessionId) + return YES; + + NSString* message = [response stringForKeyPath:@"message"]; + if (message) + *error = [NSError errorWithDomain:HSSErrorDomain code:-1 userInfo:[NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]]; + else *error = [NSError errorWithDomain:HSSErrorDomain code:-1 userInfo:[NSDictionary dictionaryWithObject:NSLocalizedString(@"Unexpected HSS API response", nil) forKey:NSLocalizedDescriptionKey]]; + + return NO; +} + +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint parameters:(NSDictionary*)params error:(NSError**)error { + return [self.api requestWithMethod:method + endpoint:endpoint + cookies:[NSDictionary dictionaryWithObject:self.apiSessionId forKey:@"studyshare_session"] + parameters:params + error:error]; +} + +- (id)requestWithMethod:(NSString*)method endpoint:(NSString*)endpoint parameters:(NSDictionary*)params timeout:(NSTimeInterval)timeout progressDelegate:(id)delegate error:(NSError**)error { + return [self.api requestWithMethod:method + endpoint:endpoint + cookies:[NSDictionary dictionaryWithObject:self.apiSessionId forKey:@"studyshare_session"] + parameters:params + timeout:timeout + progressDelegate:delegate + error:error]; +} + +/*- (NSArray*)medcasesWithError:(NSError**)error { + + NSDictionary* response = [self requestWithMethod:@"" + endpoint:@"medcase" + parameters:nil + error:error]; + + + NSLog(@"Medcases: %@", response); + + return nil; +}*/ + +- (id)_treatResponseErrors:(id)response error:(NSError**)error { + if (![response isKindOfClass:[NSDictionary class]]) + return response; + + NSString* errorString = [response stringForKeyPath:@"error"]; + if (errorString) { + if (error) + *error = [NSError errorWithDomain:HSSErrorDomain code:-1 userInfo:[NSDictionary dictionaryWithObject:errorString forKey:NSLocalizedDescriptionKey]]; + return nil; + } + + return response; +} + +- (NSArray*)getHomeFolderTreeWithError:(NSError**)error { + //return [self getFolderTreeWithOid:_userHomeFolderOid error:error]; // a dictionary + NSDictionary* response = [self requestWithMethod:@"GET" + endpoint:[NSString stringWithFormat:@"user/%@/folder/tree", _userOid] + parameters:nil + error:error]; + response = [self _treatResponseErrors:response error:error]; + return response? [response objectForKey:@"folders"] : nil; +} + +- (NSDictionary*)getFolderWithOid:(NSString*)oid error:(NSError**)error { + NSDictionary* response = [self requestWithMethod:@"GET" + endpoint:[NSString stringWithFormat:@"folder/%@", oid] + parameters:nil + error:error]; + return [self _treatResponseErrors:response error:error]; +} + +- (NSDictionary*)getFolderTreeWithOid:(NSString*)oid error:(NSError**)error { + NSArray* response = [self requestWithMethod:@"GET" + endpoint:[NSString stringWithFormat:@"folder/%@/tree", oid] + parameters:nil + error:error]; + response = [self _treatResponseErrors:response error:error]; + return response? [response objectAtIndex:0] : nil; +} + +- (NSDictionary*)getMedcaseWithOid:(NSString*)oid error:(NSError**)error { + NSDictionary* response = [self requestWithMethod:@"GET" + endpoint:[NSString stringWithFormat:@"medcase/%@", oid] + parameters:nil + error:error]; + return [self _treatResponseErrors:response error:error]; +} + +- (NSArray*)getMedcasesRelatedToPatientId:(NSString*)patientId error:(NSError**)error { + NSArray* response = [self requestWithMethod:@"POST" + endpoint:@"medcase/related" + parameters:[NSDictionary dictionaryWithObject:patientId forKey:@"patient_id"] + timeout:(self.api.timeoutInterval > 400 ? self.api.timeoutInterval : 400) // we're doing this because HSS is currently extremely slow at returning this information, depending on the patient + progressDelegate:nil + error:error]; + return [self _treatResponseErrors:response error:error]; +} + +- (NSDictionary*)postMedcaseWithZipFileAtPath:(NSString*)zipFilePath folderOid:(NSString*)folderOid progressDelegate:(id)delegate error:(NSError**)error { + NSMutableDictionary* parameters = [NSMutableDictionary dictionaryWithObject:[NSArray arrayWithObjects: @"mirc.zip", [NSData dataWithContentsOfFile:zipFilePath options:NSDataReadingUncached error:error], nil] forKey:@"mirc"]; + if (folderOid.length) + [parameters setObject:folderOid forKey:@"folder"]; + + NSDictionary* response = [self requestWithMethod:@"POST" + endpoint:@"medcase" + parameters:parameters + timeout:MAXFLOAT + progressDelegate:delegate + error:error]; + return [self _treatResponseErrors:response error:error]; +} + +- (NSDictionary*)putFileAtPath:(NSString*)filePath intoMedcaseWithOid:(NSString*)oid error:(NSError**)error { + NSDictionary* response = [self requestWithMethod:@"PUT" + endpoint:[NSString stringWithFormat:@"medcase/%@", oid] + parameters:[NSMutableDictionary dictionaryWithObject:[NSArray arrayWithObjects: @"image", [NSData dataWithContentsOfFile:filePath options:NSDataReadingUncached error:error], nil] forKey:@"image"] + timeout:MAXFLOAT + progressDelegate:nil + error:error]; + return [self _treatResponseErrors:response error:error]; +} + +@end diff --git a/HSS/Sources/HSSAuthenticationWindowController.h b/HSS/Sources/HSSAuthenticationWindowController.h new file mode 100644 index 0000000..6074b26 --- /dev/null +++ b/HSS/Sources/HSSAuthenticationWindowController.h @@ -0,0 +1,44 @@ +// +// AuthenticationWindowController.h +// HUG Framework +// +// Created by Alessandro Volz on 26.05.11. +// Copyright 2011 OsiriX Team. All rights reserved. +// + +#import + +@class HSSAPISession; + +@interface HSSAuthenticationWindowController : NSWindowController { + NSTextField* _infoLabel; + NSTextView* _messageLabel; + NSButton* _okButton; + NSTextField* _loginField; + NSTextField* _passwordField; + NSTimer* _timer; + NSLock* _timerLock; + HSSAPISession* _session; + CGFloat _previousMessageHeight; + NSString* _lastEnterpriseUsername; +} + +@property(assign) IBOutlet NSTextField* infoLabel; +@property(assign) IBOutlet NSTextView* messageLabel; +@property(assign) IBOutlet NSButton* okButton; +@property(assign) IBOutlet NSTextField* loginField; +@property(assign) IBOutlet NSTextField* passwordField; + +@property(retain,readonly) HSSAPISession* session; +@property(retain,readonly) NSString* lastEnterpriseUsername; + +- (void)beginSheetOnWindow:(NSWindow*)parentWindow callbackTarget:(id)target selector:(SEL)sel context:(void*)context; + +- (IBAction)cancelAction:(id)sender; +- (IBAction)okAction:(id)sender; + +- (IBAction)updateOkButton:(id)sender; + +- (void)invalidate; + +@end diff --git a/HSS/Sources/HSSAuthenticationWindowController.mm b/HSS/Sources/HSSAuthenticationWindowController.mm new file mode 100644 index 0000000..01f2e96 --- /dev/null +++ b/HSS/Sources/HSSAuthenticationWindowController.mm @@ -0,0 +1,235 @@ +// +// AuthenticationWindowController.mm +// HUG Framework +// +// Created by Alessandro Volz on 26.05.11. +// Copyright 2011 OsiriX Team. All rights reserved. +// + +#import "HSSAuthenticationWindowController.h" +#import "HSS.h" +//#import "HUGSOAPWebServiceClient.h" +//#import "Utils.h" +//#import "HUG.h" +#import +#import +#import "HSSAPI.h" + +@interface HSSAuthenticationWindowController () + +@property(retain,readwrite) HSSAPISession* session; +@property(retain,readwrite) NSString* lastEnterpriseUsername; + +- (void)_timerCallback:(NSTimer*)timer; + +@end + +@implementation HSSAuthenticationWindowController + +@synthesize infoLabel = _infoLabel; +@synthesize messageLabel = _messageLabel; +@synthesize okButton = _okButton; +@synthesize loginField = _loginField; +@synthesize passwordField = _passwordField; + +@synthesize session = _session; +@synthesize lastEnterpriseUsername = _lastEnterpriseUsername; + + +- (id)init { + if ((self = [super initWithWindowNibName:@"HSSAuthenticationWindow"])) { + _timerLock = [[NSLock alloc] init]; + _previousMessageHeight = -7; + //[HUG checkMasterUserAgain]; + [self _timerCallback:nil]; + _timer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(_timerCallback:) userInfo:nil repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; + } + return self; +} + +- (void)awakeFromNib { +// _infoLabel.stringValue = MessageInsererCarte; + _loginField.stringValue = @""; + _passwordField.stringValue = @""; + [_messageLabel setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + [_okButton setEnabled:NO]; + [self.window center]; +} + +- (void)dealloc { +// NSLog(@"-[AuthenticationWindowController dealloc]"); + + NSLock* lock = _timerLock; + [lock lock]; + _timerLock = nil; + [lock unlock]; + [lock release]; + + self.session = nil; + self.lastEnterpriseUsername = nil; + + [super dealloc]; +} + +- (void)_timerCallback:(NSTimer*)timer { + if ([_timerLock tryLock]) { + [self performSelectorInBackground:@selector(_getUsernameThread) withObject:nil]; + [_timerLock unlock]; + } +} + +- (void)_getUsernameThread { + NSAutoreleasePool* pool = [NSAutoreleasePool new]; + [_timerLock lock]; + @try { + Class enterprise = [HSS enterpriseClass]; + NSString* enterpriseUsername = nil; + if ([enterprise respondsToSelector:@selector(Username)]) + enterpriseUsername = [enterprise performSelector:@selector(Username)]; + + [self performSelectorOnMainThread:@selector(_getUsernameResult:) withObject:enterpriseUsername waitUntilDone:NO]; + } @catch (NSException* e) { + N2LogExceptionWithStackTrace(e); + } @finally { + [_timerLock unlock]; + [pool release]; + } +} + +- (void)_getUsernameResult:(NSString*)enterpriseUsername { + BOOL sameAsLast = _lastEnterpriseUsername == enterpriseUsername || [_lastEnterpriseUsername isEqualToString:enterpriseUsername]; + self.lastEnterpriseUsername = enterpriseUsername; + if (enterpriseUsername.length) { + if (!sameAsLast) { + _loginField.stringValue = enterpriseUsername; + // if (_loginField.isEnabled) + // [_loginField setEnabled:NO]; + [_passwordField selectText:self]; + + Class enterprise = [HSS enterpriseClass]; + NSString* storedPassword = nil; + if ([enterprise respondsToSelector:@selector(StoredPasswordForUsername:)]) + storedPassword = [enterprise performSelector:@selector(StoredPasswordForUsername:) withObject:enterpriseUsername]; + if (storedPassword) + _passwordField.stringValue = storedPassword; + } + } else { +// if (!_loginField.isEnabled) +// [_loginField setEnabled:YES]; + if (!sameAsLast) + [_loginField selectText:self]; + } + + [self updateOkButton:self]; +} + +- (void)updateOkButton:(id)sender { + [_okButton setEnabled: _loginField.stringValue.length && _passwordField.stringValue.length]; +} + +- (void)invalidate { +// NSLog(@"-[AuthenticationWindowController invalidate]"); + + [_timer invalidate]; + _timer = nil; + + //[self.window orderOut:self]; + [self.window close]; + [self.window autorelease]; + [self autorelease]; +} + +- (void)beginSheetOnWindow:(NSWindow*)parentWindow callbackTarget:(id)target selector:(SEL)sel context:(void*)context { + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[HSSAuthenticationWindowController instanceMethodSignatureForSelector:@selector(_dummySheetCallbackWithSession:contextInfo:)]]; + invocation.selector = sel; + invocation.target = target; + [invocation setArgument:&context atIndex:3]; + + [self.window retain]; [self retain]; // release in [invalidate] + + [NSApp beginSheet:self.window modalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(_sheetDidEnd:returnCode:contextInfo:) contextInfo:[[NSArray alloc] initWithObjects: invocation, NULL]]; + [self.window makeKeyAndOrderFront:self]; +} + +- (void)_sheetDidEnd:(NSWindow*)sheet returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo { + NSArray* context = [(NSArray*)contextInfo autorelease]; + NSInvocation* invocation = [context objectAtIndex:0]; + + HSSAPISession* session = nil; + if (returnCode == NSRunStoppedResponse) + session = [[self.session retain] autorelease]; + + [self invalidate]; + + [invocation setArgument:&session atIndex:2]; + [invocation invoke]; +} + +- (void)_dummySheetCallbackWithSession:(HSSAPISession*)session contextInfo:(void*)contextInfo { + // nothing +} + +- (IBAction)cancelAction:(id)sender { + // stop sheet + [NSApp endSheet:self.window returnCode:NSRunAbortedResponse]; +} + +- (void)setMessage:(NSString*)message { + _messageLabel.string = message; + + NSSize oldSize = _messageLabel.frame.size; + [_messageLabel adaptToContent:oldSize.width]; + NSSize size = _messageLabel.frame.size; + size.width = oldSize.width; + [_messageLabel setFrameSize:size]; + + CGFloat delta = size.height-_previousMessageHeight; + _previousMessageHeight = size.height; + NSRect frame = self.window.frame; + frame.origin.y -= delta; + frame.size.height += delta; + + [self.window setFrame:frame display:YES animate:YES]; + + _messageLabel.hidden = NO; +} + +- (IBAction)okAction:(id)sender { + @try { + NSError* error = nil; + self.session = [HSSAPI.defaultAPI newSessionWithLogin:_loginField.stringValue password:_passwordField.stringValue timeout:10 error:&error]; + if (error) + [self setMessage:error.localizedDescription]; + else { + Class enterprise = [HSS enterpriseClass]; + SEL sel = @selector(StorePassword:forUsername:); + if (enterprise && [enterprise respondsToSelector:sel]) { + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[enterprise methodSignatureForSelector:sel]]; + invocation.selector = sel; + invocation.target = enterprise; + NSString* login = _loginField.stringValue; + NSString* password = _passwordField.stringValue; + [invocation setArgument:&password atIndex:2]; + [invocation setArgument:&login atIndex:3]; + [invocation invoke]; + } + + [NSApp endSheet:self.window]; + } + } @catch (NSException* e) { + N2LogExceptionWithStackTrace(e); + [self setMessage:e.reason]; + } +} + +/*- (IBAction)checkHybridAction:(id)sender { + [HUG checkMasterUserAgain]; + [_messageLabel setStringValue:@"Vérification du lecteur du PC: cette fonctionnalité ne fonctionne que si sur le PC les Applications Cliniques sont actives. En cas de problème, réouvrez les."]; +}*/ + +/*- (BOOL)_dummyVerifyPassword:(NSString*)password forUsername:(NSString*)username { + return NO; +}*/ + +@end diff --git a/HSS/Sources/HSSCell.h b/HSS/Sources/HSSCell.h new file mode 100644 index 0000000..fea7b7a --- /dev/null +++ b/HSS/Sources/HSSCell.h @@ -0,0 +1,13 @@ +// +// HSSCell.h +// HSS +// +// Created by Alessandro Volz on 11.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import + +@interface HSSCell : NSCell + +@end diff --git a/HSS/Sources/HSSCell.m b/HSS/Sources/HSSCell.m new file mode 100644 index 0000000..4b8fe50 --- /dev/null +++ b/HSS/Sources/HSSCell.m @@ -0,0 +1,68 @@ +// +// HSSCell.m +// HSS +// +// Created by Alessandro Volz on 11.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSCell.h" +#import +#import + +/*@interface NSCell (Secret) + +-(void)_drawAttributedText:(NSAttributedString*)str inFrame:(NSRect)frame; + +@end*/ + +@implementation HSSCell + ++ (NSAttributedString*)suspendedAttributedStringWithAttributedString:(NSAttributedString*)s size:(NSSize)size { + if ([s sizeForWidth:FLT_MAX height:FLT_MAX].width <= size.width) + return s; + + NSString* const suspension = @"..."; + + NSInteger m = 0, M = s.length, pivot; + while ((pivot = (m+M)/2) > m) { + NSMutableAttributedString* ms = [[[s attributedSubstringFromRange:NSMakeRange(0, pivot)] mutableCopy] autorelease]; + [ms appendAttributedString:[[[NSAttributedString alloc] initWithString:suspension attributes:[s attributesAtIndex:pivot-1 effectiveRange:NULL]] autorelease]]; + if ([ms sizeForWidth:FLT_MAX height:FLT_MAX].width <= size.width) + m = pivot; + else M = pivot; + } + + if (M < s.length) { + NSMutableAttributedString* ms; + while ([s.string characterAtIndex:M-1] == ' ') --M; + ms = [[[s attributedSubstringFromRange:NSMakeRange(0,M)] mutableCopy] autorelease]; + [ms appendAttributedString:[[[NSAttributedString alloc] initWithString:suspension attributes:[s attributesAtIndex:M-1 effectiveRange:NULL]] autorelease]]; + return ms; + } + + return s; +} + +- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { + NSAttributedString* backup = [self.attributedStringValue retain]; + + NSRect drawingFrame = [self drawingRectForBounds:cellFrame]; + + if (self.isBordered || self.isBezeled) { + drawingFrame.origin.x += 3; + drawingFrame.size.width -= 6; + drawingFrame.origin.y += 1; + drawingFrame.size.height -= 2; + } + + NSRect titleRect = [self titleRectForBounds:drawingFrame]; + + self.attributedStringValue = [[self class] suspendedAttributedStringWithAttributedString:backup size:titleRect.size]; + + [super drawInteriorWithFrame:cellFrame inView:controlView]; + + self.attributedStringValue = [backup autorelease]; +} + +@end diff --git a/HSS/Sources/HSSCreateSubfolderWindowController.h b/HSS/Sources/HSSCreateSubfolderWindowController.h new file mode 100644 index 0000000..0752c78 --- /dev/null +++ b/HSS/Sources/HSSCreateSubfolderWindowController.h @@ -0,0 +1,28 @@ +// +// HSSCreateSubfolderWindowController.h +// HSS +// +// Created by Alessandro Volz on 10.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import + +@class HSSFolder; + +@interface HSSCreateSubfolderWindowController : NSWindowController { + HSSFolder* _folder; + // outlets + NSTextField* _messageField; + NSTextField* _nameField; + NSTextField* _descriptionField; +} + +@property(assign) IBOutlet NSTextField* messageField; +@property(assign) IBOutlet NSTextField* nameField; +@property(assign) IBOutlet NSTextField* descriptionField; + +-(IBAction)createAction:(id)sender; +-(IBAction)cancelAction:(id)sender; + +@end diff --git a/HSS/Sources/HSSCreateSubfolderWindowController.m b/HSS/Sources/HSSCreateSubfolderWindowController.m new file mode 100644 index 0000000..65c7a35 --- /dev/null +++ b/HSS/Sources/HSSCreateSubfolderWindowController.m @@ -0,0 +1,52 @@ +// +// HSSCreateSubfolderWindowController.m +// HSS +// +// Created by Alessandro Volz on 10.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSCreateSubfolderWindowController.h" +#import "HSSFolder.h" + +@interface HSSCreateSubfolderWindowController () + +@property(retain) HSSFolder* folder; + +@end + +@implementation HSSCreateSubfolderWindowController + +@synthesize folder = _folder; +@synthesize messageField = _messageField; +@synthesize nameField = _nameField; +@synthesize descriptionField = _descriptionField; + +-(id)initWithFolder:(HSSFolder*)folder { + if ((self = [super initWithWindowNibName:@"HSSCreateSubfolderWindow"])) { + self.folder = folder; + } + + return self; +} + +-(void)dealloc { + self.folder = nil; + [super dealloc]; +} + +-(void)windowDidLoad { + [super windowDidLoad]; + [self.messageField setStringValue:[NSString stringWithFormat:self.messageField.stringValue, self.folder.name]]; +} + +-(IBAction)createAction:(id)sender { + [NSApp endSheet:self.window]; +} + +-(IBAction)cancelAction:(id)sender { + [NSApp endSheet:self.window returnCode:NSRunAbortedResponse]; +} + + +@end diff --git a/HSS/Sources/HSSExportWindowController.h b/HSS/Sources/HSSExportWindowController.h new file mode 100644 index 0000000..f030b9f --- /dev/null +++ b/HSS/Sources/HSSExportWindowController.h @@ -0,0 +1,63 @@ +// +// HSSExportWindowController.h +// HSS +// +// Created by Alessandro Volz on 21.12.11. +// Copyright (c) 2011 OsiriX Team. All rights reserved. +// + +#import + +@class HSSAPISession; +@class HSSFolder; +@class DicomImage; +@class HSSMedcaseCreation; + +@interface HSSExportWindowController : NSWindowController { + BOOL _alreadyDidBecomeSheet; + NSArray* _images; + NSArray* _series; + HSSAPISession* _session; + HSSMedcaseCreation* _medcase; + NSAnimation* _animation; + // outlets + HSSFolder* _folder; + NSOutlineView* _foldersOutline; + NSTreeController* _treeController; + NSTextField* _usernameField; + NSTextField* _nameField; + NSMatrix* _imagesMatrix; + NSTextField* _diagnosisField; + NSTextField* _historyField; + NSButton* _openCheckbox; + NSProgressIndicator* _progressIndicator; + NSScrollView* _foldersOutlineScroll; + NSButton* _sendButton; +} + +@property(retain,readonly) NSArray* images; +@property(retain,readonly) NSArray* series; +@property(retain,readonly) HSSAPISession* session; +@property(retain,readonly) HSSMedcaseCreation* medcase; +@property(retain,readonly) NSAnimation* animation; + +@property(assign) IBOutlet HSSFolder* folder; +@property(assign) IBOutlet NSOutlineView* foldersOutline; +@property(assign) IBOutlet NSTreeController* treeController; +@property(assign) IBOutlet NSTextField* usernameField; +@property(assign) IBOutlet NSTextField* nameField; +@property(assign) IBOutlet NSMatrix* imagesMatrix; +@property(assign) IBOutlet NSTextField* diagnosisField; +@property(assign) IBOutlet NSTextField* historyField; +@property(assign) IBOutlet NSButton* openCheckbox; +@property(assign) IBOutlet NSProgressIndicator* progressIndicator; +@property(assign) IBOutlet NSScrollView* foldersOutlineScroll; +@property(assign) IBOutlet NSButton* sendButton; + +- (id)initWithSeries:(NSArray*)series images:(NSArray*)images; +- (void)beginSheetOnWindow:(NSWindow*)parentWindow; + +- (IBAction)sendAction:(id)sender; +- (IBAction)cancelAction:(id)sender; + +@end diff --git a/HSS/Sources/HSSExportWindowController.mm b/HSS/Sources/HSSExportWindowController.mm new file mode 100644 index 0000000..4076367 --- /dev/null +++ b/HSS/Sources/HSSExportWindowController.mm @@ -0,0 +1,702 @@ +// +// HSSExportWindowController.m +// HSS +// +// Created by Alessandro Volz on 21.12.11. +// Copyright (c) 2011 OsiriX Team. All rights reserved. +// + +#import "HSSExportWindowController.h" +#import "HSSAuthenticationWindowController.h" +#import +#import "HSS.h" +#import "HSSAPI.h" +#import +#import +#import "HSSAPISession.h" +#import "HSSFolder.h" +#import "HSSMedcase.h" +#import +#import +#import "HSSMedcaseCreation.h" +#import "HSSCell.h" +#import +#import +#import +#import + +@interface HSSExportWindowController () + +@property(retain,readwrite) NSArray* series; +@property(retain,readwrite) NSArray* images; +@property(retain,readwrite) HSSAPISession* session; +@property(retain,readwrite) HSSMedcaseCreation* medcase; +@property(retain,readwrite) NSAnimation* animation; + +@end + +@implementation HSSExportWindowController + +@synthesize series = _series; +@synthesize images = _images; +@synthesize session = _session; +@synthesize medcase = _medcase; +@synthesize folder = _folder; +@synthesize animation = _animation; + +@synthesize foldersOutline = _foldersOutline; +@synthesize treeController = _treeController; +@synthesize usernameField = _usernameField; +@synthesize nameField = _nameField; +@synthesize imagesMatrix = _imagesMatrix; +@synthesize diagnosisField = _diagnosisField; +@synthesize historyField = _historyField; +@synthesize openCheckbox = _openCheckbox; +@synthesize progressIndicator = _progressIndicator; +@synthesize foldersOutlineScroll = _foldersOutlineScroll; +@synthesize sendButton = _sendButton; + +static NSString* const HSSExportWindowControllerContext = @"HSSExportWindowControllerContext"; + +- (id)initWithSeries:(NSArray*)series images:(NSArray*)images { + if ((self = [super initWithWindowNibName:@"HSSExportWindow"])) { + self.series = series; + self.images = images; + self.medcase = [[[HSSMedcaseCreation alloc] initWithSession:nil] autorelease]; + } + + return self; +} + ++ (NSArray*)imagesInSeries:(NSArray*)series { + NSMutableArray* images = [NSMutableArray array]; + + for (DicomSeries* serie in series) + for (DicomImage* image in serie.images) + if (![images containsObject:image]) + [images addObject:image]; + + return images; +} + ++ (NSArray*)keyImagesInSeries:(NSArray*)series { + NSMutableArray* images = [NSMutableArray array]; + + for (DicomImage* image in [self imagesInSeries:series]) + if (image.storedIsKeyImage.boolValue) + [images addObject:image]; + + return images; +} + ++ (NSArray*)imagesExcludingMultiframes:(NSArray*)images { + NSMutableArray* rimages = [NSMutableArray array]; + + for (NSInteger i = images.count-1; i >= 0; --i) { + DicomImage* image = [images objectAtIndex:i]; + DicomSeries* series = image.series; + if (series.images.count > 1) { + BOOL isMultiframe = NO; + for (DicomImage* oimage in series.images) + if (oimage != image && [oimage.completePath isEqualToString:image.completePath]) { + isMultiframe = YES; + break; + } + if (!isMultiframe) + [rimages addObject:image]; + } + } + + return rimages; +} + +- (void)awakeFromNib { + NSCell* cell; + NSInteger count; + + cell = [_imagesMatrix cellWithTag:0]; + count = [[[self class] imagesInSeries:self.series] count]; + if (self.series.count == 1) + cell.title = [NSString stringWithFormat:NSLocalizedString(@"All images in this series (%d)", nil), (int)count]; + else cell.title = [NSString stringWithFormat:NSLocalizedString(@"All images in these series (%d)", nil), (int)count];; + [cell setEnabled:(count > 0)]; + + NSArray* pimages = [[self.images retain] autorelease]; + self.images = [[self class] imagesExcludingMultiframes:self.images]; + + cell = [_imagesMatrix cellWithTag:1]; + NSArray* pkeyImages = [[self class] keyImagesInSeries:self.series]; + NSArray* keyImages = [[self class] imagesExcludingMultiframes:pkeyImages]; + count = [keyImages count]; + if (self.series.count == 1) + cell.title = [NSString stringWithFormat:NSLocalizedString(@"Key images in this series (%d)", nil), (int)count]; + else cell.title = [NSString stringWithFormat:NSLocalizedString(@"Key images in these series (%d)", nil), (int)count];; + if (pkeyImages.count > count) + cell.title = [NSString stringWithFormat:@"%@ - %@", cell.title, NSLocalizedString(@"multiframe files excluded", @"keep this string short...")]; + [cell setEnabled:(count > 0)]; + + cell = [_imagesMatrix cellWithTag:2]; + count = self.images.count; + if (count == 1) + cell.title = [NSString stringWithFormat:NSLocalizedString(@"Current image", nil)]; + else cell.title = [NSString stringWithFormat:NSLocalizedString(@"Selected images (%d)", nil), (int)count]; + if (pimages.count > count) + cell.title = [NSString stringWithFormat:@"%@ - %@", cell.title, NSLocalizedString(@"multiframe files excluded", @"keep this string short...")]; + [cell setEnabled:(count > 0)]; + + [_imagesMatrix selectCellWithTag:2]; // default d'apres la spec + while (_imagesMatrix.selectedTag > 0 && ![_imagesMatrix.selectedCell isEnabled]) + [_imagesMatrix selectCellWithTag:_imagesMatrix.selectedTag-1]; + + [_progressIndicator startAnimation:self]; + + cell = [[[HSSCell alloc] initTextCell:@""] autorelease]; + cell.font = [NSFont systemFontOfSize:NSFont.smallSystemFontSize]; + cell.lineBreakMode = NSLineBreakByClipping; + _foldersOutline.cell = cell; + + _foldersOutline.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; + + [_treeController addObserver:self forKeyPath:@"selectedObjects" options:0 context:HSSExportWindowControllerContext]; // useless: McKesson said "adding images to an existing case is outside the scope of this project" (Rex Jakobovits, 2012/1/27 00:47:26 HNEC) + +/* NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; + menu.delegate = self; + [_foldersOutline setMenu:menu];*/ +} + +- (void)dealloc { + [_treeController removeObserver:self forKeyPath:@"selectedObjects"]; + + self.animation = nil; + + self.session = nil; + self.images = nil; + self.series = nil; + self.medcase = nil; + [super dealloc]; +} + +- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context { + if (context != HSSExportWindowControllerContext) { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + return; + } + + if ([keyPath isEqualToString:@"selectedObjects"] && object == _treeController && _treeController.selectedObjects.count) { // useless: McKesson said "adding images to an existing case is outside the scope of this project" (Rex Jakobovits, 2012/1/27 00:47:26 HNEC) + HSSItem* item = [_treeController.selectedObjects objectAtIndex:0]; + + BOOL merge = [item isKindOfClass:[HSSMedcase class]]; + BOOL enable = !merge; + + [_diagnosisField setEnabled:enable]; + [_historyField setEnabled:enable]; + if (enable != [_nameField isEnabled]) { + [_nameField setEnabled:enable]; + [self.medcase setCaseName:(merge? item.name : @"")]; + } + + [_sendButton setTitle:(merge? NSLocalizedString(@"Merge", nil) : NSLocalizedString(@"Send", nil))]; + } +} + +- (void)beginSheetOnWindow:(NSWindow*)parentWindow { + [NSApp beginSheet:self.window modalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(_sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; + self.medcase.docWindow = parentWindow; +// [self.window makeKeyAndOrderFront:self]; +} + + +- (void)windowDidBecomeKey:(NSNotification*)notification { + if (!_alreadyDidBecomeSheet) { + _alreadyDidBecomeSheet = YES; + + @try { + Class enterprise = [HSS enterpriseClass]; + NSString* enterpriseUsername = nil; + if ([enterprise respondsToSelector:@selector(Username)]) + enterpriseUsername = [enterprise performSelector:@selector(Username)]; + if (enterpriseUsername.length) { + NSString* storedPassword = nil; + if ([enterprise respondsToSelector:@selector(StoredPasswordForUsername:)]) + storedPassword = [enterprise performSelector:@selector(StoredPasswordForUsername:) withObject:enterpriseUsername]; + if (storedPassword.length) { + self.session = [HSSAPI.defaultAPI newSessionWithLogin:enterpriseUsername password:storedPassword timeout:2.5 error:NULL]; + } + } + } @catch (...) { + } + + if (!self.session) + [[HSSAuthenticationWindowController new] beginSheetOnWindow:self.window callbackTarget:self selector:@selector(_authSheetCallbackWithSession:contextInfo:) context:nil]; + else [self performSelectorInBackground:@selector(_getUserFolderTreeThread) withObject:nil]; + } +} + +- (NSString*)_patientId { + for (DicomImage* image in self.images) + if (image.series.study.patientID.length) + return image.series.study.patientID; + for (DicomSeries* series in self.series) + if (series.study.patientID.length) + return series.study.patientID; + return nil; +} + + +- (void)_authSheetCallbackWithSession:(HSSAPISession*)session contextInfo:(void*)contextInfo { + if (!session) { + [self cancelAction:self]; + return; + } + + self.session = session; + [self.usernameField performSelectorOnMainThread:@selector(setStringValue:) withObject:session.userName waitUntilDone:NO]; + +// [self performSelectorInBackground:@selector(_getFolderThread:) withObject:[NSArray arrayWithObjects: session.userHomeFolderOid, _folder, nil]]; + + [self performSelectorInBackground:@selector(_getUserFolderTreeThread) withObject:nil]; +} + +- (void)_getUserFolderTreeThread { + @autoreleasepool { + NSArray* related = nil; + @try { + NSError* error = nil; + + NSArray* response = [self.session getHomeFolderTreeWithError:&error]; + + if (error) + [NSException raise:NSGenericException format:@"%@", error.localizedDescription]; + + HSSFolder* temp = [[[HSSFolder alloc] init] autorelease]; + [temp syncWithAPIFolders:response]; + + [self performSelectorOnMainThread:@selector(_fillUserFolderTreeWithResponse:) withObject:temp.content waitUntilDone:NO]; + + related = [self.session getMedcasesRelatedToPatientId:[self _patientId] error:&error]; + + if (error) + [NSException raise:NSGenericException format:@"%@", error.localizedDescription]; + + } @catch (NSException* e) { + N2LogExceptionWithStackTrace(e); + [self performSelectorOnMainThread:@selector(_showErrorAlert:) withObject:[NSError errorWithDomain:HSSErrorDomain code:-1 userInfo:[NSDictionary dictionaryWithObject:e.reason forKey:NSLocalizedDescriptionKey]] waitUntilDone:NO]; + return; + } + + @try { + NSError* error = nil; + + NSMutableDictionary* medcasesByFolder = [NSMutableDictionary dictionary]; + for (NSDictionary* medcase in related) { + NSString* medcaseOid = [medcase objectForKey:@"oid"]; + + NSDictionary* medcaseInfo = [self.session getMedcaseWithOid:medcaseOid error:&error]; + + if (error) + [NSException raise:NSGenericException format:@"%@", error.localizedDescription]; + + for (NSDictionary* folder in [medcaseInfo objectForKey:@"folders"]) { + NSString* folderOid = [folder objectForKey:@"oid"]; + + NSMutableArray* medcasesInFolder = [medcasesByFolder objectForKey:folderOid]; + if (!medcasesInFolder) + [medcasesByFolder setObject:(medcasesInFolder = [NSMutableArray array]) forKey:folderOid]; + + [medcasesInFolder addObject:medcase]; + } + } + + [self performSelectorOnMainThread:@selector(_fillUserMedcases:) withObject:medcasesByFolder waitUntilDone:NO]; + + } @catch (NSException* e) { + [self performSelectorOnMainThread:@selector(_fillUserMedcases:) withObject:nil waitUntilDone:NO]; + N2LogExceptionWithStackTrace(e); + [self performSelectorOnMainThread:@selector(_showWarningAlert:) withObject:[NSError errorWithDomain:HSSErrorDomain code:-1 userInfo:[NSDictionary dictionaryWithObject:e.reason forKey:NSLocalizedDescriptionKey]] waitUntilDone:NO]; + } + } +} + ++ (NSIndexPath*)node:(id)node findFolderWithOid:(NSString*)oid { + NSArray* childNodes = [node childNodes]; + for (NSUInteger i = 0; i < childNodes.count; ++i) { + id childNode = [childNodes objectAtIndex:i]; + HSSFolder* child = [childNode representedObject]; + if ([child isKindOfClass:[HSSFolder class]]) { + if ([child.oid isEqualToString:oid]) { + return [NSIndexPath indexPathWithIndex:i]; + } else { + NSIndexPath* subpath = [self node:childNode findFolderWithOid:oid]; + if (subpath) { + NSUInteger indexes[subpath.length+1]; + indexes[0] = i; + [subpath getIndexes:&indexes[1]]; + return [NSIndexPath indexPathWithIndexes:indexes length:subpath.length+1]; + } + } + } + } + + return nil; +} + ++ (NSString*)_defaultsKeyForLastFolderUidForUser:(NSString*)user { + return [NSString stringWithFormat:@"HSS folder selection for user %@", user]; +} + +- (void)_expandIndexPath:(NSIndexPath*)path { + if (path.length > 1) + [self _expandIndexPath:[path indexPathByRemovingLastIndex]]; + [_foldersOutline expandItem:[_treeController.arrangedObjects descendantNodeAtIndexPath:path]]; +} + +- (void)_fillUserFolderTreeWithResponse:(id)content { + if (![self.window isVisible]) + return; + + @synchronized (content) { + _folder.content = content; + + NSString* userLastFolderOid = [NSUserDefaults.standardUserDefaults stringForKey:[[self class] _defaultsKeyForLastFolderUidForUser:_session.userLogin]]; + NSIndexPath* path = [[self class] node:_treeController.arrangedObjects findFolderWithOid:userLastFolderOid]; + if (!path) + path = [[self class] node:_treeController.arrangedObjects findFolderWithOid:_session.userHomeFolderOid]; + + if (path) { + [self _expandIndexPath:path]; + [_treeController setSelectionIndexPath:path]; + } + } + + NSRect rect; + + NSMutableDictionary* progressAnimation = [NSMutableDictionary dictionary]; + [progressAnimation setObject:_progressIndicator forKey:NSViewAnimationTargetKey]; + rect = _progressIndicator.frame; + [progressAnimation setObject:[NSValue valueWithRect:rect] forKey:NSViewAnimationStartFrameKey]; + rect.origin = NSMakePoint([_progressIndicator.superview frame].size.width-rect.size.width, 65); + [progressAnimation setObject:[NSValue valueWithRect:rect] forKey:NSViewAnimationEndFrameKey]; + + NSMutableDictionary* nameAnimation = [NSMutableDictionary dictionary]; + [nameAnimation setObject:_nameField forKey:NSViewAnimationTargetKey]; + rect = _nameField.frame; + [nameAnimation setObject:[NSValue valueWithRect:rect] forKey:NSViewAnimationStartFrameKey]; + rect.size.width -= 20; + [nameAnimation setObject:[NSValue valueWithRect:rect] forKey:NSViewAnimationEndFrameKey]; + + NSViewAnimation* a = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects: progressAnimation, nameAnimation, nil]] autorelease]; + a.duration = 0.25; + a.animationCurve = NSAnimationEaseIn; + [a startAnimation]; + self.animation = a; +} + +/*- (void)setAnimation:(NSAnimation*)animation { + if (animation != _animation) { + if ([self.animation isAnimating]) + [self.animation stopAnimation]; + [self.animation release]; + + _animation = [animation retain]; + } +}*/ + ++ (BOOL)indexPath:(NSIndexPath*)ip withRoot:(id)node includesFolderWithOid:(NSString*)folderOid { + @try { + for (NSUInteger p = 0; p < ip.length; ++p) { + node = [[node childNodes] objectAtIndex:[ip indexAtPosition:p]]; + HSSFolder* folder = [node representedObject]; + if ([folder isKindOfClass:[HSSFolder class]]) { + if ([folder.oid isEqualToString:folderOid]) + return YES; + } else + return NO; + } + } @catch (...) { + // do nothing + } + + return NO; +} + +- (void)_fillUserMedcases:(NSDictionary*)medcasesByFolder { + [self.animation stopAnimation]; + + NSMutableArray* mips = [NSMutableArray array]; + + for (NSString* folderOid in medcasesByFolder) { + NSArray* medcases = [medcasesByFolder objectForKey:folderOid]; + + NSIndexPath* fip = [[self class] node:_treeController.arrangedObjects findFolderWithOid:folderOid]; + if (fip) { + id item = [_treeController.arrangedObjects descendantNodeAtIndexPath:fip]; + HSSFolder* folder = [item representedObject]; + + [folder syncWithAPIMedcases:medcases]; + + for (NSUInteger i = 0; i < medcases.count; ++i) + [mips addObject:[fip indexPathByAddingIndex:i]]; + } + } + + NSString* favoriteFolderOid = [NSUserDefaults.standardUserDefaults stringForKey:[[self class] _defaultsKeyForLastFolderUidForUser:_session.userLogin]]; + if (favoriteFolderOid && ![[self class] node:_treeController.arrangedObjects findFolderWithOid:favoriteFolderOid]) + favoriteFolderOid = nil; + if (!favoriteFolderOid) + favoriteFolderOid = _session.userHomeFolderOid; + + [mips sortUsingComparator:^NSComparisonResult(NSIndexPath* ip1, NSIndexPath* ip2) { // prefer folders in the user's home folder + BOOL ip1home = [[self class] indexPath:ip1 withRoot:_treeController.arrangedObjects includesFolderWithOid:favoriteFolderOid]; + BOOL ip2home = [[self class] indexPath:ip2 withRoot:_treeController.arrangedObjects includesFolderWithOid:favoriteFolderOid]; + + if (ip1home != ip2home) + return ip1home > ip2home ? NSOrderedAscending : NSOrderedDescending; + + return [ip1 compare:ip2]; + }]; + + for (NSInteger i = mips.count-1; i >= 0; --i) { + NSIndexPath* path = [mips objectAtIndex:i]; + [self _expandIndexPath:path]; + if (!i) + [_treeController setSelectionIndexPath:path]; + } + + [_progressIndicator stopAnimation:self]; + + NSRect rect; + + NSMutableDictionary* nameAnimation = [NSMutableDictionary dictionary]; + [nameAnimation setObject:_nameField forKey:NSViewAnimationTargetKey]; + rect = _nameField.frame; + [nameAnimation setObject:[NSValue valueWithRect:rect] forKey:NSViewAnimationStartFrameKey]; + rect.size.width = [_progressIndicator.superview frame].size.width-rect.origin.x; + [nameAnimation setObject:[NSValue valueWithRect:rect] forKey:NSViewAnimationEndFrameKey]; + + NSViewAnimation* a = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects: nameAnimation, nil]] autorelease]; + a.duration = 0.25; + a.animationCurve = NSAnimationEaseIn; + [a startAnimation]; + self.animation = a; +} + +/*- (void)_getFolderThread:(NSArray*)args { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + @try { + NSString* folderOid = [args objectAtIndex:0]; + HSSFolder* folder = [args objectAtIndex:1]; + + NSError* error; + + NSDictionary* response = [self.session getFolderWithOid:folderOid error:&error]; + + [self performSelectorOnMainThread:@selector(_fillFolderWithResponse:) + withObject:[NSArray arrayWithObjects: folder, response, nil] + waitUntilDone:NO]; + } @catch (NSException* e) { + [self performSelectorOnMainThread:@selector(_showErrorAlert:) withObject:[NSError errorWithDomain:HSSErrorDomain code:-1 userInfo:[NSDictionary dictionaryWithObject:e.reason forKey:NSLocalizedDescriptionKey]] waitUntilDone:NO]; + } @finally { + [pool release]; + } +} + +- (void)_fillFolderWithResponse:(NSArray*)args { + HSSFolder* folder = [args objectAtIndex:0]; + NSDictionary* response = [args objectAtIndex:1]; + + [folder syncWithAPIFolders:[response valueForKey:@"children"]]; +// [folder syncWithAPIMedcases:[response valueForKey:@"medcases"]]; + + for (HSSFolder* child in folder.content) + if ([child isKindOfClass:[HSSFolder class]]) + [self performSelectorInBackground:@selector(_getFolderThread:) withObject:[NSArray arrayWithObjects: child.oid, child, nil]]; + + [_progressIndicator stopAnimation:self]; + [_foldersOutline expandItem:nil expandChildren:YES]; + + NSTreeNode* node; + BOOL selected = NO; + for (NSInteger i = 0; (node = [_foldersOutline itemAtRow:i]) != nil; ++i) { + HSSItem* item = node.representedObject; + if (item.assignable) { + [_foldersOutline selectRowIndexes:[NSIndexSet indexSetWithIndex:i] byExtendingSelection:NO]; + selected = YES; + break; + } + } + if (!selected) // if there is no assignable folder, deselect all + [_foldersOutline selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; +}*/ + +- (void)_showErrorAlert:(NSError*)error { + [[NSAlert alertWithError:error] beginSheetModalForWindow:(self.window.isVisible? self.window : nil) modalDelegate:self didEndSelector:@selector(_errorAlertSheedDidEnd:returnCode:contextInfo:) contextInfo:nil]; +} + +- (void)_showWarningAlert:(NSError*)error { + if (self.window.isVisible) + [[NSAlert alertWithError:error] beginSheetModalForWindow:self.window modalDelegate:nil didEndSelector:nil contextInfo:nil]; +} + +- (void)_errorAlertSheedDidEnd:(NSAlert*)alert returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo { + [self cancelAction:self]; +} + +- (NSArray*)imagesToSend { + switch (_imagesMatrix.selectedTag) { + case 0: // all images + return [[self class] imagesInSeries:self.series]; + case 1: // key images + return [[self class] keyImagesInSeries:self.series]; + case 2: // current image(s) + return self.images; + } + + return nil; +} + +- (IBAction)sendAction:(id)sender { + NSArray* images = [self imagesToSend]; + if (images.count > 20) + [[NSAlert alertWithMessageText:nil defaultButton:@"Send" alternateButton:NSLocalizedString(@"Cancel", nil) otherButton:nil informativeTextWithFormat:NSLocalizedString(@"You are adding %d images to HSS, a rather large amount of images for the purpose of HSS. Do you wish to proceed?", nil), (int)images.count] beginSheetModalForWindow:self.window modalDelegate:self didEndSelector:@selector(_confirmSendSheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; + else [NSApp endSheet:self.window]; +} + +- (void)_confirmSendSheetDidEnd:(NSAlert*)alert returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo { + [alert.window orderOut:self]; + if (returnCode == NSAlertDefaultReturn) + [NSApp endSheet:self.window]; +} + +- (IBAction)cancelAction:(id)sender { + [NSApp endSheet:self.window returnCode:NSRunAbortedResponse]; +} + +- (void)_sheetDidEnd:(NSWindow*)sheet returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo { + @try { + if (returnCode != NSRunAbortedResponse) { + [sheet makeFirstResponder:nil]; // makes the currently edited text commit + + NSIndexPath* sp = [_treeController selectionIndexPath]; + HSSItem* destination = [[_treeController.arrangedObjects descendantNodeAtIndexPath:sp] representedObject]; + + self.medcase.session = self.session; + + self.medcase.images = [self imagesToSend]; + + self.medcase.destination = destination; + + NSString* folderOid = destination.oid; + if ([destination isKindOfClass:[HSSMedcase class]]) // we want its containing folder + folderOid = [[[[_treeController.arrangedObjects descendantNodeAtIndexPath:sp] parentNode] representedObject] oid]; + [NSUserDefaults.standardUserDefaults setObject:folderOid forKey:[[self class] _defaultsKeyForLastFolderUidForUser:_session.userLogin]]; + + [ThreadsManager.defaultManager addThreadAndStart:self.medcase]; + } + } @catch (NSException* e) { + N2LogExceptionWithStackTrace(e); + } @finally { + [self.window close]; +// [self.window release]; + [self autorelease]; + } +} + +#pragma mark NSOutlineViewDelegate + +- (BOOL)outlineView:(NSOutlineView*)outlineView shouldSelectItem:(NSTreeNode*)node { + HSSItem* item = node.representedObject; + return item.assignable; +} + +- (NSCell*)outlineView:(NSOutlineView*)outlineView dataCellForTableColumn:(NSTableColumn*)tableColumn item:(NSTreeNode*)node { + if (!tableColumn) { // column not specified, they want to know if there's a cell to span over the whole table width instead of a cell per column + HSSItem* item = node.representedObject; + if ([item isKindOfClass:[HSSMedcase class]]) // medcases always span over the description column + return outlineView.cell; + if ([item isKindOfClass:[HSSFolder class]]) // folders only span over the description column if the description is empty + if (!((HSSFolder*)item).desc.string.length) + return outlineView.cell; + + return nil; // return nil to make them know that there's no span + } + + return outlineView.cell; +} + +- (void)outlineView:(NSOutlineView*)outlineView willDisplayCell:(HSSCell*)cell forTableColumn:(NSTableColumn*)tableColumn item:(NSTreeNode*)node { + HSSItem* item = node.representedObject; + if ([item isKindOfClass:[HSSMedcase class]]) { + NSRect f = [outlineView frameOfCellAtColumn:(tableColumn? [[outlineView tableColumns] indexOfObject:tableColumn] : 0) row:[outlineView rowForItem:node]]; + f.origin.x -= 9; + [@"●" drawAtPoint:f.origin withAttributes:[[cell attributedStringValue] attributesAtIndex:0 effectiveRange:NULL]]; + } +} + + +/*#pragma mark NSMenuDelegate + +- (void)menuWillOpen:(NSMenu*)menu { + if (![_foldersOutline.selectedRowIndexes containsIndex:_foldersOutline.clickedRow]) + [_foldersOutline selectRowIndexes:[NSIndexSet indexSetWithIndex:_foldersOutline.clickedRow] byExtendingSelection:NO]; + + [menu removeAllItems]; + NSMenuItem* mi; + +// Impossible: the API doesn't offer this feature +// mi = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Create Subfolder...", nil) action:@selector(_addSubfolderAction:) keyEquivalent:@""]; +// mi.target = self; +// mi.representedObject = [[_foldersOutline itemAtRow:_foldersOutline.clickedRow] representedObject]; +// [menu addItem:mi]; +} + +- (void)_addSubfolderAction:(NSMenuItem*)mi { + HSSFolder* folder = mi.representedObject; + NSLog(@"Add subfolder to %@", folder); +}*/ + +#pragma mark NSSplitViewDelegate + +- (CGFloat)splitView:(NSSplitView*)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex { + CGFloat dividerThickness = splitView.dividerThickness; + switch (dividerIndex) { + case 0: return 147; + case 1: return NSHeight([[splitView.subviews objectAtIndex:0] bounds])+dividerThickness+51; + } + + return proposedMin; +} + +- (CGFloat)splitView:(NSSplitView*)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex { + CGFloat dividerThickness = splitView.dividerThickness; + CGFloat totHeight = NSHeight(splitView.bounds); + + switch (dividerIndex) { + case 0: return totHeight-NSHeight([[splitView.subviews objectAtIndex:2] bounds])-dividerThickness-51-dividerThickness; + case 1: return totHeight-41-dividerThickness; + } + + return proposedMax; +} + +@end + + + + + + + + + + + + + + + + + + + + + diff --git a/HSS/Sources/HSSFolder.h b/HSS/Sources/HSSFolder.h new file mode 100644 index 0000000..94b0b99 --- /dev/null +++ b/HSS/Sources/HSSFolder.h @@ -0,0 +1,22 @@ +// +// HSSFolder.h +// HSS +// +// Created by Alessandro Volz on 06.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSItem.h" + +@interface HSSFolder : HSSItem { + NSAttributedString* _desc; + NSInteger _numCases; +} + +@property(retain) NSAttributedString* desc; +@property NSInteger numCases; + +- (void)syncWithAPIFolders:(NSArray*)items; +- (void)syncWithAPIMedcases:(NSArray*)items; + +@end diff --git a/HSS/Sources/HSSFolder.m b/HSS/Sources/HSSFolder.m new file mode 100644 index 0000000..9d25d3d --- /dev/null +++ b/HSS/Sources/HSSFolder.m @@ -0,0 +1,121 @@ +// +// HSSFolder.m +// HSS +// +// Created by Alessandro Volz on 06.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSFolder.h" +#import "HSSMedcase.h" +#import + +@interface NSObject (HSS) + +- (id)ofClass:(Class)c; + +@end + +@implementation HSSFolder + +@synthesize desc = _desc; +@synthesize numCases = _numCases; + ++ (id)mutableArray:(NSMutableArray*)items findAndRemoveItemWithOid:(NSString*)oid { + for (HSSItem* item in items) + if ([item.oid isEqualToString:oid]) { + [items removeObject:item]; + return item; + } + return nil; +} + +/*+(NSSet*)keyPathsForValuesAffectingIsLeaf { + return [NSSet setWithObject:@"arrangedObjects"]; +}*/ + +- (void)syncWithAPIFolders:(NSArray*)items { + NSMutableArray* folders = [[[self.content filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"className = %@", [HSSFolder className]]] mutableCopy] autorelease]; + + for (NSDictionary* item in items) { + NSString* oid = [[item valueForKey:@"oid"] ofClass:[NSString class]]; + + HSSFolder* folder = [[self class] mutableArray:folders findAndRemoveItemWithOid:oid]; + if (!folder) { + folder = [[[[self class] alloc] init] autorelease]; + folder.oid = oid; + } + + folder.name = [[item valueForKey:@"name"] ofClass:[NSString class]]; + folder.assignable = YES; // [[[item valueForKey:@"assignable"] ofClass:[NSString class]] boolValue]; // since feb 10 2012 the API only returns assignable folders + folder.numCases = [[[item valueForKey:@"num_cases"] ofClass:[NSString class]] integerValue]; + + NSMutableAttributedString* desc = [[[NSMutableAttributedString alloc] initWithHTML:[[item valueForKey:@"description"] dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:NULL] autorelease]; + [desc setAttributes:[NSDictionary dictionary] range:desc.range]; // this removes all formatting, including links + [desc addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:NSFont.smallSystemFontSize] range:desc.range]; + folder.desc = desc; + + [folder syncWithAPIFolders:[[item valueForKey:@"children"] ofClass:[NSArray class]]]; + + if (![self.content containsObject:folder]) + [self addObject:folder]; + } + + [self removeObjects:folders]; +} + +- (void)syncWithAPIMedcases:(NSArray*)items { + NSMutableArray* medcases = [[[self.content filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"className = %@", [HSSMedcase className]]] mutableCopy] autorelease]; + + for (NSDictionary* item in items) { + NSString* oid = [[item valueForKey:@"oid"] ofClass:[NSString class]]; + + HSSMedcase* medcase = [[self class] mutableArray:medcases findAndRemoveItemWithOid:oid]; + if (!medcase) { + medcase = [[[HSSMedcase alloc] init] autorelease]; + medcase.oid = oid; + } + + medcase.name = [[item valueForKey:@"title"] ofClass:[NSString class]]; + + medcase.assignable = YES; + + if (![self.content containsObject:medcase]) + [self addObject:medcase]; + } + + [self removeObjects:medcases]; +} + +- (BOOL)isLeaf { + return NO; // [self.content count] == 0 +} + +- (NSMutableString*)descriptionWithTab:(NSInteger)t { + NSMutableString* desc = [super descriptionWithTab:t]; + [desc appendString:@" {\n"]; + for (HSSItem* folder in self.arrangedObjects) + [desc appendFormat:@"%@\n", [folder descriptionWithTab:t+1]]; + for (int i = 0; i < t; ++i) [desc appendString:HSSTab]; + [desc appendString:@"}"]; + return desc; +} + +@end + +@implementation NSObject (HSS) + +- (id)ofClass:(Class)c { + if ([self isKindOfClass:c]) + return self; + return nil; +} + +@end + + + + + + + diff --git a/HSS/Sources/HSSIsNonEmptyString.h b/HSS/Sources/HSSIsNonEmptyString.h new file mode 100644 index 0000000..353b3a2 --- /dev/null +++ b/HSS/Sources/HSSIsNonEmptyString.h @@ -0,0 +1,14 @@ +// +// HSSIsNonEmptyString.h +// HSS +// +// Created by Alessandro Volz on 10.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import + +@interface HSSIsNonEmptyString : NSValueTransformer + + +@end diff --git a/HSS/Sources/HSSIsNonEmptyString.m b/HSS/Sources/HSSIsNonEmptyString.m new file mode 100644 index 0000000..9e1f878 --- /dev/null +++ b/HSS/Sources/HSSIsNonEmptyString.m @@ -0,0 +1,29 @@ +// +// HSSIsNonEmptyString.m +// HSS +// +// Created by Alessandro Volz on 10.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSIsNonEmptyString.h" + +@implementation HSSIsNonEmptyString + ++ (void)load { + [NSValueTransformer setValueTransformer:[[[self class] alloc] init] forName:@"HSSIsNonEmptyString"]; +} + ++ (BOOL)allowsReverseTransformation { + return NO; +} + ++ (Class)transformedValueClass { + return [NSString class]; +} + +- (NSNumber*)transformedValue:(NSString*)value { + return [NSNumber numberWithBool:(value.length > 0)]; +} + +@end diff --git a/HSS/Sources/HSSItem.h b/HSS/Sources/HSSItem.h new file mode 100644 index 0000000..cb91f61 --- /dev/null +++ b/HSS/Sources/HSSItem.h @@ -0,0 +1,25 @@ +// +// HSSItem.h +// HSS +// +// Created by Alessandro Volz on 11.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import + +@interface HSSItem : NSArrayController { + NSString* _oid; + NSString* _name; + BOOL _assignable; +} + +@property(retain) NSString* oid; +@property(retain) NSString* name; +@property BOOL assignable; + +extern NSString* const HSSTab; + +- (NSMutableString*)descriptionWithTab:(NSInteger)t; + +@end diff --git a/HSS/Sources/HSSItem.m b/HSS/Sources/HSSItem.m new file mode 100644 index 0000000..b63078d --- /dev/null +++ b/HSS/Sources/HSSItem.m @@ -0,0 +1,30 @@ +// +// HSSItem.h +// HSS +// +// Created by Alessandro Volz on 11.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSItem.h" + +@implementation HSSItem + +@synthesize oid = _oid; +@synthesize name = _name; +@synthesize assignable = _assignable; + +NSString* const HSSTab = @" "; + +-( NSMutableString*)descriptionWithTab:(NSInteger)t { + NSMutableString* desc = [NSMutableString string]; + for (int i = 0; i < t; ++i) [desc appendString:HSSTab]; + [desc appendFormat:@"[%@ %@: %@]", self.className, self.oid, self.name]; + return desc; +} + +- (NSString*)description { + return [self descriptionWithTab:0]; +} + +@end diff --git a/HSS/Sources/HSSMedcase.h b/HSS/Sources/HSSMedcase.h new file mode 100644 index 0000000..c051f52 --- /dev/null +++ b/HSS/Sources/HSSMedcase.h @@ -0,0 +1,14 @@ +// +// HSSMedcase.h +// HSS +// +// Created by Alessandro Volz on 11.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSItem.h" + +@interface HSSMedcase : HSSItem { +} + +@end diff --git a/HSS/Sources/HSSMedcase.m b/HSS/Sources/HSSMedcase.m new file mode 100644 index 0000000..3321ea5 --- /dev/null +++ b/HSS/Sources/HSSMedcase.m @@ -0,0 +1,27 @@ +// +// HSSMedcase.m +// HSS +// +// Created by Alessandro Volz on 11.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSMedcase.h" + +@implementation HSSMedcase + +- (BOOL)isLeaf { + return YES; +} + +- (BOOL)assignable { + return YES; + // McKesson said "adding images to an existing case is outside the scope of this project" (Rex Jakobovits, 2012/1/27 00:47:26 HNEC) + // then Christian insisted... +} + +- (NSString*)desc { + return @""; // zero-length description -> medcase name spans over the description cell +} + +@end diff --git a/HSS/Sources/HSSMedcaseCreation.h b/HSS/Sources/HSSMedcaseCreation.h new file mode 100644 index 0000000..f16bc60 --- /dev/null +++ b/HSS/Sources/HSSMedcaseCreation.h @@ -0,0 +1,39 @@ +// +// HSSMedcaseCreation.h +// HSS +// +// Created by Alessandro Volz on 09.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import + +@class HSSAPISession; +@class HSSItem; + +@interface HSSMedcaseCreation : NSThread { + HSSAPISession* _session; + NSString* _caseName; + NSArray* _images; + HSSItem* _destination; + NSString* _diagnosis; + NSString* _history; + BOOL _openFlag; +@private + BOOL _connectionDone; + NSMutableData* _connectionData; + NSWindow* _docWindow; +} + +@property(retain) HSSAPISession* session; +@property(retain) NSString* caseName; +@property(retain) NSArray* images; +@property(retain) HSSItem* destination; +@property(retain) NSString* diagnosis; +@property(retain) NSString* history; +@property BOOL openFlag; +@property(retain) NSWindow* docWindow; + +- (id)initWithSession:(HSSAPISession*)session; + +@end diff --git a/HSS/Sources/HSSMedcaseCreation.m b/HSS/Sources/HSSMedcaseCreation.m new file mode 100644 index 0000000..58150f5 --- /dev/null +++ b/HSS/Sources/HSSMedcaseCreation.m @@ -0,0 +1,260 @@ +// +// HSSMedcaseCreation.m +// HSS +// +// Created by Alessandro Volz on 09.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSMedcaseCreation.h" +#import "HSSAPISession.h" +#import "HSSFolder.h" +#import "HSSMedcase.h" +#import +#import +#import +#import +#import +#import + +@implementation HSSMedcaseCreation + +@synthesize session = _session; +@synthesize caseName = _caseName; +@synthesize images = _images; +@synthesize destination = _destination; +//@synthesize destinationMedcaseOid = _destinationMedcaseOid; +@synthesize diagnosis = _diagnosis; +@synthesize history = _history; +@synthesize openFlag = _openFlag; +@synthesize docWindow = _docWindow; + +- (id)initWithSession:(HSSAPISession*)session { + if ((self = [super init])) { + self.name = NSLocalizedString(@"HSS Submission", nil); + self.session = session; + } + + return self; +} + +- (void)dealloc { + self.history = nil; + self.diagnosis = nil; + self.destination = nil; +// self.destinationMedcaseOid = nil; + self.images = nil; + self.caseName = nil; + self.session = nil; + self.docWindow = nil; + [super dealloc]; +} + ++ (NSArray*)arrayWithUniqueObjectsInArray:(NSArray*)array { + NSMutableArray* r = [NSMutableArray array]; + + for (id obj in array) + if (![r containsObject:obj]) + [r addObject:obj]; + + return r; +} + +- (void)main { + NSString* tempDirPath = nil; + NSString* tempZipPath = nil; + NSDictionary* response = nil; + + @try { + self.status = NSLocalizedString(@"Copying and decompressing DICOM files...", nil); + if (self.docWindow) [self startModalForWindow:self.docWindow]; + if ([self respondsToSelector:@selector(setSupportsBackgrounding:)]) + [self setSupportsBackgrounding:YES]; + + NSArray* completePaths = [[self class] arrayWithUniqueObjectsInArray:[self.images valueForKey:@"completePath"]]; + + tempDirPath = [NSFileManager.defaultManager tmpFilePathInTmp]; + [NSFileManager.defaultManager confirmDirectoryAtPath:tempDirPath]; + + NSError* error = nil; + [NSFileManager.defaultManager setAttributes:[NSDictionary dictionaryWithObjectsAndKeys: + NSUserName(), NSFileOwnerAccountName, + [NSNumber numberWithInt:0755], NSFilePosixPermissions, nil] + ofItemAtPath:tempDirPath error:&error]; + if (error) + NSLog(@"setAttributes error: %@", error); + + // decompress JPEG2000 DICOM files: first copy, then decompress them + + /*{ + BOOL d, e = [NSFileManager.defaultManager fileExistsAtPath:tempDirPath isDirectory:&d]; + NSLog(@"check 0: tempDirPath is %@, exists? %d, dir? %d", tempDirPath, e, d); + NSLog(@"check 1: completePaths count is %d {", (int)completePaths.count); + for (NSString* path in completePaths) { + NSLog(@"\texists? %d for %@", [NSFileManager.defaultManager fileExistsAtPath:path], path); + } + }*/ + + NSMutableArray* dcmFilePaths = [NSMutableArray array]; + NSInteger dcmIndex = 0; + for (NSString* path in completePaths) { + NSString* destPath = [tempDirPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.dcm", (int)(dcmIndex++)]]; + NSError* error = nil; + if ([NSFileManager.defaultManager copyItemAtPath:path toPath:destPath error:&error]) + [dcmFilePaths addObject:destPath]; + else NSLog(@"HSS plugin copy file error: %@", error); + } + + /*{ + NSLog(@"check 2: dcmFilePaths count is %d {", (int)dcmFilePaths.count); + for (NSString* path in dcmFilePaths) { + NSLog(@"\texists? %d for %@", [NSFileManager.defaultManager fileExistsAtPath:path], path); + } + }*/ + + + [DicomCompressor decompressFiles:dcmFilePaths toDirectory:@"sameAsDestination"]; + + /*{ + NSLog(@"check 3: dcmFilePaths count is %d, decompression done... {", (int)dcmFilePaths.count); + for (NSString* path in dcmFilePaths) { + NSLog(@"\texists? %d for %@", [NSFileManager.defaultManager fileExistsAtPath:path], path); + } + }*/ + + if ([self.destination isKindOfClass:[HSSFolder class]]) { + // XML + + self.status = NSLocalizedString(@"Creating MIRC index file...", nil); + + NSString* tempXmlPath = [tempDirPath stringByAppendingPathComponent:@"mirc.xml"]; + + NSXMLElement* root = [NSXMLElement elementWithName:@"MIRCdocument"]; + // hopefully unnecessary: [root addAttribute:[NSXMLNode attributeWithName:@"login" stringValue:self.session.userLogin]]; + //[root addAttribute:[NSXMLNode attributeWithName:@"login" stringValue:self.session.userLogin]]; + // hopefully unnecessary: [root addAttribute:[NSXMLNode attributeWithName:@"password" stringValue:self.session.userPassword]]; + //[root addAttribute:[NSXMLNode attributeWithName:@"password" stringValue:self.session.userPassword]]; + NSXMLDocument* xml = [[NSXMLDocument alloc] initWithRootElement:root]; + + NSXMLElement* section; + + [root addChild:[NSXMLElement elementWithName:@"title" text:self.caseName]]; + // hopefully unnecessary: 2011-12-31 + // hopefully unnecessary: MIRC Mona Bomb Psuedoscorpion + + if (self.history.length){ + section = [NSXMLElement elementWithName:@"section"]; + [section addAttribute:[NSXMLNode attributeWithName:@"heading" stringValue:@"history"]]; + [root addChild:section]; + [section addChild:[NSXMLElement elementWithName:@"history" text:self.history]]; + } + + // hopefully unnecessary:
The <a href="http://rsna.org">RSNA</a> ate my findings.
+ + if (self.diagnosis.length) { + section = [NSXMLElement elementWithName:@"section"]; + [section addAttribute:[NSXMLNode attributeWithName:@"heading" stringValue:@"diagnosis"]]; + [root addChild:section]; + [section addChild:[NSXMLElement elementWithName:@"diagnosis" text:self.diagnosis]]; + } + + // hopefully unnecessary:
I never talk about my cases at dinner because I have 144 of them.
+ // hopefully unnecessary:
Ref: ibid, ¥€$.
+ // hopefully unnecessary:
§ Content containing PHI shouldn't be exported.
+ + section = [NSXMLElement elementWithName:@"section"]; + [section addAttribute:[NSXMLNode attributeWithName:@"heading" stringValue:@"images"]]; + [root addChild:section]; + for (NSString* dcmFileName in [dcmFilePaths valueForKey:@"lastPathComponent"]) { + NSXMLElement* image = [NSXMLElement elementWithName:@"image"]; + [image addAttribute:[NSXMLNode attributeWithName:@"src" stringValue:dcmFileName]]; + [section addChild:image]; + // hopefully unnecessary: O HAI! IM IN UR CR, BOMMING UR RADEEOLAJIST + } + + // hopefully unnecessary: 343.4444 + // hopefully unnecessary: radiologic teaching file + // hopefully unnecessary: Neoplasm + // hopefully unnecessary: Face and Neck + // hopefully unnecessary: CR, Photograph, Other + // hopefully unnecessary: restricted + // hopefully unnecessary: female497 + + xml.characterEncoding = @"UTF-8"; + xml.standalone = YES; + + [[xml XMLData] writeToFile:tempXmlPath atomically:YES]; + + // ZIP + + self.status = NSLocalizedString(@"Archiving image data...", nil); + + // TODO: if two files have the same name (even on different paths), the zip will probably fail... + + tempZipPath = [[NSFileManager.defaultManager tmpFilePathInTmp] stringByAppendingPathExtension:@"zip"]; + + NSMutableArray* args = [NSMutableArray arrayWithObjects: @"-rj", tempZipPath, tempXmlPath, nil]; + [args addObjectsFromArray:dcmFilePaths]; + + /*{ + NSLog(@"Zip command arguments: %@", args); + }*/ + + NSTask* task = [[[NSTask alloc] init] autorelease]; + task.launchPath = @"/usr/bin/zip"; + task.arguments = args; + // task.standardOutput = [NSPipe pipe]; + [task launch]; + [task waitUntilExit]; + // NSLog(@"zip output: %@", [[[NSString alloc] initWithData:[[(NSPipe*)task.standardOutput fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding] autorelease]); + + // UPLOAD + + self.status = NSLocalizedString(@"Posting data to HSS...", nil); + + NSError* error = nil; + response = [self.session postMedcaseWithZipFileAtPath:tempZipPath folderOid:self.destination.oid progressDelegate:self error:&error]; + if (error) + [NSException raise:NSGenericException format:@"%@", error.localizedDescription]; + } + else // so, if destination is not a folder, it must be a medcase.... + { + self.status = NSLocalizedString(@"Posting data to HSS...", nil); + + for (NSInteger i = 0; i < dcmFilePaths.count; ++i) { + self.progress = 1.0/dcmFilePaths.count*i; + + NSString* path = [dcmFilePaths objectAtIndex:i]; + + NSError* error = nil; + response = [self.session putFileAtPath:path intoMedcaseWithOid:self.destination.oid error:&error]; + if (error) + [NSException raise:NSGenericException format:@"%@", error.localizedDescription]; + } + + self.progress = -1; + } + + if (_openFlag) + [NSWorkspace.sharedWorkspace openURL:[NSURL URLWithString:[response valueForKey:@"viewer"]]]; + } @catch (NSException* e) { + [self performSelectorOnMainThread:@selector(_reportException:) withObject:e waitUntilDone:NO]; + } @finally { + if (tempDirPath) [NSFileManager.defaultManager removeItemAtPath:tempDirPath error:NULL]; + if (tempZipPath) [NSFileManager.defaultManager removeItemAtPath:tempZipPath error:NULL]; + } +} + +- (void)HSSAPIProgress:(NSNumber*)progress { + self.progress = progress.floatValue; + if (progress.floatValue == 1) { + self.progress = -1; + self.status = NSLocalizedString(@"Waiting on HSS...", nil); + } +} + +- (void)_reportException:(NSException*)e { + [[NSAlert alertWithMessageText:NSLocalizedString(@"HSS Medcase Creation Error", nil) defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"%@", e.reason] beginSheetModalForWindow:nil modalDelegate:nil didEndSelector:nil contextInfo:nil]; +} + +@end diff --git a/HSS/Sources/HSSPopUpButtonCell.h b/HSS/Sources/HSSPopUpButtonCell.h new file mode 100644 index 0000000..940b454 --- /dev/null +++ b/HSS/Sources/HSSPopUpButtonCell.h @@ -0,0 +1,13 @@ +// +// HSSPopUpButtonCell.h +// HSS +// +// Created by Alessandro Volz on 10.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import + +@interface HSSPopUpButtonCell : NSPopUpButtonCell + +@end diff --git a/HSS/Sources/HSSPopUpButtonCell.m b/HSS/Sources/HSSPopUpButtonCell.m new file mode 100644 index 0000000..5aa0fc9 --- /dev/null +++ b/HSS/Sources/HSSPopUpButtonCell.m @@ -0,0 +1,18 @@ +// +// HSSPopUpButtonCell.m +// HSS +// +// Created by Alessandro Volz on 10.01.12. +// Copyright (c) 2012 OsiriX Team. All rights reserved. +// + +#import "HSSPopUpButtonCell.h" + +@implementation HSSPopUpButtonCell + +-(NSRect)drawTitle:(NSAttributedString*)title withFrame:(NSRect)frame inView:(NSView*)controlView { + title = [[[NSAttributedString alloc] initWithString:self.alternateTitle attributes:[title attributesAtIndex:0 effectiveRange:NULL]] autorelease]; + return [super drawTitle:title withFrame:frame inView:controlView]; +} + +@end diff --git a/HSS/Sources/HSS_Prefix.pch b/HSS/Sources/HSS_Prefix.pch new file mode 100644 index 0000000..bba4261 --- /dev/null +++ b/HSS/Sources/HSS_Prefix.pch @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'ExportSeriesAsNifti' target in the 'ExportSeriesAsNifti' project. +// + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/HSS/Sources/NSString+HSS.h b/HSS/Sources/NSString+HSS.h new file mode 100644 index 0000000..e2d3512 --- /dev/null +++ b/HSS/Sources/NSString+HSS.h @@ -0,0 +1,13 @@ +// +// NSString+HSS.h +// HSS +// +// Created by Alessandro Volz on 03.09.12. +// +// + + + +@interface NSString (HSS) + +@end diff --git a/HSS/Sources/NSString+HSS.m b/HSS/Sources/NSString+HSS.m new file mode 100644 index 0000000..a00cd9b --- /dev/null +++ b/HSS/Sources/NSString+HSS.m @@ -0,0 +1,17 @@ +// +// NSString+HSS.m +// HSS +// +// Created by Alessandro Volz on 03.09.12. +// +// + +#import "NSString+HSS.h" + +@implementation NSString (HSS) + +- (NSComparisonResult)caseDiacriticInsensitiveNumericCompare:(NSString*)str { + return [self compare:str options:NSCaseInsensitiveSearch|NSNumericSearch|NSDiacriticInsensitiveSearch]; +} + +@end