From bea72ac3bbebb9e35e34b840968b4ba0f433e87a Mon Sep 17 00:00:00 2001 From: stuconnolly Date: Sun, 20 Feb 2011 13:06:06 +0000 Subject: Bring outline view branch up to date with trunk (r3188:r3201). --- Interfaces/English.lproj/DBView.xib | 1008 +++++------------------------------ Source/NoodleLineNumberView.h | 12 + Source/NoodleLineNumberView.m | 117 ++-- Source/SPConnectionHandler.m | 7 + Source/SPCustomQuery.m | 7 +- Source/SPDataImport.m | 2 +- Source/SPDatabaseViewController.m | 25 +- Source/SPSQLParser.m | 33 +- Source/SPSSHTunnel.h | 72 +-- Source/SPSSHTunnel.m | 138 ++--- Source/SPTableContent.m | 56 +- Source/SPTableData.h | 4 +- Source/SPTableData.m | 119 +++-- Source/SPTableStructureDelegate.m | 5 +- Source/SPTableTriggers.h | 1 + Source/SPTableTriggers.m | 41 +- Source/SPWindow.m | 2 +- 17 files changed, 483 insertions(+), 1166 deletions(-) diff --git a/Interfaces/English.lproj/DBView.xib b/Interfaces/English.lproj/DBView.xib index 29c812fd..bd064e22 100644 --- a/Interfaces/English.lproj/DBView.xib +++ b/Interfaces/English.lproj/DBView.xib @@ -24,13 +24,12 @@ YES - YES com.brandonwalkin.BWToolkit - com.apple.WebKitIBPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.WebKitIBPlugin PluginDependencyRecalculationVersion @@ -72,6 +71,7 @@ 268 {{-1, -1}, {32, 24}} + YES 67239424 @@ -100,6 +100,7 @@ 268 {{30, -1}, {32, 24}} + YES 71433792 @@ -301,6 +302,7 @@ 268 {{61, -1}, {32, 24}} + YES 67239424 @@ -325,6 +327,7 @@ 268 {{92, -1}, {32, 24}} + YES -2080244224 @@ -351,6 +354,7 @@ {{0, 1}, {214, 23}} + YES YES NO @@ -381,6 +385,7 @@ 290 {{5, 2}, {204, 19}} + YES 343014976 @@ -469,6 +474,7 @@ 4362 {218, 38} + YES @@ -509,7 +515,7 @@ controlColor 3 - MC42NjY2NjY2ODY1AA + MC42NjY2NjY2NjY3AA @@ -554,6 +560,7 @@ {{1, 1}, {218, 38}} + @@ -569,6 +576,7 @@ -2147483392 {{-100, -100}, {15, 8}} + _doScroller: 0.42105263471603394 @@ -578,6 +586,7 @@ -2147483392 {{-100, -100}, {223, 15}} + 1 _doScroller: @@ -586,6 +595,7 @@ {{-1, -13}, {220, 40}} + 2 @@ -596,6 +606,7 @@ {214, 26} + NSView @@ -618,6 +629,7 @@ 4352 {214, 352} + YES @@ -684,6 +696,7 @@ {214, 352} + @@ -694,6 +707,7 @@ -2147483392 {{197, 0}, {15, 292}} + _doScroller: 0.096045196056365967 @@ -704,6 +718,7 @@ -2147483392 {{-100, -100}, {141, 11}} + 257 _doScroller: @@ -712,6 +727,7 @@ {214, 352} + 528 @@ -722,11 +738,13 @@ {{0, 27}, {214, 352}} + NSView {214, 379} + 2 6 @@ -760,6 +778,7 @@ {214, 379} + NSView @@ -782,6 +801,7 @@ 4352 {216, 145} + YES @@ -848,6 +868,7 @@ {216, 145} + @@ -858,6 +879,7 @@ -2147483392 {{-100, -100}, {15, 20}} + _doScroller: 0.99315071105957031 @@ -867,6 +889,7 @@ -2147483392 {{-100, -100}, {141, 11}} + 257 _doScroller: @@ -875,6 +898,7 @@ {{-1, 0}, {216, 145}} + 528 @@ -897,6 +921,7 @@ 4352 {214, 145} + YES @@ -963,6 +988,7 @@ {214, 145} + @@ -973,6 +999,7 @@ -2147483392 {{-100, -100}, {15, 20}} + _doScroller: 0.48965516686439514 @@ -983,6 +1010,7 @@ -2147483392 {{-100, -100}, {141, 11}} + 257 _doScroller: @@ -991,6 +1019,7 @@ {214, 145} + 512 @@ -1001,11 +1030,13 @@ {{0, 380}, {214, 145}} + NSView {{0, 24}, {214, 525}} + 2 NO @@ -1049,6 +1080,7 @@ {214, 549} + NSView @@ -1061,6 +1093,7 @@ 274 {{-7, -10}, {741, 564}} + YES @@ -1085,6 +1118,7 @@ 289 {{662, -1}, {32, 24}} + YES 71433792 @@ -1192,6 +1226,7 @@ -2147483356 {{124, -1}, {33, 25}} + YES -2080244224 @@ -1226,12 +1261,14 @@ 4352 {694, 288} + YES 256 {694, 17} + @@ -1239,6 +1276,7 @@ -2147483392 {{-26, 0}, {16, 17}} + YES @@ -1792,6 +1830,7 @@ {{1, 17}, {694, 288}} + @@ -1802,6 +1841,7 @@ -2147483392 {{674, 17}, {15, 274}} + _doScroller: 0.9480968713760376 @@ -1811,6 +1851,7 @@ -2147483392 {{1, 291}, {694, 15}} + 1 _doScroller: @@ -1826,6 +1867,7 @@ {{1, 0}, {694, 17}} + @@ -1835,6 +1877,7 @@ {{-1, 22}, {696, 306}} + 562 @@ -1849,6 +1892,7 @@ 292 {{-1, -1}, {32, 25}} + YES -1543373312 @@ -1873,6 +1917,7 @@ 292 {{93, -1}, {32, 25}} + YES -2080244224 @@ -1897,6 +1942,7 @@ 292 {{30, -1}, {32, 25}} + YES -1543373312 @@ -1921,6 +1967,7 @@ 292 {{61, -1}, {32, 25}} + YES -1543373312 @@ -1957,6 +2004,7 @@ {{125, 0}, {507, 23}} + YES 130560 @@ -1977,6 +2025,7 @@ 289 {{632, -1}, {32, 25}} + YES -1543373312 @@ -1999,6 +2048,7 @@ {695, 328} + NSView @@ -2016,6 +2066,7 @@ 268 {{3, 3}, {49, 14}} + YES 68288064 @@ -2054,6 +2105,7 @@ {{678, 4}, {10, 13}} + YES 130560 @@ -2072,6 +2124,7 @@ {{0, 181}, {695, 20}} + 1 MC42NzU3Njg0OTQ2IDAuNzIxOTQ4MTQ2OCAwLjc2NTMwNjExNTIAA @@ -2114,12 +2167,14 @@ 4352 {694, 140} + YES 256 {694, 17} + @@ -2127,6 +2182,7 @@ -2147483392 {{-26, 0}, {16, 17}} + YES @@ -2382,6 +2438,7 @@ {{1, 17}, {694, 140}} + @@ -2392,6 +2449,7 @@ -2147483392 {{611, 17}, {15, 126}} + _doScroller: 0.97794115543365479 @@ -2401,6 +2459,7 @@ -2147483392 {{1, 143}, {610, 15}} + 1 _doScroller: @@ -2415,6 +2474,7 @@ {{1, 0}, {694, 17}} + @@ -2424,6 +2484,7 @@ {{-1, 23}, {696, 158}} + 562 @@ -2438,6 +2499,7 @@ 292 {{-1, 0}, {32, 25}} + YES -1543373312 @@ -2459,6 +2521,7 @@ 292 {{61, 0}, {32, 25}} + YES -2080244224 @@ -2480,6 +2543,7 @@ 292 {{30, 0}, {32, 25}} + YES -1543373312 @@ -2513,6 +2577,7 @@ {{93, 1}, {602, 23}} + YES 130560 @@ -2528,16 +2593,19 @@ {{0, 329}, {695, 201}} + NSView {{7, 10}, {695, 530}} + 2 {{10, 7}, {706, 544}} + Structure @@ -6010,7 +6078,7 @@ YES TriggerTableName - 116 + 100 8 1000 @@ -6040,7 +6108,7 @@ TriggerName - 100 + 95 10 3.4028230607370965e+38 @@ -6067,7 +6135,7 @@ TriggerEvent - 85 + 60 10 3.4028230607370965e+38 @@ -6087,14 +6155,14 @@ - 3 + 2 YES YES TriggerActionTime - 121 + 59 10 3.4028230607370965e+38 @@ -6114,20 +6182,20 @@ - 3 + 2 YES YES TriggerStatement - 68 + 144 10 3.4028230607370965e+38 75628096 2048 - Stagement + Statement @@ -6202,7 +6270,7 @@ TriggerSQLMode - 60 + 92 10 3.4028234663852886e+38 @@ -6257,17 +6325,17 @@ _doScroller: - 0.9669603705406189 + 0.96696035242290745 -2147483392 - {{1, 470}, {688, 15}} + {{1, 456}, {694, 15}} 1 _doScroller: - 0.91195797920227051 + 0.99856115107913668 @@ -6425,11 +6493,13 @@ {{215, 0}, {728, 549}} + NSView {943, 549} + YES 2 DBViewSplitter @@ -6478,6 +6548,7 @@ {943, 549} + NSView @@ -7200,7 +7271,7 @@ View - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {213, 107} @@ -7345,7 +7416,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1440, 878}} {213, 129} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 7 @@ -7355,7 +7426,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 Reset Auto Increment NSWindow - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {255, 95} @@ -7518,7 +7589,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1280, 1002}} {255, 117} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 1 @@ -7528,7 +7599,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 New Relation NSPanel - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 256 @@ -8129,7 +8200,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {302, 307} {{0, 0}, {1440, 878}} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 9 @@ -8139,7 +8210,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 New Trigger NSPanel - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {360, 348} @@ -8546,7 +8617,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1680, 1028}} {360, 370} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 3 @@ -8558,7 +8629,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 View - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {213, 50} @@ -8678,7 +8749,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1920, 1178}} {213, 72} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 9 @@ -8690,7 +8761,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 View - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {213, 107} @@ -8825,7 +8896,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1440, 878}} {213, 129} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 15 @@ -8837,7 +8908,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 View - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {350, 200} @@ -9060,7 +9131,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1680, 1028}} {350, 222} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} SPCreateSyntaxSheet @@ -9073,7 +9144,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 View - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {213, 107} @@ -9243,7 +9314,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1440, 878}} {213, 129} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 31 @@ -9253,7 +9324,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 MySQL Help NSPanel - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {351, 120} @@ -9651,7 +9722,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1280, 1002}} {351, 136} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} MYSQL_HELP_WINDOW @@ -9662,7 +9733,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 Filter NSPanel - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} {380, 170} @@ -10304,7 +10375,7 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8 {{0, 0}, {1280, 778}} {380, 192} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} SPTableFilterPanel @@ -10804,7 +10875,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA Secure Text Input Sheet NSPanel - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} 256 @@ -10908,7 +10979,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA {338, 150} {{0, 0}, {1280, 778}} - {3.40282e+38, 3.40282e+38} + {1.79769e+308, 1.79769e+308} @@ -11092,7 +11163,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA disabledControlTextColor 3 - MC4zMzMzMzMzNDMzAA + MC4zMzMzMzMzMzMzAA @@ -25771,7 +25842,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{337, 207}, {943, 549}} + {{59, 207}, {943, 549}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -27899,6 +27970,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA YES YES + activitiesScrollView addDatabaseButton chooseDatabaseButton connectionErrorDialog @@ -27952,6 +28024,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA tableDataInstance tableDumpInstance tableInfoCollapseButton + tableInfoScrollView tableInfoTable tableListSplitter tableRelationsInstance @@ -27970,6 +28043,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA YES + NSScrollView id id NSWindow @@ -28023,6 +28097,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA id id NSButton + NSScrollView NSTableView NSSplitView id @@ -28044,6 +28119,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA YES YES + activitiesScrollView addDatabaseButton chooseDatabaseButton connectionErrorDialog @@ -28097,6 +28173,7 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA tableDataInstance tableDumpInstance tableInfoCollapseButton + tableInfoScrollView tableInfoTable tableListSplitter tableRelationsInstance @@ -28115,6 +28192,10 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA YES + + activitiesScrollView + NSScrollView + addDatabaseButton id @@ -28327,6 +28408,10 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA tableInfoCollapseButton NSButton + + tableInfoScrollView + NSScrollView + tableInfoTable NSTableView @@ -31903,851 +31988,6 @@ bGQgTmFtZQkgID0gQAoJCQkJICBMRU5HVEgoYEBgKSA+IEA - - YES - - SPDatabaseDocument - NSObject - - YES - - YES - addConnectionToFavorites: - addDatabase: - analyzeTable: - backForwardInHistory: - cancelTask: - checkTable: - checksumTable: - chooseDatabase: - chooseEncoding: - closePanelSheet: - closePasswordSheet: - closeSheet: - copyChecksumFromSheet: - copyCreateTableSyntax: - copyCreateTableSyntaxFromSheet: - copyDatabase: - export: - exportSelectedTablesAs: - flushPrivileges: - flushTable: - focusOnTableContentFilter: - focusOnTableListFilter: - import: - importFromClipboard: - openCurrentConnectionInNewWindow: - openDatabaseInNewTab: - optimizeTable: - refreshTables: - removeDatabase: - renameDatabase: - repairTable: - saveConnectionSheet: - saveCreateSyntax: - setDatabases: - showConsole: - showCreateTableSyntax: - showMySQLHelp: - showNavigator: - showServerProcesses: - showServerVariables: - showUserManager: - toggleNavigator: - updateWindowTitle: - validateSaveConnectionAccessory: - - - YES - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - id - - - - YES - - YES - addConnectionToFavorites: - addDatabase: - analyzeTable: - backForwardInHistory: - cancelTask: - checkTable: - checksumTable: - chooseDatabase: - chooseEncoding: - closePanelSheet: - closePasswordSheet: - closeSheet: - copyChecksumFromSheet: - copyCreateTableSyntax: - copyCreateTableSyntaxFromSheet: - copyDatabase: - export: - exportSelectedTablesAs: - flushPrivileges: - flushTable: - focusOnTableContentFilter: - focusOnTableListFilter: - import: - importFromClipboard: - openCurrentConnectionInNewWindow: - openDatabaseInNewTab: - optimizeTable: - refreshTables: - removeDatabase: - renameDatabase: - repairTable: - saveConnectionSheet: - saveCreateSyntax: - setDatabases: - showConsole: - showCreateTableSyntax: - showMySQLHelp: - showNavigator: - showServerProcesses: - showServerVariables: - showUserManager: - toggleNavigator: - updateWindowTitle: - validateSaveConnectionAccessory: - - - YES - - addConnectionToFavorites: - id - - - addDatabase: - id - - - analyzeTable: - id - - - backForwardInHistory: - id - - - cancelTask: - id - - - checkTable: - id - - - checksumTable: - id - - - chooseDatabase: - id - - - chooseEncoding: - id - - - closePanelSheet: - id - - - closePasswordSheet: - id - - - closeSheet: - id - - - copyChecksumFromSheet: - id - - - copyCreateTableSyntax: - id - - - copyCreateTableSyntaxFromSheet: - id - - - copyDatabase: - id - - - export: - id - - - exportSelectedTablesAs: - id - - - flushPrivileges: - id - - - flushTable: - id - - - focusOnTableContentFilter: - id - - - focusOnTableListFilter: - id - - - import: - id - - - importFromClipboard: - id - - - openCurrentConnectionInNewWindow: - id - - - openDatabaseInNewTab: - id - - - optimizeTable: - id - - - refreshTables: - id - - - removeDatabase: - id - - - renameDatabase: - id - - - repairTable: - id - - - saveConnectionSheet: - id - - - saveCreateSyntax: - id - - - setDatabases: - id - - - showConsole: - id - - - showCreateTableSyntax: - id - - - showMySQLHelp: - id - - - showNavigator: - id - - - showServerProcesses: - id - - - showServerVariables: - id - - - showUserManager: - id - - - toggleNavigator: - id - - - updateWindowTitle: - id - - - validateSaveConnectionAccessory: - id - - - - - YES - - YES - activitiesScrollView - addDatabaseButton - chooseDatabaseButton - connectionErrorDialog - contentViewSplitter - copyDatabaseButton - copyDatabaseDataButton - copyDatabaseMessageField - createTableSyntaxTextField - createTableSyntaxTextView - createTableSyntaxWindow - customQueryInstance - customQueryTextView - databaseCopyNameField - databaseCopySheet - databaseDataInstance - databaseEncodingButton - databaseNameField - databaseRenameNameField - databaseRenameSheet - databaseSheet - dbTablesTableView - encodingPopUp - exportControllerInstance - extendedTableInfoInstance - favoritesButton - historyControl - inputTextWindow - inputTextWindowHeader - inputTextWindowMessage - inputTextWindowSecureTextField - listFilterField - parentView - queryProgressBar - renameDatabaseButton - renameDatabaseMessageField - saveConnectionAccessory - saveConnectionAutoConnect - saveConnectionEncrypt - saveConnectionEncryptString - saveConnectionIncludeData - saveConnectionIncludeQuery - saveConnectionSavePassword - saveConnectionSavePasswordAlert - sidebarGrabber - spHistoryControllerInstance - statusTableAccessoryView - statusTableCopyChecksum - statusTableView - statusValues - tableContentInstance - tableDataInstance - tableDumpInstance - tableInfoCollapseButton - tableInfoScrollView - tableInfoTable - tableListSplitter - tableRelationsInstance - tableSourceInstance - tableTabView - tableTriggersInstance - tablesListInstance - taskCancelButton - taskCancellationCallbackObject - taskDescriptionText - taskProgressIndicator - taskProgressLayer - titleAccessoryView - titleImageView - titleStringView - - - YES - NSScrollView - id - id - NSWindow - NSSplitView - id - id - id - NSTextField - NSTextView - NSWindow - id - NSTextView - id - id - id - id - id - id - id - id - NSTableView - NSPopUpButton - id - id - id - id - id - id - id - id - NSSearchField - NSView - id - id - id - id - id - id - NSSecureTextField - id - id - id - id - id - id - id - id - id - id - id - id - id - NSButton - NSScrollView - NSTableView - NSSplitView - id - id - NSTabView - id - id - NSButton - id - id - id - NSBox - id - id - id - - - - YES - - YES - activitiesScrollView - addDatabaseButton - chooseDatabaseButton - connectionErrorDialog - contentViewSplitter - copyDatabaseButton - copyDatabaseDataButton - copyDatabaseMessageField - createTableSyntaxTextField - createTableSyntaxTextView - createTableSyntaxWindow - customQueryInstance - customQueryTextView - databaseCopyNameField - databaseCopySheet - databaseDataInstance - databaseEncodingButton - databaseNameField - databaseRenameNameField - databaseRenameSheet - databaseSheet - dbTablesTableView - encodingPopUp - exportControllerInstance - extendedTableInfoInstance - favoritesButton - historyControl - inputTextWindow - inputTextWindowHeader - inputTextWindowMessage - inputTextWindowSecureTextField - listFilterField - parentView - queryProgressBar - renameDatabaseButton - renameDatabaseMessageField - saveConnectionAccessory - saveConnectionAutoConnect - saveConnectionEncrypt - saveConnectionEncryptString - saveConnectionIncludeData - saveConnectionIncludeQuery - saveConnectionSavePassword - saveConnectionSavePasswordAlert - sidebarGrabber - spHistoryControllerInstance - statusTableAccessoryView - statusTableCopyChecksum - statusTableView - statusValues - tableContentInstance - tableDataInstance - tableDumpInstance - tableInfoCollapseButton - tableInfoScrollView - tableInfoTable - tableListSplitter - tableRelationsInstance - tableSourceInstance - tableTabView - tableTriggersInstance - tablesListInstance - taskCancelButton - taskCancellationCallbackObject - taskDescriptionText - taskProgressIndicator - taskProgressLayer - titleAccessoryView - titleImageView - titleStringView - - - YES - - activitiesScrollView - NSScrollView - - - addDatabaseButton - id - - - chooseDatabaseButton - id - - - connectionErrorDialog - NSWindow - - - contentViewSplitter - NSSplitView - - - copyDatabaseButton - id - - - copyDatabaseDataButton - id - - - copyDatabaseMessageField - id - - - createTableSyntaxTextField - NSTextField - - - createTableSyntaxTextView - NSTextView - - - createTableSyntaxWindow - NSWindow - - - customQueryInstance - id - - - customQueryTextView - NSTextView - - - databaseCopyNameField - id - - - databaseCopySheet - id - - - databaseDataInstance - id - - - databaseEncodingButton - id - - - databaseNameField - id - - - databaseRenameNameField - id - - - databaseRenameSheet - id - - - databaseSheet - id - - - dbTablesTableView - NSTableView - - - encodingPopUp - NSPopUpButton - - - exportControllerInstance - id - - - extendedTableInfoInstance - id - - - favoritesButton - id - - - historyControl - id - - - inputTextWindow - id - - - inputTextWindowHeader - id - - - inputTextWindowMessage - id - - - inputTextWindowSecureTextField - id - - - listFilterField - NSSearchField - - - parentView - NSView - - - queryProgressBar - id - - - renameDatabaseButton - id - - - renameDatabaseMessageField - id - - - saveConnectionAccessory - id - - - saveConnectionAutoConnect - id - - - saveConnectionEncrypt - id - - - saveConnectionEncryptString - NSSecureTextField - - - saveConnectionIncludeData - id - - - saveConnectionIncludeQuery - id - - - saveConnectionSavePassword - id - - - saveConnectionSavePasswordAlert - id - - - sidebarGrabber - id - - - spHistoryControllerInstance - id - - - statusTableAccessoryView - id - - - statusTableCopyChecksum - id - - - statusTableView - id - - - statusValues - id - - - tableContentInstance - id - - - tableDataInstance - id - - - tableDumpInstance - id - - - tableInfoCollapseButton - NSButton - - - tableInfoScrollView - NSScrollView - - - tableInfoTable - NSTableView - - - tableListSplitter - NSSplitView - - - tableRelationsInstance - id - - - tableSourceInstance - id - - - tableTabView - NSTabView - - - tableTriggersInstance - id - - - tablesListInstance - id - - - taskCancelButton - NSButton - - - taskCancellationCallbackObject - id - - - taskDescriptionText - id - - - taskProgressIndicator - id - - - taskProgressLayer - NSBox - - - titleAccessoryView - id - - - titleImageView - id - - - titleStringView - id - - - - - IBDocumentRelativeSource - ../../Source/SPDatabaseDocument.h - - - YES diff --git a/Source/NoodleLineNumberView.h b/Source/NoodleLineNumberView.h index 1d600720..855f75ee 100644 --- a/Source/NoodleLineNumberView.h +++ b/Source/NoodleLineNumberView.h @@ -48,11 +48,23 @@ CGFloat maxWidthOfGlyph6; CGFloat maxWidthOfGlyph7; CGFloat maxWidthOfGlyph8; + CGFloat currentRuleThickness; NSDictionary *textAttributes; // Add support for selection by clicking/dragging NSUInteger dragSelectionStartLine; + SEL lineNumberForCharacterIndexSel; + IMP lineNumberForCharacterIndexIMP; + SEL lineRangeForRangeSel; + SEL numberWithUnsignedIntegerSel; + IMP numberWithUnsignedIntegerIMP; + SEL addObjectSel; + IMP addObjectIMP; + + NSLayoutManager *layoutManager; + NSTextContainer *container; + } @property(retain) NSColor *alternateTextColor; diff --git a/Source/NoodleLineNumberView.m b/Source/NoodleLineNumberView.m index ebca542d..1d44c939 100644 --- a/Source/NoodleLineNumberView.m +++ b/Source/NoodleLineNumberView.m @@ -50,11 +50,13 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); +// Cache loop methods for speed + #pragma mark - @interface NoodleLineNumberView (Private) -- (NSMutableArray *)lineIndices; +- (NSArray *)lineIndices; - (void)invalidateLineIndices; - (void)calculateLines; - (void)updateGutterThicknessConstants; @@ -80,6 +82,17 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); nil] retain]; maxWidthOfGlyph = [[NSString stringWithString:@"8"] sizeWithAttributes:textAttributes].width; [self updateGutterThicknessConstants]; + currentRuleThickness = 0.0f; + + // Cache loop methods for speed + lineNumberForCharacterIndexSel = @selector(lineNumberForCharacterIndex:); + lineNumberForCharacterIndexIMP = [self methodForSelector:lineNumberForCharacterIndexSel]; + lineRangeForRangeSel = @selector(lineRangeForRange:); + addObjectSel = @selector(addObject:); + numberWithUnsignedIntegerSel = @selector(numberWithUnsignedInteger:); + numberWithUnsignedIntegerIMP = [NSNumber methodForSelector:numberWithUnsignedIntegerSel]; + + } return self; @@ -162,9 +175,13 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); if ((aView != nil) && [aView isKindOfClass:[NSTextView class]]) { + layoutManager = [aView layoutManager]; + container = [aView textContainer]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:NSTextStorageDidProcessEditingNotification object:[(NSTextView *)aView textStorage]]; [self invalidateLineIndices]; } + } #pragma mark - @@ -190,8 +207,6 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); NSUInteger line, count, rectCount, i; NSRectArray rects; NSRect visibleRect; - NSLayoutManager *layoutManager; - NSTextContainer *container; NSRange nullRange; NSArray *lines; id view; @@ -207,8 +222,6 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); { nullRange = NSMakeRange(NSNotFound, 0); - layoutManager = [view layoutManager]; - container = [view textContainer]; count = [lines count]; // Find the characters that are currently visible @@ -218,10 +231,6 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); // It doesn't show up in the glyphs so would not be accounted for. range.length++; - // Cache loop methods for speed - SEL lineNumberForCharacterIndexSel = @selector(lineNumberForCharacterIndex:); - IMP lineNumberForCharacterIndexIMP = [self methodForSelector:lineNumberForCharacterIndexSel]; - for (line = (NSUInteger)(*lineNumberForCharacterIndexIMP)(self, lineNumberForCharacterIndexSel, range.location); line < count; line++) { @@ -268,29 +277,6 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); return left; } -- (CGFloat)requiredThickness -{ - NSUInteger lineCount = [[self lineIndices] count]; - if(lineCount < 10) - return maxWidthOfGlyph1; - else if(lineCount < 100) - return maxWidthOfGlyph2; - else if(lineCount < 1000) - return maxWidthOfGlyph3; - else if(lineCount < 10000) - return maxWidthOfGlyph4; - else if(lineCount < 100000) - return maxWidthOfGlyph5; - else if(lineCount < 1000000) - return maxWidthOfGlyph6; - else if(lineCount < 10000000) - return maxWidthOfGlyph7; - else if(lineCount < 100000000) - return maxWidthOfGlyph8; - else - return 100; -} - - (void)drawHashMarksAndLabelsInRect:(NSRect)aRect { id view; @@ -298,21 +284,19 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); bounds = [self bounds]; - if (backgroundColor != nil) - { - [backgroundColor set]; - NSRectFill(bounds); - - [[NSColor colorWithCalibratedWhite:0.58 alpha:1.0] set]; - [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(bounds) - 0.5, NSMinY(bounds)) toPoint:NSMakePoint(NSMaxX(bounds) - 0.5, NSMaxY(bounds))]; - } + // if (backgroundColor != nil) + // { + // [backgroundColor set]; + // NSRectFill(bounds); + // + // [[NSColor colorWithCalibratedWhite:0.58 alpha:1.0] set]; + // [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(bounds) - 0.5, NSMinY(bounds)) toPoint:NSMakePoint(NSMaxX(bounds) - 0.5, NSMaxY(bounds))]; + // } view = [self clientView]; if ([view isKindOfClass:[NSTextView class]]) { - NSLayoutManager *layoutManager; - NSTextContainer *container; NSRect visibleRect; NSRange range, nullRange; NSString *labelText; @@ -322,8 +306,6 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); NSSize stringSize; NSArray *lines; - layoutManager = [view layoutManager]; - container = [view textContainer]; nullRange = NSMakeRange(NSNotFound, 0); yinset = [view textContainerInset].height; @@ -345,9 +327,6 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); CGFloat yinsetMinY = yinset - NSMinY(visibleRect); CGFloat rectHeight; - // Cache loop methods for speed - SEL lineNumberForCharacterIndexSel = @selector(lineNumberForCharacterIndex:); - IMP lineNumberForCharacterIndexIMP = [self methodForSelector:lineNumberForCharacterIndexSel]; for (line = (NSUInteger)(*lineNumberForCharacterIndexIMP)(self, lineNumberForCharacterIndexSel, range.location); line < count; line++) { @@ -496,7 +475,7 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); #pragma mark - #pragma mark PrivateAPI -- (NSMutableArray *)lineIndices +- (NSArray *)lineIndices { if (lineIndices == nil) @@ -519,7 +498,8 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); if ([view isKindOfClass:[NSTextView class]]) { - NSUInteger index, stringLength, lineEnd, contentEnd; + + NSUInteger index, stringLength, lineEnd, contentEnd, lastLine; NSString *textString; CGFloat newThickness; @@ -539,26 +519,47 @@ typedef NSRange (*RangeOfLineIMP)(id object, SEL selector, NSRange range); index = 0; // Cache loop methods for speed - SEL lineRangeForRangeSel = @selector(lineRangeForRange:); - SEL addObjectSel = @selector(addObject:); RangeOfLineIMP rangeOfLineIMP = (RangeOfLineIMP)[textString methodForSelector:lineRangeForRangeSel]; - IMP addObjectIMP = [lineIndices methodForSelector:addObjectSel]; - + addObjectIMP = [lineIndices methodForSelector:addObjectSel]; + do { - (void*)(*addObjectIMP)(lineIndices, addObjectSel, [NSNumber numberWithUnsignedInteger:index]); + (void*)(*addObjectIMP)(lineIndices, addObjectSel, (*numberWithUnsignedIntegerIMP)([NSNumber class], numberWithUnsignedIntegerSel, index)); + lastLine = index; index = NSMaxRange((*rangeOfLineIMP)(textString, lineRangeForRangeSel, NSMakeRange(index, 0))); } while (index < stringLength); // Check if text ends with a new line. - [textString getLineStart:NULL end:&lineEnd contentsEnd:&contentEnd forRange:NSMakeRange([[lineIndices lastObject] unsignedIntegerValue], 0)]; + [textString getLineStart:NULL end:&lineEnd contentsEnd:&contentEnd forRange:NSMakeRange(lastLine, 0)]; if (contentEnd < lineEnd) - (void*)(*addObjectIMP)(lineIndices, addObjectSel, [NSNumber numberWithUnsignedInteger:index]); + (void*)(*addObjectIMP)(lineIndices, addObjectSel, (*numberWithUnsignedIntegerIMP)([NSNumber class], numberWithUnsignedIntegerSel, index)); + + NSUInteger lineCount = [lineIndices count]; + if(lineCount < 10) + newThickness = maxWidthOfGlyph1; + else if(lineCount < 100) + newThickness = maxWidthOfGlyph2; + else if(lineCount < 1000) + newThickness = maxWidthOfGlyph3; + else if(lineCount < 10000) + newThickness = maxWidthOfGlyph4; + else if(lineCount < 100000) + newThickness = maxWidthOfGlyph5; + else if(lineCount < 1000000) + newThickness = maxWidthOfGlyph6; + else if(lineCount < 10000000) + newThickness = maxWidthOfGlyph7; + else if(lineCount < 100000000) + newThickness = maxWidthOfGlyph8; + else + newThickness = 100; - newThickness = [self requiredThickness]; - if (fabs([self ruleThickness] - newThickness) > 1) + if (fabs(currentRuleThickness - newThickness) > 1) { + + currentRuleThickness = newThickness; + // Not a good idea to resize the view during calculations (which can happen during // display). Do a delayed perform (using NSInvocation since arg is a float). NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(setRuleThickness:)]]; diff --git a/Source/SPConnectionHandler.m b/Source/SPConnectionHandler.m index adf21ffa..e66580a9 100644 --- a/Source/SPConnectionHandler.m +++ b/Source/SPConnectionHandler.m @@ -282,6 +282,13 @@ certificateAuthorityCertificatePath:[self sslCACertFileLocationEnabled] ? [self NSInteger newState = [theTunnel state]; + // If the user cancelled the password prompt dialog + if ([theTunnel passwordPromptCancelled]) { + [self _restoreConnectionInterface]; + + return; + } + if (newState == PROXY_STATE_IDLE) { [dbDocument setTitlebarStatus:NSLocalizedString(@"SSH Disconnected", @"SSH disconnected titlebar marker")]; diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m index b2859010..c43df1cd 100644 --- a/Source/SPCustomQuery.m +++ b/Source/SPCustomQuery.m @@ -882,12 +882,13 @@ [resultData removeAllRows]; pthread_mutex_unlock(&resultDataLock); + // Set the column count on the data store before setting up anything else - + // ensures that SPDataStorage is set up for timer-driven data loads + [resultData setColumnCount:[theResult numOfFields]]; + // Set up the table updates timer [[self onMainThread] initQueryLoadTimer]; - // Set the column count on the data store - [resultData setColumnCount:[theResult numOfFields]]; - // Set up an autorelease pool for row processing dataLoadingPool = [[NSAutoreleasePool alloc] init]; diff --git a/Source/SPDataImport.m b/Source/SPDataImport.m index 0a521cc2..bfde3a26 100644 --- a/Source/SPDataImport.m +++ b/Source/SPDataImport.m @@ -1014,7 +1014,7 @@ insertBaseStringHasEntries = NO; for (i = 0; i < [fieldMappingArray count]; i++) { if ([NSArrayObjectAtIndex(fieldMapperOperator, i) integerValue] == 0) { - if (insertBaseStringHasEntries) [insertBaseString appendString:@","]; + if (insertBaseStringHasEntries) [insertRemainingBaseString appendString:@","]; else insertBaseStringHasEntries = YES; [insertRemainingBaseString appendString:[NSArrayObjectAtIndex(fieldMappingTableColumnNames, i) backtickQuotedString]]; } diff --git a/Source/SPDatabaseViewController.m b/Source/SPDatabaseViewController.m index 03153230..a6600d3f 100644 --- a/Source/SPDatabaseViewController.m +++ b/Source/SPDatabaseViewController.m @@ -280,7 +280,7 @@ [tableSourceInstance loadTable:nil]; [tableContentInstance loadTable:nil]; [[extendedTableInfoInstance onMainThread] loadTable:nil]; - [[tableTriggersInstance onMainThread] loadTriggers]; + [[tableTriggersInstance onMainThread] resetInterface]; structureLoaded = NO; contentLoaded = NO; statusLoaded = NO; @@ -393,8 +393,12 @@ // Update the window title [self updateWindowTitle:self]; - // Reset table information caches + // Reset table information caches and mark that all loaded views require their data reloading [tableDataInstance resetAllData]; + structureLoaded = NO; + contentLoaded = NO; + statusLoaded = NO; + triggersLoaded = NO; // Ensure status and details are fetched using UTF8 NSString *previousEncoding = [mySQLConnection encoding]; @@ -408,7 +412,8 @@ [tableDataInstance updateStatusInformationForCurrentTable]; // Check the current encoding against the table encoding to see whether - // an encoding change and reset is required + // an encoding change and reset is required. This also caches table information on + // the working thread. if( selectedTableType == SPTableTypeView || selectedTableType == SPTableTypeTable) { // tableEncoding == nil indicates that there was an error while retrieving table data @@ -426,24 +431,12 @@ if (changeEncoding) [mySQLConnection restoreStoredEncoding]; - // Cache table information on the working thread - if (selectedTableType == SPTableTypeView) - [tableDataInstance updateInformationForCurrentView]; - else - [tableDataInstance updateInformationForCurrentTable]; - // Notify listeners of the table change now that the state is fully set up. [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPTableChangedNotification object:self]; // Restore view states as appropriate [spHistoryControllerInstance restoreViewStates]; - // Mark that all loaded views require their data reloading - structureLoaded = NO; - contentLoaded = NO; - statusLoaded = NO; - triggersLoaded = NO; - // Load the currently selected view if looking at a table or view if (tableEncoding && (selectedTableType == SPTableTypeView || selectedTableType == SPTableTypeTable)) { @@ -475,7 +468,7 @@ if (!structureLoaded) [tableSourceInstance loadTable:nil]; if (!contentLoaded) [tableContentInstance loadTable:nil]; if (!statusLoaded) [[extendedTableInfoInstance onMainThread] loadTable:nil]; - if (!triggersLoaded) [[tableTriggersInstance onMainThread] loadTriggers]; + if (!triggersLoaded) [[tableTriggersInstance onMainThread] resetInterface]; // Update the "Show Create Syntax" window if it's already opened // according to the selected table/view/proc/func diff --git a/Source/SPSQLParser.m b/Source/SPSQLParser.m index 4cb0aaa4..1650b2c2 100644 --- a/Source/SPSQLParser.m +++ b/Source/SPSQLParser.m @@ -668,6 +668,7 @@ TO_BUFFER_STATE to_scan_string (const char *); // Cache frequently used selectors, avoiding dynamic binding overhead IMP charAtIndex = [self methodForSelector:@selector(_charAtIndex:)]; + SEL charAtIndexSEL = @selector(_charAtIndex:); IMP endIndex = [self methodForSelector:@selector(endIndexOfStringQuotedByCharacter:startingAtIndex:)]; IMP substringWithRange = [self methodForSelector:@selector(substringWithRange:)]; @@ -676,7 +677,7 @@ TO_BUFFER_STATE to_scan_string (const char *); // Walk along the string, processing characters for (currentStringIndex = startIndex + 1; currentStringIndex < stringLength; currentStringIndex++) { - currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex); + currentCharacter = (unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex); // Check for the ending character, and if it has been found and quoting/brackets is valid, return. // If delimiter support is active and a delimiter is set, check for the delimiter @@ -722,8 +723,8 @@ TO_BUFFER_STATE to_scan_string (const char *); // For comments starting "--[\s]", ensure the start syntax is valid before proceeding. case '-': if (stringLength < currentStringIndex + 2) break; - if ((unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex+1) != '-') break; - if (![[NSCharacterSet whitespaceCharacterSet] characterIsMember:(unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex+2)]) break; + if ((unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex+1) != '-') break; + if (![[NSCharacterSet whitespaceCharacterSet] characterIsMember:(unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex+2)]) break; currentStringIndex = [self endIndexOfCommentOfType:SPDoubleDashComment startingAtIndex:currentStringIndex]; break; @@ -736,7 +737,7 @@ TO_BUFFER_STATE to_scan_string (const char *); case '/': if(ignoreCommentStrings) break; if (stringLength < currentStringIndex + 1) break; - if ((unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex+1) != '*') break; + if ((unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex+1) != '*') break; currentStringIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex]; break; @@ -754,15 +755,15 @@ TO_BUFFER_STATE to_scan_string (const char *); // and that the "d" is the start of a word if (supportDelimiters && stringLength >= currentStringIndex + 11 && (currentStringIndex == 0 - || [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:(unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex-1)])) + || [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:(unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex-1)])) { - switch((unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex+1)) { + switch((unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex+1)) { case 'e': case 'E': - switch((unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex+2)) { + switch((unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex+2)) { case 'l': case 'L': - switch((unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex+3)) { + switch((unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex+3)) { case 'i': case 'I': if([self isMatchedByRegex:@"^(delimiter[ \\t]+(\\S+))(?=\\s|\\Z)" @@ -819,18 +820,19 @@ TO_BUFFER_STATE to_scan_string (const char *); // Cache the charAtIndex selector, avoiding dynamic binding overhead IMP charAtIndex = [self methodForSelector:@selector(_charAtIndex:)]; + SEL charAtIndexSEL = @selector(_charAtIndex:); stringLength = [string length]; // Walk the string looking for the string end for ( currentStringIndex = index; currentStringIndex < stringLength; currentStringIndex++) { - currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex); + currentCharacter = (unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex); // If the string end is a backtick and one has been encountered, treat it as end of string if (quoteCharacter == '`' && currentCharacter == '`') { // ...as long as the next character isn't also a backtick, in which case it's being quoted. Skip both. - if ((currentStringIndex + 1) < stringLength && (unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex+1) == '`') { + if ((currentStringIndex + 1) < stringLength && (unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex+1) == '`') { currentStringIndex++; continue; } @@ -844,7 +846,7 @@ TO_BUFFER_STATE to_scan_string (const char *); characterIsEscaped = NO; i = 1; quotedStringLength = currentStringIndex - 1; - while ((quotedStringLength - i) > 0 && (unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex - i) == '\\') { + while ((quotedStringLength - i) > 0 && (unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex - i) == '\\') { characterIsEscaped = !characterIsEscaped; i++; } @@ -852,7 +854,7 @@ TO_BUFFER_STATE to_scan_string (const char *); // If an even number have been found, it may be the end of the string - as long as the subsequent character // isn't also the same character, in which case it's another form of escaping. if (!characterIsEscaped) { - if ((currentStringIndex + 1) < stringLength && (unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), currentStringIndex+1) == quoteCharacter) { + if ((currentStringIndex + 1) < stringLength && (unichar)(long)(*charAtIndex)(self, charAtIndexSEL, currentStringIndex+1) == quoteCharacter) { currentStringIndex++; continue; } @@ -876,6 +878,7 @@ TO_BUFFER_STATE to_scan_string (const char *); // Cache the charAtIndex selector, avoiding dynamic binding overhead IMP charAtIndex = [self methodForSelector:@selector(_charAtIndex:)]; + SEL charAtIndexSEL = @selector(_charAtIndex:); switch (commentType) { @@ -888,7 +891,7 @@ TO_BUFFER_STATE to_scan_string (const char *); case SPHashComment: index++; for ( ; index < stringLength; index++ ) { - currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), index); + currentCharacter = (unichar)(long)(*charAtIndex)(self, charAtIndexSEL, index); if (currentCharacter == '\r') containsCRs = YES; if (currentCharacter == '\r' || currentCharacter == '\n') { return index-1; @@ -901,8 +904,8 @@ TO_BUFFER_STATE to_scan_string (const char *); case SPCStyleComment: index = index+2; for ( ; index < stringLength; index++ ) { - if ((unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), index) == '*') { - if ((stringLength > index + 1) && (unichar)(long)(*charAtIndex)(self, @selector(_charAtIndex:), index+1) == '/') { + if ((unichar)(long)(*charAtIndex)(self, charAtIndexSEL, index) == '*') { + if ((stringLength > index + 1) && (unichar)(long)(*charAtIndex)(self, charAtIndexSEL, index+1) == '/') { return (index+1); } } diff --git a/Source/SPSSHTunnel.h b/Source/SPSSHTunnel.h index 067cf42a..4afec738 100644 --- a/Source/SPSSHTunnel.h +++ b/Source/SPSSHTunnel.h @@ -27,18 +27,11 @@ @interface SPSSHTunnel : NSObject { - IBOutlet NSWindow *sshQuestionDialog; - IBOutlet NSTextField *sshQuestionText; - IBOutlet NSButton *sshPasswordKeychainCheckbox; - IBOutlet NSWindow *sshPasswordDialog; - IBOutlet NSTextField *sshPasswordText; - IBOutlet NSSecureTextField *sshPasswordField; + id delegate; NSWindow *parentWindow; NSTask *task; NSPipe *standardError; - id delegate; - SEL stateChangeSelector; NSConnection *tunnelConnection; NSString *lastError; NSString *tunnelConnectionName; @@ -53,9 +46,6 @@ NSString *identityFilePath; NSMutableArray *debugMessages; NSLock *debugMessagesLock; - BOOL useHostFallback; - BOOL requestedResponse; - BOOL passwordInKeychain; NSInteger sshPort; NSInteger remotePort; NSInteger localPort; @@ -64,29 +54,45 @@ NSLock *answerAvailableLock; NSString *currentKeyName; + + SEL stateChangeSelector; + + BOOL useHostFallback; + BOOL requestedResponse; + BOOL passwordInKeychain; + BOOL passwordPromptCancelled; + + IBOutlet NSWindow *sshQuestionDialog; + IBOutlet NSTextField *sshQuestionText; + IBOutlet NSButton *sshPasswordKeychainCheckbox; + IBOutlet NSWindow *sshPasswordDialog; + IBOutlet NSTextField *sshPasswordText; + IBOutlet NSSecureTextField *sshPasswordField; } -- (id) initToHost:(NSString *) theHost port:(NSInteger) thePort login:(NSString *) theLogin tunnellingToPort:(NSInteger) targetPort onHost:(NSString *) targetHost; -- (BOOL) setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate; -- (void) setParentWindow:(NSWindow *)theWindow; -- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount; -- (BOOL) setPassword:(NSString *)thePassword; -- (BOOL) setKeyFilePath:(NSString *)thePath; -- (NSInteger) state; -- (NSString *) lastError; -- (NSString *) debugMessages; -- (NSInteger) localPort; -- (NSInteger) localPortFallback; -- (void) connect; -- (void) launchTask:(id) dummy; -- (void) disconnect; -- (void) standardErrorHandler:(NSNotification*)aNotification; -- (NSString *) getPasswordWithVerificationHash:(NSString *)theHash; -- (BOOL) getResponseForQuestion:(NSString *)theQuestion; -- (void) workerGetResponseForQuestion:(NSString *)theQuestion; -- (NSString *) getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash; -- (void) workerGetPasswordForQuery:(NSString *)theQuery; -- (IBAction) closeSSHQuestionSheet:(id)sender; -- (IBAction) closeSSHPasswordSheet:(id)sender; +@property (readonly) BOOL passwordPromptCancelled; + +- (id)initToHost:(NSString *)theHost port:(NSInteger)thePort login:(NSString *)theLogin tunnellingToPort:(NSInteger)targetPort onHost:(NSString *)targetHost; +- (BOOL)setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate; +- (void)setParentWindow:(NSWindow *)theWindow; +- (BOOL)setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount; +- (BOOL)setPassword:(NSString *)thePassword; +- (BOOL)setKeyFilePath:(NSString *)thePath; +- (NSInteger)state; +- (NSString *)lastError; +- (NSString *)debugMessages; +- (NSInteger)localPort; +- (NSInteger)localPortFallback; +- (void)connect; +- (void)launchTask:(id)dummy; +- (void)disconnect; +- (void)standardErrorHandler:(NSNotification*)aNotification; +- (NSString *)getPasswordWithVerificationHash:(NSString *)theHash; +- (BOOL)getResponseForQuestion:(NSString *)theQuestion; +- (void)workerGetResponseForQuestion:(NSString *)theQuestion; +- (NSString *)getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash; +- (void)workerGetPasswordForQuery:(NSString *)theQuery; +- (IBAction)closeSSHQuestionSheet:(id)sender; +- (IBAction)closeSSHPasswordSheet:(id)sender; @end diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m index 0d4b5993..e112e624 100644 --- a/Source/SPSSHTunnel.m +++ b/Source/SPSSHTunnel.m @@ -32,57 +32,62 @@ @implementation SPSSHTunnel +@synthesize passwordPromptCancelled; + /* * Initialise with the supplied connection details. Host, login and port should all be provided. * The password can either be set later via setPassword:, which stores the password locally and is * therefore not recommended, or via setPasswordKeychainName:, which will use the keychain on-demand * and is therefore preferred. */ -- (id) initToHost:(NSString *) theHost port:(NSInteger) thePort login:(NSString *) theLogin tunnellingToPort:(NSInteger) targetPort onHost:(NSString *) targetHost +- (id)initToHost:(NSString *)theHost port:(NSInteger)thePort login:(NSString *)theLogin tunnellingToPort:(NSInteger)targetPort onHost:(NSString *)targetHost { if (!theHost || !targetPort || !targetHost) return nil; - self = [super init]; - - // Store the connection settings as appropriate - sshHost = [[NSString alloc] initWithString:theHost]; - sshLogin = [[NSString alloc] initWithString:(theLogin?theLogin:@"")]; - sshPort = thePort; - useHostFallback = [theHost isEqualToString:targetHost]; - remoteHost = [[NSString alloc] initWithString:targetHost]; - remotePort = targetPort; - delegate = nil; - stateChangeSelector = nil; - lastError = nil; - debugMessages = [[NSMutableArray alloc] init]; - debugMessagesLock = [[NSLock alloc] init]; - answerAvailableLock = [[NSLock alloc] init]; - - // Set up a connection for use by the tunnel process - tunnelConnectionName = [[NSString alloc] initWithFormat:@"SequelPro-%lu", (unsigned long)[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; - tunnelConnectionVerifyHash = [[NSString alloc] initWithFormat:@"%lu", (unsigned long)[[NSString stringWithFormat:@"%f-seeded", [[NSDate date] timeIntervalSince1970]] hash]]; - tunnelConnection = [NSConnection new]; - [tunnelConnection runInNewThread]; - [tunnelConnection removeRunLoop:[NSRunLoop currentRunLoop]]; - [tunnelConnection setRootObject:self]; - if ([tunnelConnection registerName:tunnelConnectionName] == NO) { - return nil; + if ((self = [super init])) { + + // Store the connection settings as appropriate + sshHost = [[NSString alloc] initWithString:theHost]; + sshLogin = [[NSString alloc] initWithString:(theLogin?theLogin:@"")]; + sshPort = thePort; + useHostFallback = [theHost isEqualToString:targetHost]; + remoteHost = [[NSString alloc] initWithString:targetHost]; + remotePort = targetPort; + delegate = nil; + stateChangeSelector = nil; + lastError = nil; + debugMessages = [[NSMutableArray alloc] init]; + debugMessagesLock = [[NSLock alloc] init]; + answerAvailableLock = [[NSLock alloc] init]; + + // Set up a connection for use by the tunnel process + tunnelConnectionName = [[NSString alloc] initWithFormat:@"SequelPro-%lu", (unsigned long)[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; + tunnelConnectionVerifyHash = [[NSString alloc] initWithFormat:@"%lu", (unsigned long)[[NSString stringWithFormat:@"%f-seeded", [[NSDate date] timeIntervalSince1970]] hash]]; + tunnelConnection = [NSConnection new]; + + [tunnelConnection runInNewThread]; + [tunnelConnection removeRunLoop:[NSRunLoop currentRunLoop]]; + [tunnelConnection setRootObject:self]; + + if (![tunnelConnection registerName:tunnelConnectionName]) return nil; + + parentWindow = nil; + identityFilePath = nil; + sshQuestionDialog = nil; + sshPasswordDialog = nil; + password = nil; + keychainName = nil; + keychainAccount = nil; + requestedPassphrase = nil; + task = nil; + localPort = 0; + connectionState = PROXY_STATE_IDLE; + + requestedResponse = NO; + passwordInKeychain = NO; + passwordPromptCancelled = NO; } - parentWindow = nil; - identityFilePath = nil; - sshQuestionDialog = nil; - sshPasswordDialog = nil; - password = nil; - keychainName = nil; - keychainAccount = nil; - passwordInKeychain = NO; - requestedPassphrase = nil; - requestedResponse = NO; - task = nil; - localPort = 0; - connectionState = PROXY_STATE_IDLE; - return self; } @@ -90,7 +95,7 @@ * Sets the connection callback selector; a function to be called whenever the tunnel state changes. * The callback function will be called and passed this SSH Tunnel object.. */ -- (BOOL) setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate +- (BOOL)setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate { delegate = theDelegate; stateChangeSelector = theStateChangeSelector; @@ -119,7 +124,7 @@ * Sets the password to be stored (and returned to the tunnel authenticator) locally. * Providing a keychain name is much more secure. */ -- (BOOL) setPassword:(NSString *)thePassword +- (BOOL)setPassword:(NSString *)thePassword { if (passwordInKeychain) return NO; password = [[NSString alloc] initWithString:thePassword]; @@ -130,7 +135,7 @@ /** * Sets the path of an identity file, or public key file, to use when connecting. */ -- (BOOL) setKeyFilePath:(NSString *)thePath +- (BOOL)setKeyFilePath:(NSString *)thePath { NSString *expandedPath = [thePath stringByExpandingTildeInPath]; if (![[NSFileManager defaultManager] fileExistsAtPath:expandedPath]) return NO; @@ -144,7 +149,7 @@ * Sets the keychain name to use to retrieve the password. This is the recommended and * secure way of supplying a password to the SSH tunnel. */ -- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount +- (BOOL)setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount { if (password) [password release], password = nil; @@ -158,7 +163,7 @@ /* * Get the state of the connection. */ -- (NSInteger) state +- (NSInteger)state { // See if an auth dialog is up @@ -174,9 +179,10 @@ /* * Returns the last error string, if any. */ -- (NSString *) lastError +- (NSString *)lastError { if (!lastError) return nil; + return [NSString stringWithString:lastError]; } @@ -184,7 +190,7 @@ * Returns all the debug text for this tunnel as a string, separated * by line endings. */ -- (NSString *) debugMessages { +- (NSString *)debugMessages { [debugMessagesLock lock]; NSString *debugMessagesString = [debugMessages componentsJoinedByString:@"\n"]; [debugMessagesLock unlock]; @@ -194,7 +200,7 @@ /* * Initiate the SSH tunnel connection, launching the task in a background thread. */ -- (void) connect +- (void)connect { localPort = 0; @@ -210,7 +216,7 @@ * tunnel to the remote server. * Sets up and tears down as appropriate for usage in a background thread. */ -- (void) launchTask:(id) dummy +- (void)launchTask:(id) dummy { if (connectionState != PROXY_STATE_IDLE || task) return; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -482,7 +488,7 @@ /* * Returns the local port assigned for use by the tunnel */ -- (NSInteger) localPort +- (NSInteger)localPort { return localPort; } @@ -490,9 +496,10 @@ /* * Returns the local port assigned for fallback use by the tunnel, if any */ -- (NSInteger) localPortFallback +- (NSInteger)localPortFallback { if (!useHostFallback) return 0; + return localPortFallback; } @@ -513,7 +520,7 @@ * a boolean. This is used by the SSH_ASKPASS environment setting to deal with situations like * host key mismatches. */ -- (BOOL) getResponseForQuestion:(NSString *)theQuestion +- (BOOL)getResponseForQuestion:(NSString *)theQuestion { // Lock the answer available lock [[answerAvailableLock onMainThread] lock]; @@ -530,12 +537,12 @@ // Unlock the lock again [answerAvailableLock unlock]; - //return the answer + // Return the answer return response; } -- (void) workerGetResponseForQuestion:(NSString *)theQuestion -{ +- (void)workerGetResponseForQuestion:(NSString *)theQuestion +{ NSSize questionTextSize; NSRect windowFrameRect; @@ -550,10 +557,11 @@ [NSApp beginSheet:sshQuestionDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [parentWindow makeKeyAndOrderFront:self]; } + /* * Ends an existing modal session */ -- (IBAction) closeSSHQuestionSheet:(id)sender +- (IBAction)closeSSHQuestionSheet:(id)sender { requestedResponse = [sender tag]==1 ? YES : NO; [NSApp endSheet:sshQuestionDialog]; @@ -565,9 +573,11 @@ * Method to allow an SSH tunnel to request a password. This is used by the program set by the * SSH_ASKPASS environment setting to request passphrases for SSH keys. */ -- (NSString *) getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash +- (NSString *)getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash { if (![theHash isEqualToString:tunnelConnectionVerifyHash]) return nil; + + if (passwordPromptCancelled) return nil; // Lock the answer available lock [[answerAvailableLock onMainThread] lock]; @@ -591,18 +601,21 @@ // Return the answer return thePassword; } -- (void) workerGetPasswordForQuery:(NSString *)theQuery + +- (void)workerGetPasswordForQuery:(NSString *)theQuery { NSSize queryTextSize; NSRect windowFrameRect; // Work out whether a passphrase is being requested, extracting the key name NSString *keyName = [theQuery stringByMatching:@"^\\s*Enter passphrase for key \\'(.*)\\':\\s*$" capture:1L]; + if (keyName) { [sshPasswordText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Enter your password for the SSH key\n\"%@\"", @"SSH key password prompt"), keyName]]; [sshPasswordKeychainCheckbox setHidden:NO]; currentKeyName = [keyName retain]; - } else { + } + else { [sshPasswordText setStringValue:theQuery]; [sshPasswordKeychainCheckbox setHidden:YES]; currentKeyName = nil; @@ -612,6 +625,7 @@ queryTextSize = [[sshPasswordText cell] cellSizeForBounds:NSMakeRect(0, 0, [sshPasswordText bounds].size.width, 500)]; windowFrameRect = [sshPasswordDialog frame]; windowFrameRect.size.height = ((queryTextSize.height < 40)?40:queryTextSize.height) + 140 + ([sshPasswordDialog isSheet]?0:22); + [sshPasswordDialog setFrame:windowFrameRect display:NO]; [NSApp beginSheet:sshPasswordDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [parentWindow makeKeyAndOrderFront:self]; @@ -620,9 +634,10 @@ /* * Ends an existing modal session */ -- (IBAction) closeSSHPasswordSheet:(id)sender +- (IBAction)closeSSHPasswordSheet:(id)sender { requestedResponse = [sender tag]==1 ? YES : NO; + [NSApp endSheet:sshPasswordDialog]; [sshPasswordDialog orderOut:nil]; @@ -645,10 +660,13 @@ currentKeyName = nil; } } + + if (!requestedPassphrase) passwordPromptCancelled = YES; [[answerAvailableLock onMainThread] unlock]; } +#pragma mark - - (void)dealloc { diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 7acc0187..a0b184dd 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -445,7 +445,13 @@ // Set up the column theCol = [[NSTableColumn alloc] initWithIdentifier:[columnDefinition objectForKey:@"datacolumnindex"]]; [[theCol headerCell] setStringValue:[columnDefinition objectForKey:@"name"]]; - [theCol setHeaderToolTip:[NSString stringWithFormat:@"%@ – %@%@", [columnDefinition objectForKey:@"name"], [columnDefinition objectForKey:@"type"], ([columnDefinition objectForKey:@"length"]) ? [NSString stringWithFormat:@"(%@)", [columnDefinition objectForKey:@"length"]] : @""]]; + [theCol setHeaderToolTip:[NSString stringWithFormat:@"%@ – %@%@%@%@", + [columnDefinition objectForKey:@"name"], + [columnDefinition objectForKey:@"type"], + ([columnDefinition objectForKey:@"length"]) ? [NSString stringWithFormat:@"(%@)", [columnDefinition objectForKey:@"length"]] : @"", + ([columnDefinition objectForKey:@"values"]) ? [NSString stringWithFormat:@"(\n- %@\n)", [[columnDefinition objectForKey:@"values"] componentsJoinedByString:@"\n- "]] : @"", + ([columnDefinition objectForKey:@"comment"] && [[columnDefinition objectForKey:@"comment"] length]) ? [NSString stringWithFormat:@"\n%@", [[columnDefinition objectForKey:@"comment"] stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"]] : @"" + ]]; [theCol setEditable:YES]; // Set up column for filterTable @@ -799,12 +805,13 @@ BOOL *columnBlobStatuses = malloc(dataColumnsCount * sizeof(BOOL)); tableLoadTargetRowCount = targetRowCount; + // Set the column count on the data store before setting up anything else - + // ensures that SPDataStorage is set up for timer-driven data loads + [tableValues setColumnCount:dataColumnsCount]; + // Set up the table updates timer [[self onMainThread] initTableLoadTimer]; - // Set the column count on the data store - [tableValues setColumnCount:dataColumnsCount]; - NSAutoreleasePool *dataLoadingPool; NSProgressIndicator *dataLoadingIndicator = [tableDocumentInstance valueForKey:@"queryProgressBar"]; BOOL prefsLoadBlobsAsNeeded = [prefs boolForKey:SPLoadBlobsAsNeeded]; @@ -2251,7 +2258,10 @@ return; } + // Retrieve the current field comparison setting for later restoration if possible + NSString *titleToRestore = [[compareField selectedItem] title]; + // Reset the menu before building it back up [compareField removeAllItems]; NSString *fieldTypeGrouping; @@ -2372,6 +2382,10 @@ [menu addItem:item]; [item release]; + // Attempt to reselect the previously selected title, falling back to the first item + [compareField selectItemWithTitle:titleToRestore]; + if (![compareField selectedItem]) [compareField selectItemAtIndex:0]; + // Update the argumentField enabled state [self performSelectorOnMainThread:@selector(toggleFilterField:) withObject:self waitUntilDone:YES]; @@ -3455,7 +3469,7 @@ * - if blob data can be interpret as image data display the image as transparent thumbnail * (up to now using base64 encoded HTML data) */ -- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(SPTextAndLinkCell *)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation +- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(id)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation { if(aTableView == filterTableView) { @@ -4063,9 +4077,17 @@ BOOL isBlob = [tableDataInstance columnIsBlobOrText:[[aTableColumn headerCell] stringValue]]; BOOL isFieldEditable = YES; + // Retrieve the column defintion + NSDictionary *columnDefinition = nil; + for(id c in cqColumnDefinition) { + if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:[aTableColumn identifier]]) { + columnDefinition = [NSDictionary dictionaryWithDictionary:c]; + break; + } + } // Open the sheet if the multipleLineEditingButton is enabled or the column was a blob or a text. - if ([multipleLineEditingButton state] == NSOnState || isBlob) { + if (([multipleLineEditingButton state] == NSOnState || isBlob) && ![[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"]) { // A table is per definitionem editable isFieldEditable = YES; @@ -4081,19 +4103,13 @@ NSString *fieldEncoding = nil; BOOL allowNULL = YES; - // Retrieve the column defintion - for(id c in cqColumnDefinition) { - if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:[aTableColumn identifier]]) { - fieldType = [c objectForKey:@"type"]; - if([c objectForKey:@"char_length"]) - fieldLength = [[c objectForKey:@"char_length"] integerValue]; - if([c objectForKey:@"null"]) - allowNULL = (![[c objectForKey:@"null"] integerValue]); - if([c objectForKey:@"charset_name"] && ![[c objectForKey:@"charset_name"] isEqualToString:@"binary"]) - fieldEncoding = [c objectForKey:@"charset_name"]; - break; - } - } + fieldType = [columnDefinition objectForKey:@"type"]; + if([columnDefinition objectForKey:@"char_length"]) + fieldLength = [[columnDefinition objectForKey:@"char_length"] integerValue]; + if([columnDefinition objectForKey:@"null"]) + allowNULL = (![[columnDefinition objectForKey:@"null"] integerValue]); + if([columnDefinition objectForKey:@"charset_name"] && ![[columnDefinition objectForKey:@"charset_name"] isEqualToString:@"binary"]) + fieldEncoding = [columnDefinition objectForKey:@"charset_name"]; if(fieldEditor) [fieldEditor release], fieldEditor = nil; fieldEditor = [[SPFieldEditorController alloc] init]; @@ -4393,7 +4409,7 @@ // Check if current edited field is a blob if ((fieldType = [[tableDataInstance columnWithName:[[NSArrayObjectAtIndex([tableContentView tableColumns], column) headerCell] stringValue]] objectForKey:@"typegrouping"]) - && ([fieldType isEqualToString:@"textdata"] || [fieldType isEqualToString:@"blobdata"] || [multipleLineEditingButton state] == NSOnState)) + && ![fieldType isEqualToString:@"enum"] && ([fieldType isEqualToString:@"textdata"] || [fieldType isEqualToString:@"blobdata"] || [multipleLineEditingButton state] == NSOnState)) { [tableContentView setFieldEditorSelectedRange:[fieldEditor selectedRange]]; diff --git a/Source/SPTableData.h b/Source/SPTableData.h index cb24d51f..4503bd03 100644 --- a/Source/SPTableData.h +++ b/Source/SPTableData.h @@ -41,9 +41,9 @@ MCPConnection *mySQLConnection; - BOOL isWorking; - BOOL tableHasAutoIncrementField; + pthread_mutex_t dataProcessingLock; + BOOL tableHasAutoIncrementField; } @property (readwrite, assign) BOOL isWorking; diff --git a/Source/SPTableData.m b/Source/SPTableData.m index 28717864..7ae8f563 100644 --- a/Source/SPTableData.m +++ b/Source/SPTableData.m @@ -31,11 +31,13 @@ #import "RegexKitLite.h" #import "SPServerSupport.h" +@interface SPTableData (PrivateAPI) +- (void)_loopWhileWorking; +@end + @implementation SPTableData -@synthesize isWorking; @synthesize tableHasAutoIncrementField; -; /** * Init class. @@ -52,9 +54,9 @@ tableEncoding = nil; tableCreateSyntax = nil; mySQLConnection = nil; - isWorking = NO; tableHasAutoIncrementField = NO; + pthread_mutex_init(&dataProcessingLock, NULL); } return self; @@ -79,8 +81,8 @@ - (NSString *) tableEncoding { - // Return if CREATE SYNTAX is being parsed - if(isWorking) return nil; + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if (tableEncoding == nil) { if ([tableListInstance tableType] == SPTableTypeView) { @@ -98,8 +100,8 @@ - (NSString *) tableCreateSyntax { - // Return if CREATE SYNTAX is being parsed - if(isWorking) return nil; + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if (tableCreateSyntax == nil) { if ([tableListInstance tableType] == SPTableTypeView) { @@ -123,8 +125,8 @@ - (NSArray *) columns { - // Return if CREATE SYNTAX is being parsed - if(isWorking) return [NSArray array]; + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if ([columns count] == 0) { if ([tableListInstance tableType] == SPTableTypeView) { @@ -149,9 +151,8 @@ */ - (NSArray *) triggers { - // Return if CREATE SYNTAX is being parsed - if (isWorking) return [NSArray array]; - + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; // If triggers is nil, the triggers need to be loaded - if a table is selected on MySQL >= 5.0.2 if (!triggers) { @@ -173,10 +174,8 @@ */ - (NSDictionary *) columnWithName:(NSString *)colName { - - // Return if CREATE SYNTAX is being parsed - if(isWorking) return nil; - + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if ([columns count] == 0) { if ([tableListInstance tableType] == SPTableTypeView) { @@ -196,10 +195,8 @@ */ - (NSArray *) columnNames { - - // Return if CREATE SYNTAX is being parsed - if(isWorking) return [NSArray array]; - + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if ([columnNames count] == 0) { if ([tableListInstance tableType] == SPTableTypeView) { @@ -219,9 +216,8 @@ */ - (NSDictionary *) columnAtIndex:(NSInteger)index { - - // Return if CREATE SYNTAX is being parsed - if(isWorking) return nil; + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if ([columns count] == 0) { if ([tableListInstance tableType] == SPTableTypeView) { @@ -242,9 +238,8 @@ - (BOOL) columnIsBlobOrText:(NSString *)colName { - - // Return if CREATE SYNTAX is being parsed - if(isWorking) return YES; // to be at the safe side + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if ([columns count] == 0) { if ([tableListInstance tableType] == SPTableTypeView) { @@ -266,9 +261,8 @@ - (BOOL) columnIsGeometry:(NSString *)colName { - - // Return if CREATE SYNTAX is being parsed - if(isWorking) return YES; // to be at the safe side + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if ([columns count] == 0) { if ([tableListInstance tableType] == SPTableTypeView) { @@ -289,9 +283,8 @@ */ - (NSString *) statusValueForKey:(NSString *)aKey { - - // Return if CREATE SYNTAX is being parsed - if(isWorking) return nil; + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if ([status count] == 0) { [self updateStatusInformationForCurrentTable]; @@ -318,9 +311,8 @@ */ - (NSDictionary *) statusValues { - - // Return if CREATE SYNTAX is being parsed - if(isWorking) return nil; + // If processing is already in action, wait for it to complete + [self _loopWhileWorking]; if ([status count] == 0) { [self updateStatusInformationForCurrentTable]; @@ -380,7 +372,7 @@ */ - (BOOL) updateInformationForCurrentTable { - isWorking = YES; + pthread_mutex_lock(&dataProcessingLock); NSDictionary *tableData = nil; NSDictionary *columnData; @@ -395,8 +387,12 @@ tableData = [self informationForTable:[tableListInstance tableName]]; } + // If nil is returned, return failure. if (tableData == nil ) { - isWorking = NO; + + // The table information fetch may have already unlocked the data lock. + pthread_mutex_trylock(&dataProcessingLock); + pthread_mutex_unlock(&dataProcessingLock); return FALSE; } @@ -412,7 +408,7 @@ } tableEncoding = [[NSString alloc] initWithString:[tableData objectForKey:@"encoding"]]; - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return TRUE; } @@ -423,8 +419,7 @@ */ - (BOOL) updateInformationForCurrentView { - - isWorking = YES; + pthread_mutex_lock(&dataProcessingLock); NSDictionary *viewData = [self informationForView:[tableListInstance tableName]]; NSDictionary *columnData; @@ -436,7 +431,7 @@ [columns removeAllObjects]; [columnNames removeAllObjects]; [constraints removeAllObjects]; - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return FALSE; } @@ -452,7 +447,7 @@ } tableEncoding = [[NSString alloc] initWithString:[viewData objectForKey:@"encoding"]]; - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return TRUE; } @@ -476,10 +471,6 @@ unichar quoteCharacter; BOOL changeEncoding = ![[mySQLConnection encoding] isEqualToString:@"utf8"]; - [columns removeAllObjects]; - [columnNames removeAllObjects]; - [constraints removeAllObjects]; - // Catch unselected tables and return nil if ([tableName isEqualToString:@""] || !tableName) return nil; @@ -500,8 +491,13 @@ nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving the information for table '%@'. Please try again.\n\nMySQL said: %@", @"error retrieving table information informative message"), tableName, [mySQLConnection getLastErrorMessage]]); + // If the current table doesn't exist anymore reload table list if([mySQLConnection getLastErrorID] == 1146) { + + // Release the table loading lock to allow reselection/reloading to requery the database. + pthread_mutex_unlock(&dataProcessingLock); + [[tableListInstance valueForKeyPath:@"tablesListView"] deselectAll:nil]; [tableListInstance updateTables:self]; } @@ -925,14 +921,13 @@ */ - (BOOL)updateStatusInformationForCurrentTable { - - isWorking = YES; + pthread_mutex_lock(&dataProcessingLock); BOOL changeEncoding = ![[mySQLConnection encoding] isEqualToString:@"utf8"]; // Catch unselected tables and return false if ([[tableListInstance tableName] isEqualToString:@""] || ![tableListInstance tableName]) { - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return FALSE; } @@ -979,7 +974,7 @@ [mySQLConnection getLastErrorMessage]]); if (changeEncoding) [mySQLConnection restoreStoredEncoding]; } - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return FALSE; } @@ -997,7 +992,7 @@ if ([[status objectForKey:@"Engine"] isNSNull]) { [status setDictionary:[NSDictionary dictionaryWithObjectsAndKeys:@"Error", @"Engine", [NSString stringWithFormat:NSLocalizedString(@"An error occurred retrieving table information. MySQL said: %@", @"MySQL table info retrieval error message"), [status objectForKey:@"Comment"]], @"Comment", [tableListInstance tableName], @"Name", nil]]; if (changeEncoding) [mySQLConnection restoreStoredEncoding]; - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return FALSE; } @@ -1032,7 +1027,7 @@ if (changeEncoding) [mySQLConnection restoreStoredEncoding]; - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return TRUE; } @@ -1042,8 +1037,7 @@ */ - (BOOL) updateTriggersForCurrentTable { - - isWorking = YES; + pthread_mutex_lock(&dataProcessingLock); // Ensure queries are made in UTF8 BOOL changeEncoding = ![[mySQLConnection encoding] isEqualToString:@"utf8"]; @@ -1067,7 +1061,7 @@ if (changeEncoding) [mySQLConnection restoreStoredEncoding]; } - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return NO; } @@ -1079,7 +1073,7 @@ if (changeEncoding) [mySQLConnection restoreStoredEncoding]; - isWorking = NO; + pthread_mutex_unlock(&dataProcessingLock); return YES; } @@ -1364,7 +1358,18 @@ if (tableCreateSyntax) [tableCreateSyntax release]; if (mySQLConnection) [mySQLConnection release]; + pthread_mutex_destroy(&dataProcessingLock); + [super dealloc]; } -@end +#pragma mark - +#pragma mark Private API + +- (void)_loopWhileWorking +{ + while (pthread_mutex_trylock(&dataProcessingLock)) usleep(10000); + pthread_mutex_unlock(&dataProcessingLock); +} + +@end \ No newline at end of file diff --git a/Source/SPTableStructureDelegate.m b/Source/SPTableStructureDelegate.m index 17ad817d..729091d6 100644 --- a/Source/SPTableStructureDelegate.m +++ b/Source/SPTableStructureDelegate.m @@ -49,7 +49,10 @@ NSInteger end = [enc length] - start - 1; collations = [databaseDataInstance getDatabaseCollationsForEncoding:[enc substringWithRange:NSMakeRange(start, end)]]; } else { - if([tableDataInstance tableEncoding] != nil) { + + // If the structure has loaded (not still loading!) and the table encoding + // is set, use the appropriate collations. + if([tableDocumentInstance structureLoaded] && [tableDataInstance tableEncoding] != nil) { collations = [databaseDataInstance getDatabaseCollationsForEncoding:[tableDataInstance tableEncoding]]; } else { collations = [NSArray array]; diff --git a/Source/SPTableTriggers.h b/Source/SPTableTriggers.h index 0dfd6f30..70eeeb2e 100644 --- a/Source/SPTableTriggers.h +++ b/Source/SPTableTriggers.h @@ -65,6 +65,7 @@ @property (readwrite, assign) MCPConnection *connection; - (void)loadTriggers; +- (void)resetInterface; // IB action methods - (IBAction)addTrigger:(id)sender; diff --git a/Source/SPTableTriggers.m b/Source/SPTableTriggers.m index 44a17549..f5c15db0 100644 --- a/Source/SPTableTriggers.m +++ b/Source/SPTableTriggers.m @@ -111,17 +111,7 @@ static const NSString *SPTriggerSQLMode = @"TriggerSQLMode"; { BOOL enableInteraction = ((![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:SPMainToolbarTableTriggers]) || (![tableDocumentInstance isWorking])); - // Disable all interface elements by default - [addTriggerButton setEnabled:NO]; - [refreshTriggersButton setEnabled:NO]; - [triggersTableView setEnabled:NO]; - [labelTextField setStringValue:@""]; - - // Show a warning if the version of MySQL is too low to support triggers - if (![[tableDocumentInstance serverSupport] supportsTriggers]) { - [labelTextField setStringValue:NSLocalizedString(@"This version of MySQL does not support triggers. Support for triggers was added in MySQL 5.0.2", @"triggers not supported label")]; - return; - } + [self resetInterface]; // If no item is selected, or the item selected is not a table, return. if (![tablesListInstance tableName] || [tablesListInstance tableType] != SPTableTypeTable) @@ -139,6 +129,26 @@ static const NSString *SPTriggerSQLMode = @"TriggerSQLMode"; [self _refreshTriggerDataForcingCacheRefresh:NO]; } +/** + * Reset the trigger interface, as for no selected table. + */ +- (void)resetInterface +{ + [triggerData removeAllObjects]; + [triggersTableView noteNumberOfRowsChanged]; + + // Disable all interface elements by default + [addTriggerButton setEnabled:NO]; + [refreshTriggersButton setEnabled:NO]; + [triggersTableView setEnabled:NO]; + [labelTextField setStringValue:@""]; + + // Show a warning if the version of MySQL is too low to support triggers + if (![[tableDocumentInstance serverSupport] supportsTriggers]) { + [labelTextField setStringValue:NSLocalizedString(@"This version of MySQL does not support triggers. Support for triggers was added in MySQL 5.0.2", @"triggers not supported label")]; + } +} + #pragma mark - #pragma mark IB action methods @@ -400,15 +410,16 @@ static const NSString *SPTriggerSQLMode = @"TriggerSQLMode"; while (row != NSNotFound) { - NSString *triggerName = [[triggerData objectAtIndex:row] objectForKey:@"trigger"]; + NSString *triggerName = [[triggerData objectAtIndex:row] objectForKey:SPTriggerName]; NSString *query = [NSString stringWithFormat:@"DROP TRIGGER %@.%@", [database backtickQuotedString], [triggerName backtickQuotedString]]; [connection queryString:query]; if ([connection queryErrored]) { + [[alert window] orderOut:self]; SPBeginAlertSheet(NSLocalizedString(@"Unable to delete trigger", @"error deleting trigger message"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], nil, nil, nil, + nil, nil, [tableDocumentInstance parentWindow], nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"The selected trigger couldn't be deleted.\n\nMySQL said: %@", @"error deleting trigger informative message"), [connection getLastErrorMessage]]); // Abort loop @@ -554,7 +565,7 @@ static const NSString *SPTriggerSQLMode = @"TriggerSQLMode"; // Timin title is different then what we have saved in the database (case difference) for (NSUInteger i = 0; i < [[triggerActionTimePopUpButton itemArray] count]; i++) { - if ([[[triggerActionTimePopUpButton itemTitleAtIndex:i] uppercaseString] isEqualToString:[[trigger objectForKey:@"timing"] uppercaseString]]) { + if ([[[triggerActionTimePopUpButton itemTitleAtIndex:i] uppercaseString] isEqualToString:[[trigger objectForKey:SPTriggerActionTime] uppercaseString]]) { [triggerActionTimePopUpButton selectItemAtIndex:i]; break; } @@ -563,7 +574,7 @@ static const NSString *SPTriggerSQLMode = @"TriggerSQLMode"; // Event title is different then what we have saved in the database (case difference) for (NSUInteger i = 0; i < [[triggerEventPopUpButton itemArray] count]; i++) { - if ([[[triggerEventPopUpButton itemTitleAtIndex:i] uppercaseString] isEqualToString:[[trigger objectForKey:@"event"] uppercaseString]]) { + if ([[[triggerEventPopUpButton itemTitleAtIndex:i] uppercaseString] isEqualToString:[[trigger objectForKey:SPTriggerEvent] uppercaseString]]) { [triggerEventPopUpButton selectItemAtIndex:i]; break; } diff --git a/Source/SPWindow.m b/Source/SPWindow.m index 58f38d0b..8f2354db 100644 --- a/Source/SPWindow.m +++ b/Source/SPWindow.m @@ -38,7 +38,7 @@ - (void) sendEvent:(NSEvent *)theEvent { - if ([theEvent type] == NSKeyDown && [[theEvent characters] length]) { + if ([theEvent type] == NSKeyDown && [[theEvent charactersIgnoringModifiers] length]) { unichar theCharacter = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]; -- cgit v1.2.3