From d4dd7e79ce8373fe94521da2294a076887758ee2 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Sun, 19 Apr 2009 14:34:30 +0000 Subject: Bring Tiger branch up to version 0.9.5: - Merge in revisions up to r592 from trunk - Rewrite code where appropriate to use Tiger-compaible methods (no easy object enumeriation, no @properties, etc) - Remove printing again - problems printing, and template engine is 10.5-only - Rework xibs as nibs, and ensure everything looks and works correctly on Tiger; revert interface elements where necessary. - Add a method to check whether the app is being run on 10.5+, and show appropriate warning about interface and features - Alter strings and change sparkle URL to Tiger-specific appcast --- Frameworks/Sparkle.framework/Headers | 1 + Frameworks/Sparkle.framework/Resources | 1 + Frameworks/Sparkle.framework/Sparkle | 1 + .../Versions/A/Headers/SUAppcast.h | 33 + .../Versions/A/Headers/SUAppcastItem.h | 47 + .../Versions/A/Headers/SUUpdater.h | 118 ++ .../A/Headers/SUVersionComparisonProtocol.h | 27 + .../Sparkle.framework/Versions/A/Headers/Sparkle.h | 21 + .../Versions/A/Resources/Info.plist | 24 + .../Versions/A/Resources/License.txt | 7 + .../Versions/A/Resources/SUModelTranslation.plist | 174 +++ .../Versions/A/Resources/SUStatus.nib/classes.nib | 56 + .../Versions/A/Resources/SUStatus.nib/info.nib | 20 + .../A/Resources/SUStatus.nib/keyedobjects.nib | Bin 0 -> 7344 bytes .../SUAutomaticUpdateAlert.nib/classes.nib | 50 + .../de.lproj/SUAutomaticUpdateAlert.nib/info.nib | 20 + .../SUAutomaticUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 7278 bytes .../de.lproj/SUUpdateAlert.nib/classes.nib | 67 ++ .../Resources/de.lproj/SUUpdateAlert.nib/info.nib | 20 + .../de.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10493 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 + .../de.lproj/SUUpdatePermissionPrompt.nib/info.nib | 20 + .../SUUpdatePermissionPrompt.nib/keyedobjects.nib | Bin 0 -> 13189 bytes .../Versions/A/Resources/de.lproj/Sparkle.strings | Bin 0 -> 9806 bytes .../SUAutomaticUpdateAlert.nib/classes.nib | 50 + .../en.lproj/SUAutomaticUpdateAlert.nib/info.nib | 20 + .../SUAutomaticUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 7148 bytes .../en.lproj/SUUpdateAlert.nib/classes.nib | 67 ++ .../Resources/en.lproj/SUUpdateAlert.nib/info.nib | 20 + .../en.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10623 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 + .../en.lproj/SUUpdatePermissionPrompt.nib/info.nib | 21 + .../SUUpdatePermissionPrompt.nib/keyedobjects.nib | Bin 0 -> 13263 bytes .../Versions/A/Resources/en.lproj/Sparkle.strings | Bin 0 -> 8216 bytes .../SUAutomaticUpdateAlert.nib/classes.nib | 50 + .../es.lproj/SUAutomaticUpdateAlert.nib/info.nib | 20 + .../SUAutomaticUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 7273 bytes .../es.lproj/SUUpdateAlert.nib/classes.nib | 67 ++ .../Resources/es.lproj/SUUpdateAlert.nib/info.nib | 20 + .../es.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10668 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 + .../es.lproj/SUUpdatePermissionPrompt.nib/info.nib | 20 + .../SUUpdatePermissionPrompt.nib/keyedobjects.nib | Bin 0 -> 13404 bytes .../Versions/A/Resources/es.lproj/Sparkle.strings | Bin 0 -> 8020 bytes .../SUAutomaticUpdateAlert.nib/classes.nib | 50 + .../fr.lproj/SUAutomaticUpdateAlert.nib/info.nib | 16 + .../SUAutomaticUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 7245 bytes .../fr.lproj/SUUpdateAlert.nib/classes.nib | 67 ++ .../Resources/fr.lproj/SUUpdateAlert.nib/info.nib | 16 + .../fr.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10338 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 + .../fr.lproj/SUUpdatePermissionPrompt.nib/info.nib | 16 + .../SUUpdatePermissionPrompt.nib/keyedobjects.nib | Bin 0 -> 13156 bytes .../Versions/A/Resources/fr.lproj/Sparkle.strings | Bin 0 -> 8554 bytes .../Versions/A/Resources/fr.lproj/fr.lproj | 1 + .../Versions/A/Resources/fr_CA.lproj | 1 + .../SUAutomaticUpdateAlert.nib/classes.nib | 50 + .../it.lproj/SUAutomaticUpdateAlert.nib/info.nib | 20 + .../SUAutomaticUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 7161 bytes .../it.lproj/SUUpdateAlert.nib/classes.nib | 67 ++ .../Resources/it.lproj/SUUpdateAlert.nib/info.nib | 20 + .../it.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10360 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 + .../it.lproj/SUUpdatePermissionPrompt.nib/info.nib | 20 + .../SUUpdatePermissionPrompt.nib/keyedobjects.nib | Bin 0 -> 12659 bytes .../Versions/A/Resources/it.lproj/Sparkle.strings | Bin 0 -> 8914 bytes .../SUAutomaticUpdateAlert.nib/classes.nib | 50 + .../nl.lproj/SUAutomaticUpdateAlert.nib/info.nib | 18 + .../SUAutomaticUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 7234 bytes .../nl.lproj/SUUpdateAlert.nib/classes.nib | 67 ++ .../Resources/nl.lproj/SUUpdateAlert.nib/info.nib | 16 + .../nl.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10220 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 + .../nl.lproj/SUUpdatePermissionPrompt.nib/info.nib | 16 + .../SUUpdatePermissionPrompt.nib/keyedobjects.nib | Bin 0 -> 12535 bytes .../Versions/A/Resources/nl.lproj/Sparkle.strings | Bin 0 -> 8514 bytes .../Versions/A/Resources/relaunch | Bin 0 -> 58924 bytes .../SUAutomaticUpdateAlert.nib/classes.nib | 50 + .../ru.lproj/SUAutomaticUpdateAlert.nib/info.nib | 20 + .../SUAutomaticUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 7675 bytes .../ru.lproj/SUUpdateAlert.nib/classes.nib | 67 ++ .../Resources/ru.lproj/SUUpdateAlert.nib/info.nib | 20 + .../ru.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10895 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 + .../ru.lproj/SUUpdatePermissionPrompt.nib/info.nib | 18 + .../SUUpdatePermissionPrompt.nib/keyedobjects.nib | Bin 0 -> 12898 bytes .../Versions/A/Resources/ru.lproj/Sparkle.strings | Bin 0 -> 8364 bytes .../SUAutomaticUpdateAlert.nib/classes.nib | 50 + .../sv.lproj/SUAutomaticUpdateAlert.nib/info.nib | 20 + .../SUAutomaticUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 7474 bytes .../sv.lproj/SUUpdateAlert.nib/classes.nib | 39 + .../Resources/sv.lproj/SUUpdateAlert.nib/info.nib | 18 + .../sv.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10180 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 + .../sv.lproj/SUUpdatePermissionPrompt.nib/info.nib | 20 + .../SUUpdatePermissionPrompt.nib/keyedobjects.nib | Bin 0 -> 13483 bytes .../Versions/A/Resources/sv.lproj/Sparkle.strings | Bin 0 -> 8142 bytes Frameworks/Sparkle.framework/Versions/A/Sparkle | Bin 0 -> 463540 bytes Frameworks/Sparkle.framework/Versions/Current | 1 + Interfaces/English.lproj/Console.nib/classes.nib | 63 ++ Interfaces/English.lproj/Console.nib/info.nib | 21 + .../English.lproj/Console.nib/keyedobjects.nib | Bin 0 -> 11296 bytes Interfaces/English.lproj/Credits.rtf | 16 +- Interfaces/English.lproj/DBView.nib/classes.nib | 1196 ++++++++------------ Interfaces/English.lproj/DBView.nib/info.nib | 29 +- .../English.lproj/DBView.nib/keyedobjects.nib | Bin 182365 -> 188484 bytes Interfaces/English.lproj/InfoPlist.strings | 2 +- Interfaces/English.lproj/Localizable.strings | Bin 33234 -> 34298 bytes Interfaces/English.lproj/MainMenu.nib/classes.nib | 117 +- Interfaces/English.lproj/MainMenu.nib/info.nib | 6 +- .../English.lproj/MainMenu.nib/keyedobjects.nib | Bin 61336 -> 36581 bytes .../English.lproj/Preferences.nib/classes.nib | 49 + Interfaces/English.lproj/Preferences.nib/info.nib | 33 + .../English.lproj/Preferences.nib/keyedobjects.nib | Bin 0 -> 48076 bytes Resources/Images/TablePropertyIcon.png | Bin 3405 -> 0 bytes Resources/Images/button_action.tiff | Bin 0 -> 1316 bytes Resources/Images/button_bar_handle.tiff | Bin 0 -> 986 bytes Resources/Images/button_bar_spacer.tiff | Bin 0 -> 888 bytes Resources/Images/button_duplicate.tiff | Bin 0 -> 2628 bytes Resources/Images/button_edit.tiff | Bin 0 -> 26264 bytes Resources/Images/button_edit_mode.tiff | Bin 0 -> 2558 bytes Resources/Images/button_edit_mode_selected.tiff | Bin 0 -> 2558 bytes Resources/Images/button_info_pane_hide.tiff | Bin 0 -> 768 bytes Resources/Images/button_info_pane_show.tiff | Bin 0 -> 774 bytes Resources/Images/button_refresh.tiff | Bin 0 -> 2558 bytes Resources/Images/button_remove.tiff | Bin 0 -> 2558 bytes Resources/Images/button_subtract.tiff | Bin 2558 -> 0 bytes Resources/Images/clearconsole.tiff | Bin 16528 -> 4452 bytes Resources/Images/columnadd.tiff | Bin 16208 -> 0 bytes Resources/Images/columncopy.tiff | Bin 15292 -> 0 bytes Resources/Images/columndelete.tiff | Bin 16984 -> 0 bytes Resources/Images/columnrefresh.tiff | Bin 15400 -> 0 bytes Resources/Images/connectionadd.tiff | Bin 16208 -> 0 bytes Resources/Images/connectioncopy.tiff | Bin 15296 -> 0 bytes Resources/Images/connectiondelete.tiff | Bin 17000 -> 0 bytes Resources/Images/connectionrefresh.tiff | Bin 15440 -> 0 bytes Resources/Images/createtablesyntax.tiff | Bin 15940 -> 0 bytes Resources/Images/database.png | Bin 0 -> 1877 bytes Resources/Images/databases.tiff | Bin 15436 -> 0 bytes Resources/Images/dbadd.tiff | Bin 16324 -> 0 bytes Resources/Images/dbdelete.tiff | Bin 17084 -> 0 bytes Resources/Images/dbrefresh.tiff | Bin 15544 -> 0 bytes Resources/Images/dimple.tiff | Bin 0 -> 2074 bytes Resources/Images/flushprivileges.tiff | Bin 17236 -> 0 bytes Resources/Images/hideconsole.tiff | Bin 16972 -> 4452 bytes Resources/Images/indexadd.tiff | Bin 16144 -> 0 bytes Resources/Images/indexdelete.tiff | Bin 16124 -> 0 bytes Resources/Images/optimizetable.tiff | Bin 16840 -> 0 bytes Resources/Images/query.tiff | Bin 15332 -> 0 bytes Resources/Images/queryadd.tiff | Bin 16284 -> 0 bytes Resources/Images/querycopy.tiff | Bin 15352 -> 0 bytes Resources/Images/querydelete.tiff | Bin 16280 -> 0 bytes Resources/Images/queryrefresh.tiff | Bin 15496 -> 0 bytes Resources/Images/reload.tiff | Bin 15488 -> 0 bytes Resources/Images/rowadd.tiff | Bin 16112 -> 0 bytes Resources/Images/rowcopy.tiff | Bin 15280 -> 0 bytes Resources/Images/rowdelete.tiff | Bin 16144 -> 0 bytes Resources/Images/rowrefresh.tiff | Bin 15364 -> 0 bytes Resources/Images/selectall.tiff | Bin 15396 -> 1308 bytes Resources/Images/selectnone.tiff | Bin 15236 -> 1308 bytes Resources/Images/sheettotable.tiff | Bin 15376 -> 0 bytes Resources/Images/showconsole.tiff | Bin 16880 -> 4452 bytes Resources/Images/showvariables.tiff | Bin 16980 -> 4452 bytes Resources/Images/table-property.png | Bin 0 -> 3405 bytes Resources/Images/table-small.tif | Bin 28408 -> 0 bytes Resources/Images/table-small.tiff | Bin 0 -> 4542 bytes Resources/Images/table-view-small.tif | Bin 24768 -> 0 bytes Resources/Images/table-view-small.tiff | Bin 0 -> 1386 bytes Resources/Images/tableadd.tiff | Bin 16348 -> 0 bytes Resources/Images/tablecopy.tiff | Bin 15380 -> 0 bytes Resources/Images/tabledelete.tiff | Bin 16324 -> 0 bytes Resources/Images/tablerefresh.tiff | Bin 15572 -> 0 bytes Resources/Images/tables.tiff | Bin 15316 -> 0 bytes Resources/Images/tabletosheet.tiff | Bin 15368 -> 0 bytes Resources/Images/textencode.tiff | Bin 15240 -> 0 bytes Resources/Images/toolbar-export-csv.tiff | Bin 0 -> 27284 bytes Resources/Images/toolbar-export-html.tiff | Bin 0 -> 4898 bytes Resources/Images/toolbar-export-pdf.tiff | Bin 0 -> 30596 bytes Resources/Images/toolbar-export-sql.tiff | Bin 0 -> 4462 bytes Resources/Images/toolbar-export-xls.tiff | Bin 0 -> 27488 bytes Resources/Images/toolbar-export-xml.tiff | Bin 0 -> 65460 bytes Resources/Images/toolbar-preferences-advanced.tiff | Bin 0 -> 3586 bytes .../Images/toolbar-preferences-autoupdate.tiff | Bin 0 -> 4334 bytes Resources/Images/toolbar-preferences-favorites.png | Bin 0 -> 3748 bytes Resources/Images/toolbar-preferences-general.tiff | Bin 0 -> 2046 bytes Resources/Images/toolbar-preferences-network.png | Bin 0 -> 1146 bytes .../Images/toolbar-preferences-notifications.tiff | Bin 0 -> 2044 bytes Resources/Images/toolbar-preferences-security.tiff | Bin 0 -> 2164 bytes Resources/Images/toolbar-preferences-tables.tiff | Bin 0 -> 2946 bytes Resources/Images/toolbar-switch-to-procedures.tiff | Bin 0 -> 4900 bytes Resources/Info.plist | 10 +- Resources/PreferenceDefaults.plist | 50 + Resources/sparkle-public-key.pem | 20 + Scripts/package-application.sh | 65 ++ Source/CMMCPConnection.h | 13 +- Source/CMMCPConnection.m | 117 +- Source/CMTextView.h | 26 +- Source/CMTextView.m | 1190 +++++++++++++++++-- Source/CustomQuery.h | 22 +- Source/CustomQuery.m | 642 ++++++++--- Source/MainController.h | 55 +- Source/MainController.m | 779 ++----------- Source/NoodleLineNumberView.h | 60 + Source/NoodleLineNumberView.m | 493 ++++++++ Source/SPArrayAdditions.h | 29 + Source/SPArrayAdditions.m | 45 + Source/SPConsoleMessage.h | 44 + Source/SPConsoleMessage.m | 84 ++ Source/SPEditorTokens.h | 19 + Source/SPEditorTokens.l | 666 +++++++++++ Source/SPExportController.h | 91 ++ Source/SPExportController.m | 178 +++ Source/SPFavoriteTextFieldCell.h | 44 + Source/SPFavoriteTextFieldCell.m | 239 ++++ Source/SPGrowlController.h | 1 + Source/SPGrowlController.m | 71 +- Source/SPPreferenceController.h | 84 ++ Source/SPPreferenceController.m | 842 ++++++++++++++ Source/SPQueryConsole.h | 24 +- Source/SPQueryConsole.m | 510 +++++++-- Source/SPSQLParser.h | 18 + Source/SPSQLParser.m | 116 +- Source/SPStringAdditions.h | 4 + Source/SPStringAdditions.m | 177 ++- Source/SPTableData.h | 1 + Source/SPTableData.m | 120 +- Source/SPTableInfo.m | 2 +- Source/SPTextViewAdditions.h | 39 + Source/SPTextViewAdditions.m | 287 +++++ Source/SPWindowAdditions.h | 30 + Source/SPWindowAdditions.m | 75 ++ Source/TableContent.h | 8 +- Source/TableContent.m | 414 ++++--- Source/TableDocument.h | 47 +- Source/TableDocument.m | 800 ++++++++----- Source/TableDump.h | 16 +- Source/TableDump.m | 313 +++-- Source/TableSource.m | 137 ++- Source/TableStatus.h | 5 +- Source/TableStatus.m | 45 +- Source/TablesList.h | 39 +- Source/TablesList.m | 588 +++++----- sequel-pro.xcodeproj/project.pbxproj | 670 ++++++----- 243 files changed, 11168 insertions(+), 3196 deletions(-) create mode 120000 Frameworks/Sparkle.framework/Headers create mode 120000 Frameworks/Sparkle.framework/Resources create mode 120000 Frameworks/Sparkle.framework/Sparkle create mode 100644 Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcast.h create mode 100644 Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h create mode 100644 Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdater.h create mode 100644 Frameworks/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h create mode 100644 Frameworks/Sparkle.framework/Versions/A/Headers/Sparkle.h create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/License.txt create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings create mode 120000 Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/fr.lproj create mode 120000 Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings create mode 100755 Frameworks/Sparkle.framework/Versions/A/Resources/relaunch create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings create mode 100755 Frameworks/Sparkle.framework/Versions/A/Sparkle create mode 120000 Frameworks/Sparkle.framework/Versions/Current create mode 100644 Interfaces/English.lproj/Console.nib/classes.nib create mode 100644 Interfaces/English.lproj/Console.nib/info.nib create mode 100644 Interfaces/English.lproj/Console.nib/keyedobjects.nib create mode 100644 Interfaces/English.lproj/Preferences.nib/classes.nib create mode 100644 Interfaces/English.lproj/Preferences.nib/info.nib create mode 100644 Interfaces/English.lproj/Preferences.nib/keyedobjects.nib delete mode 100644 Resources/Images/TablePropertyIcon.png create mode 100644 Resources/Images/button_action.tiff create mode 100644 Resources/Images/button_bar_handle.tiff create mode 100644 Resources/Images/button_bar_spacer.tiff create mode 100644 Resources/Images/button_duplicate.tiff create mode 100644 Resources/Images/button_edit.tiff create mode 100644 Resources/Images/button_edit_mode.tiff create mode 100644 Resources/Images/button_edit_mode_selected.tiff create mode 100644 Resources/Images/button_info_pane_hide.tiff create mode 100644 Resources/Images/button_info_pane_show.tiff create mode 100644 Resources/Images/button_refresh.tiff create mode 100644 Resources/Images/button_remove.tiff delete mode 100644 Resources/Images/button_subtract.tiff delete mode 100644 Resources/Images/columnadd.tiff delete mode 100644 Resources/Images/columncopy.tiff delete mode 100644 Resources/Images/columndelete.tiff delete mode 100644 Resources/Images/columnrefresh.tiff delete mode 100644 Resources/Images/connectionadd.tiff delete mode 100644 Resources/Images/connectioncopy.tiff delete mode 100644 Resources/Images/connectiondelete.tiff delete mode 100644 Resources/Images/connectionrefresh.tiff delete mode 100644 Resources/Images/createtablesyntax.tiff create mode 100644 Resources/Images/database.png delete mode 100644 Resources/Images/databases.tiff delete mode 100644 Resources/Images/dbadd.tiff delete mode 100644 Resources/Images/dbdelete.tiff delete mode 100644 Resources/Images/dbrefresh.tiff create mode 100644 Resources/Images/dimple.tiff delete mode 100644 Resources/Images/flushprivileges.tiff delete mode 100644 Resources/Images/indexadd.tiff delete mode 100644 Resources/Images/indexdelete.tiff delete mode 100644 Resources/Images/optimizetable.tiff delete mode 100644 Resources/Images/query.tiff delete mode 100644 Resources/Images/queryadd.tiff delete mode 100644 Resources/Images/querycopy.tiff delete mode 100644 Resources/Images/querydelete.tiff delete mode 100644 Resources/Images/queryrefresh.tiff delete mode 100644 Resources/Images/reload.tiff delete mode 100644 Resources/Images/rowadd.tiff delete mode 100644 Resources/Images/rowcopy.tiff delete mode 100644 Resources/Images/rowdelete.tiff delete mode 100644 Resources/Images/rowrefresh.tiff delete mode 100644 Resources/Images/sheettotable.tiff create mode 100644 Resources/Images/table-property.png delete mode 100644 Resources/Images/table-small.tif create mode 100644 Resources/Images/table-small.tiff delete mode 100644 Resources/Images/table-view-small.tif create mode 100644 Resources/Images/table-view-small.tiff delete mode 100644 Resources/Images/tableadd.tiff delete mode 100644 Resources/Images/tablecopy.tiff delete mode 100644 Resources/Images/tabledelete.tiff delete mode 100644 Resources/Images/tablerefresh.tiff delete mode 100644 Resources/Images/tables.tiff delete mode 100644 Resources/Images/tabletosheet.tiff delete mode 100644 Resources/Images/textencode.tiff create mode 100644 Resources/Images/toolbar-export-csv.tiff create mode 100644 Resources/Images/toolbar-export-html.tiff create mode 100644 Resources/Images/toolbar-export-pdf.tiff create mode 100644 Resources/Images/toolbar-export-sql.tiff create mode 100644 Resources/Images/toolbar-export-xls.tiff create mode 100644 Resources/Images/toolbar-export-xml.tiff create mode 100644 Resources/Images/toolbar-preferences-advanced.tiff create mode 100644 Resources/Images/toolbar-preferences-autoupdate.tiff create mode 100644 Resources/Images/toolbar-preferences-favorites.png create mode 100644 Resources/Images/toolbar-preferences-general.tiff create mode 100644 Resources/Images/toolbar-preferences-network.png create mode 100644 Resources/Images/toolbar-preferences-notifications.tiff create mode 100644 Resources/Images/toolbar-preferences-security.tiff create mode 100644 Resources/Images/toolbar-preferences-tables.tiff create mode 100644 Resources/Images/toolbar-switch-to-procedures.tiff create mode 100644 Resources/PreferenceDefaults.plist create mode 100644 Resources/sparkle-public-key.pem create mode 100755 Scripts/package-application.sh create mode 100644 Source/NoodleLineNumberView.h create mode 100644 Source/NoodleLineNumberView.m create mode 100644 Source/SPArrayAdditions.h create mode 100644 Source/SPArrayAdditions.m create mode 100644 Source/SPConsoleMessage.h create mode 100644 Source/SPConsoleMessage.m create mode 100644 Source/SPEditorTokens.h create mode 100644 Source/SPEditorTokens.l create mode 100644 Source/SPExportController.h create mode 100644 Source/SPExportController.m create mode 100644 Source/SPFavoriteTextFieldCell.h create mode 100644 Source/SPFavoriteTextFieldCell.m create mode 100644 Source/SPPreferenceController.h create mode 100644 Source/SPPreferenceController.m create mode 100644 Source/SPTextViewAdditions.h create mode 100644 Source/SPTextViewAdditions.m create mode 100644 Source/SPWindowAdditions.h create mode 100644 Source/SPWindowAdditions.m diff --git a/Frameworks/Sparkle.framework/Headers b/Frameworks/Sparkle.framework/Headers new file mode 120000 index 00000000..a177d2a6 --- /dev/null +++ b/Frameworks/Sparkle.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Frameworks/Sparkle.framework/Resources b/Frameworks/Sparkle.framework/Resources new file mode 120000 index 00000000..953ee36f --- /dev/null +++ b/Frameworks/Sparkle.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Frameworks/Sparkle.framework/Sparkle b/Frameworks/Sparkle.framework/Sparkle new file mode 120000 index 00000000..b2c52731 --- /dev/null +++ b/Frameworks/Sparkle.framework/Sparkle @@ -0,0 +1 @@ +Versions/Current/Sparkle \ No newline at end of file diff --git a/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcast.h b/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcast.h new file mode 100644 index 00000000..171148a4 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcast.h @@ -0,0 +1,33 @@ +// +// SUAppcast.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCAST_H +#define SUAPPCAST_H + +@class SUAppcastItem; +@interface SUAppcast : NSObject { + NSArray *items; + NSString *userAgentString; + id delegate; + NSMutableData *incrementalData; +} + +- (void)fetchAppcastFromURL:(NSURL *)url; +- (void)setDelegate:delegate; +- (void)setUserAgentString:(NSString *)userAgentString; + +- (NSArray *)items; + +@end + +@interface NSObject (SUAppcastDelegate) +- (void)appcastDidFinishLoading:(SUAppcast *)appcast; +- (void)appcast:(SUAppcast *)appcast failedToLoadWithError:(NSError *)error; +@end + +#endif diff --git a/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h b/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h new file mode 100644 index 00000000..7f1ca65c --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h @@ -0,0 +1,47 @@ +// +// SUAppcastItem.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCASTITEM_H +#define SUAPPCASTITEM_H + +@interface SUAppcastItem : NSObject { + NSString *title; + NSDate *date; + NSString *itemDescription; + + NSURL *releaseNotesURL; + + NSString *DSASignature; + NSString *minimumSystemVersion; + + NSURL *fileURL; + NSString *versionString; + NSString *displayVersionString; + + NSDictionary *propertiesDictionary; +} + +// Initializes with data from a dictionary provided by the RSS class. +- initWithDictionary:(NSDictionary *)dict; + +- (NSString *)title; +- (NSString *)versionString; +- (NSString *)displayVersionString; +- (NSDate *)date; +- (NSString *)itemDescription; +- (NSURL *)releaseNotesURL; +- (NSURL *)fileURL; +- (NSString *)DSASignature; +- (NSString *)minimumSystemVersion; + +// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. +- (NSDictionary *)propertiesDictionary; + +@end + +#endif diff --git a/Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdater.h b/Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdater.h new file mode 100644 index 00000000..e78c4d35 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdater.h @@ -0,0 +1,118 @@ +// +// SUUpdater.h +// Sparkle +// +// Created by Andy Matuschak on 1/4/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUUPDATER_H +#define SUUPDATER_H + +#import + +@class SUUpdateDriver, SUAppcastItem, SUHost, SUAppcast; +@interface SUUpdater : NSObject { + NSTimer *checkTimer; + SUUpdateDriver *driver; + + SUHost *host; + IBOutlet id delegate; +} + ++ (SUUpdater *)sharedUpdater; ++ (SUUpdater *)updaterForBundle:(NSBundle *)bundle; +- (NSBundle *)hostBundle; + +- (void)setDelegate:(id)delegate; +- delegate; + +- (void)setAutomaticallyChecksForUpdates:(BOOL)automaticallyChecks; +- (BOOL)automaticallyChecksForUpdates; + +- (void)setUpdateCheckInterval:(NSTimeInterval)interval; +- (NSTimeInterval)updateCheckInterval; + +- (void)setFeedURL:(NSURL *)feedURL; +- (NSURL *)feedURL; + +- (void)setSendsSystemProfile:(BOOL)sendsSystemProfile; +- (BOOL)sendsSystemProfile; + +- (void)setAutomaticallyDownloadsUpdates:(BOOL)automaticallyDownloadsUpdates; +- (BOOL)automaticallyDownloadsUpdates; + +// This IBAction is meant for a main menu item. Hook up any menu item to this action, +// and Sparkle will check for updates and report back its findings verbosely. +- (IBAction)checkForUpdates:sender; + +// This kicks off an update meant to be programmatically initiated. That is, it will display no UI unless it actually finds an update, +// in which case it proceeds as usual. If the fully automated updating is turned on, however, this will invoke that behavior, and if an +// update is found, it will be downloaded and prepped for installation. +- (void)checkForUpdatesInBackground; + +// Date of last update check. Returns null if no check has been performed. +- (NSDate*)lastUpdateCheckDate; + +// This begins a "probing" check for updates which will not actually offer to update to that version. The delegate methods, though, +// (up to updater:didFindValidUpdate: and updaterDidNotFindUpdate:), are called, so you can use that information in your UI. +- (void)checkForUpdateInformation; + +// Call this to appropriately schedule or cancel the update checking timer according to the preferences for time interval and automatic checks. This call does not change the date of the next check, but only the internal NSTimer. +- (void)resetUpdateCycle; + +- (BOOL)updateInProgress; +@end + +@interface NSObject (SUUpdaterDelegateInformalProtocol) +// This method allows you to add extra parameters to the appcast URL, potentially based on whether or not Sparkle will also be sending along the system profile. This method should return an array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. +- (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; + +// Use this to override the default behavior for Sparkle prompting the user about automatic update checks. +- (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)bundle; + +// Implement this if you want to do some special handling with the appcast once it finishes loading. +- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; + +// If you're using special logic or extensions in your appcast, implement this to use your own logic for finding +// a valid update, if any, in the given appcast. +- (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)bundle; + +// Sent when a valid update is found by the update driver. +- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update; + +// Sent when a valid update is not found. +- (void)updaterDidNotFindUpdate:(SUUpdater *)update; + +// Sent immediately before installing the specified update. +- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update; + +// Return YES to delay the relaunch until you do some processing; invoke the given NSInvocation to continue. +- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update untilInvoking:(NSInvocation *)invocation; + +// Called immediately before relaunching. +- (void)updaterWillRelaunchApplication:(SUUpdater *)updater; + +// This method allows you to provide a custom version comparator. +// If you don't implement this method or return nil, the standard version comparator will be used. +- (id )versionComparatorForUpdater:(SUUpdater *)updater; + +// Returns the path which is used to relaunch the client after the update is installed. By default, the path of the host bundle. +- (NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater; + +@end + +// Define some minimum intervals to avoid DOS-like checking attacks. These are in seconds. +#ifdef DEBUG +#define SU_MIN_CHECK_INTERVAL 60 +#else +#define SU_MIN_CHECK_INTERVAL 60*60 +#endif + +#ifdef DEBUG +#define SU_DEFAULT_CHECK_INTERVAL 60 +#else +#define SU_DEFAULT_CHECK_INTERVAL 60*60*24 +#endif + +#endif diff --git a/Frameworks/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h b/Frameworks/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h new file mode 100644 index 00000000..3d11ae87 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h @@ -0,0 +1,27 @@ +// +// SUVersionComparisonProtocol.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUVERSIONCOMPARISONPROTOCOL_H +#define SUVERSIONCOMPARISONPROTOCOL_H + +/*! + @protocol + @abstract Implement this protocol to provide version comparison facilities for Sparkle. +*/ +@protocol SUVersionComparison + +/*! + @method + @abstract An abstract method to compare two version strings. + @discussion Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, and NSOrderedSame if they are equivalent. +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; + +@end + +#endif diff --git a/Frameworks/Sparkle.framework/Versions/A/Headers/Sparkle.h b/Frameworks/Sparkle.framework/Versions/A/Headers/Sparkle.h new file mode 100644 index 00000000..08dd5777 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Headers/Sparkle.h @@ -0,0 +1,21 @@ +// +// Sparkle.h +// Sparkle +// +// Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07) +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SPARKLE_H +#define SPARKLE_H + +// This list should include the shared headers. It doesn't matter if some of them aren't shared (unless +// there are name-space collisions) so we can list all of them to start with: + +#import + +#import +#import +#import + +#endif diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist b/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist new file mode 100644 index 00000000..c7f277d0 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + Sparkle + CFBundleIdentifier + org.andymatuschak.Sparkle + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Sparkle + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.5 Beta 6 + CFBundleSignature + ???? + CFBundleVersion + 313 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/License.txt b/Frameworks/Sparkle.framework/Versions/A/Resources/License.txt new file mode 100644 index 00000000..20466c41 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/License.txt @@ -0,0 +1,7 @@ +Copyright (c) 2006 Andy Matuschak + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist b/Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist new file mode 100644 index 00000000..92ef9471 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist @@ -0,0 +1,174 @@ + + + + + ADP2,1 + Developer Transition Kit + MacBook1,1 + MacBook (Core Duo) + MacBook2,1 + MacBook (Core 2 Duo) + MacBook4,1 + MacBook (Core 2 Duo Feb 2008) + MacBookAir1,1 + MacBook Air (January 2008) + MacBookPro1,1 + MacBook Pro Core Duo (15-inch) + MacBookPro1,2 + MacBook Pro Core Duo (17-inch) + MacBookPro2,1 + MacBook Pro Core 2 Duo (17-inch) + MacBookPro2,2 + MacBook Pro Core 2 Duo (15-inch) + MacBookPro3,1 + MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo) + MacBookPro3,2 + MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) + MacBookPro4,1 + MacBook Pro (Core 2 Duo Feb 2008) + MacPro1,1 + Mac Pro (four-core) + MacPro2,1 + Mac Pro (eight-core) + MacPro3,1 + Mac Pro (January 2008 4- or 8- core "Harpertown") + Macmini1,1 + Mac Mini (Core Solo/Duo) + PowerBook1,1 + PowerBook G3 + PowerBook2,1 + iBook G3 + PowerBook2,2 + iBook G3 (FireWire) + PowerBook2,3 + iBook G3 + PowerBook2,4 + iBook G3 + PowerBook3,1 + PowerBook G3 (FireWire) + PowerBook3,2 + PowerBook G4 + PowerBook3,3 + PowerBook G4 (Gigabit Ethernet) + PowerBook3,4 + PowerBook G4 (DVI) + PowerBook3,5 + PowerBook G4 (1GHz / 867MHz) + PowerBook4,1 + iBook G3 (Dual USB, Late 2001) + PowerBook4,2 + iBook G3 (16MB VRAM) + PowerBook4,3 + iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003) + PowerBook5,1 + PowerBook G4 (17 inch) + PowerBook5,2 + PowerBook G4 (15 inch FW 800) + PowerBook5,3 + PowerBook G4 (17-inch 1.33GHz) + PowerBook5,4 + PowerBook G4 (15 inch 1.5/1.33GHz) + PowerBook5,5 + PowerBook G4 (17-inch 1.5GHz) + PowerBook5,6 + PowerBook G4 (15 inch 1.67GHz/1.5GHz) + PowerBook5,7 + PowerBook G4 (17-inch 1.67GHz) + PowerBook5,8 + PowerBook G4 (Double layer SD, 15 inch) + PowerBook5,9 + PowerBook G4 (Double layer SD, 17 inch) + PowerBook6,1 + PowerBook G4 (12 inch) + PowerBook6,2 + PowerBook G4 (12 inch, DVI) + PowerBook6,3 + iBook G4 + PowerBook6,4 + PowerBook G4 (12 inch 1.33GHz) + PowerBook6,5 + iBook G4 (Early-Late 2004) + PowerBook6,7 + iBook G4 (Mid 2005) + PowerBook6,8 + PowerBook G4 (12 inch 1.5GHz) + PowerMac1,1 + Power Macintosh G3 (Blue & White) + PowerMac1,2 + Power Macintosh G4 (PCI Graphics) + PowerMac10,1 + Mac Mini G4 + PowerMac10,2 + Mac Mini (Late 2005) + PowerMac11,2 + Power Macintosh G5 (Late 2005) + PowerMac12,1 + iMac G5 (iSight) + PowerMac2,1 + iMac G3 (Slot-loading CD-ROM) + PowerMac2,2 + iMac G3 (Summer 2000) + PowerMac3,1 + Power Macintosh G4 (AGP Graphics) + PowerMac3,2 + Power Macintosh G4 (AGP Graphics) + PowerMac3,3 + Power Macintosh G4 (Gigabit Ethernet) + PowerMac3,4 + Power Macintosh G4 (Digital Audio) + PowerMac3,5 + Power Macintosh G4 (Quick Silver) + PowerMac3,6 + Power Macintosh G4 (Mirrored Drive Door) + PowerMac4,1 + iMac G3 (Early/Summer 2001) + PowerMac4,2 + iMac G4 (Flat Panel) + PowerMac4,4 + eMac + PowerMac4,5 + iMac G4 (17-inch Flat Panel) + PowerMac5,1 + Power Macintosh G4 Cube + PowerMac6,1 + iMac G4 (USB 2.0) + PowerMac6,3 + iMac G4 (20-inch Flat Panel) + PowerMac6,4 + eMac (USB 2.0, 2005) + PowerMac7,2 + Power Macintosh G5 + PowerMac7,3 + Power Macintosh G5 + PowerMac8,1 + iMac G5 + PowerMac8,2 + iMac G5 (Ambient Light Sensor) + PowerMac9,1 + Power Macintosh G5 (Late 2005) + RackMac1,1 + Xserve G4 + RackMac1,2 + Xserve G4 (slot-loading, cluster node) + RackMac3,1 + Xserve G5 + Xserve1,1 + Xserve (Intel Xeon) + Xserve2,1 + Xserve (January 2008 quad-core) + iMac1,1 + iMac G3 (Rev A-D) + iMac4,1 + iMac (Core Duo) + iMac4,2 + iMac for Education (17-inch, Core Duo) + iMac5,1 + iMac (Core 2 Duo, 17 or 20 inch, SuperDrive) + iMac5,2 + iMac (Core 2 Duo, 17 inch, Combo Drive) + iMac6,1 + iMac (Core 2 Duo, 24 inch, SuperDrive) + iMac8,1 + iMac (April 2008) + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/classes.nib new file mode 100644 index 00000000..22f13f8b --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/classes.nib @@ -0,0 +1,56 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUStatusController + LANGUAGE + ObjC + OUTLETS + + actionButton + NSButton + progressBar + NSProgressIndicator + + SUPERCLASS + SUWindowController + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/info.nib new file mode 100644 index 00000000..a9ac8673 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 10A96 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/keyedobjects.nib new file mode 100644 index 00000000..4f1d5981 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..4b1ab30e --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2e04cfa0 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..6b926302 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..994d4c36 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,67 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + installUpdate + id + remindMeLater + id + skipThisVersion + id + + CLASS + SUUpdateAlert + LANGUAGE + ObjC + OUTLETS + + delegate + id + description + NSTextField + releaseNotesView + WebView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2e04cfa0 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..b4353d2f Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 00000000..5220a221 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 00000000..2e04cfa0 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 00000000..b403a3e4 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings new file mode 100644 index 00000000..b31f928f Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..4b1ab30e --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 00000000..ab36d310 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 658 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9C7010 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..7630390c Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..994d4c36 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,67 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + installUpdate + id + remindMeLater + id + skipThisVersion + id + + CLASS + SUUpdateAlert + LANGUAGE + ObjC + OUTLETS + + delegate + id + description + NSTextField + releaseNotesView + WebView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2fb8a837 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 18 + + IBSystem Version + 10A96 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..e7e7497d Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 00000000..5220a221 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 00000000..b1cd28ed --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,21 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + 41 + + IBSystem Version + 10A96 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 00000000..e8dc5b88 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings new file mode 100644 index 00000000..16e0787b Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..4b1ab30e --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2e04cfa0 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..6b2f938f Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..994d4c36 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,67 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + installUpdate + id + remindMeLater + id + skipThisVersion + id + + CLASS + SUUpdateAlert + LANGUAGE + ObjC + OUTLETS + + delegate + id + description + NSTextField + releaseNotesView + WebView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2e04cfa0 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..c9b1e7d8 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 00000000..5220a221 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 00000000..3eb7f818 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 00000000..8c54c217 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings new file mode 100644 index 00000000..f83ea23c Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..4b1ab30e --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 00000000..33a60200 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,16 @@ + + + + + IBFramework Version + 629 + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..4cd529a5 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..994d4c36 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,67 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + installUpdate + id + remindMeLater + id + skipThisVersion + id + + CLASS + SUUpdateAlert + LANGUAGE + ObjC + OUTLETS + + delegate + id + description + NSTextField + releaseNotesView + WebView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 00000000..d2586ea2 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,16 @@ + + + + + IBFramework Version + 629 + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..65dfc95e Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 00000000..5220a221 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 00000000..d2586ea2 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,16 @@ + + + + + IBFramework Version + 629 + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 00000000..4b7cc905 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 00000000..ea175ae7 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/fr.lproj b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/fr.lproj new file mode 120000 index 00000000..88614fe2 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/fr.lproj @@ -0,0 +1 @@ +/Users/andym/Development/Build Products/Release/Sparkle.framework/Resources/fr.lproj \ No newline at end of file diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj b/Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj new file mode 120000 index 00000000..88614fe2 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj @@ -0,0 +1 @@ +/Users/andym/Development/Build Products/Release/Sparkle.framework/Resources/fr.lproj \ No newline at end of file diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..4b1ab30e --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2e04cfa0 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..15ba8f4c Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..994d4c36 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,67 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + installUpdate + id + remindMeLater + id + skipThisVersion + id + + CLASS + SUUpdateAlert + LANGUAGE + ObjC + OUTLETS + + delegate + id + description + NSTextField + releaseNotesView + WebView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2e04cfa0 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..29840645 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 00000000..5220a221 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 00000000..c4934850 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 5 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 00000000..55cc2c27 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings new file mode 100644 index 00000000..5c410d07 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..4b1ab30e --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 00000000..3f097908 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 629 + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..aa38f86b Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..994d4c36 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,67 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + installUpdate + id + remindMeLater + id + skipThisVersion + id + + CLASS + SUUpdateAlert + LANGUAGE + ObjC + OUTLETS + + delegate + id + description + NSTextField + releaseNotesView + WebView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 00000000..d2586ea2 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,16 @@ + + + + + IBFramework Version + 629 + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..c82d3581 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 00000000..5220a221 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 00000000..d2586ea2 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,16 @@ + + + + + IBFramework Version + 629 + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 00000000..ac298ce7 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 00000000..67cf535e Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/relaunch b/Frameworks/Sparkle.framework/Versions/A/Resources/relaunch new file mode 100755 index 00000000..e7b96d61 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/relaunch differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..4b1ab30e --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2b3d4257 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..1d4655c5 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..994d4c36 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,67 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + installUpdate + id + remindMeLater + id + skipThisVersion + id + + CLASS + SUUpdateAlert + LANGUAGE + ObjC + OUTLETS + + delegate + id + description + NSTextField + releaseNotesView + WebView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 00000000..2b3d4257 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..103b1cf8 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 00000000..0f776c89 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 00000000..5132e29f --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 00000000..c09d9e70 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 00000000..f3ff9d86 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..4b1ab30e --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 00000000..c5a067e8 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 10A96 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..53cb91a9 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 00000000..018710af --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,39 @@ +{ + IBClasses = ( + { + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, + { + CLASS = NSApplication; + LANGUAGE = ObjC; + SUPERCLASS = NSResponder; + }, + { + CLASS = NSObject; + LANGUAGE = ObjC; + }, + { + ACTIONS = { + installUpdate = id; + remindMeLater = id; + skipThisVersion = id; + }; + CLASS = SUUpdateAlert; + LANGUAGE = ObjC; + OUTLETS = { + delegate = id; + description = NSTextField; + releaseNotesView = WebView; + }; + SUPERCLASS = SUWindowController; + }, + { + CLASS = SUWindowController; + LANGUAGE = ObjC; + SUPERCLASS = NSWindowController; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 00000000..6b787d4b --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBDocumentLocation + 69 14 356 240 0 0 1280 778 + IBFramework Version + 489.0 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 00000000..7e6d490e Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 00000000..5220a221 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/info.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 00000000..c5a067e8 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 10A96 + targetFramework + IBCocoaFramework + + diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 00000000..64babac1 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 00000000..b676a4f5 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings differ diff --git a/Frameworks/Sparkle.framework/Versions/A/Sparkle b/Frameworks/Sparkle.framework/Versions/A/Sparkle new file mode 100755 index 00000000..0db0a8f0 Binary files /dev/null and b/Frameworks/Sparkle.framework/Versions/A/Sparkle differ diff --git a/Frameworks/Sparkle.framework/Versions/Current b/Frameworks/Sparkle.framework/Versions/Current new file mode 120000 index 00000000..8c7e5a66 --- /dev/null +++ b/Frameworks/Sparkle.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Interfaces/English.lproj/Console.nib/classes.nib b/Interfaces/English.lproj/Console.nib/classes.nib new file mode 100644 index 00000000..0328b33e --- /dev/null +++ b/Interfaces/English.lproj/Console.nib/classes.nib @@ -0,0 +1,63 @@ + + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + NSWindow + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + clearConsole + id + copy + id + saveConsoleAs + id + toggleShowSelectShowStatements + id + toggleShowTimeStamps + id + + CLASS + SPQueryConsole + LANGUAGE + ObjC + OUTLETS + + clearConsoleButton + NSButton + consoleSearchField + NSSearchField + consoleTableView + NSTableView + includeTimeStampsButton + NSButton + progressIndicator + NSProgressIndicator + saveConsoleButton + NSButton + saveLogView + NSView + + SUPERCLASS + NSWindowController + + + IBVersion + 1 + + diff --git a/Interfaces/English.lproj/Console.nib/info.nib b/Interfaces/English.lproj/Console.nib/info.nib new file mode 100644 index 00000000..2ca73e20 --- /dev/null +++ b/Interfaces/English.lproj/Console.nib/info.nib @@ -0,0 +1,21 @@ + + + + + IBFramework Version + 677 + IBLastKnownRelativeProjectPath + ../../sequel-pro.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 49 + 7 + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/Interfaces/English.lproj/Console.nib/keyedobjects.nib b/Interfaces/English.lproj/Console.nib/keyedobjects.nib new file mode 100644 index 00000000..5cb43a2e Binary files /dev/null and b/Interfaces/English.lproj/Console.nib/keyedobjects.nib differ diff --git a/Interfaces/English.lproj/Credits.rtf b/Interfaces/English.lproj/Credits.rtf index 4ca6fc5d..2f5301ee 100644 --- a/Interfaces/English.lproj/Credits.rtf +++ b/Interfaces/English.lproj/Credits.rtf @@ -1,17 +1,22 @@ {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf430 {\fonttbl\f0\fnil\fcharset0 LucidaGrande;} -{\colortbl;\red255\green255\blue255;\red25\green25\blue25;} +{\colortbl;\red255\green255\blue255;\red25\green25\blue25;\red0\green27\blue199;} \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\qc\pardirnatural \f0\b\fs22 \cf2 Current Developers \b0 \cf0 \ {\field{\*\fldinst{HYPERLINK "http://www.abhibeckert.com/"}}{\fldrslt Abhi Beckert}}\ -{\field{\*\fldinst{HYPERLINK "http://www.mattlangtree.com/"}}{\fldrslt Matt Langtree}}\ +{\field{\*\fldinst{HYPERLINK "http://mattlangtree.com.au/"}}{\fldrslt Matt Langtree}}\ {\field{\*\fldinst{HYPERLINK "http://www.benperry.com.au/"}}{\fldrslt Ben Perry}}\ {\field{\*\fldinst{HYPERLINK "http://www.markltownsend.com"}}{\fldrslt Mark Townsend}}\ Rowan Beentje \ \pard\pardeftab720\qc {\field{\*\fldinst{HYPERLINK "http://stuconnolly.com/"}}{\fldrslt \cf0 Stuart Connolly}}\ +\pard\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "http://marius.me.uk/"}}{\fldrslt \cf0 Marius Ursache}}\ +Jakob Egger\ +\pard\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "http://www.google.com/url?q=http://www.bibiko.de/&ei=7XnmSe_aJpCc-gakkKTiBQ&sa=X&oi=spellmeleon_result&resnum=1&ct=result&usg=AFQjCNHkKis7UP61ILUxK7GxnUHortnn_A"}}{\fldrslt \cf3 \ul \ulc3 Hans-J\'f6rg Bibiko}}\ \ \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\qc\pardirnatural @@ -34,6 +39,7 @@ Jason Hallford\ Carsten Bl\'fcm\ Andrea Salomoni\ Greg Hulands\ +Paul Kim (NoodleLineNumberView)\ \ \b Artwork @@ -45,9 +51,11 @@ GUI design by Abhi Beckert, Matt Langtree and snowhouse ({\field{\*\fldinst{HYPE \b Additional Thanks \b0 \ City of Cairns.com ({\field{\*\fldinst{HYPERLINK "http://www.cityofcairns.com/"}}{\fldrslt http://cityofcairns.com/}}), for allowing Abhi and Matt to work on Sequel Pro when we'd otherwise be doing billable work.\ -\ +\pard\pardeftab720\ql\qnatural +\cf0 \ +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\qc\pardirnatural -\b GPL +\b \cf0 GPL \b0 \ Copyright (C) 2002-2008 Sequel Pro and CocoaMySQL teams. All rights reserved.\ \ diff --git a/Interfaces/English.lproj/DBView.nib/classes.nib b/Interfaces/English.lproj/DBView.nib/classes.nib index 3877cced..d4bac6a8 100644 --- a/Interfaces/English.lproj/DBView.nib/classes.nib +++ b/Interfaces/English.lproj/DBView.nib/classes.nib @@ -1,752 +1,444 @@ - - - - - IBClasses - - - ACTIONS - - copy - id - - CLASS - CMCopyTable - LANGUAGE - ObjC - SUPERCLASS - NSTableView - - - CLASS - ImageAndTextCell - LANGUAGE - ObjC - SUPERCLASS - NSTextFieldCell - - - CLASS - SPTableData - LANGUAGE - ObjC - OUTLETS - - tableDocumentInstance - id - tableListInstance - id - - SUPERCLASS - NSObject - - - CLASS - KeyChain - LANGUAGE - ObjC - SUPERCLASS - NSObject - - - ACTIONS - - addTable - id - closeCopyTableSheet - id - copyTable - id - removeTable - id - updateTables - id - - CLASS - TablesList - LANGUAGE - ObjC - OUTLETS - - copyTableContentSwitch - id - copyTableNameField - id - copyTableSheet - id - customQueryInstance - id - tabView - id - tableContentInstance - id - tableDataInstance - id - tableDocumentInstance - id - tableDumpInstance - id - tableSourceInstance - id - tableStatusInstance - id - tableWindow - id - tablesListView - id - - SUPERCLASS - NSObject - - - CLASS - NSImageView - LANGUAGE - ObjC - SUPERCLASS - NSControl - - - ACTIONS - - reloadTable - id - - CLASS - TableStatus - LANGUAGE - ObjC - OUTLETS - - commentsBox - id - rowsAutoIncrement - id - rowsAvgLength - id - rowsFormat - id - rowsNumber - id - sizeData - id - sizeFree - id - sizeIndex - id - sizeMaxData - id - tableCreatedAt - id - tableDataInstance - id - tableName - id - tableType - id - tableUpdatedAt - id - - SUPERCLASS - NSObject - - - ACTIONS - - addRow - id - closeEditSheet - id - copyRow - id - dropImage - id - filterTable - id - openEditSheet - id - reloadTable - id - reloadTableValues - id - removeRow - id - saveEditSheet - id - setCompareTypes - id - showAll - id - stepLimitRows - id - toggleFilterField - id - - CLASS - TableContent - LANGUAGE - ObjC - OUTLETS - - addButton - id - argumentField - id - compareField - id - copyButton - id - countText - id - editData - id - editImage - id - editSheet - id - editSheetTabView - id - editTextView - id - fieldField - id - filterButton - id - hexTextView - id - limitRowsButton - id - limitRowsField - id - limitRowsStepper - id - limitRowsText - id - multipleLineEditingButton - id - queryConsoleInstance - id - removeButton - id - tableContentView - CMCopyTable - tableDataInstance - id - tableDocumentInstance - id - tableWindow - id - tablesListInstance - id - - SUPERCLASS - NSObject - - - ACTIONS - - copyCreateTableSyntax - id - - CLASS - FirstResponder - LANGUAGE - ObjC - SUPERCLASS - NSObject - - - CLASS - NSObject - LANGUAGE - ObjC - - - CLASS - CMTextView - LANGUAGE - ObjC - SUPERCLASS - NSTextView - - - ACTIONS - - addQueryFavorite - id - chooseQueryFavorite - id - chooseQueryHistory - id - closeQueryFavoritesSheet - id - closeSheet - id - copyQueryFavorite - id - performQuery - id - removeQueryFavorite - id - - CLASS - CustomQuery - LANGUAGE - ObjC - OUTLETS - - affectedRowsText - id - copyQueryFavoriteButton - id - customQueryView - CMCopyTable - errorText - id - queryFavoritesButton - id - queryFavoritesSheet - id - queryFavoritesView - id - queryHistoryButton - id - removeQueryFavoriteButton - id - tableWindow - id - textView - id - valueSheet - id - valueTextField - id - - SUPERCLASS - NSObject - - - CLASS - CMImageView - LANGUAGE - ObjC - OUTLETS - - delegate - id - - SUPERCLASS - NSImageView - - - ACTIONS - - cancelProgressBar - id - changeFormat - id - changeTable - id - closeSheet - id - panelSelectionDidChange - id - reloadTables - id - selectTables - id - stepRow - id - - CLASS - TableDump - LANGUAGE - ObjC - OUTLETS - - addCreateTableSwitch - id - addDropTableSwitch - id - addErrorsSwitch - id - addTableContentSwitch - id - customQueryInstance - id - errorsSheet - id - errorsView - id - exportCSVView - id - exportDumpTableView - id - exportDumpView - id - exportFieldNamesSwitch - id - exportFieldsEnclosedField - id - exportFieldsEscapedField - id - exportFieldsTerminatedField - id - exportLinesTerminatedField - id - exportMultipleCSVTableView - id - exportMultipleCSVView - id - exportMultipleFieldNamesSwitch - id - exportMultipleFieldsEnclosedField - id - exportMultipleFieldsEscapedField - id - exportMultipleFieldsTerminatedField - id - exportMultipleLinesTerminatedField - id - exportMultipleXMLTableView - id - exportMultipleXMLView - id - fieldMappingPopup - id - fieldMappingSheet - id - fieldMappingTableView - id - importCSVBox - id - importCSVView - id - importFieldNamesSwitch - id - importFieldsEnclosedField - id - importFieldsEscapedField - id - importFieldsTerminatedField - id - importFormatPopup - NSPopUpButton - importLinesTerminatedField - id - recordCountLabel - id - rowDownButton - id - rowUpButton - id - singleProgressBar - id - singleProgressSheet - id - singleProgressText - id - tableContentInstance - id - tableDataInstance - id - tableDocumentInstance - id - tableListView - id - tableSourceInstance - id - tableWindow - id - tablesListInstance - id - - SUPERCLASS - NSObject - - - CLASS - SPTableInfo - LANGUAGE - ObjC - OUTLETS - - infoTable - id - tableDataInstance - id - tableDocumentInstance - id - tableList - id - tableListInstance - id - - SUPERCLASS - NSObject - - - ACTIONS - - clearConsole - id - saveConsoleAs - id - - CLASS - SPQueryConsole - LANGUAGE - ObjC - OUTLETS - - consoleTextView - NSTextView - - SUPERCLASS - NSWindowController - - - CLASS - TableDocumentSplitView - LANGUAGE - ObjC - SUPERCLASS - NSSplitView - - - ACTIONS - - addField - id - addIndex - id - chooseIndexType - id - closeIndexSheet - id - closeKeySheet - id - copyField - id - openIndexSheet - id - reloadTable - id - removeField - id - removeIndex - id - typeChanged - id - - CLASS - TableSource - LANGUAGE - ObjC - OUTLETS - - addFieldButton - id - addIndexButton - id - chooseKeyButton - id - copyFieldButton - id - indexNameField - id - indexSheet - id - indexTypeField - id - indexView - id - indexedColumnsField - id - keySheet - id - removeFieldButton - id - removeIndexButton - id - structureGrabber - id - tableDataInstance - id - tableSourceView - id - tableTypeButton - id - tableWindow - id - tablesListInstance - id - - SUPERCLASS - NSObject - - - ACTIONS - - addDatabase - id - analyzeTable - id - cancelConnectSheet - id - checkTable - id - checksumTable - id - chooseDatabase - id - chooseEncoding - id - chooseFavorite - id - closeDatabaseSheet - id - closeSheet - id - connect - id - connectSheetAddToFavorites - id - connectToDB - id - copyCreateTableSyntax - id - export - id - exportMultipleTables - id - exportTable - id - flushPrivileges - id - flushTable - id - import - id - optimizeTable - id - removeDatabase - id - removeFavorite - id - repairTable - id - setDatabases - id - showCreateTableSyntax - id - showVariables - id - toggleUseSSH - id - viewContent - id - viewQuery - id - viewStatus - id - viewStructure - id - - CLASS - TableDocument - LANGUAGE - ObjC - OUTLETS - - addDatabaseButton - id - chooseDatabaseButton - id - connectFavoritesTableView - NSTableView - connectProgressBar - id - connectProgressStatusText - id - connectSheet - id - createTableSyntaxWindow - NSWindow - customQueryInstance - id - databaseEncodingButton - id - databaseField - id - databaseNameField - id - databaseSheet - id - dbTablesTableView - NSTableView - favoritesButton - id - favoritesController - NSArrayController - hostField - id - keyChainInstance - id - passwordField - id - portField - id - queryConsoleInstance - id - queryProgressBar - id - sidebarGrabber - id - socketField - id - sshCheckbox - id - sshHostField - id - sshPasswordField - id - sshPortField - id - sshUserField - id - syntaxView - id - syntaxViewContent - id - tableContentInstance - id - tableDataInstance - id - tableDumpInstance - id - tableSourceInstance - id - tableStatusInstance - id - tableTabView - NSTabView - tableWindow - id - tablesListInstance - id - userField - id - variablesSheet - id - variablesTableView - id - - SUPERCLASS - NSDocument - - - IBVersion - 1 - - +{ + IBClasses = ( + { + ACTIONS = {copy = id; }; + CLASS = CMCopyTable; + LANGUAGE = ObjC; + SUPERCLASS = NSTableView; + }, + { + CLASS = CMImageView; + LANGUAGE = ObjC; + OUTLETS = {delegate = id; }; + SUPERCLASS = NSImageView; + }, + { + CLASS = CMTextView; + LANGUAGE = ObjC; + OUTLETS = {scrollView = NSScrollView; }; + SUPERCLASS = NSTextView; + }, + { + ACTIONS = { + addQueryFavorite = id; + chooseQueryFavorite = id; + chooseQueryHistory = id; + closeQueryFavoritesSheet = id; + closeSheet = id; + copyQueryFavorite = id; + gearMenuItemSelected = id; + removeQueryFavorite = id; + runAllQueries = id; + runSelectedQueries = id; + }; + CLASS = CustomQuery; + LANGUAGE = ObjC; + OUTLETS = { + affectedRowsText = id; + autoindentMenuItem = NSMenuItem; + autopairMenuItem = NSMenuItem; + autouppercaseKeywordsMenuItem = NSMenuItem; + clearHistoryMenuItem = NSMenuItem; + completionListMenuItem = NSMenuItem; + copyQueryFavoriteButton = id; + customQueryView = CMCopyTable; + editorFontMenuItem = NSMenuItem; + errorText = id; + queryFavoritesButton = id; + queryFavoritesSheet = id; + queryFavoritesView = id; + queryHistoryButton = id; + removeQueryFavoriteButton = id; + runAllButton = id; + runSelectionButton = id; + runSelectionMenuItem = NSMenuItem; + shiftLeftMenuItem = NSMenuItem; + shiftRightMenuItem = NSMenuItem; + tableWindow = id; + textView = CMTextView; + valueSheet = id; + valueTextField = id; + }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = {copyCreateTableSyntax = id; }; + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, + {CLASS = ImageAndTextCell; LANGUAGE = ObjC; SUPERCLASS = NSTextFieldCell; }, + {CLASS = KeyChain; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + {CLASS = NSImageView; LANGUAGE = ObjC; SUPERCLASS = NSControl; }, + {CLASS = NSObject; LANGUAGE = ObjC; }, + { + ACTIONS = { + doDecomposedStringWithCanonicalMapping = id; + doDecomposedStringWithCompatibilityMapping = id; + doPrecomposedStringWithCanonicalMapping = id; + doPrecomposedStringWithCompatibilityMapping = id; + doRemoveDiacritics = id; + doSelectionLowerCase = id; + doSelectionTitleCase = id; + doSelectionUpperCase = id; + doTranspose = id; + selectCurrentLine = id; + selectCurrentWord = id; + }; + CLASS = NSTextView; + LANGUAGE = ObjC; + SUPERCLASS = NSText; + }, + {CLASS = NSWindow; LANGUAGE = ObjC; SUPERCLASS = NSResponder; }, + { + ACTIONS = {closeSheet = id; switchInput = id; switchTab = id; }; + CLASS = SPExportController; + LANGUAGE = ObjC; + OUTLETS = { + exampleNameLabel = id; + exportCSVFieldsEscapedField = id; + exportCSVFieldsTerminatedField = id; + exportCSVFieldsWrappedField = id; + exportCSVIncludeFieldNamesCheck = id; + exportCSVLinesTerminatedField = id; + exportExcelSheetOrFilePerTableMatrix = id; + exportFilePerTableCheck = id; + exportFilePerTableNote = id; + exportHTMLIncludeHeadAndBodyTagsCheck = id; + exportHTMLIncludeStructureCheck = id; + exportInputMatrix = id; + exportPDFIncludeStructureCheck = id; + exportSQLIncludeDropSyntaxCheck = id; + exportSQLIncludeErrorsCheck = id; + exportSQLIncludeStructureCheck = id; + exportTabBar = id; + exportTableList = id; + exportToolbar = id; + exportWindow = id; + exportXMLIncludeStructureCheck = id; + tableDocumentInstance = id; + tableWindow = id; + tablesListInstance = id; + tokenNameField = id; + tokenNameTokensField = id; + tokenNameView = id; + }; + SUPERCLASS = NSObject; + }, + { + CLASS = SPTableData; + LANGUAGE = ObjC; + OUTLETS = {tableDocumentInstance = id; tableListInstance = id; }; + SUPERCLASS = NSObject; + }, + { + CLASS = SPTableInfo; + LANGUAGE = ObjC; + OUTLETS = { + infoTable = id; + tableDataInstance = id; + tableDocumentInstance = id; + tableList = id; + tableListInstance = id; + }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = { + addRow = id; + closeEditSheet = id; + copyRow = id; + dropImage = id; + filterTable = id; + openEditSheet = id; + reloadTable = id; + reloadTableValues = id; + removeRow = id; + saveEditSheet = id; + setCompareTypes = id; + showAll = id; + stepLimitRows = id; + toggleFilterField = id; + }; + CLASS = TableContent; + LANGUAGE = ObjC; + OUTLETS = { + addButton = id; + argumentField = id; + compareField = id; + copyButton = id; + countText = id; + editData = id; + editImage = id; + editSheet = id; + editSheetTabView = id; + editTextView = id; + fieldField = id; + filterButton = id; + hexTextView = id; + limitRowsButton = id; + limitRowsField = id; + limitRowsStepper = id; + limitRowsText = id; + multipleLineEditingButton = id; + removeButton = id; + tableContentView = CMCopyTable; + tableDataInstance = id; + tableDocumentInstance = id; + tableWindow = id; + tablesListInstance = id; + }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = { + addConnectionToFavorites = id; + addDatabase = id; + analyzeTable = id; + cancelConnectSheet = id; + checkTable = id; + checksumTable = id; + chooseDatabase = id; + chooseEncoding = id; + chooseFavorite = id; + closeDatabaseSheet = id; + closeSheet = id; + connect = id; + connectSheetAddToFavorites = id; + connectToDB = id; + copyCreateTableSyntax = id; + editFavorites = id; + export = id; + exportMultipleTables = id; + exportTable = id; + flushPrivileges = id; + flushTable = id; + import = id; + optimizeTable = id; + removeDatabase = id; + repairTable = id; + setDatabases = id; + showCreateTableSyntax = id; + showVariables = id; + toggleUseSSH = id; + viewContent = id; + viewQuery = id; + viewStatus = id; + viewStructure = id; + }; + CLASS = TableDocument; + LANGUAGE = ObjC; + OUTLETS = { + addDatabaseButton = id; + chooseDatabaseButton = id; + connectFavoritesTableView = NSTableView; + connectProgressBar = id; + connectProgressStatusText = NSTextField; + connectSheet = id; + createTableSyntaxWindow = NSWindow; + customQueryInstance = id; + customQueryTextView = NSTextView; + databaseEncodingButton = id; + databaseField = id; + databaseNameField = id; + databaseSheet = id; + dbTablesTableView = NSTableView; + favoritesButton = id; + favoritesController = NSArrayController; + hostField = id; + keyChainInstance = id; + nameField = id; + passwordField = id; + portField = id; + queryProgressBar = id; + sidebarGrabber = id; + socketField = id; + spExportControllerInstance = id; + sshCheckbox = id; + sshHostField = id; + sshPasswordField = id; + sshPortField = id; + sshUserField = id; + syntaxView = id; + syntaxViewContent = id; + tableContentInstance = id; + tableDataInstance = id; + tableDumpInstance = id; + tableSourceInstance = id; + tableStatusInstance = id; + tableTabView = NSTabView; + tableWindow = id; + tablesListInstance = id; + userField = id; + variablesSheet = id; + variablesTableView = id; + }; + SUPERCLASS = NSDocument; + }, + {CLASS = TableDocumentSplitView; LANGUAGE = ObjC; SUPERCLASS = NSSplitView; }, + { + ACTIONS = { + cancelProgressBar = id; + changeFormat = id; + changeTable = id; + closeSheet = id; + panelSelectionDidChange = id; + reloadTables = id; + selectTables = id; + stepRow = id; + switchInput = id; + switchTab = id; + }; + CLASS = TableDump; + LANGUAGE = ObjC; + OUTLETS = { + addCreateTableSwitch = id; + addDropTableSwitch = id; + addErrorsSwitch = id; + addTableContentSwitch = id; + customQueryInstance = id; + errorsSheet = id; + errorsView = id; + exportCSVView = id; + exportDumpTableView = id; + exportDumpView = id; + exportFieldNamesSwitch = id; + exportFieldsEnclosedField = id; + exportFieldsEscapedField = id; + exportFieldsTerminatedField = id; + exportLinesTerminatedField = id; + exportMultipleCSVTableView = id; + exportMultipleCSVView = id; + exportMultipleFieldNamesSwitch = id; + exportMultipleFieldsEnclosedField = id; + exportMultipleFieldsEscapedField = id; + exportMultipleFieldsTerminatedField = id; + exportMultipleLinesTerminatedField = id; + exportMultipleXMLTableView = id; + exportMultipleXMLView = id; + exportTabBar = id; + exportTableList = id; + exportToolbar = id; + exportWindow = id; + fieldMappingPopup = id; + fieldMappingSheet = id; + fieldMappingTableView = id; + importCSVBox = id; + importCSVView = id; + importFieldNamesSwitch = id; + importFieldsEnclosedField = id; + importFieldsEscapedField = id; + importFieldsTerminatedField = id; + importFormatPopup = NSPopUpButton; + importLinesTerminatedField = id; + recordCountLabel = id; + rowDownButton = id; + rowUpButton = id; + singleProgressBar = id; + singleProgressSheet = id; + singleProgressText = id; + tableContentInstance = id; + tableDataInstance = id; + tableDocumentInstance = id; + tableListView = id; + tableSourceInstance = id; + tableWindow = id; + tablesListInstance = id; + }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = { + addField = id; + addIndex = id; + chooseIndexType = id; + closeIndexSheet = id; + closeKeySheet = id; + copyField = id; + openIndexSheet = id; + reloadTable = id; + removeField = id; + removeIndex = id; + typeChanged = id; + }; + CLASS = TableSource; + LANGUAGE = ObjC; + OUTLETS = { + addFieldButton = id; + addIndexButton = id; + chooseKeyButton = id; + copyFieldButton = id; + indexNameField = id; + indexSheet = id; + indexTypeField = id; + indexView = id; + indexedColumnsField = id; + keySheet = id; + removeFieldButton = id; + removeIndexButton = id; + structureGrabber = id; + tableDataInstance = id; + tableSourceView = id; + tableTypeButton = id; + tableWindow = id; + tablesListInstance = id; + }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = {reloadTable = id; }; + CLASS = TableStatus; + LANGUAGE = ObjC; + OUTLETS = { + commentsBox = id; + rowsAutoIncrement = id; + rowsAvgLength = id; + rowsFormat = id; + rowsNumber = id; + sizeData = id; + sizeFree = id; + sizeIndex = id; + sizeMaxData = id; + tableCreatedAt = id; + tableDataInstance = id; + tableName = id; + tableType = id; + tableUpdatedAt = id; + }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = { + addTable = id; + closeCopyTableSheet = id; + closeTableSheet = id; + copyTable = id; + removeTable = id; + updateTables = id; + }; + CLASS = TablesList; + LANGUAGE = ObjC; + OUTLETS = { + addTableButton = id; + copyTableContentSwitch = id; + copyTableNameField = id; + copyTableSheet = id; + customQueryInstance = id; + tabView = id; + tableContentInstance = id; + tableDataInstance = id; + tableDocumentInstance = id; + tableDumpInstance = id; + tableEncodingButton = id; + tableNameField = id; + tableSheet = id; + tableSourceInstance = id; + tableStatusInstance = id; + tableWindow = id; + tablesListView = id; + }; + SUPERCLASS = NSObject; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/Interfaces/English.lproj/DBView.nib/info.nib b/Interfaces/English.lproj/DBView.nib/info.nib index 763696d5..1995ac1f 100644 --- a/Interfaces/English.lproj/DBView.nib/info.nib +++ b/Interfaces/English.lproj/DBView.nib/info.nib @@ -1,17 +1,36 @@ - + + IBDocumentLocation + 69 85 356 240 0 0 1024 746 + IBEditorPositions + + 1058 + 263 422 498 199 0 0 1024 746 + 1113 + 361 422 301 199 0 0 1024 746 + 3994 + 402 485 220 74 0 0 1024 746 + 4529 + 293 433 437 177 0 0 1024 746 + 815 + 290 443 444 158 0 0 1024 746 + 910 + 273 426 477 191 0 0 1024 746 + IBFramework Version - 677 + 489.0 IBLastKnownRelativeProjectPath ../../sequel-pro.xcodeproj + IBLockedTabItems + + 28 + IBOldestOS 5 - IBOpenObjects - IBSystem Version - 9G55 + 8S165 targetFramework IBCocoaFramework diff --git a/Interfaces/English.lproj/DBView.nib/keyedobjects.nib b/Interfaces/English.lproj/DBView.nib/keyedobjects.nib index 3ee4c7ad..c44f2194 100644 Binary files a/Interfaces/English.lproj/DBView.nib/keyedobjects.nib and b/Interfaces/English.lproj/DBView.nib/keyedobjects.nib differ diff --git a/Interfaces/English.lproj/InfoPlist.strings b/Interfaces/English.lproj/InfoPlist.strings index a213db55..6048d98c 100644 --- a/Interfaces/English.lproj/InfoPlist.strings +++ b/Interfaces/English.lproj/InfoPlist.strings @@ -1,4 +1,4 @@ /* Localized versions of Info.plist keys */ -CFBundleGetInfoString = "Sequel Pro version 0.9.4, Copyright 2002-2009 Sequel Pro and CocoaMySQL team."; +CFBundleGetInfoString = "Sequel Pro version 0.9.5 Tiger Version, Copyright 2002-2009 Sequel Pro and CocoaMySQL team."; NSHumanReadableCopyright = "Copyright 2002-2009 Sequel Pro and CocoaMySQL team."; \ No newline at end of file diff --git a/Interfaces/English.lproj/Localizable.strings b/Interfaces/English.lproj/Localizable.strings index 7bc7a52d..bba6709a 100644 Binary files a/Interfaces/English.lproj/Localizable.strings and b/Interfaces/English.lproj/Localizable.strings differ diff --git a/Interfaces/English.lproj/MainMenu.nib/classes.nib b/Interfaces/English.lproj/MainMenu.nib/classes.nib index e80e7306..cfb78d7c 100644 --- a/Interfaces/English.lproj/MainMenu.nib/classes.nib +++ b/Interfaces/English.lproj/MainMenu.nib/classes.nib @@ -39,65 +39,54 @@ LANGUAGE = ObjC; SUPERCLASS = NSObject; }, - {CLASS = KeyChain; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, { - ACTIONS = { - addFavorite = id; - checkForUpdates = id; - chooseLimitRows = id; - closeFavoriteSheet = id; - closePreferences = id; - copyFavorite = id; - donate = id; - openPreferences = id; - removeFavorite = id; - toggleUseSSH = id; - visitHelpWebsite = id; - visitWebsite = id; - }; + ACTIONS = {donate = id; openPreferences = id; visitHelpWebsite = id; visitWebsite = id; }; CLASS = MainController; LANGUAGE = ObjC; - OUTLETS = { - databaseField = id; - dontShowBlobSwitch = id; - encodingPopUpButton = id; - favoriteSheet = id; - fetchRowCountSwitch = id; - hostField = id; - keyChainInstance = id; - limitRowsField = id; - limitRowsSwitch = id; - nameField = id; - nullValueField = id; - passwordField = id; - portField = id; - preferencesWindow = id; - reloadAfterAddingSwitch = id; - reloadAfterEditingSwitch = id; - reloadAfterRemovingSwitch = id; - showErrorSwitch = id; - socketField = id; - sshCheckbox = id; - sshHostField = id; - sshPasswordField = id; - sshPortField = id; - sshUserField = id; - tableView = id; - useMonospacedFontsSwitch = id; - userField = id; - }; SUPERCLASS = NSObject; }, {CLASS = NSMenu; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, {CLASS = NSObject; LANGUAGE = ObjC; }, { - ACTIONS = {clearConsole = id; saveConsoleAs = id; }; + ACTIONS = { + doDecomposedStringWithCanonicalMapping = id; + doDecomposedStringWithCompatibilityMapping = id; + doPrecomposedStringWithCanonicalMapping = id; + doPrecomposedStringWithCompatibilityMapping = id; + doRemoveDiacritics = id; + doSelectionLowerCase = id; + doSelectionTitleCase = id; + doSelectionUpperCase = id; + doTranspose = id; + selectCurrentLine = id; + selectCurrentWord = id; + }; + CLASS = NSTextView; + LANGUAGE = ObjC; + SUPERCLASS = NSText; + }, + {CLASS = NSWindow; LANGUAGE = ObjC; SUPERCLASS = NSResponder; }, + { + ACTIONS = { + clearConsole = id; + copy = id; + saveConsoleAs = id; + toggleShowSelectShowStatements = id; + toggleShowTimeStamps = id; + }; CLASS = SPQueryConsole; LANGUAGE = ObjC; - OUTLETS = {consoleView = SPQueryConsoleView; }; + OUTLETS = { + clearConsoleButton = NSButton; + consoleSearchField = NSSearchField; + consoleTableView = NSTableView; + includeTimeStampsButton = NSButton; + progressIndicator = NSProgressIndicator; + saveConsoleButton = NSButton; + saveLogView = NSView; + }; SUPERCLASS = NSWindowController; }, - {CLASS = SPQueryConsoleView; LANGUAGE = ObjC; SUPERCLASS = NSView; }, { ACTIONS = {checkForUpdates = id; }; CLASS = SUUpdater; @@ -107,6 +96,7 @@ }, { ACTIONS = { + addConnectionToFavorites = id; addDatabase = id; analyzeTable = id; cancelConnectSheet = id; @@ -121,6 +111,7 @@ connectSheetAddToFavorites = id; connectToDB = id; copyCreateTableSyntax = id; + editFavorites = id; export = id; exportMultipleTables = id; exportTable = id; @@ -129,7 +120,6 @@ import = id; optimizeTable = id; removeDatabase = id; - removeFavorite = id; repairTable = id; setDatabases = id; showCreateTableSyntax = id; @@ -150,6 +140,7 @@ connectSheet = id; createTableSyntaxWindow = NSWindow; customQueryInstance = id; + customQueryTextView = NSTextView; databaseEncodingButton = id; databaseField = id; databaseNameField = id; @@ -159,12 +150,13 @@ favoritesController = NSArrayController; hostField = id; keyChainInstance = id; + nameField = id; passwordField = id; portField = id; - queryConsoleInstance = id; queryProgressBar = id; sidebarGrabber = id; socketField = id; + spExportControllerInstance = id; syntaxView = id; syntaxViewContent = id; tableContentInstance = id; @@ -180,33 +172,6 @@ variablesTableView = id; }; SUPERCLASS = NSDocument; - }, - { - ACTIONS = { - addTable = id; - closeCopyTableSheet = id; - copyTable = id; - removeTable = id; - updateTables = id; - }; - CLASS = TablesList; - LANGUAGE = ObjC; - OUTLETS = { - copyTableContentSwitch = id; - copyTableNameField = id; - copyTableSheet = id; - customQueryInstance = id; - tabView = id; - tableContentInstance = id; - tableDataInstance = id; - tableDocumentInstance = id; - tableDumpInstance = id; - tableSourceInstance = id; - tableStatusInstance = id; - tableWindow = id; - tablesListView = id; - }; - SUPERCLASS = NSObject; } ); IBVersion = 1; diff --git a/Interfaces/English.lproj/MainMenu.nib/info.nib b/Interfaces/English.lproj/MainMenu.nib/info.nib index 810872d5..0ce25e8a 100644 --- a/Interfaces/English.lproj/MainMenu.nib/info.nib +++ b/Interfaces/English.lproj/MainMenu.nib/info.nib @@ -3,18 +3,18 @@ IBDocumentLocation - 69 85 356 240 0 0 1024 746 + 116 228 356 240 0 0 1024 746 IBEditorPositions 29 - 69 330 481 44 0 0 1024 746 + 116 473 481 44 0 0 1024 746 IBFramework Version 489.0 IBLastKnownRelativeProjectPath ../../sequel-pro.xcodeproj IBOldestOS - 4 + 5 IBOpenObjects 29 diff --git a/Interfaces/English.lproj/MainMenu.nib/keyedobjects.nib b/Interfaces/English.lproj/MainMenu.nib/keyedobjects.nib index a7c4e7bb..c11c2e69 100644 Binary files a/Interfaces/English.lproj/MainMenu.nib/keyedobjects.nib and b/Interfaces/English.lproj/MainMenu.nib/keyedobjects.nib differ diff --git a/Interfaces/English.lproj/Preferences.nib/classes.nib b/Interfaces/English.lproj/Preferences.nib/classes.nib new file mode 100644 index 00000000..1ae0ade6 --- /dev/null +++ b/Interfaces/English.lproj/Preferences.nib/classes.nib @@ -0,0 +1,49 @@ +{ + IBClasses = ( + {CLASS = NSObject; LANGUAGE = ObjC; }, + {CLASS = NSWindow; LANGUAGE = ObjC; SUPERCLASS = NSResponder; }, + { + ACTIONS = { + addFavorite = id; + displayAutoUpdatePreferences = id; + displayFavoritePreferences = id; + displayGeneralPreferences = id; + displayNetworkPreferences = id; + displayNotificationPreferences = id; + displayTablePreferences = id; + duplicateFavorite = id; + removeFavorite = id; + saveFavorite = id; + updateDefaultFavorite = id; + }; + CLASS = SPPreferenceController; + LANGUAGE = ObjC; + OUTLETS = { + autoUpdateView = NSView; + databaseField = NSTextField; + defaultFavoritePopup = NSPopUpButton; + favoritesController = NSArrayController; + favoritesTableView = NSTableView; + favoritesView = NSView; + generalView = NSView; + hostField = NSTextField; + nameField = NSTextField; + networkView = NSView; + notificationsView = NSView; + passwordField = NSSecureTextField; + preferencesWindow = NSWindow; + tablesView = NSView; + userField = NSTextField; + }; + SUPERCLASS = NSWindowController; + }, + { + ACTIONS = {checkForUpdates = id; }; + CLASS = SUUpdater; + LANGUAGE = ObjC; + OUTLETS = {delegate = id; }; + SUPERCLASS = NSObject; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/Interfaces/English.lproj/Preferences.nib/info.nib b/Interfaces/English.lproj/Preferences.nib/info.nib new file mode 100644 index 00000000..2538cf8e --- /dev/null +++ b/Interfaces/English.lproj/Preferences.nib/info.nib @@ -0,0 +1,33 @@ + + + + + IBDocumentLocation + 69 85 356 240 0 0 1024 746 + IBEditorPositions + + 17 + 252 392 520 260 0 0 1024 746 + 400 + 252 310 520 423 0 0 1024 746 + 512 + 252 379 520 286 0 0 1024 746 + 56 + 252 454 520 135 0 0 1024 746 + 60 + 252 440 520 164 0 0 1024 746 + 641 + 252 454 520 135 0 0 1024 746 + + IBFramework Version + 489.0 + IBLastKnownRelativeProjectPath + ../../sequel-pro.xcodeproj + IBOldestOS + 5 + IBSystem Version + 8S165 + targetFramework + IBCocoaFramework + + diff --git a/Interfaces/English.lproj/Preferences.nib/keyedobjects.nib b/Interfaces/English.lproj/Preferences.nib/keyedobjects.nib new file mode 100644 index 00000000..226e3341 Binary files /dev/null and b/Interfaces/English.lproj/Preferences.nib/keyedobjects.nib differ diff --git a/Resources/Images/TablePropertyIcon.png b/Resources/Images/TablePropertyIcon.png deleted file mode 100644 index c7e9b928..00000000 Binary files a/Resources/Images/TablePropertyIcon.png and /dev/null differ diff --git a/Resources/Images/button_action.tiff b/Resources/Images/button_action.tiff new file mode 100644 index 00000000..2b586ff4 Binary files /dev/null and b/Resources/Images/button_action.tiff differ diff --git a/Resources/Images/button_bar_handle.tiff b/Resources/Images/button_bar_handle.tiff new file mode 100644 index 00000000..41c0cfea Binary files /dev/null and b/Resources/Images/button_bar_handle.tiff differ diff --git a/Resources/Images/button_bar_spacer.tiff b/Resources/Images/button_bar_spacer.tiff new file mode 100644 index 00000000..a3274f83 Binary files /dev/null and b/Resources/Images/button_bar_spacer.tiff differ diff --git a/Resources/Images/button_duplicate.tiff b/Resources/Images/button_duplicate.tiff new file mode 100644 index 00000000..1695b52c Binary files /dev/null and b/Resources/Images/button_duplicate.tiff differ diff --git a/Resources/Images/button_edit.tiff b/Resources/Images/button_edit.tiff new file mode 100644 index 00000000..e243b363 Binary files /dev/null and b/Resources/Images/button_edit.tiff differ diff --git a/Resources/Images/button_edit_mode.tiff b/Resources/Images/button_edit_mode.tiff new file mode 100644 index 00000000..5fc82d9b Binary files /dev/null and b/Resources/Images/button_edit_mode.tiff differ diff --git a/Resources/Images/button_edit_mode_selected.tiff b/Resources/Images/button_edit_mode_selected.tiff new file mode 100644 index 00000000..b452e8a1 Binary files /dev/null and b/Resources/Images/button_edit_mode_selected.tiff differ diff --git a/Resources/Images/button_info_pane_hide.tiff b/Resources/Images/button_info_pane_hide.tiff new file mode 100644 index 00000000..4ec99d38 Binary files /dev/null and b/Resources/Images/button_info_pane_hide.tiff differ diff --git a/Resources/Images/button_info_pane_show.tiff b/Resources/Images/button_info_pane_show.tiff new file mode 100644 index 00000000..6b4867dd Binary files /dev/null and b/Resources/Images/button_info_pane_show.tiff differ diff --git a/Resources/Images/button_refresh.tiff b/Resources/Images/button_refresh.tiff new file mode 100644 index 00000000..864769ef Binary files /dev/null and b/Resources/Images/button_refresh.tiff differ diff --git a/Resources/Images/button_remove.tiff b/Resources/Images/button_remove.tiff new file mode 100644 index 00000000..e54033ee Binary files /dev/null and b/Resources/Images/button_remove.tiff differ diff --git a/Resources/Images/button_subtract.tiff b/Resources/Images/button_subtract.tiff deleted file mode 100644 index a8d6a19d..00000000 Binary files a/Resources/Images/button_subtract.tiff and /dev/null differ diff --git a/Resources/Images/clearconsole.tiff b/Resources/Images/clearconsole.tiff index ec7d181a..86c91ba9 100644 Binary files a/Resources/Images/clearconsole.tiff and b/Resources/Images/clearconsole.tiff differ diff --git a/Resources/Images/columnadd.tiff b/Resources/Images/columnadd.tiff deleted file mode 100644 index 1eaa8822..00000000 Binary files a/Resources/Images/columnadd.tiff and /dev/null differ diff --git a/Resources/Images/columncopy.tiff b/Resources/Images/columncopy.tiff deleted file mode 100644 index 55848ac1..00000000 Binary files a/Resources/Images/columncopy.tiff and /dev/null differ diff --git a/Resources/Images/columndelete.tiff b/Resources/Images/columndelete.tiff deleted file mode 100644 index 5ddee6ff..00000000 Binary files a/Resources/Images/columndelete.tiff and /dev/null differ diff --git a/Resources/Images/columnrefresh.tiff b/Resources/Images/columnrefresh.tiff deleted file mode 100644 index 37ba5d75..00000000 Binary files a/Resources/Images/columnrefresh.tiff and /dev/null differ diff --git a/Resources/Images/connectionadd.tiff b/Resources/Images/connectionadd.tiff deleted file mode 100644 index 73437571..00000000 Binary files a/Resources/Images/connectionadd.tiff and /dev/null differ diff --git a/Resources/Images/connectioncopy.tiff b/Resources/Images/connectioncopy.tiff deleted file mode 100644 index 1072e270..00000000 Binary files a/Resources/Images/connectioncopy.tiff and /dev/null differ diff --git a/Resources/Images/connectiondelete.tiff b/Resources/Images/connectiondelete.tiff deleted file mode 100644 index edd40a9e..00000000 Binary files a/Resources/Images/connectiondelete.tiff and /dev/null differ diff --git a/Resources/Images/connectionrefresh.tiff b/Resources/Images/connectionrefresh.tiff deleted file mode 100644 index 4dd27062..00000000 Binary files a/Resources/Images/connectionrefresh.tiff and /dev/null differ diff --git a/Resources/Images/createtablesyntax.tiff b/Resources/Images/createtablesyntax.tiff deleted file mode 100644 index 0bf7f1a2..00000000 Binary files a/Resources/Images/createtablesyntax.tiff and /dev/null differ diff --git a/Resources/Images/database.png b/Resources/Images/database.png new file mode 100644 index 00000000..826c1b4b Binary files /dev/null and b/Resources/Images/database.png differ diff --git a/Resources/Images/databases.tiff b/Resources/Images/databases.tiff deleted file mode 100644 index f535fb2c..00000000 Binary files a/Resources/Images/databases.tiff and /dev/null differ diff --git a/Resources/Images/dbadd.tiff b/Resources/Images/dbadd.tiff deleted file mode 100644 index 8b5a71ca..00000000 Binary files a/Resources/Images/dbadd.tiff and /dev/null differ diff --git a/Resources/Images/dbdelete.tiff b/Resources/Images/dbdelete.tiff deleted file mode 100644 index 0b1c1eaf..00000000 Binary files a/Resources/Images/dbdelete.tiff and /dev/null differ diff --git a/Resources/Images/dbrefresh.tiff b/Resources/Images/dbrefresh.tiff deleted file mode 100644 index e85c6d1e..00000000 Binary files a/Resources/Images/dbrefresh.tiff and /dev/null differ diff --git a/Resources/Images/dimple.tiff b/Resources/Images/dimple.tiff new file mode 100644 index 00000000..4b44c051 Binary files /dev/null and b/Resources/Images/dimple.tiff differ diff --git a/Resources/Images/flushprivileges.tiff b/Resources/Images/flushprivileges.tiff deleted file mode 100644 index 94c4fca1..00000000 Binary files a/Resources/Images/flushprivileges.tiff and /dev/null differ diff --git a/Resources/Images/hideconsole.tiff b/Resources/Images/hideconsole.tiff index d6c116ea..8caf391f 100644 Binary files a/Resources/Images/hideconsole.tiff and b/Resources/Images/hideconsole.tiff differ diff --git a/Resources/Images/indexadd.tiff b/Resources/Images/indexadd.tiff deleted file mode 100644 index b985d693..00000000 Binary files a/Resources/Images/indexadd.tiff and /dev/null differ diff --git a/Resources/Images/indexdelete.tiff b/Resources/Images/indexdelete.tiff deleted file mode 100644 index d692adf7..00000000 Binary files a/Resources/Images/indexdelete.tiff and /dev/null differ diff --git a/Resources/Images/optimizetable.tiff b/Resources/Images/optimizetable.tiff deleted file mode 100644 index 761e44cd..00000000 Binary files a/Resources/Images/optimizetable.tiff and /dev/null differ diff --git a/Resources/Images/query.tiff b/Resources/Images/query.tiff deleted file mode 100644 index 4d7d23e1..00000000 Binary files a/Resources/Images/query.tiff and /dev/null differ diff --git a/Resources/Images/queryadd.tiff b/Resources/Images/queryadd.tiff deleted file mode 100644 index e174b9b0..00000000 Binary files a/Resources/Images/queryadd.tiff and /dev/null differ diff --git a/Resources/Images/querycopy.tiff b/Resources/Images/querycopy.tiff deleted file mode 100644 index 54f43e93..00000000 Binary files a/Resources/Images/querycopy.tiff and /dev/null differ diff --git a/Resources/Images/querydelete.tiff b/Resources/Images/querydelete.tiff deleted file mode 100644 index 98e5a0c1..00000000 Binary files a/Resources/Images/querydelete.tiff and /dev/null differ diff --git a/Resources/Images/queryrefresh.tiff b/Resources/Images/queryrefresh.tiff deleted file mode 100644 index 5d233769..00000000 Binary files a/Resources/Images/queryrefresh.tiff and /dev/null differ diff --git a/Resources/Images/reload.tiff b/Resources/Images/reload.tiff deleted file mode 100644 index 40d020e5..00000000 Binary files a/Resources/Images/reload.tiff and /dev/null differ diff --git a/Resources/Images/rowadd.tiff b/Resources/Images/rowadd.tiff deleted file mode 100644 index 9cc750b6..00000000 Binary files a/Resources/Images/rowadd.tiff and /dev/null differ diff --git a/Resources/Images/rowcopy.tiff b/Resources/Images/rowcopy.tiff deleted file mode 100644 index 348a53ce..00000000 Binary files a/Resources/Images/rowcopy.tiff and /dev/null differ diff --git a/Resources/Images/rowdelete.tiff b/Resources/Images/rowdelete.tiff deleted file mode 100644 index 8f5cd340..00000000 Binary files a/Resources/Images/rowdelete.tiff and /dev/null differ diff --git a/Resources/Images/rowrefresh.tiff b/Resources/Images/rowrefresh.tiff deleted file mode 100644 index 09231808..00000000 Binary files a/Resources/Images/rowrefresh.tiff and /dev/null differ diff --git a/Resources/Images/selectall.tiff b/Resources/Images/selectall.tiff index 17b35cad..928913ea 100644 Binary files a/Resources/Images/selectall.tiff and b/Resources/Images/selectall.tiff differ diff --git a/Resources/Images/selectnone.tiff b/Resources/Images/selectnone.tiff index de505f10..29540219 100644 Binary files a/Resources/Images/selectnone.tiff and b/Resources/Images/selectnone.tiff differ diff --git a/Resources/Images/sheettotable.tiff b/Resources/Images/sheettotable.tiff deleted file mode 100644 index 90f7ccb7..00000000 Binary files a/Resources/Images/sheettotable.tiff and /dev/null differ diff --git a/Resources/Images/showconsole.tiff b/Resources/Images/showconsole.tiff index 861be377..384a1227 100644 Binary files a/Resources/Images/showconsole.tiff and b/Resources/Images/showconsole.tiff differ diff --git a/Resources/Images/showvariables.tiff b/Resources/Images/showvariables.tiff index 4c4b3400..4535a95d 100644 Binary files a/Resources/Images/showvariables.tiff and b/Resources/Images/showvariables.tiff differ diff --git a/Resources/Images/table-property.png b/Resources/Images/table-property.png new file mode 100644 index 00000000..c7e9b928 Binary files /dev/null and b/Resources/Images/table-property.png differ diff --git a/Resources/Images/table-small.tif b/Resources/Images/table-small.tif deleted file mode 100644 index d3c3a705..00000000 Binary files a/Resources/Images/table-small.tif and /dev/null differ diff --git a/Resources/Images/table-small.tiff b/Resources/Images/table-small.tiff new file mode 100644 index 00000000..45f02ac3 Binary files /dev/null and b/Resources/Images/table-small.tiff differ diff --git a/Resources/Images/table-view-small.tif b/Resources/Images/table-view-small.tif deleted file mode 100644 index 4e78e81c..00000000 Binary files a/Resources/Images/table-view-small.tif and /dev/null differ diff --git a/Resources/Images/table-view-small.tiff b/Resources/Images/table-view-small.tiff new file mode 100644 index 00000000..42e0e929 Binary files /dev/null and b/Resources/Images/table-view-small.tiff differ diff --git a/Resources/Images/tableadd.tiff b/Resources/Images/tableadd.tiff deleted file mode 100644 index 792277d9..00000000 Binary files a/Resources/Images/tableadd.tiff and /dev/null differ diff --git a/Resources/Images/tablecopy.tiff b/Resources/Images/tablecopy.tiff deleted file mode 100644 index 39e8bbdd..00000000 Binary files a/Resources/Images/tablecopy.tiff and /dev/null differ diff --git a/Resources/Images/tabledelete.tiff b/Resources/Images/tabledelete.tiff deleted file mode 100644 index a915bf8e..00000000 Binary files a/Resources/Images/tabledelete.tiff and /dev/null differ diff --git a/Resources/Images/tablerefresh.tiff b/Resources/Images/tablerefresh.tiff deleted file mode 100644 index f452b4bb..00000000 Binary files a/Resources/Images/tablerefresh.tiff and /dev/null differ diff --git a/Resources/Images/tables.tiff b/Resources/Images/tables.tiff deleted file mode 100644 index 217dcd07..00000000 Binary files a/Resources/Images/tables.tiff and /dev/null differ diff --git a/Resources/Images/tabletosheet.tiff b/Resources/Images/tabletosheet.tiff deleted file mode 100644 index a35755e5..00000000 Binary files a/Resources/Images/tabletosheet.tiff and /dev/null differ diff --git a/Resources/Images/textencode.tiff b/Resources/Images/textencode.tiff deleted file mode 100644 index 671ade4d..00000000 Binary files a/Resources/Images/textencode.tiff and /dev/null differ diff --git a/Resources/Images/toolbar-export-csv.tiff b/Resources/Images/toolbar-export-csv.tiff new file mode 100644 index 00000000..5ae48395 Binary files /dev/null and b/Resources/Images/toolbar-export-csv.tiff differ diff --git a/Resources/Images/toolbar-export-html.tiff b/Resources/Images/toolbar-export-html.tiff new file mode 100644 index 00000000..65f5ff9f Binary files /dev/null and b/Resources/Images/toolbar-export-html.tiff differ diff --git a/Resources/Images/toolbar-export-pdf.tiff b/Resources/Images/toolbar-export-pdf.tiff new file mode 100644 index 00000000..ecbac4b9 Binary files /dev/null and b/Resources/Images/toolbar-export-pdf.tiff differ diff --git a/Resources/Images/toolbar-export-sql.tiff b/Resources/Images/toolbar-export-sql.tiff new file mode 100644 index 00000000..e2dec6fc Binary files /dev/null and b/Resources/Images/toolbar-export-sql.tiff differ diff --git a/Resources/Images/toolbar-export-xls.tiff b/Resources/Images/toolbar-export-xls.tiff new file mode 100644 index 00000000..d63e8992 Binary files /dev/null and b/Resources/Images/toolbar-export-xls.tiff differ diff --git a/Resources/Images/toolbar-export-xml.tiff b/Resources/Images/toolbar-export-xml.tiff new file mode 100644 index 00000000..a22e974e Binary files /dev/null and b/Resources/Images/toolbar-export-xml.tiff differ diff --git a/Resources/Images/toolbar-preferences-advanced.tiff b/Resources/Images/toolbar-preferences-advanced.tiff new file mode 100644 index 00000000..7dc6dbda Binary files /dev/null and b/Resources/Images/toolbar-preferences-advanced.tiff differ diff --git a/Resources/Images/toolbar-preferences-autoupdate.tiff b/Resources/Images/toolbar-preferences-autoupdate.tiff new file mode 100644 index 00000000..69f4c743 Binary files /dev/null and b/Resources/Images/toolbar-preferences-autoupdate.tiff differ diff --git a/Resources/Images/toolbar-preferences-favorites.png b/Resources/Images/toolbar-preferences-favorites.png new file mode 100644 index 00000000..8067e5c0 Binary files /dev/null and b/Resources/Images/toolbar-preferences-favorites.png differ diff --git a/Resources/Images/toolbar-preferences-general.tiff b/Resources/Images/toolbar-preferences-general.tiff new file mode 100644 index 00000000..5fd226bc Binary files /dev/null and b/Resources/Images/toolbar-preferences-general.tiff differ diff --git a/Resources/Images/toolbar-preferences-network.png b/Resources/Images/toolbar-preferences-network.png new file mode 100644 index 00000000..e07c3c1f Binary files /dev/null and b/Resources/Images/toolbar-preferences-network.png differ diff --git a/Resources/Images/toolbar-preferences-notifications.tiff b/Resources/Images/toolbar-preferences-notifications.tiff new file mode 100644 index 00000000..67943dd0 Binary files /dev/null and b/Resources/Images/toolbar-preferences-notifications.tiff differ diff --git a/Resources/Images/toolbar-preferences-security.tiff b/Resources/Images/toolbar-preferences-security.tiff new file mode 100644 index 00000000..0b151787 Binary files /dev/null and b/Resources/Images/toolbar-preferences-security.tiff differ diff --git a/Resources/Images/toolbar-preferences-tables.tiff b/Resources/Images/toolbar-preferences-tables.tiff new file mode 100644 index 00000000..b3efdb75 Binary files /dev/null and b/Resources/Images/toolbar-preferences-tables.tiff differ diff --git a/Resources/Images/toolbar-switch-to-procedures.tiff b/Resources/Images/toolbar-switch-to-procedures.tiff new file mode 100644 index 00000000..eca231a2 Binary files /dev/null and b/Resources/Images/toolbar-switch-to-procedures.tiff differ diff --git a/Resources/Info.plist b/Resources/Info.plist index 72b08600..086a569b 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -42,13 +42,15 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.9.4 + 0.9.5 CFBundleVersion NSAppleScriptEnabled YES NSHumanReadableCopyright - 0.9.4 (383) + 0.9.5 (593) + LSMinimumSystemVersion + 10.4.0 NSMainNibFile MainMenu NSPrincipalClass @@ -77,6 +79,8 @@ SUFeedURL - http://sequelpro.com/appcast/app-releases.xml + http://sequelpro.com/appcast/app-releases-tiger.xml + SUPublicDSAKeyFile + sparkle-public-key.pem diff --git a/Resources/PreferenceDefaults.plist b/Resources/PreferenceDefaults.plist new file mode 100644 index 00000000..72f04178 --- /dev/null +++ b/Resources/PreferenceDefaults.plist @@ -0,0 +1,50 @@ + + + + + DefaultEncoding + Autodetect + UseMonospacedFonts + + CustomQueryMaxHistoryItems + 20 + ReloadAfterAddingRow + + ReloadAfterEditingRow + + ReloadAfterRemovingRow + + LoadBlobsAsNeeded + + FetchCorrectRowCount + + LimitResults + + LimitResultsValue + 1000 + NullValue + NULL + ShowNoAffectedRowsError + + GrowlEnabled + + ConnectionTimeoutValue + 10 + UseKeepAlive + + KeepAliveInterval + 60 + CustomQueryEditorFont + BAtzdHJlYW10eXBlZIHoA4QBQISEhAZOU0ZvbnQehIQITlNPYmplY3QAhYQBaRiEBVsyNGNdBgAAAA4AAAD//k0AbwBuAGEAYwBvAAAAhAFmCoQBYwCYAZgAmACG + CustomQueryAutoindent + + CustomQueryAutopair + + CustomQueryAutouppercaseKeywords + + SelectLastFavoriteUsed + + LastFavoriteIndex + 0 + + diff --git a/Resources/sparkle-public-key.pem b/Resources/sparkle-public-key.pem new file mode 100644 index 00000000..39e3016a --- /dev/null +++ b/Resources/sparkle-public-key.pem @@ -0,0 +1,20 @@ +-----BEGIN PUBLIC KEY----- +MIIDOzCCAi0GByqGSM44BAEwggIgAoIBAQDckzRQXtWHOiUeim82sbnHHf0ubiPK +PeWm+oXEGa9y7nrXMcYPJxDvvfqP9RvDeR1xH7f2e+R7MjBHcw9Jp4eCZfT4lKw0 +HxKfHYse9vblBnIPJ1NIoyL4nHq90OW/nyCnjAnbsx0M1NIQT2uRljmRb7cNkyju +qOmSNr/BMHNKDTYgJUCb02cp27bi3Yf/5YzXl270aSO2NT3OtRCYqmzaQIoubOcs +PSQluqFbXPpzJIsKMefT4WEWG8rpZ/M2cte7CXetR/n4XQN2skYPhZkCNShUEfdN +OuZYTCKUD2wbTAyPp+orHADRSD6o8zIjxhyCctgy34sGzAnFGtp0WNDXAhUArhRi +89FrNm/jTK7k3Vk1V8a2BiUCggEAbfrV4tcTTRfuOxFA+kQfaR+hFTTPp70CJnHI +syokWI5J7+b4SkYQrPBAZXyXAiSFg21LzUAhFM+vh4ZHENPRxQajnsEbkNh+QjUb +p5Jhn9ZE3MjvwphDH/4HOlQnBmTYVZ37dqdpTFt8cP1tdahoqVAnyJHC32hL70bz +lCn0lPmVbJ9tv3jHty1LYNqGTdNN+ATrLtbip/VeXw87XYLwWXKgv4u1+6FPQ7Rb +tCEaqjGoTqm9LeNXczAmRfolfId+ThGQeJuhDs4IpxYdrN8iY6EFG4nT75ScmwyG +rffk86170l54+EOQCDrispt7c0yv2LeEUHbLhiphrR9M1eAhzgOCAQYAAoIBAQCq +U1se9vsM+Z1SLECcOehGIN8r7PhTRIWWYvGkT9LJAwttIhBy7btg6+SLkWjqRsL3 +oMl6I4sU9HYKfbLhgBPYH7B5XQ+s/yoTEj/KQ9V7SJLA7rH7GER+NXAZhJy1bOF3 +eZQ3KLV52yr61kxGyGxe5zcFqU4ZJRGj5oaBwFPlC71M7CiwSv3k0i9/+lKnWhee +AzSwrZXfE4KcBV52uMTp0qMd4x4DuTbXYAYQgnJhu2yk/7+pK+gifoXg3hioZRg/ +ASxTytgKA34MtOlVwhk6NIyOeQvTzN575xOsQrOt/qZrXKOi3fxNZ9+cvhVgzY1U +1JyOmmqeLtQQzjvVqFtq +-----END PUBLIC KEY----- diff --git a/Scripts/package-application.sh b/Scripts/package-application.sh new file mode 100755 index 00000000..efe63717 --- /dev/null +++ b/Scripts/package-application.sh @@ -0,0 +1,65 @@ +#!/bin/sh + +# package-application.sh +# sequel-pro +# +# A very basic script to build and sign a disk image for Sequel Pro; based on better work by Stuart Connolly. +# +# Created by Rowan Beentje on 25/03/2009. +# Copyright 2009 Sequel Pro Team. All rights reserved. + +# Ensure the path to the application has been supplied - should have occurred when the +# script was run by selecting "Distribution" target and building. +if [ $# -eq 0 ] +then + echo 'The path to the application must be supplied when running this script.' + exit +fi + +# Grab the version number from the info.plist file +VERSION_NUMBER=`cat "${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}/Contents/Info.plist" | tr -d "\n\t" | sed -e 's/.*CFBundleShortVersionString<\/key>\([^<]*\)<\/string>.*/\1/'` + +# Define target disk image name and temporary names +DMG_VOLUME_NAME="Sequel Pro ${VERSION_NUMBER}" +DMG_NAME="sequel-pro-${VERSION_NUMBER}" +DMG_BUILD_PATH="${BUILT_PRODUCTS_DIR}" +DISTTEMP="${DMG_BUILD_PATH}/disttemp" + +# Remove any existing disk images and files with this name +if [ -e "${DMG_BUILD_PATH}/${DMG_NAME}.dmg" ] +then + rm -f "${DMG_BUILD_PATH}/${DMG_NAME}.dmg" +fi +if [ -e "${DMG_BUILD_PATH}/${DMG_NAME}.dmg.signature" ] +then + rm -f "${DMG_BUILD_PATH}/${DMG_NAME}.dmg.signature" +fi + +# Create a temporary folder to house the disk image contents +mkdir "${DISTTEMP}" + +# Copy in the required distribution files +cp -R "${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}" "${DMG_BUILD_PATH}/disttemp" + +# Create a disk image +hdiutil create -srcfolder "${DISTTEMP}" -volname "$DMG_VOLUME_NAME" -fs HFS+ -fsargs '-c c=64,a=16,e=16' -format UDRW "${DMG_BUILD_PATH}/${DMG_NAME}.temp.dmg" > /dev/null + +# Compress the disk image +hdiutil convert "${DMG_BUILD_PATH}/${DMG_NAME}.temp.dmg" -format UDZO -imagekey lib-level=9 -o "${DMG_BUILD_PATH}/${DMG_NAME}.dmg" > /dev/null + +# Remove temporary files and copies +rm -rf "${DISTTEMP}" +rm "${DMG_BUILD_PATH}/${DMG_NAME}.temp.dmg" + +# Ask for the location of the private key to use when signing the disk image +PRIVATE_KEY_LOCATION=`osascript <<-eof + tell application "Xcode" + set theKey to POSIX path of ((choose file with prompt "Please locate the private key (sequelpro-sparkle-private-key.pem) for signing:" ) as string) + end tell + theKey + eof` +if [ -e "$PRIVATE_KEY_LOCATION" ] +then + SIGNATURE=`openssl dgst -sha1 -binary < "${DMG_BUILD_PATH}/${DMG_NAME}.dmg" | openssl dgst -dss1 -sign "$PRIVATE_KEY_LOCATION" | openssl enc -base64` + echo "$SIGNATURE" > "${DMG_BUILD_PATH}/${DMG_NAME}.dmg.signature" +fi \ No newline at end of file diff --git a/Source/CMMCPConnection.h b/Source/CMMCPConnection.h index 584b8056..8473c962 100644 --- a/Source/CMMCPConnection.h +++ b/Source/CMMCPConnection.h @@ -26,15 +26,11 @@ #import #import "CMMCPResult.h" -// Set the connection timeout to enforce for all connections - used for the initial connection -// timeout and ping timeouts, but not for long queries/reads/writes. -// Probably worth moving this to a preference at some point. -#define SP_CONNECTION_TIMEOUT 10 - @interface NSObject (CMMCPConnectionDelegate) - (void)willQueryString:(NSString *)query; - (void)queryGaveError:(NSString *)error; +- (BOOL)connectionEncodingViaLatin1; @end @@ -49,6 +45,10 @@ NSString *connectionHost; int connectionPort; NSString *connectionSocket; + float lastQueryExecutionTime; + int connectionTimeout; + BOOL useKeepAlive; + float keepAliveInterval; NSTimer *keepAliveTimer; NSDate *lastKeepAliveSuccess; @@ -66,6 +66,8 @@ - (void) setParentWindow:(NSWindow *)theWindow; - (BOOL) selectDB:(NSString *) dbName; - (CMMCPResult *) queryString:(NSString *) query; +- (CMMCPResult *) queryString:(NSString *) query usingEncoding:(NSStringEncoding) encoding; +- (float) lastQueryExecutionTime; - (MCPResult *) listDBsLike:(NSString *) dbsName; - (BOOL) checkConnection; - (void) setDelegate:(id)object; @@ -75,5 +77,6 @@ - (void) stopKeepAliveTimer; - (void) keepAlive:(NSTimer *)theTimer; - (void) threadedKeepAlive; +- (const char *) cStringFromString:(NSString *) theString usingEncoding:(NSStringEncoding) encoding; @end diff --git a/Source/CMMCPConnection.m b/Source/CMMCPConnection.m index 14cd6ba7..99b72e42 100644 --- a/Source/CMMCPConnection.m +++ b/Source/CMMCPConnection.m @@ -67,8 +67,16 @@ static void forcePingTimeout(int signalNumber); connectionPort = 0; connectionSocket = nil; keepAliveTimer = nil; + connectionTimeout = [[[NSUserDefaults standardUserDefaults] objectForKey:@"ConnectionTimeout"] intValue]; + if (!connectionTimeout) connectionTimeout = 10; + useKeepAlive = [[[NSUserDefaults standardUserDefaults] objectForKey:@"UseKeepAlive"] doubleValue]; + keepAliveInterval = [[[NSUserDefaults standardUserDefaults] objectForKey:@"KeepAliveInterval"] doubleValue]; + if (!keepAliveInterval) keepAliveInterval = 0; lastKeepAliveSuccess = nil; - [NSBundle loadNibNamed:@"ConnectionErrorDialog" owner:self]; + lastQueryExecutionTime = 0; + if (![NSBundle loadNibNamed:@"ConnectionErrorDialog" owner:self]) { + NSLog(@"Connection error dialog could not be loaded; connection failure handling will not function correctly."); + } } @@ -90,7 +98,6 @@ static void forcePingTimeout(int signalNumber); if (socket) connectionSocket = [[NSString alloc] initWithString:socket]; if (mConnection != NULL) { - unsigned int connectionTimeout = SP_CONNECTION_TIMEOUT; mysql_options(mConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout); } @@ -130,6 +137,7 @@ static void forcePingTimeout(int signalNumber); - (BOOL) reconnect { NSString *currentEncoding = nil; + BOOL currentEncodingUsesLatin1Transport = NO; NSString *currentDatabase = nil; // Store the current database and encoding so they can be re-set if reconnection was successful @@ -139,6 +147,9 @@ static void forcePingTimeout(int signalNumber); if (delegate && [delegate valueForKey:@"_encoding"]) { currentEncoding = [NSString stringWithString:[delegate valueForKey:@"_encoding"]]; } + if (delegate && [delegate respondsToSelector:@selector(connectionEncodingViaLatin1)]) { + currentEncodingUsesLatin1Transport = [delegate connectionEncodingViaLatin1]; + } // Close the connection if it exists. if (mConnected) { @@ -155,7 +166,6 @@ static void forcePingTimeout(int signalNumber); if (mConnection != NULL) { // Set a connection timeout for the new connection - unsigned int connectionTimeout = SP_CONNECTION_TIMEOUT; mysql_options(mConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout); // Attempt to reestablish the connection - using own method so everything gets set up as standard. @@ -169,8 +179,11 @@ static void forcePingTimeout(int signalNumber); [self selectDB:currentDatabase]; } if (currentEncoding) { - [self queryString:[NSString stringWithFormat:@"SET NAMES '%@'", currentEncoding]]; + [self queryString:[NSString stringWithFormat:@"/*!40101 SET NAMES '%@' */", currentEncoding]]; [self setEncoding:[CMMCPConnection encodingForMySQLEncoding:[currentEncoding UTF8String]]]; + if (currentEncodingUsesLatin1Transport) { + [self queryString:@"/*!40101 SET CHARACTER_SET_RESULTS=latin1 */"]; + } } } else if (parentWindow) { @@ -332,23 +345,37 @@ static void forcePingTimeout(int signalNumber); } +/* + * Override the standard queryString: method to default to the connection encoding, as before, + * before pssing on to queryString: usingEncoding:. + */ +- (CMMCPResult *)queryString:(NSString *) query +{ + return [self queryString:query usingEncoding:mEncoding]; +} + + /* * Modified version of queryString to be used in Sequel Pro. * Error checks extensively - if this method fails, it will ask how to proceed and loop depending * on the status, not returning control until either the query has been executed and the result can * be returned or the connection and document have been closed. */ -- (CMMCPResult *)queryString:(NSString *) query +- (CMMCPResult *)queryString:(NSString *) query usingEncoding:(NSStringEncoding) encoding { CMMCPResult *theResult; - const char *theCQuery = [self cStringFromString:query]; + const char *theCQuery; int theQueryCode; + NSDate *queryStartDate; // If no connection is present, return nil. if (!mConnected) return nil; [self stopKeepAliveTimer]; + // Generate the cString as appropriate + theCQuery = [self cStringFromString:query usingEncoding:encoding]; + // Check the connection. This triggers reconnects as necessary, and should only return false if a disconnection // has been requested - in which case return nil if (![self checkConnection]) return nil; @@ -358,10 +385,16 @@ static void forcePingTimeout(int signalNumber); [delegate willQueryString:query]; } - if (0 == (theQueryCode = mysql_query(mConnection, theCQuery))) { + // Run the query, storing run time (note this will include some network and overhead) + queryStartDate = [NSDate date]; + theQueryCode = mysql_query(mConnection, theCQuery); + lastQueryExecutionTime = [[NSDate date] timeIntervalSinceDate:queryStartDate]; + + // Retrieve the result or error appropriately. + if (0 == theQueryCode) { if (mysql_field_count(mConnection) != 0) { - // Use CMMCPResult instad of MCPResult + // Use CMMCPResult instead of MCPResult theResult = [[CMMCPResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone]; } else { return nil; @@ -382,6 +415,16 @@ static void forcePingTimeout(int signalNumber); } +/* + * Return the time taken to execute the last query. This should be close to the time it took + * the server to run the query, but will include network lag and some client library overhead. + */ +- (float) lastQueryExecutionTime +{ + return lastQueryExecutionTime; +} + + /* * Modified version of selectDB to be used in Sequel Pro. * Checks the connection exists, and handles keepalive, otherwise calling the parent implementation. @@ -402,10 +445,15 @@ static void forcePingTimeout(int signalNumber); */ - (BOOL)checkConnection { + unsigned long threadid; + if (!mConnected) return NO; BOOL connectionVerified = FALSE; + // Get the current thread ID for this connection + threadid = mConnection->thread_id; + // Check whether the connection is still operational via a wrapped version of MySQL ping. connectionVerified = [self pingConnection]; @@ -432,6 +480,16 @@ static void forcePingTimeout(int signalNumber); default: return [self checkConnection]; } + + // If a connection exists, check whether the thread id differs; if so, the connection has + // probably been reestablished and we need to reset the connection encoding + } else if (threadid != mConnection->thread_id) { + if (delegate && [delegate valueForKey:@"_encoding"]) { + [self queryString:[NSString stringWithFormat:@"/*!40101 SET NAMES '%@' */", [NSString stringWithString:[delegate valueForKey:@"_encoding"]]]]; + if (delegate && [delegate respondsToSelector:@selector(connectionEncodingViaLatin1)]) { + if ([delegate connectionEncodingViaLatin1]) [self queryString:@"/*!40101 SET CHARACTER_SET_RESULTS=latin1 */"]; + } + } } return connectionVerified; @@ -522,7 +580,7 @@ static void forcePingTimeout(int signalNumber); sigemptyset(&timeoutAction.sa_mask); timeoutAction.sa_flags = 0; sigaction(SIGALRM, &timeoutAction, NULL); - alarm(SP_CONNECTION_TIMEOUT+1); + alarm(connectionTimeout+1); // Set up a "restore point", returning 0; if longjmp is used later with this reference, execution // jumps back to this point and returns a nonzero value, so this function evaluates to false when initially @@ -579,14 +637,16 @@ static void forcePingTimeout(int signalNumber) [lastKeepAliveSuccess release]; lastKeepAliveSuccess = nil; } - - keepAliveTimer = [NSTimer - scheduledTimerWithTimeInterval:[[[NSUserDefaults standardUserDefaults] objectForKey:@"keepAliveInterval"] doubleValue] - target:self - selector:@selector(keepAlive:) - userInfo:nil - repeats:NO]; - [keepAliveTimer retain]; + + if (useKeepAlive && keepAliveInterval) { + keepAliveTimer = [NSTimer + scheduledTimerWithTimeInterval:keepAliveInterval + target:self + selector:@selector(keepAlive:) + userInfo:nil + repeats:NO]; + [keepAliveTimer retain]; + } } /* @@ -612,7 +672,7 @@ static void forcePingTimeout(int signalNumber) // cut but mysql doesn't pick up on the fact - see comment for pingConnection above. The same // forced-timeout approach cannot be used here on a background thread. // When the connection is disconnected in code, these 5 "hanging" threads are automatically cleaned. - if (lastKeepAliveSuccess && [lastKeepAliveSuccess timeIntervalSinceNow] < -5 * [[[NSUserDefaults standardUserDefaults] objectForKey:@"keepAliveInterval"] doubleValue]) return; + if (lastKeepAliveSuccess && [lastKeepAliveSuccess timeIntervalSinceNow] < -5 * keepAliveInterval) return; [NSThread detachNewThreadSelector:@selector(threadedKeepAlive) toTarget:self withObject:nil]; [self startKeepAliveTimerResettingState:NO]; @@ -631,4 +691,23 @@ static void forcePingTimeout(int signalNumber) } lastKeepAliveSuccess = [[NSDate alloc] initWithTimeIntervalSinceNow:0]; } -@end + + +/* + * Modified version of the original to support a supplied encoding. + * For internal use only. Transforms a NSString to a C type string (ending with \0). + * Lossy conversions are enabled. + */ +- (const char *) cStringFromString:(NSString *) theString usingEncoding:(NSStringEncoding) encoding +{ + NSMutableData *theData; + + if (! theString) { + return (const char *)NULL; + } + + theData = [NSMutableData dataWithData:[theString dataUsingEncoding:encoding allowLossyConversion:YES]]; + [theData increaseLengthBy:1]; + return (const char *)[theData bytes]; +} +@end \ No newline at end of file diff --git a/Source/CMTextView.h b/Source/CMTextView.h index e70c6ea4..8f32ff8e 100644 --- a/Source/CMTextView.h +++ b/Source/CMTextView.h @@ -22,11 +22,33 @@ // Or mail to #import +#import "NoodleLineNumberView.h" @interface CMTextView : NSTextView { + BOOL autoindentEnabled; + BOOL autopairEnabled; + BOOL autoindentIgnoresEnter; + BOOL autouppercaseKeywordsEnabled; + BOOL delBackwardsWasPressed; + NoodleLineNumberView *lineNumberView; + + IBOutlet NSScrollView *scrollView; } --(NSArray *)completionsForPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int *)index; --(NSArray *)keywords; +- (BOOL) isNextCharMarkedBy:(id)attribute; +- (BOOL) areAdjacentCharsLinked; +- (BOOL) wrapSelectionWithPrefix:(NSString *)prefix suffix:(NSString *)suffix; +- (BOOL) shiftSelectionRight; +- (BOOL) shiftSelectionLeft; +- (NSArray *) completionsForPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int *)index; +- (NSArray *) keywords; +- (void) setAutoindent:(BOOL)enableAutoindent; +- (BOOL) autoindent; +- (void) setAutoindentIgnoresEnter:(BOOL)enableAutoindentIgnoresEnter; +- (BOOL) autoindentIgnoresEnter; +- (void) setAutopair:(BOOL)enableAutopair; +- (BOOL) autopair; +- (void) setAutouppercaseKeywords:(BOOL)enableAutouppercaseKeywords; +- (BOOL) autouppercaseKeywords; @end diff --git a/Source/CMTextView.m b/Source/CMTextView.m index 7ca2f782..427c66d1 100644 --- a/Source/CMTextView.m +++ b/Source/CMTextView.m @@ -24,15 +24,481 @@ #import "CMTextView.h" #import "SPStringAdditions.h" +/* + * Include all the extern variables and prototypes required for flex (used for syntax highlighting) + */ +#import "SPEditorTokens.h" +extern int yylex(); +extern int yyuoffset, yyuleng; +typedef struct yy_buffer_state *YY_BUFFER_STATE; +void yy_switch_to_buffer(YY_BUFFER_STATE); +YY_BUFFER_STATE yy_scan_string (const char *); + +#define kAPlinked @"Linked" // attribute for a via auto-pair inserted char +#define kAPval @"linked" +#define kWQquoted @"Quoted" // set via lex to indicate a quoted string +#define kWQval @"quoted" +#define kSQLkeyword @"SQLkw" // attribute for found SQL keywords +#define kQuote @"Quote" + + @implementation CMTextView +/* + * Checks if the char after the current caret position/selection matches a supplied attribute + */ +- (BOOL) isNextCharMarkedBy:(id)attribute +{ + unsigned int caretPosition = [self selectedRange].location; + + // Perform bounds checking + if (caretPosition >= [[self string] length]) return NO; + + // Perform the check + if ([[self textStorage] attribute:attribute atIndex:caretPosition effectiveRange:nil]) + return YES; + + return NO; +} + + +/* + * Checks if the caret is wrapped by auto-paired characters. + * e.g. [| := caret]: "|" + */ +- (BOOL) areAdjacentCharsLinked +{ + unsigned int caretPosition = [self selectedRange].location; + unichar leftChar, matchingChar; + + // Perform bounds checking + if ([self selectedRange].length) return NO; + if (caretPosition < 1) return NO; + if (caretPosition >= [[self string] length]) return NO; + + // Check the character to the left of the cursor and set the pairing character if appropriate + leftChar = [[self string] characterAtIndex:caretPosition - 1]; + if (leftChar == '(') + matchingChar = ')'; + else if (leftChar == '"' || leftChar == '`' || leftChar == '\'') + matchingChar = leftChar; + else + return NO; + + // Check that the pairing character exists after the caret, and is tagged with the link attribute + if (matchingChar == [[self string] characterAtIndex:caretPosition] + && [[self textStorage] attribute:kAPlinked atIndex:caretPosition effectiveRange:nil]) { + return YES; + } + + return NO; +} + + +/* + * If the textview has a selection, wrap it with the supplied prefix and suffix strings; + * return whether or not any wrap was performed. + */ +- (BOOL) wrapSelectionWithPrefix:(NSString *)prefix suffix:(NSString *)suffix +{ + + // Only proceed if a selection is active + if ([self selectedRange].length == 0) + return NO; + + // Replace the current selection with the selected string wrapped in prefix and suffix + [self insertText: + [NSString stringWithFormat:@"%@%@%@", + prefix, + [[self string] substringWithRange:[self selectedRange]], + suffix + ] + ]; + return YES; +} + +/* + * Copy selected text chunk as RTF to preserve syntax highlighting + */ +- (void)copyAsRTF +{ + + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + NSTextStorage *textStorage = [self textStorage]; + NSData *rtf = [textStorage RTFFromRange:[self selectedRange] + documentAttributes:nil]; + + if (rtf) + { + [pb declareTypes:[NSArray arrayWithObject:NSRTFPboardType] owner:self]; + [pb setData:rtf forType:NSRTFPboardType]; + } + +} + + +/* + * Handle some keyDown events in order to provide autopairing functionality (if enabled). + */ +- (void) keyDown:(NSEvent *)theEvent +{ + + long allFlags = (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask); + + // Check if user pressed ⌥ to allow composing of accented characters. + // e.g. for US keyboard "⌥u a" to insert ä + // or for non-US keyboards to allow to enter dead keys + // e.g. for German keyboard ` is a dead key, press space to enter ` + if (([theEvent modifierFlags] & allFlags) == NSAlternateKeyMask || [[theEvent characters] length] == 0) + { + [super keyDown: theEvent]; + return; + } + + NSString *characters = [theEvent characters]; + NSString *charactersIgnMod = [theEvent charactersIgnoringModifiers]; + unichar insertedCharacter = [characters characterAtIndex:0]; + long curFlags = ([theEvent modifierFlags] & allFlags); + + + // Note: switch(insertedCharacter) {} does not work instead use charactersIgnoringModifiers + if([charactersIgnMod isEqualToString:@"c"]) // ^C copy as RTF + if(curFlags==(NSControlKeyMask)) + { + [self copyAsRTF]; + return; + } + + // Only process for character autopairing if autopairing is enabled and a single character is being added. + if (autopairEnabled && characters && [characters length] == 1) { + + delBackwardsWasPressed = NO; + + NSString *matchingCharacter = nil; + BOOL processAutopair = NO, skipTypedLinkedCharacter = NO; + NSRange currentRange; + + // When a quote character is being inserted into a string quoted with other + // quote characters, or if it's the same character but is escaped, don't + // automatically match it. + if( + // Only for " ` or ' quote characters + (insertedCharacter == '\'' || insertedCharacter == '"' || insertedCharacter == '`') + + // And if the next char marked as linked auto-pair + && [self isNextCharMarkedBy:kAPlinked] + + // And we are inside a quoted string + && [self isNextCharMarkedBy:kWQquoted] + + // And there is no selection, just the text caret + && ![self selectedRange].length + + && ( + // And the user is inserting an escaped string + [[self string] characterAtIndex:[self selectedRange].location-1] == '\\' + + // Or the user is inserting a character not matching the characters used to quote this string + || [[self string] characterAtIndex:[self selectedRange].location] != insertedCharacter + ) + ) + { + [super keyDown: theEvent]; + return; + } + + // If the caret is inside a text string, without any selection, skip autopairing. + // There is one exception to this - if the caret is before a linked pair character, + // processing continues in order to check whether the next character should be jumped + // over; e.g. [| := caret]: "foo|" and press " => only caret will be moved "foo"| + if(![self isNextCharMarkedBy:kAPlinked] && [self isNextCharMarkedBy:kWQquoted] && ![self selectedRange].length) { + [super keyDown:theEvent]; + return; + } + + // Check whether the submitted character should trigger autopair processing. + switch (insertedCharacter) + { + case '(': + matchingCharacter = @")"; + processAutopair = YES; + break; + case '"': + matchingCharacter = @"\""; + processAutopair = YES; + skipTypedLinkedCharacter = YES; + break; + case '`': + matchingCharacter = @"`"; + processAutopair = YES; + skipTypedLinkedCharacter = YES; + break; + case '\'': + matchingCharacter = @"'"; + processAutopair = YES; + skipTypedLinkedCharacter = YES; + break; + case ')': + skipTypedLinkedCharacter = YES; + break; + } + + // Check to see whether the next character should be compared to the typed character; + // if it matches the typed character, and is marked with the is-linked-pair attribute, + // select the next character and replace it with the typed character. This allows + // a normally quoted string to be typed in full, with the autopair appearing as a hint and + // then being automatically replaced when the user types it. + if (skipTypedLinkedCharacter) { + currentRange = [self selectedRange]; + if (currentRange.location != NSNotFound && currentRange.length == 0) { + if ([self isNextCharMarkedBy:kAPlinked]) { + if ([[[self textStorage] string] characterAtIndex:currentRange.location] == insertedCharacter) { + currentRange.length = 1; + [self setSelectedRange:currentRange]; + processAutopair = NO; + } + } + } + } + + // If an appropriate character has been typed, and a matching character has been set, + // some form of autopairing is required. + if (processAutopair && matchingCharacter) { + + // Check to see whether several characters are selected, and if so, wrap them with + // the auto-paired characters. This returns false if the selection has zero length. + if ([self wrapSelectionWithPrefix:characters suffix:matchingCharacter]) + return; + + // Otherwise, start by inserting the original character - the first half of the autopair. + [super keyDown:theEvent]; + + // Then process the second half of the autopair - the matching character. + currentRange = [self selectedRange]; + if (currentRange.location != NSNotFound) { + NSTextStorage *textStorage = [self textStorage]; + + // Register the auto-pairing for undo + [self shouldChangeTextInRange:currentRange replacementString:matchingCharacter]; + + // Insert the matching character and give it the is-linked-pair-character attribute + [self replaceCharactersInRange:currentRange withString:matchingCharacter]; + currentRange.length = 1; + [textStorage addAttribute:kAPlinked value:kAPval range:currentRange]; + + // Restore the original selection. + currentRange.length=0; + [self setSelectedRange:currentRange]; + } + return; + } + } + + // The default action is to perform the normal key-down action. + [super keyDown:theEvent]; + +} + + +- (void) deleteBackward:(id)sender +{ + + // If the caret is currently inside a marked auto-pair, delete the characters on both sides + // of the caret. + NSRange currentRange = [self selectedRange]; + if (currentRange.length == 0 && currentRange.location > 0 && [self areAdjacentCharsLinked]) + [self setSelectedRange:NSMakeRange(currentRange.location - 1,2)]; + + // Avoid auto-uppercasing if resulting word would be a SQL keyword; + // e.g. type inta| and deleteBackward: + delBackwardsWasPressed = YES; + + [super deleteBackward:sender]; + +} + + +/* + * Handle special commands - see NSResponder.h for a sample list. + * This subclass currently handles insertNewline: in order to preserve indentation + * when adding newlines. + */ +- (void) doCommandBySelector:(SEL)aSelector +{ + + // Handle newlines, adding any indentation found on the current line to the new line - ignoring the enter key if appropriate + if (aSelector == @selector(insertNewline:) + && autoindentEnabled + && (!autoindentIgnoresEnter || [[NSApp currentEvent] keyCode] != 0x4C)) + { + NSString *textViewString = [[self textStorage] string]; + NSString *currentLine, *indentString = nil; + NSScanner *whitespaceScanner; + NSRange currentLineRange; + + // Extract the current line based on the text caret or selection start position + currentLineRange = [textViewString lineRangeForRange:NSMakeRange([self selectedRange].location, 0)]; + currentLine = [[NSString alloc] initWithString:[textViewString substringWithRange:currentLineRange]]; + + // Scan all indentation characters on the line into a string + whitespaceScanner = [[NSScanner alloc] initWithString:currentLine]; + [whitespaceScanner setCharactersToBeSkipped:nil]; + [whitespaceScanner scanCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&indentString]; + [whitespaceScanner release]; + [currentLine release]; + + // Always add the newline, whether or not we want to indent the next line + [self insertNewline:self]; + + // Replicate the indentation on the previous line if one was found. + if (indentString) [self insertText:indentString]; + + // Return to avoid the original implementation, preventing double linebreaks + return; + } + [super doCommandBySelector:aSelector]; +} + + +/* + * Shifts the selection, if any, rightwards by indenting any selected lines with one tab. + * If the caret is within a line, the selection is not changed after the index; if the selection + * has length, all lines crossed by the length are indented and fully selected. + * Returns whether or not an indentation was performed. + */ +- (BOOL) shiftSelectionRight +{ + NSString *textViewString = [[self textStorage] string]; + NSRange currentLineRange; + NSArray *lineRanges; + NSString *tabString = @"\t"; + int i, indentedLinesLength = 0; + + if ([self selectedRange].location == NSNotFound) return NO; + + // Indent the currently selected line if the caret is within a single line + if ([self selectedRange].length == 0) { + NSRange currentLineRange; + + // Extract the current line range based on the text caret + currentLineRange = [textViewString lineRangeForRange:[self selectedRange]]; + + // Register the indent for undo + [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 0) replacementString:tabString]; + + // Insert the new tab + [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 0) withString:tabString]; + + return YES; + } + + // Otherwise, the selection has a length - get an array of current line ranges for the specified selection + lineRanges = [textViewString lineRangesForRange:[self selectedRange]]; + + // Loop through the ranges, storing a count of the overall length. + for (i = 0; i < [lineRanges count]; i++) { + currentLineRange = NSRangeFromString([lineRanges objectAtIndex:i]); + indentedLinesLength += currentLineRange.length + 1; + + // Register the indent for undo + [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location+i, 0) replacementString:tabString]; + + // Insert the new tab + [self replaceCharactersInRange:NSMakeRange(currentLineRange.location+i, 0) withString:tabString]; + } + + // Select the entirety of the new range + [self setSelectedRange:NSMakeRange(NSRangeFromString([lineRanges objectAtIndex:0]).location, indentedLinesLength)]; + + return YES; +} + + +/* + * Shifts the selection, if any, leftwards by un-indenting any selected lines by one tab if possible. + * If the caret is within a line, the selection is not changed after the undent; if the selection has + * length, all lines crossed by the length are un-indented and fully selected. + * Returns whether or not an indentation was performed. + */ +- (BOOL) shiftSelectionLeft +{ + NSString *textViewString = [[self textStorage] string]; + NSRange currentLineRange; + NSArray *lineRanges; + int i, unindentedLines = 0, unindentedLinesLength = 0; + + if ([self selectedRange].location == NSNotFound) return NO; + + // Undent the currently selected line if the caret is within a single line + if ([self selectedRange].length == 0) { + NSRange currentLineRange; + + // Extract the current line range based on the text caret + currentLineRange = [textViewString lineRangeForRange:[self selectedRange]]; + + // Ensure that the line has length and that the first character is a tab + if (currentLineRange.length < 1 + || [textViewString characterAtIndex:currentLineRange.location] != '\t') + return NO; + + // Register the undent for undo + [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 1) replacementString:@""]; + + // Remove the tab + [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 1) withString:@""]; + + return YES; + } + + // Otherwise, the selection has a length - get an array of current line ranges for the specified selection + lineRanges = [textViewString lineRangesForRange:[self selectedRange]]; + + // Loop through the ranges, storing a count of the total lines changed and the new length. + for (i = 0; i < [lineRanges count]; i++) { + currentLineRange = NSRangeFromString([lineRanges objectAtIndex:i]); + unindentedLinesLength += currentLineRange.length; + + // Ensure that the line has length and that the first character is a tab + if (currentLineRange.length < 1 + || [textViewString characterAtIndex:currentLineRange.location-unindentedLines] != '\t') + continue; + + // Register the undent for undo + [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location-unindentedLines, 1) replacementString:@""]; + + // Remove the tab + [self replaceCharactersInRange:NSMakeRange(currentLineRange.location-unindentedLines, 1) withString:@""]; + + // As a line has been unindented, modify counts and lengths + unindentedLines++; + unindentedLinesLength--; + } + + // If a change was made, select the entirety of the new range and return success + if (unindentedLines) { + [self setSelectedRange:NSMakeRange(NSRangeFromString([lineRanges objectAtIndex:0]).location, unindentedLinesLength)]; + return YES; + } + + return NO; +} + +/* + * Handle autocompletion, returning a list of suggested completions for the supplied character range. + */ - (NSArray *)completionsForPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int *)index { + // Check if the caret is inside quotes "" or ''; if so + // return the normal word suggestion due to the spelling's settings + if([[self textStorage] attribute:kQuote atIndex:charRange.location effectiveRange:nil]) + return [[NSSpellChecker sharedSpellChecker] completionsForPartialWordRange:NSMakeRange(0,charRange.length) inString:[[self string] substringWithRange:charRange] language:nil inSpellDocumentWithTag:0]; + NSCharacterSet *separators = [NSCharacterSet characterSetWithCharactersInString:@" \t\r\n,()\"'`-!"]; - NSArray *textViewWords = [[self string] componentsSeparatedByCharactersInSet:separators]; - NSString *partialString = [[self string] substringWithRange:charRange]; + NSArray *textViewWords = [[self string] componentsSeparatedByCharactersInSet:separators]; + NSString *partialString = [[self string] substringWithRange:charRange]; unsigned int partialLength = [partialString length]; + id tableNames = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"tables"]; //unsigned int options = NSCaseInsensitiveSearch | NSAnchoredSearch; @@ -52,254 +518,568 @@ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF beginswith[cd] %@ AND length > %d", partialString, partialLength]; NSArray *matchingCompletions = [[possibleCompletions filteredArrayUsingPredicate:predicate] sortedArrayUsingSelector:@selector(compare:)]; + unsigned i, insindex; insindex = 0; - for (i = 0; i < [matchingCompletions count]; i ++) + for (i = 0; i < [matchingCompletions count]; i++) { - if ([partialString isEqualToString:[[matchingCompletions objectAtIndex:i] substringToIndex:partialLength]]) - { - // Matches case --> Insert at beginning of completion list - [compl insertObject:[matchingCompletions objectAtIndex:i] atIndex:insindex++]; - } - else - { - // Not matching case --> Insert at end of completion list - [compl addObject:[matchingCompletions objectAtIndex:i]]; - } + NSString* obj = [matchingCompletions objectAtIndex:i]; + if(![compl containsObject:obj]) + if ([partialString isEqualToString:[obj substringToIndex:partialLength]]) + // Matches case --> Insert at beginning of completion list + [compl insertObject:obj atIndex:insindex++]; + else + // Not matching case --> Insert at end of completion list + [compl addObject:obj]; } - + return [compl autorelease]; } +/* + * Hook to invoke the auto-uppercasing of SQL keywords after pasting + */ +- (void)paste:(id)sender +{ + + [super paste:sender]; + // Invoke the auto-uppercasing of SQL keywords via an additional trigger + [self insertText:@""]; +} --(NSArray *)keywords { + +/* + * List of keywords for autocompletion. If you add a keyword here, + * it should also be added to the flex file SPEditorTokens.l + */ +-(NSArray *)keywords +{ return [NSArray arrayWithObjects: + @"ACCESSIBLE", + @"ACTION", @"ADD", + @"AFTER", + @"AGAINST", + @"AGGREGATE", + @"ALGORITHM", @"ALL", - @"ALTER TABLE", - @"ALTER VIEW", - @"ALTER SCHEMA", - @"ALTER SCHEMA", - @"ALTER FUNCTION", + @"ALTER", @"ALTER COLUMN", @"ALTER DATABASE", + @"ALTER EVENT", + @"ALTER FUNCTION", + @"ALTER LOGFILE GROUP", @"ALTER PROCEDURE", + @"ALTER SCHEMA", + @"ALTER SERVER", + @"ALTER TABLE", + @"ALTER TABLESPACE", + @"ALTER VIEW", @"ANALYZE", + @"ANALYZE TABLE", @"AND", + @"ANY", + @"AS", @"ASC", + @"ASCII", @"ASENSITIVE", + @"AT", + @"AUTHORS", + @"AUTOEXTEND_SIZE", + @"AUTO_INCREMENT", + @"AVG", + @"AVG_ROW_LENGTH", + @"BACKUP", + @"BACKUP TABLE", @"BEFORE", + @"BEGIN", @"BETWEEN", @"BIGINT", @"BINARY", + @"BINLOG", + @"BIT", @"BLOB", + @"BOOL", + @"BOOLEAN", @"BOTH", + @"BTREE", + @"BY", + @"BYTE", + @"CACHE", + @"CACHE INDEX", @"CALL", @"CASCADE", + @"CASCADED", @"CASE", + @"CHAIN", @"CHANGE", + @"CHANGED", @"CHAR", @"CHARACTER", + @"CHARACTER SET", + @"CHARSET", @"CHECK", + @"CHECK TABLE", + @"CHECKSUM", + @"CHECKSUM TABLE", + @"CIPHER", + @"CLIENT", + @"CLOSE", + @"COALESCE", + @"CODE", @"COLLATE", + @"COLLATION", @"COLUMN", @"COLUMNS", + @"COLUMN_FORMAT" + @"COMMENT", + @"COMMIT", + @"COMMITTED", + @"COMPACT", + @"COMPLETION", + @"COMPRESSED", + @"CONCURRENT", @"CONDITION", @"CONNECTION", + @"CONSISTENT", @"CONSTRAINT", + @"CONTAINS", @"CONTINUE", + @"CONTRIBUTORS", @"CONVERT", - @"CREATE VIEW", - @"CREATE INDEX", - @"CREATE FUNCTION", + @"CREATE", @"CREATE DATABASE", + @"CREATE EVENT", + @"CREATE FUNCTION", + @"CREATE INDEX", + @"CREATE LOGFILE GROUP", @"CREATE PROCEDURE", @"CREATE SCHEMA", - @"CREATE TRIGGER", @"CREATE TABLE", + @"CREATE TABLESPACE", + @"CREATE TRIGGER", @"CREATE USER", + @"CREATE VIEW", @"CROSS", + @"CUBE", @"CURRENT_DATE", @"CURRENT_TIME", @"CURRENT_TIMESTAMP", @"CURRENT_USER", @"CURSOR", + @"DATA", @"DATABASE", @"DATABASES", + @"DATAFILE", + @"DATE", + @"DATETIME", + @"DAY", @"DAY_HOUR", @"DAY_MICROSECOND", @"DAY_MINUTE", @"DAY_SECOND", + @"DEALLOCATE", + @"DEALLOCATE PREPARE", @"DEC", @"DECIMAL", @"DECLARE", @"DEFAULT", + @"DEFINER", @"DELAYED", + @"DELAY_KEY_WRITE", @"DELETE", @"DESC", @"DESCRIBE", + @"DES_KEY_FILE", @"DETERMINISTIC", + @"DIRECTORY", + @"DISABLE", + @"DISCARD", + @"DISK", @"DISTINCT", @"DISTINCTROW", @"DIV", + @"DO", @"DOUBLE", - @"DROP TABLE", - @"DROP TRIGGER", - @"DROP VIEW", - @"DROP SCHEMA", - @"DROP USER", - @"DROP PROCEDURE", - @"DROP FUNCTION", + @"DROP", + @"DROP DATABASE", + @"DROP EVENT", @"DROP FOREIGN KEY", + @"DROP FUNCTION", @"DROP INDEX", + @"DROP LOGFILE GROUP", @"DROP PREPARE", @"DROP PRIMARY KEY", - @"DROP DATABASE", + @"DROP PREPARE", + @"DROP PROCEDURE", + @"DROP SCHEMA", + @"DROP SERVER", + @"DROP TABLE", + @"DROP TABLESPACE", + @"DROP TRIGGER", + @"DROP USER", + @"DROP VIEW", @"DUAL", + @"DUMPFILE", + @"DUPLICATE", + @"DYNAMIC", @"EACH", @"ELSE", @"ELSEIF", + @"ENABLE", @"ENCLOSED", + @"END", + @"ENDS", + @"ENGINE", + @"ENGINES", + @"ENUM", + @"ERRORS", + @"ESCAPE", @"ESCAPED", + @"EVENT", + @"EVENTS", + @"EVERY", + @"EXECUTE", @"EXISTS", @"EXIT", + @"EXPANSION", @"EXPLAIN", + @"EXTENDED", + @"EXTENT_SIZE", @"FALSE", + @"FAST", @"FETCH", @"FIELDS", + @"FILE", + @"FIRST", + @"FIXED", @"FLOAT", + @"FLOAT4", + @"FLOAT8", + @"FLUSH", @"FOR", @"FORCE", @"FOREIGN KEY", + @"FOREIGN", @"FOUND", + @"FRAC_SECOND", @"FROM", + @"FULL", @"FULLTEXT", - @"GOTO", + @"FUNCTION", + @"GEOMETRY", + @"GEOMETRYCOLLECTION", + @"GET_FORMAT", + @"GLOBAL", @"GRANT", + @"GRANTS", @"GROUP", + @"HANDLER", + @"HASH", @"HAVING", + @"HELP", @"HIGH_PRIORITY", + @"HOSTS", + @"HOUR", @"HOUR_MICROSECOND", @"HOUR_MINUTE", @"HOUR_SECOND", + @"IDENTIFIED", + @"IF", @"IGNORE", + @"IMPORT", + @"IN", @"INDEX", + @"INDEXES", @"INFILE", + @"INITIAL_SIZE", @"INNER", + @"INNOBASE", + @"INNODB", @"INOUT", @"INSENSITIVE", @"INSERT", + @"INSERT_METHOD", + @"INSTALL", + @"INSTALL PLUGIN", @"INT", + @"INT1", + @"INT2", + @"INT3", + @"INT4", + @"INT8", @"INTEGER", @"INTERVAL", @"INTO", + @"INVOKER", + @"IO_THREAD", + @"IS", + @"ISOLATION", + @"ISSUER", @"ITERATE", @"JOIN", @"KEY", @"KEYS", + @"KEY_BLOCK_SIZE", @"KILL", + @"LANGUAGE", + @"LAST", @"LEADING", @"LEAVE", + @"LEAVES", @"LEFT", + @"LESS", + @"LEVEL", @"LIKE", @"LIMIT", + @"LINEAR", @"LINES", - @"LOAD", + @"LINESTRING", + @"LIST", + @"LOAD DATA", + @"LOAD INDEX INTO CACHE", + @"LOCAL", @"LOCALTIME", @"LOCALTIMESTAMP", @"LOCK", + @"LOCK TABLES", + @"LOCKS", + @"LOGFILE", + @"LOGS", @"LONG", @"LONGBLOB", @"LONGTEXT", @"LOOP", @"LOW_PRIORITY", + @"MASTER", + @"MASTER_CONNECT_RETRY", + @"MASTER_HOST", + @"MASTER_LOG_FILE", + @"MASTER_LOG_POS", + @"MASTER_PASSWORD", + @"MASTER_PORT", + @"MASTER_SERVER_ID", + @"MASTER_SSL", + @"MASTER_SSL_CA", + @"MASTER_SSL_CAPATH", + @"MASTER_SSL_CERT", + @"MASTER_SSL_CIPHER", + @"MASTER_SSL_KEY", + @"MASTER_USER", @"MATCH", + @"MAXVALUE", + @"MAX_CONNECTIONS_PER_HOUR", + @"MAX_QUERIES_PER_HOUR", + @"MAX_ROWS", + @"MAX_SIZE", + @"MAX_UPDATES_PER_HOUR", + @"MAX_USER_CONNECTIONS", + @"MEDIUM", @"MEDIUMBLOB", @"MEDIUMINT", @"MEDIUMTEXT", + @"MEMORY", + @"MERGE", + @"MICROSECOND", @"MIDDLEINT", + @"MIGRATE", + @"MINUTE", @"MINUTE_MICROSECOND", @"MINUTE_SECOND", + @"MIN_ROWS", @"MOD", + @"MODE", + @"MODIFIES", + @"MODIFY", + @"MONTH", + @"MULTILINESTRING", + @"MULTIPOINT", + @"MULTIPOLYGON", + @"MUTEX", + @"NAME", + @"NAMES", + @"NATIONAL", @"NATURAL", + @"NCHAR", + @"NDB", + @"NDBCLUSTER", + @"NEW", + @"NEXT", + @"NO", + @"NODEGROUP", + @"NONE", @"NOT", + @"NO_WAIT", @"NO_WRITE_TO_BINLOG", @"NULL", @"NUMERIC", + @"NVARCHAR", + @"OFFSET", + @"OLD_PASSWORD", @"ON", + @"ONE", + @"ONE_SHOT", + @"OPEN", @"OPTIMIZE", + @"OPTIMIZE TABLE", @"OPTION", @"OPTIONALLY", + @"OPTIONS", + @"OR", @"ORDER", @"OUT", @"OUTER", @"OUTFILE", + @"PACK_KEYS", + @"PARSER", + @"PARTIAL", + @"PARTITION", + @"PARTITIONING", + @"PARTITIONS", + @"PASSWORD", + @"PHASE", + @"PLUGIN", + @"PLUGINS", + @"POINT", + @"POLYGON", @"PRECISION", + @"PREPARE", + @"PRESERVE", + @"PREV", @"PRIMARY", @"PRIVILEGES", @"PROCEDURE", + @"PROCESS", + @"PROCESSLIST", @"PURGE", + @"QUARTER", + @"QUERY", + @"QUICK", + @"RANGE", @"READ", + @"READS", + @"READ_ONLY", + @"READ_WRITE", @"REAL", + @"REBUILD", + @"RECOVER", + @"REDOFILE", + @"REDO_BUFFER_SIZE", + @"REDUNDANT", @"REFERENCES", @"REGEXP", + @"RELAY_LOG_FILE", + @"RELAY_LOG_POS", + @"RELAY_THREAD", + @"RELEASE", + @"RELOAD", + @"REMOVE", @"RENAME", + @"RENAME DATABASE", + @"RENAME TABLE", + @"REORGANIZE", + @"REPAIR", + @"REPAIR TABLE", @"REPEAT", + @"REPEATABLE", @"REPLACE", + @"REPLICATION", @"REQUIRE", + @"RESET", + @"RESET MASTER", + @"RESTORE", + @"RESTORE TABLE", @"RESTRICT", + @"RESUME", @"RETURN", + @"RETURNS", @"REVOKE", @"RIGHT", @"RLIKE", + @"ROLLBACK", + @"ROLLUP", + @"ROUTINE", + @"ROW", + @"ROWS", + @"ROW_FORMAT", + @"RTREE", + @"SAVEPOINT", + @"SCHEDULE", + @"SCHEDULER", + @"SCHEMA", + @"SCHEMAS", + @"SECOND", @"SECOND_MICROSECOND", + @"SECURITY", @"SELECT", @"SENSITIVE", @"SEPARATOR", + @"SERIAL", + @"SERIALIZABLE", + @"SESSION", @"SET", - @"SHOW PROCEDURE STATUS", - @"SHOW PROCESSLIST", - @"SHOW SCHEMAS", - @"SHOW SLAVE HOSTS", - @"SHOW PRIVILEGES", - @"SHOW OPEN TABLES", - @"SHOW MASTER STATUS", - @"SHOW SLAVE STATUS", - @"SHOW PLUGIN", - @"SHOW STORAGE ENGINES", - @"SHOW VARIABLES", - @"SHOW WARNINGS", - @"SHOW TRIGGERS", - @"SHOW TABLES", - @"SHOW MASTER LOGS", - @"SHOW TABLE STATUS", - @"SHOW TABLE TYPES", - @"SHOW STATUS", - @"SHOW INNODB STATUS", + @"SET PASSWORD", + @"SHARE", + @"SHOW", + @"SHOW BINARY LOGS", + @"SHOW BINLOG EVENTS", + @"SHOW CHARACTER SET", + @"SHOW COLLATION", + @"SHOW COLUMNS", + @"SHOW CONTRIBUTORS", @"SHOW CREATE DATABASE", + @"SHOW CREATE EVENT", @"SHOW CREATE FUNCTION", @"SHOW CREATE PROCEDURE", @"SHOW CREATE SCHEMA", - @"SHOW COLUMNS", - @"SHOW COLLATION", - @"SHOW BINARY LOGS", - @"SHOW BINLOG EVENTS", - @"SHOW CHARACTER SET", @"SHOW CREATE TABLE", + @"SHOW CREATE TRIGGERS", @"SHOW CREATE VIEW", - @"SHOW FUNCTION STATUS", - @"SHOW GRANTS", - @"SHOW INDEX", - @"SHOW FIELDS", - @"SHOW ERRORS", @"SHOW DATABASES", @"SHOW ENGINE", @"SHOW ENGINES", + @"SHOW ERRORS", + @"SHOW EVENTS", + @"SHOW FIELDS", + @"SHOW FUNCTION CODE", + @"SHOW FUNCTION STATUS", + @"SHOW GRANTS", + @"SHOW INDEX", + @"SHOW INNODB STATUS", @"SHOW KEYS", + @"SHOW MASTER LOGS", + @"SHOW MASTER STATUS", + @"SHOW OPEN TABLES", + @"SHOW PLUGINS", + @"SHOW PRIVILEGES", + @"SHOW PROCEDURE CODE", + @"SHOW PROCEDURE STATUS", + @"SHOW PROFILE", + @"SHOW PROFILES", + @"SHOW PROCESSLIST", + @"SHOW SCHEDULER STATUS", + @"SHOW SCHEMAS", + @"SHOW SLAVE HOSTS", + @"SHOW SLAVE STATUS", + @"SHOW STATUS", + @"SHOW STORAGE ENGINES", + @"SHOW TABLE STATUS", + @"SHOW TABLE TYPES", + @"SHOW TABLES", + @"SHOW TRIGGERS", + @"SHOW VARIABLES", + @"SHOW WARNINGS", + @"SHUTDOWN", + @"SIGNED", + @"SIMPLE", + @"SLAVE", @"SMALLINT", + @"SNAPSHOT", + @"SOME", @"SONAME", + @"SOUNDS", @"SPATIAL", @"SPECIFIC", @"SQL", @@ -307,47 +1087,331 @@ @"SQLSTATE", @"SQLWARNING", @"SQL_BIG_RESULT", + @"SQL_BUFFER_RESULT", + @"SQL_CACHE", @"SQL_CALC_FOUND_ROWS", + @"SQL_NO_CACHE", @"SQL_SMALL_RESULT", + @"SQL_THREAD", + @"SQL_TSI_DAY", + @"SQL_TSI_FRAC_SECOND", + @"SQL_TSI_HOUR", + @"SQL_TSI_MINUTE", + @"SQL_TSI_MONTH", + @"SQL_TSI_QUARTER", + @"SQL_TSI_SECOND", + @"SQL_TSI_WEEK", + @"SQL_TSI_YEAR", @"SSL", + @"START", + @"START TRANSACTION", @"STARTING", + @"STARTS", + @"STATUS", + @"STOP", + @"STORAGE", @"STRAIGHT_JOIN", + @"STRING", + @"SUBJECT", + @"SUBPARTITION", + @"SUBPARTITIONS", + @"SUPER", + @"SUSPEND", @"TABLE", @"TABLES", + @"TABLESPACE", + @"TEMPORARY", + @"TEMPTABLE", @"TERMINATED", + @"TEXT", + @"THAN", @"THEN", + @"TIME", + @"TIMESTAMP", + @"TIMESTAMPADD", + @"TIMESTAMPDIFF", @"TINYBLOB", @"TINYINT", @"TINYTEXT", + @"TO", @"TRAILING", + @"TRANSACTION", @"TRIGGER", + @"TRIGGERS", @"TRUE", + @"TRUNCATE", + @"TYPE", + @"TYPES", + @"UNCOMMITTED", + @"UNDEFINED", @"UNDO", + @"UNDOFILE", + @"UNDO_BUFFER_SIZE", + @"UNICODE", + @"UNINSTALL", + @"UNINSTALL PLUGIN", @"UNION", @"UNIQUE", + @"UNKNOWN", @"UNLOCK", + @"UNLOCK TABLES", @"UNSIGNED", + @"UNTIL", @"UPDATE", + @"UPGRADE", @"USAGE", @"USE", + @"USER", + @"USER_RESOURCES", + @"USE_FRM", @"USING", @"UTC_DATE", @"UTC_TIME", @"UTC_TIMESTAMP", + @"VALUE", @"VALUES", @"VARBINARY", @"VARCHAR", @"VARCHARACTER", + @"VARIABLES", @"VARYING", + @"VIEW", + @"WAIT", + @"WARNINGS", + @"WEEK", @"WHEN", @"WHERE", @"WHILE", @"WITH", + @"WORK", @"WRITE", + @"X509", + @"XA", @"XOR", + @"YEAR", @"YEAR_MONTH", @"ZEROFILL", nil]; } + +/* + * Set whether this text view should apply the indentation on the current line to new lines. + */ +- (void)setAutoindent:(BOOL)enableAutoindent +{ + autoindentEnabled = enableAutoindent; +} + +/* + * Retrieve whether this text view applies indentation on the current line to new lines. + */ +- (BOOL)autoindent +{ + return autoindentEnabled; +} + +/* + * Set whether this text view should not autoindent when the Enter key is used, as opposed + * to the return key. Also catches function-return. + */ +- (void)setAutoindentIgnoresEnter:(BOOL)enableAutoindentIgnoresEnter +{ + autoindentIgnoresEnter = enableAutoindentIgnoresEnter; +} + +/* + * Retrieve whether this text view should not autoindent when the Enter key is used. + */ +- (BOOL)autoindentIgnoresEnter +{ + return autoindentIgnoresEnter; +} + +/* + * Set whether this text view should automatically create the matching closing char for ", ', ` and ( chars. + */ +- (void)setAutopair:(BOOL)enableAutopair +{ + autopairEnabled = enableAutopair; +} + +/* + * Retrieve whether this text view automatically creates the matching closing char for ", ', ` and ( chars. + */ +- (BOOL)autopair +{ + return autopairEnabled; +} + +/* + * Set whether SQL keywords should be automatically uppercased. + */ +- (void)setAutouppercaseKeywords:(BOOL)enableAutouppercaseKeywords +{ + autouppercaseKeywordsEnabled = enableAutouppercaseKeywords; +} + +/* + * Retrieve whether SQL keywords should be automaticallyuppercased. + */ +- (BOOL)autouppercaseKeywords +{ + return autouppercaseKeywordsEnabled; +} + + +/******************* +SYNTAX HIGHLIGHTING! +*******************/ +- (void)awakeFromNib +/* + * Sets self as delegate for the textView's textStorage to enable syntax highlighting, + * and set defaults for general usage + */ +{ + [[self textStorage] setDelegate:self]; + + autoindentEnabled = YES; + autopairEnabled = YES; + autoindentIgnoresEnter = NO; + autouppercaseKeywordsEnabled = YES; + delBackwardsWasPressed = NO; + + lineNumberView = [[NoodleLineNumberView alloc] initWithScrollView:scrollView]; + [scrollView setVerticalRulerView:lineNumberView]; + [scrollView setHasHorizontalRuler:NO]; + [scrollView setHasVerticalRuler:YES]; + [scrollView setRulersVisible:YES]; +} + +- (void)textStorageDidProcessEditing:(NSNotification *)notification +/* + * Performs syntax highlighting. + * This method recolors the entire text on every keypress. For performance reasons, this function does + * nothing if the text is more than 20 KB. + * + * The main bottleneck is the [NSTextStorage addAttribute:value:range:] method - the parsing itself is really fast! + * + * Some sample code from Andrew Choi ( http://members.shaw.ca/akochoi-old/blog/2003/11-09/index.html#3 ) has been reused. + */ +{ + NSTextStorage *textStore = [notification object]; + + //make sure that the notification is from the correct textStorage object + if (textStore!=[self textStorage]) return; + + + NSColor *commentColor = [NSColor colorWithDeviceRed:0.000 green:0.455 blue:0.000 alpha:1.000]; + NSColor *quoteColor = [NSColor colorWithDeviceRed:0.769 green:0.102 blue:0.086 alpha:1.000]; + NSColor *keywordColor = [NSColor colorWithDeviceRed:0.200 green:0.250 blue:1.000 alpha:1.000]; + NSColor *backtickColor = [NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.658 alpha:1.000]; + NSColor *numericColor = [NSColor colorWithDeviceRed:0.506 green:0.263 blue:0.0 alpha:1.000]; + NSColor *variableColor = [NSColor colorWithDeviceRed:0.5 green:0.5 blue:0.5 alpha:1.000]; + + NSColor *tokenColor; + + int token; + NSRange textRange, tokenRange; + + textRange = NSMakeRange(0, [textStore length]); + + //don't color texts longer than about 20KB. would be too slow + if (textRange.length > 20000) return; + + //first remove the old colors + [textStore removeAttribute:NSForegroundColorAttributeName range:textRange]; + + + //initialise flex + yyuoffset = 0; yyuleng = 0; + yy_switch_to_buffer(yy_scan_string([[textStore string] UTF8String])); + + //now loop through all the tokens + while (token=yylex()){ + switch (token) { + case SPT_SINGLE_QUOTED_TEXT: + case SPT_DOUBLE_QUOTED_TEXT: + tokenColor = quoteColor; + break; + case SPT_BACKTICK_QUOTED_TEXT: + tokenColor = backtickColor; + break; + case SPT_RESERVED_WORD: + tokenColor = keywordColor; + break; + case SPT_NUMERIC: + tokenColor = numericColor; + break; + case SPT_COMMENT: + tokenColor = commentColor; + break; + case SPT_VARIABLE: + tokenColor = variableColor; + break; + default: + tokenColor = nil; + } + + if (!tokenColor) continue; + + tokenRange = NSMakeRange(yyuoffset, yyuleng); + + // make sure that tokenRange is valid (and therefore within textRange) + // otherwise a bug in the lex code could cause the the TextView to crash + tokenRange = NSIntersectionRange(tokenRange, textRange); + if (!tokenRange.length) continue; + + // If the current token is marked as SQL keyword, uppercase it if required. + unsigned long tokenEnd = tokenRange.location+tokenRange.length-1; + // Check the end of the token + if (autouppercaseKeywordsEnabled && !delBackwardsWasPressed + && [[self textStorage] attribute:kSQLkeyword atIndex:tokenEnd effectiveRange:nil]) + // check if next char is not a kSQLkeyword or current kSQLkeyword is at the end; + // if so then upper case keyword if not already done + // @try catch() for catching valid index esp. after deleteBackward: + { + NSString* curTokenString = [[self string] substringWithRange:tokenRange]; + BOOL doIt = NO; + @try + { + doIt = ![[self textStorage] attribute:kSQLkeyword atIndex:tokenEnd+1 effectiveRange:nil]; + } @catch(id ae) { doIt = YES; } + + if(doIt && ![[curTokenString uppercaseString] isEqualToString:curTokenString]) + { + // Register it for undo works only partly for now, at least the uppercased keyword will be selected + [self shouldChangeTextInRange:tokenRange replacementString:[curTokenString uppercaseString]]; + [self replaceCharactersInRange:tokenRange withString:[curTokenString uppercaseString]]; + } + } + + [textStore addAttribute: NSForegroundColorAttributeName + value: tokenColor + range: tokenRange ]; + + // Add an attribute to be used in the auto-pairing (keyDown:) + // to disable auto-pairing if caret is inside of any token found by lex. + // For discussion: maybe change it later (only for quotes not keywords?) + [textStore addAttribute: kWQquoted + value: kWQval + range: tokenRange ]; + + + // Mark each SQL keyword for auto-uppercasing and do it for the next textStorageDidProcessEditing: event. + // Performing it one token later allows words which start as reserved keywords to be entered. + if(token == SPT_RESERVED_WORD) + [textStore addAttribute: kSQLkeyword + value: kWQval + range: tokenRange ]; + // Add an attribute to be used to distinguish quotes from keywords etc. + // used e.g. in completion suggestions + if(token == SPT_DOUBLE_QUOTED_TEXT || token == SPT_SINGLE_QUOTED_TEXT) + [textStore addAttribute: kQuote + value: kWQval + range: tokenRange ]; + } + +} + @end diff --git a/Source/CustomQuery.h b/Source/CustomQuery.h index 8e81c7e5..c2eac75f 100644 --- a/Source/CustomQuery.h +++ b/Source/CustomQuery.h @@ -25,6 +25,7 @@ #import #import #import "CMCopyTable.h" +#import "CMTextView.h" #import "CMMCPConnection.h" #import "CMMCPResult.h" @@ -33,7 +34,7 @@ IBOutlet id tableWindow; IBOutlet id queryFavoritesButton; IBOutlet id queryHistoryButton; - IBOutlet id textView; + IBOutlet CMTextView *textView; IBOutlet CMCopyTable *customQueryView; IBOutlet id errorText; IBOutlet id affectedRowsText; @@ -43,6 +44,17 @@ IBOutlet id queryFavoritesView; IBOutlet id removeQueryFavoriteButton; IBOutlet id copyQueryFavoriteButton; + IBOutlet id runSelectionButton; + IBOutlet id runAllButton; + IBOutlet NSMenuItem *runSelectionMenuItem; + IBOutlet NSMenuItem *clearHistoryMenuItem; + IBOutlet NSMenuItem *shiftLeftMenuItem; + IBOutlet NSMenuItem *shiftRightMenuItem; + IBOutlet NSMenuItem *completionListMenuItem; + IBOutlet NSMenuItem *editorFontMenuItem; + IBOutlet NSMenuItem *autoindentMenuItem; + IBOutlet NSMenuItem *autopairMenuItem; + IBOutlet NSMenuItem *autouppercaseKeywordsMenuItem; NSArray *queryResult; NSUserDefaults *prefs; @@ -52,10 +64,12 @@ } // IBAction methods -- (IBAction)performQuery:(id)sender; +- (IBAction)runAllQueries:(id)sender; +- (IBAction)runSelectedQueries:(id)sender; - (IBAction)chooseQueryFavorite:(id)sender; - (IBAction)chooseQueryHistory:(id)sender; - (IBAction)closeSheet:(id)sender; +- (IBAction)gearMenuItemSelected:(id)sender; // queryFavoritesSheet methods - (IBAction)addQueryFavorite:(id)sender; @@ -63,6 +77,10 @@ - (IBAction)copyQueryFavorite:(id)sender; - (IBAction)closeQueryFavoritesSheet:(id)sender; +// Query actions +- (void)performQueries:(NSArray *)queries; +- (NSString *)queryAtPosition:(long)position; + // Accessors - (NSArray *)currentResult; diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m index e917e984..41170b38 100644 --- a/Source/CustomQuery.m +++ b/Source/CustomQuery.m @@ -25,177 +25,82 @@ #import "CustomQuery.h" #import "SPSQLParser.h" #import "SPGrowlController.h" +#import "SPStringAdditions.h" + @implementation CustomQuery -//IBAction methods -- (IBAction)performQuery:(id)sender; + + +#pragma mark IBAction methods + + /* -performs the mysql-query given by the user -sets the tableView columns corresponding to the mysql-result -*/ -{ + * Split all the queries in the text view, split them into individual queries, + * and run sequentially. + */ +- (IBAction)runAllQueries:(id)sender +{ + SPSQLParser *queryParser; + NSArray *queries; + // Fixes bug in key equivalents. - if ([[NSApp currentEvent] type] == NSKeyUp) - { + if ([[NSApp currentEvent] type] == NSKeyUp) { return; } - - NSArray *theColumns; - NSTableColumn *theCol; - CMMCPResult *theResult = nil; - NSArray *queries; - NSMutableArray *menuItems = [NSMutableArray array]; - NSMutableArray *tempResult = [NSMutableArray array]; - NSMutableString *errors = [NSMutableString string]; - SPSQLParser *queryParser; - int i; - - // Notify listeners that a query has started - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; // Retrieve the custom query string and split it into separate SQL queries queryParser = [[SPSQLParser alloc] initWithString:[textView string]]; queries = [queryParser splitStringByCharacter:';']; [queryParser release]; - // Perform the queries in series - for ( i = 0 ; i < [queries count] ; i++ ) { - theResult = [mySQLConnection queryString:[queries objectAtIndex:i]]; - if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { - - // If the query errored, append error to the error log for display at the end - if ( [queries count] > 1 ) { - [errors appendString:[NSString stringWithFormat:NSLocalizedString(@"[ERROR in query %d] %@\n", @"error text when multiple custom query failed"), - i+1, - [mySQLConnection getLastErrorMessage]]]; - } else { - [errors setString:[mySQLConnection getLastErrorMessage]]; - } - } - } - - //perform empty query if no query is given - if ( [queries count] == 0 ) { - theResult = [mySQLConnection queryString:@""]; - [errors setString:[mySQLConnection getLastErrorMessage]]; - } - -//put result in array - [queryResult release]; - queryResult = nil; - if ( nil != theResult ) - { - int r = [theResult numOfRows]; - if (r) [theResult dataSeek:0]; - for ( i = 0 ; i < r ; i++ ) { - [tempResult addObject:[theResult fetchRowAsArray]]; - } - queryResult = [[NSArray arrayWithArray:tempResult] retain]; - } + [self performQueries:queries]; -//add query to history - [queryHistoryButton insertItemWithTitle:[textView string] atIndex:1]; - while ( [queryHistoryButton numberOfItems] > 21 ) { - [queryHistoryButton removeItemAtIndex:[queryHistoryButton numberOfItems]-1]; - } - for ( i = 1 ; i < [queryHistoryButton numberOfItems] ; i++ ) - { - [menuItems addObject:[queryHistoryButton itemTitleAtIndex:i]]; - } - [prefs setObject:menuItems forKey:@"queryHistory"]; + // Invoke textStorageDidProcessEditing: for syntax highlighting and auto-uppercase + [textView setSelectedRange:NSMakeRange(0,0)]; + [textView insertText:@""]; -//select the text of the query textView and set standard font + // Select the text of the query textView for re-editing [textView selectAll:self]; - if ( [errors length] ) { - [errorText setStringValue:errors]; - } else { - [errorText setStringValue:NSLocalizedString(@"There were no errors.", @"text shown when query was successfull")]; - } - if ( [mySQLConnection affectedRows] != -1 ) { - [affectedRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%@ row(s) affected", @"text showing how many rows have been affected"), - [[NSNumber numberWithLongLong:[mySQLConnection affectedRows]] stringValue]]]; - } else { - [affectedRowsText setStringValue:@""]; - } - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { - [textView setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; - } else { - [textView setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - } - - if ( !theResult || ![theResult numOfRows] ) { -//no rows in result - //free tableView - theColumns = [customQueryView tableColumns]; - while ([theColumns count]) { - [customQueryView removeTableColumn:[theColumns objectAtIndex:0]]; - } -// theCol = [[NSTableColumn alloc] initWithIdentifier:@""]; -// [[theCol headerCell] setStringValue:@""]; -// [customQueryView addTableColumn:theCol]; -// [customQueryView sizeLastColumnToFit]; - [customQueryView reloadData]; -// [theCol release]; - - //query finished - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; - - // Query finished Growl notification - [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" - description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] - notificationName:@"Query Finished"]; - - return; - } +} -//set columns -//remove all columns - theColumns = [customQueryView tableColumns]; -// i=0; - while ([theColumns count]) { - [customQueryView removeTableColumn:[theColumns objectAtIndex:0]]; -// i++; - } +/* + * Depending on selection, run either the query containing the selection caret (if the caret is + * at a single point within the text view), or run the selected text (if a text range is selected). + */ +- (IBAction)runSelectedQueries:(id)sender +{ + NSArray *queries; + NSString *query; + NSRange selectedRange = [textView selectedRange]; + SPSQLParser *queryParser; -//add columns, corresponding to the query result - theColumns = [theResult fetchFieldNames]; - for ( i = 0 ; i < [theResult numOfFields] ; i++) { - theCol = [[NSTableColumn alloc] initWithIdentifier:[NSNumber numberWithInt:i]]; - [theCol setResizingMask:NSTableColumnUserResizingMask]; - NSTextFieldCell *dataCell = [[[NSTextFieldCell alloc] initTextCell:@""] autorelease]; - [dataCell setEditable:NO]; - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { - [dataCell setFont:[NSFont fontWithName:@"Monaco" size:10]]; - } else { - [dataCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + // If the current selection is a single caret position, run the current query. + if (selectedRange.length == 0) { + query = [self queryAtPosition:selectedRange.location]; + if (!query) { + NSBeep(); + return; } - [dataCell setLineBreakMode:NSLineBreakByTruncatingTail]; - [theCol setDataCell:dataCell]; - [[theCol headerCell] setStringValue:[theColumns objectAtIndex:i]]; + queries = [NSArray arrayWithObject:query]; - [customQueryView addTableColumn:theCol]; - [theCol release]; + // Otherwise, run the selected text. + } else { + queryParser = [[SPSQLParser alloc] initWithString:[[textView string] substringWithRange:selectedRange]]; + queries = [queryParser splitStringByCharacter:';']; + [queryParser release]; } - [customQueryView sizeLastColumnToFit]; - //tries to fix problem with last row (otherwise to small) - //sets last column to width of the first if smaller than 30 - //problem not fixed for resizing window - if ( [[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:[theColumns count]-1]] width] < 30 ) - [[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:[theColumns count]-1]] - setWidth:[[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:0]] width]]; - [customQueryView reloadData]; - - //query finished - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; - - // Query finished Growl notification - [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" - description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] - notificationName:@"Query Finished"]; + // Invoke textStorageDidProcessEditing: for syntax highlighting and auto-uppercase + // and preserve the selection + [textView setSelectedRange:NSMakeRange(0,0)]; + [textView insertText:@""]; + [textView setSelectedRange:selectedRange]; + + [self performQueries:queries]; } + - (IBAction)chooseQueryFavorite:(id)sender /* insert the choosen favorite query in the query textView or save query to favorites or opens window to edit favorites @@ -233,7 +138,11 @@ insert the choosen favorite query in the query textView or save query to favorit [queryFavoritesSheet orderOut:nil]; } else if ( [queryFavoritesButton indexOfSelectedItem] != 3) { //choose favorite + // Register the next action for undo + [textView shouldChangeTextInRange:[textView selectedRange] replacementString:[queryFavoritesButton titleOfSelectedItem]]; [textView replaceCharactersInRange:[textView selectedRange] withString:[queryFavoritesButton titleOfSelectedItem]]; + // invoke textStorageDidProcessEditing: for syntax highlighting and auto-uppercase + [textView insertText:@""]; } } @@ -242,7 +151,11 @@ insert the choosen favorite query in the query textView or save query to favorit insert the choosen history query in the query textView */ { + // Register the next action for undo + [textView shouldChangeTextInRange:NSMakeRange(0,[[textView string] length]) replacementString:[queryHistoryButton titleOfSelectedItem]]; [textView setString:[queryHistoryButton titleOfSelectedItem]]; + // Invoke textStorageDidProcessEditing: for syntax highlighting and auto-uppercase + [textView insertText:@""]; [textView selectAll:self]; } @@ -255,7 +168,74 @@ closes the sheet } -//queryFavoritesSheet methods +/* + * Perform simple actions (which don't require their own method), triggered by selecting the appropriate menu item + * in the "gear" action menu displayed beneath the cusotm query view. + */ +- (IBAction)gearMenuItemSelected:(id)sender +{ + // "Clear History" menu item - clear query history + if (sender == clearHistoryMenuItem) { + [queryHistoryButton removeAllItems]; + [queryHistoryButton addItemWithTitle:NSLocalizedString(@"Query History…",@"Title of query history popup button")]; + [prefs setObject:[NSArray array] forKey:@"queryHistory"]; + } + + // "Shift Right" menu item - indent the selection with an additional tab. + if (sender == shiftRightMenuItem) { + [textView shiftSelectionRight]; + } + + // "Shift Left" menu item - un-indent the selection by one tab if possible. + if (sender == shiftLeftMenuItem) { + [textView shiftSelectionLeft]; + } + + // "Completion List" menu item - used to autocomplete. Uses a different shortcut to avoid the menu button flickering + // on normal autocomplete usage. + if (sender == completionListMenuItem) { + [textView complete:self]; + } + + // "Editor font..." menu item to bring up the font panel + if (sender == editorFontMenuItem) { + [[NSFontPanel sharedFontPanel] setPanelFont:[textView font] isMultiple:NO]; + [[NSFontPanel sharedFontPanel] makeKeyAndOrderFront:self]; + } + + // "Indent new lines" toggle + if (sender == autoindentMenuItem) { + BOOL enableAutoindent = ([autoindentMenuItem state] == NSOffState); + [prefs setBool:enableAutoindent forKey:@"CustomQueryAutoindent"]; + [prefs synchronize]; + [autoindentMenuItem setState:enableAutoindent?NSOnState:NSOffState]; + [textView setAutoindent:enableAutoindent]; + } + + // "Auto-pair characters" toggle + if (sender == autopairMenuItem) { + BOOL enableAutopair = ([autopairMenuItem state] == NSOffState); + [prefs setBool:enableAutopair forKey:@"CustomQueryAutopair"]; + [prefs synchronize]; + [autopairMenuItem setState:enableAutopair?NSOnState:NSOffState]; + [textView setAutopair:enableAutopair]; + } + + // "Auto-uppercase keywords" toggle + if (sender == autouppercaseKeywordsMenuItem) { + BOOL enableAutouppercaseKeywords = ([autouppercaseKeywordsMenuItem state] == NSOffState); + [prefs setBool:enableAutouppercaseKeywords forKey:@"CustomQueryAutouppercaseKeywords"]; + [prefs synchronize]; + [autouppercaseKeywordsMenuItem setState:enableAutouppercaseKeywords?NSOnState:NSOffState]; + [textView setAutouppercaseKeywords:enableAutouppercaseKeywords]; + } +} + + +#pragma mark - +#pragma mark queryFavoritesSheet methods + + - (IBAction)addQueryFavorite:(id)sender /* adds a query favorite @@ -352,7 +332,239 @@ closes queryFavoritesSheet and saves favorites to preferences } -//getter methods +#pragma mark - +#pragma mark Query actions + + +- (void)performQueries:(NSArray *)queries; +/* +performs the mysql-query given by the user +sets the tableView columns corresponding to the mysql-result +*/ +{ + + NSArray *theColumns; + NSTableColumn *theCol; + CMMCPResult *theResult = nil; + NSMutableArray *menuItems = [NSMutableArray array]; + NSMutableArray *tempResult = [NSMutableArray array]; + NSMutableString *errors = [NSMutableString string]; + int i, totalQueriesRun = 0, totalAffectedRows = 0; + float executionTime = 0; + + // Notify listeners that a query has started + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; + + // Reset the current table view as necessary to avoid redraw and reload issues. + // Restore the view position to the top left to be within the results for all datasets. + [customQueryView scrollRowToVisible:0]; + [customQueryView scrollColumnToVisible:0]; + + // Remove all the columns + theColumns = [customQueryView tableColumns]; + while ([theColumns count]) { + [customQueryView removeTableColumn:[theColumns objectAtIndex:0]]; + } + + // Perform the supplied queries in series + for ( i = 0 ; i < [queries count] ; i++ ) { + + // Don't run blank queries, or queries which only contain whitespace. + if ([[[queries objectAtIndex:i] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) + continue; + + // Run the query, timing execution (note this also includes network and overhead) + theResult = [mySQLConnection queryString:[queries objectAtIndex:i]]; + executionTime += [mySQLConnection lastQueryExecutionTime]; + totalQueriesRun++; + + // Record any affected rows + if ( [mySQLConnection affectedRows] != -1 ) + totalAffectedRows += [mySQLConnection affectedRows]; + + // Store any error messages + if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { + + // If the query errored, append error to the error log for display at the end + if ( [queries count] > 1 ) { + [errors appendString:[NSString stringWithFormat:NSLocalizedString(@"[ERROR in query %d] %@\n", @"error text when multiple custom query failed"), + i+1, + [mySQLConnection getLastErrorMessage]]]; + } else { + [errors setString:[mySQLConnection getLastErrorMessage]]; + } + } + } + + //perform empty query if no query is given + if ( [queries count] == 0 ) { + theResult = [mySQLConnection queryString:@""]; + [errors setString:[mySQLConnection getLastErrorMessage]]; + } + +//put result in array + [queryResult release]; + queryResult = nil; + if ( nil != theResult ) + { + int r = [theResult numOfRows]; + if (r) [theResult dataSeek:0]; + for ( i = 0 ; i < r ; i++ ) { + [tempResult addObject:[theResult fetchRowAsArray]]; + } + queryResult = [[NSArray arrayWithArray:tempResult] retain]; + } + +//add query to history + [queryHistoryButton insertItemWithTitle:[queries componentsJoinedByString:@"; "] atIndex:1]; + while ( [queryHistoryButton numberOfItems] > [[prefs objectForKey:@"CustomQueryMaxHistoryItems"] intValue] + 1 ) { + [queryHistoryButton removeItemAtIndex:[queryHistoryButton numberOfItems]-1]; + } + for ( i = 1 ; i < [queryHistoryButton numberOfItems] ; i++ ) + { + [menuItems addObject:[queryHistoryButton itemTitleAtIndex:i]]; + } + [prefs setObject:menuItems forKey:@"queryHistory"]; + + if ( [errors length] ) { + [errorText setStringValue:errors]; + } else { + [errorText setStringValue:NSLocalizedString(@"There were no errors.", @"text shown when query was successfull")]; + } + + // Set up the status string + if ( totalQueriesRun > 1 ) { + if (totalAffectedRows==1) { + [affectedRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"1 row affected in total, by %i queries taking %@", @"text showing one row has been affected by multiple queries"), + totalQueriesRun, + [NSString stringForTimeInterval:executionTime] + ]]; + + } else { + [affectedRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%i rows affected in total, by %i queries taking %@", @"text showing how many rows have been affected by multiple queries"), + totalAffectedRows, + totalQueriesRun, + [NSString stringForTimeInterval:executionTime] + ]]; + + } + } else { + if (totalAffectedRows==1) { + [affectedRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"1 row affected, taking %@", @"text showing one row has been affected by a single query"), + [NSString stringForTimeInterval:executionTime] + ]]; + } else { + [affectedRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%i rows affected, taking %@", @"text showing how many rows have been affected by a single query"), + totalAffectedRows, + [NSString stringForTimeInterval:executionTime] + ]]; + + } + } + + + // If no results were returned, redraw the empty table and post notifications before returning. + if ( !theResult || ![theResult numOfRows] ) { + [customQueryView reloadData]; + + // Notify any listeners that the query has completed + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; + + // Perform the Growl notification for query completion + [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" + description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] + notificationName:@"Query Finished"]; + + return; + } + + + // Otherwise add columns corresponding to the query result + theColumns = [theResult fetchFieldNames]; + for ( i = 0 ; i < [theResult numOfFields] ; i++) { + theCol = [[NSTableColumn alloc] initWithIdentifier:[NSNumber numberWithInt:i]]; + [theCol setResizingMask:NSTableColumnUserResizingMask]; + NSTextFieldCell *dataCell = [[[NSTextFieldCell alloc] initTextCell:@""] autorelease]; + [dataCell setEditable:NO]; + if ( [prefs boolForKey:@"UseMonospacedFonts"] ) { + [dataCell setFont:[NSFont fontWithName:@"Monaco" size:10]]; + } else { + [dataCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + } + [dataCell setLineBreakMode:NSLineBreakByTruncatingTail]; + [theCol setDataCell:dataCell]; + [[theCol headerCell] setStringValue:[theColumns objectAtIndex:i]]; + + [customQueryView addTableColumn:theCol]; + [theCol release]; + } + + [customQueryView sizeLastColumnToFit]; + //tries to fix problem with last row (otherwise to small) + //sets last column to width of the first if smaller than 30 + //problem not fixed for resizing window + if ( [[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:[theColumns count]-1]] width] < 30 ) + [[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:[theColumns count]-1]] + setWidth:[[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:0]] width]]; + [customQueryView reloadData]; + + //query finished + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; + + // Query finished Growl notification + [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" + description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] + notificationName:@"Query Finished"]; +} + +/* + * Retrieve the query at a position specified within the custom query + * text view. This will return nil if the position specified is beyond + * the available string or if an empty query would be returned. + */ +- (NSString *)queryAtPosition:(long)position +{ + SPSQLParser *customQueryParser; + NSArray *queries; + NSString *query = nil; + int i, queryPosition = 0; + + // If the supplied position is negative or beyond the end of the string, return nil. + if (position < 0 || position > [[textView string] length]) + return nil; + + // Split the current text into queries + customQueryParser = [[SPSQLParser alloc] initWithString:[textView string]]; + queries = [[NSArray alloc] initWithArray:[customQueryParser splitStringByCharacter:';']]; + [customQueryParser release]; + + // Walk along the array of queries to identify the current query - taking into account + // the extra semicolon at the end of each query + for (i = 0; i < [queries count]; i++ ) { + queryPosition += [[queries objectAtIndex:i] length]; + if (queryPosition >= position) { + query = [NSString stringWithString:[queries objectAtIndex:i]]; + break; + } + queryPosition++; + } + + [queries release]; + + // Ensure the string isn't empty. + // (We could also strip comments for this check, but that prevents use of conditional comments) + if ([[query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) + return nil; + + // Return the located string. + return query; +} + + +#pragma mark - +#pragma mark Accessors + + - (NSArray *)currentResult /* returns the current result (as shown in custom result view) as array, the first object containing the field names as array, the following objects containing the rows as array @@ -384,7 +596,10 @@ returns the current result (as shown in custom result view) as array, the first } -//additional methods +#pragma mark - +#pragma mark Additional methods + + - (void)setConnection:(CMMCPConnection *)theConnection /* sets the connection (received from TableDocument) and makes things that have to be done only once @@ -403,18 +618,21 @@ sets the connection (received from TableDocument) and makes things that have to queryFavorites = [[NSMutableArray array] retain]; } -//set up interface + // Set up the interface [customQueryView setVerticalMotionCanBeginDrag:NO]; - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { - [textView setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; - } else { - [textView setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - } + [textView setFont:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:@"CustomQueryEditorFont"]]]; [textView setContinuousSpellCheckingEnabled:NO]; + [autoindentMenuItem setState:([prefs boolForKey:@"CustomQueryAutoindent"]?NSOnState:NSOffState)]; + [textView setAutoindent:[prefs boolForKey:@"CustomQueryAutoindent"]]; + [textView setAutoindentIgnoresEnter:YES]; + [autopairMenuItem setState:([prefs boolForKey:@"CustomQueryAutopair"]?NSOnState:NSOffState)]; + [textView setAutopair:[prefs boolForKey:@"CustomQueryAutopair"]]; + [autouppercaseKeywordsMenuItem setState:([prefs boolForKey:@"CustomQueryAutouppercaseKeywords"]?NSOnState:NSOffState)]; + [textView setAutouppercaseKeywords:[prefs boolForKey:@"CustomQueryAutouppercaseKeywords"]]; [queryFavoritesView registerForDraggedTypes:[NSArray arrayWithObjects:@"SequelProPasteboard", nil]]; while ( (column = [enumerator nextObject]) ) { - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { + if ( [prefs boolForKey:@"UseMonospacedFonts"] ) { [[column dataCell] setFont:[NSFont fontWithName:@"Monaco" size:10]]; } else { [[column dataCell] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; @@ -447,11 +665,14 @@ inserts the query in the textView and performs query */ { [textView setString:query]; - [self performQuery:self]; + [self runAllQueries:self]; } -//tableView datasource methods +#pragma mark - +#pragma mark TableView datasource methods + + - (int)numberOfRowsInTableView:(NSTableView *)aTableView { if ( aTableView == customQueryView ) { @@ -487,7 +708,7 @@ inserts the query in the textView and performs query return [tmp autorelease]; } if ( [[theRow objectAtIndex:[theIdentifier intValue]] isMemberOfClass:[NSNull class]] ) - return [prefs objectForKey:@"nullValue"]; + return [prefs objectForKey:@"NullValue"]; return [theRow objectAtIndex:[theIdentifier intValue]]; } else if ( aTableView == queryFavoritesView ) { @@ -646,7 +867,7 @@ opens sheet with value when double clicking on a field } [theValue autorelease]; } else if ( [[theRow objectAtIndex:[theIdentifier intValue]] isMemberOfClass:[NSNull class]] ) { - theValue = [prefs objectForKey:@"nullValue"]; + theValue = [prefs objectForKey:@"NullValue"]; } else { theValue = [theRow objectAtIndex:[theIdentifier intValue]]; } @@ -668,7 +889,10 @@ opens sheet with value when double clicking on a field } -//splitView delegate methods +#pragma mark - +#pragma mark SplitView delegate methods + + - (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview /* tells the splitView that it can collapse views @@ -702,7 +926,10 @@ defines min position of splitView } -//textView delegate methods +#pragma mark - +#pragma mark TextView delegate methods + + - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector /* traps enter key and @@ -714,7 +941,7 @@ traps enter key and if ( [aTextView methodForSelector:aSelector] == [aTextView methodForSelector:@selector(insertNewline:)] && [[[NSApp currentEvent] characters] isEqualToString:@"\003"] ) { - [self performQuery:self]; + [self runAllQueries:self]; return YES; } else { return NO; @@ -731,6 +958,88 @@ traps enter key and return NO; } +/* + * A notification posted when the selection changes within the text view; + * used to control the run-currentrun-selection button state and action. + */ +- (void)textViewDidChangeSelection:(NSNotification *)aNotification +{ + + // Ensure that the notification is from the custom query text view + if ( [aNotification object] != textView ) return; + + // If no text is selected, disable the button and action menu. + if ( [textView selectedRange].location == NSNotFound ) { + [runSelectionButton setEnabled:NO]; + [runSelectionMenuItem setEnabled:NO]; + return; + } + + // If the current selection is a single caret position, update the button based on + // whether the caret is inside a valid query. + if ([textView selectedRange].length == 0) { + int selectionPosition = [textView selectedRange].location; + int movedRangeStart, movedRangeLength; + NSRange oldSelection; + + // Retrieve the old selection position + [[[aNotification userInfo] objectForKey:@"NSOldSelectedCharacterRange"] getValue:&oldSelection]; + + // Only process the query text if the selection previously had length, or moved more than 100 characters, + // or the intervening space contained a semicolon, or typing has been performed with no current query. + // This adds more checks to every keypress, but ensures the majority of the actions don't incur a + // parsing overhead - which is cheap on small text strings but heavy of large queries. + movedRangeStart = (selectionPosition < oldSelection.location)?selectionPosition:oldSelection.location; + movedRangeLength = abs(selectionPosition - oldSelection.location); + if (oldSelection.length > 0 + || movedRangeLength > 100 + || oldSelection.location > [[textView string] length] + || [[textView string] rangeOfString:@";" options:0 range:NSMakeRange(movedRangeStart, movedRangeLength)].location != NSNotFound + || (![runSelectionButton isEnabled] && selectionPosition > oldSelection.location + && [[[[textView string] substringWithRange:NSMakeRange(movedRangeStart, movedRangeLength)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length]) + ) { + + [runSelectionButton setTitle:NSLocalizedString(@"Run Current", @"Title of button to run current query in custom query view")]; + [runSelectionMenuItem setTitle:NSLocalizedString(@"Run Current Query", @"Title of action menu item to run current query in custom query view")]; + + // If a valid query is present at the cursor position, enable the button + if ([self queryAtPosition:selectionPosition]) { + [runSelectionButton setEnabled:YES]; + [runSelectionMenuItem setEnabled:YES]; + } else { + [runSelectionButton setEnabled:NO]; + [runSelectionMenuItem setEnabled:NO]; + } + } + + // For selection ranges, enable the button. + } else { + [runSelectionButton setTitle:NSLocalizedString(@"Run Selection", @"Title of button to run selected text in custom query view")]; + [runSelectionButton setEnabled:YES]; + [runSelectionMenuItem setTitle:NSLocalizedString(@"Run Selected Text", @"Title of action menu item to run selected text in custom query view")]; + [runSelectionMenuItem setEnabled:YES]; + } +} + + +/* + * Save the custom query editor font if it is changed. + */ +- (void)textViewDidChangeTypingAttributes:(NSNotification *)aNotification +{ + + // Only save the font if prefs have been loaded, ensuring the saved font has been applied once. + if (prefs) { + [prefs setObject:[NSArchiver archivedDataWithRootObject:[textView font]] forKey:@"CustomQueryEditorFont"]; + } +} + + + +#pragma mark - +#pragma mark TableView notifications + + /* * Updates various interface elements based on the current table view selection. */ @@ -744,10 +1053,15 @@ traps enter key and } } + +#pragma mark - + + // Last but not least - (id)init; { self = [super init]; + prefs = nil; return self; } diff --git a/Source/MainController.h b/Source/MainController.h index b47bb457..a153964d 100644 --- a/Source/MainController.h +++ b/Source/MainController.h @@ -24,63 +24,30 @@ #import +@class SPPreferenceController; + @interface MainController : NSObject { - IBOutlet id keyChainInstance; - - IBOutlet id preferencesWindow; - IBOutlet id favoriteSheet; - IBOutlet id reloadAfterAddingSwitch; - IBOutlet id reloadAfterEditingSwitch; - IBOutlet id reloadAfterRemovingSwitch; - IBOutlet id showErrorSwitch; - IBOutlet id dontShowBlobSwitch; - IBOutlet id useMonospacedFontsSwitch; - IBOutlet id fetchRowCountSwitch; - IBOutlet id limitRowsSwitch; - IBOutlet id limitRowsField; - IBOutlet id nullValueField; - IBOutlet id tableView; - IBOutlet id nameField; - IBOutlet id hostField; - IBOutlet id socketField; - IBOutlet id userField; - IBOutlet id passwordField; - IBOutlet id portField; - IBOutlet id databaseField; - IBOutlet id sshCheckbox; - IBOutlet id sshUserField; - IBOutlet id sshPasswordField; - IBOutlet id sshHostField; - IBOutlet id sshPortField; - IBOutlet id encodingPopUpButton; - - NSMutableArray *favorites; - NSUserDefaults *prefs; - BOOL isNewFavorite; + + SPPreferenceController *prefsController; } -//IBAction methods +// IBAction methods - (IBAction)openPreferences:(id)sender; -- (IBAction)addFavorite:(id)sender; -- (IBAction)removeFavorite:(id)sender; -- (IBAction)copyFavorite:(id)sender; -- (IBAction)chooseLimitRows:(id)sender; -- (IBAction)closeFavoriteSheet:(id)sender; -- (IBAction)toggleUseSSH:(id)sender; -//services menu methods +// Services menu methods - (void)doPerformQueryService:(NSPasteboard *)pboard userData:(NSString *)data error:(NSString **)error; -//menu methods +// Menu methods - (IBAction)donate:(id)sender; - (IBAction)visitWebsite:(id)sender; - (IBAction)visitHelpWebsite:(id)sender; -- (IBAction)checkForUpdates:(id)sender; -//SSHTunnel methods -- (id)authenticate:(NSScriptCommand *)command; +// Getters +- (SPPreferenceController *)preferenceController; + +// Other - (id)handleQuitScriptCommand:(NSScriptCommand *)command; @end diff --git a/Source/MainController.m b/Source/MainController.m index c5e2eeb0..a2dfcc36 100644 --- a/Source/MainController.m +++ b/Source/MainController.m @@ -25,737 +25,188 @@ #import "MainController.h" #import "KeyChain.h" #import "TableDocument.h" +#import "SPPreferenceController.h" + +#define SEQUEL_PRO_HOME_PAGE_URL @"http://www.sequelpro.com/" +#define SEQUEL_PRO_DONATIONS_URL @"http://www.sequelpro.com/donate.html" +#define SEQUEL_PRO_FAQ_URL @"http://www.sequelpro.com/frequently-asked-questions.html" @implementation MainController -/* -opens the preferences window -*/ -- (IBAction)openPreferences:(id)sender +/** + * Called even before init so we can register our preference defaults + */ ++ (void)initialize { - //get favorites if they exist - [favorites release]; - if ( [prefs objectForKey:@"favorites"] != nil ) { - favorites = [[NSMutableArray alloc] initWithArray:[prefs objectForKey:@"favorites"]]; - } else { - favorites = [[NSMutableArray array] retain]; - } - [tableView reloadData]; - - if ( [prefs boolForKey:@"reloadAfterAdding"] ) { - [reloadAfterAddingSwitch setState:NSOnState]; - } else { - [reloadAfterAddingSwitch setState:NSOffState]; - } - if ( [prefs boolForKey:@"reloadAfterEditing"] ) { - [reloadAfterEditingSwitch setState:NSOnState]; - } else { - [reloadAfterEditingSwitch setState:NSOffState]; - } - if ( [prefs boolForKey:@"reloadAfterRemoving"] ) { - [reloadAfterRemovingSwitch setState:NSOnState]; - } else { - [reloadAfterRemovingSwitch setState:NSOffState]; - } - if ( [prefs boolForKey:@"showError"] ) { - [showErrorSwitch setState:NSOnState]; - } else { - [showErrorSwitch setState:NSOffState]; - } - if ( [prefs boolForKey:@"dontShowBlob"] ) { - [dontShowBlobSwitch setState:NSOnState]; - } else { - [dontShowBlobSwitch setState:NSOffState]; - } - if ( [prefs boolForKey:@"limitRows"] ) { - [limitRowsSwitch setState:NSOnState]; - } else { - [limitRowsSwitch setState:NSOffState]; - } - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { - [useMonospacedFontsSwitch setState:NSOnState]; - } else { - [useMonospacedFontsSwitch setState:NSOffState]; - } - if ( [prefs boolForKey:@"fetchRowCount"] ) { - [fetchRowCountSwitch setState:NSOnState]; - } else { - [fetchRowCountSwitch setState:NSOffState]; - } - [nullValueField setStringValue:[prefs stringForKey:@"nullValue"]]; - [limitRowsField setStringValue:[prefs stringForKey:@"limitRowsValue"]]; - [self chooseLimitRows:self]; - [encodingPopUpButton selectItemWithTitle:[prefs stringForKey:@"encoding"]]; - - [preferencesWindow makeKeyAndOrderFront:self]; + // Register application defaults + [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"PreferenceDefaults" ofType:@"plist"]]]; } -/* -adds a favorite -*/ -- (IBAction)addFavorite:(id)sender +/** + * Initialisation stuff upon nib awakening + */ +- (void)awakeFromNib { - int code; - - isNewFavorite = YES; - - [nameField setStringValue:@""]; - [hostField setStringValue:@""]; - [socketField setStringValue:@""]; - [userField setStringValue:@""]; - [passwordField setStringValue:@""]; - [portField setStringValue:@""]; - [databaseField setStringValue:@""]; - [sshCheckbox setState:NSOffState]; - [sshUserField setEnabled:NO]; - [sshPasswordField setEnabled:NO]; - [sshHostField setEnabled:NO]; - [sshPortField setEnabled:NO]; - [sshHostField setStringValue:@""]; - [sshUserField setStringValue:@""]; - [sshPortField setStringValue:@"8888"]; - [sshPasswordField setStringValue:@""]; - - [NSApp beginSheet:favoriteSheet - modalForWindow:preferencesWindow - modalDelegate:self - didEndSelector:nil - contextInfo:nil]; - - code = [NSApp runModalForWindow:favoriteSheet]; + prefsController = [[SPPreferenceController alloc] init]; - [NSApp endSheet:favoriteSheet]; - [favoriteSheet orderOut:nil]; + // Register MainController as services provider + [NSApp setServicesProvider:self]; - if ( code == 1 ) { - if ( ![[socketField stringValue] isEqualToString:@""] ) { - //set host to localhost if socket is used - [hostField setStringValue:@"localhost"]; - } - - // get ssh settings - NSString *sshHost, *sshUser, *sshPassword, *sshPort; - NSNumber *ssh; - if ( [sshCheckbox state] == NSOnState ) { - if ( [[sshHostField stringValue] isEqualToString:@""] ) { - sshHost = [hostField stringValue]; - } else { - sshHost = [sshHostField stringValue]; - } - if ( [[sshUserField stringValue] isEqualToString:@""] ) { - sshUser = [userField stringValue]; - } else { - sshUser = [sshUserField stringValue]; - } - if ( [[sshPasswordField stringValue] isEqualToString:@""] ) { - sshPassword = [passwordField stringValue]; - } else { - sshPassword = [sshPasswordField stringValue]; - } - if ( [[sshPortField stringValue] isEqualToString:@""] ) { - sshPort = [portField stringValue]; - } else { - sshPort = [sshPortField stringValue]; - } - ssh = [NSNumber numberWithInt:1]; - } else { - sshHost = @""; - sshUser = @""; - sshPassword = @""; - sshPort = @""; - ssh = [NSNumber numberWithInt:0]; - } - - NSDictionary *favorite = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[nameField stringValue], [hostField stringValue], [socketField stringValue], [userField stringValue], [portField stringValue], [databaseField stringValue], ssh, sshHost, sshUser, sshPort, nil] - forKeys:[NSArray arrayWithObjects:@"name", @"host", @"socket", @"user", @"port", @"database", @"useSSH", @"sshHost", @"sshUser", @"sshPort", nil]]; - [favorites addObject:favorite]; - - if ( ![[passwordField stringValue] isEqualToString:@""] ) - [keyChainInstance addPassword:[passwordField stringValue] - forName:[NSString stringWithFormat:@"Sequel Pro : %@", [nameField stringValue]] - account:[NSString stringWithFormat:@"%@@%@/%@", [userField stringValue], [hostField stringValue], [databaseField stringValue]]]; - - if ( ![sshPassword isEqualToString:@""] ) - [keyChainInstance addPassword:sshPassword - forName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@", [nameField stringValue]] - account:[NSString stringWithFormat:@"%@@%@/%@", [userField stringValue], [hostField stringValue], [databaseField stringValue]]]; - - [tableView reloadData]; - [tableView selectRow:[tableView numberOfRows]-1 byExtendingSelection:NO]; - } + // Register MainController for AppleScript events + [[NSScriptExecutionContext sharedScriptExecutionContext] setTopLevelObject:self]; isNewFavorite = NO; -} -/* -removes a favorite -*/ -- (IBAction)removeFavorite:(id)sender -{ - if ( ![tableView numberOfSelectedRows] ) - return; - - NSString *name = [[favorites objectAtIndex:[tableView selectedRow]] objectForKey:@"name"]; - NSString *user = [[favorites objectAtIndex:[tableView selectedRow]] objectForKey:@"user"]; - NSString *host = [[favorites objectAtIndex:[tableView selectedRow]] objectForKey:@"host"]; - NSString *database = [[favorites objectAtIndex:[tableView selectedRow]] objectForKey:@"database"]; - - [keyChainInstance deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro : %@", name] - account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]]; - [keyChainInstance deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@", name] - account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]]; - [favorites removeObjectAtIndex:[tableView selectedRow]]; - [tableView reloadData]; -} - -/* -copies a favorite -*/ -- (IBAction)copyFavorite:(id)sender -{ - if ( ![tableView numberOfSelectedRows] ) - return; + // Ensure we're not being run on Leopard + int systemPrefix = 10, systemMajor = 0, systemMinor = 0; + NSString *systemVersion = [[NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"] objectForKey:@"ProductVersion"]; + NSArray *systemVersionArray = [systemVersion componentsSeparatedByString:@"."]; + if ([systemVersionArray count]) systemPrefix = [[systemVersionArray objectAtIndex:0] intValue]; + if ([systemVersionArray count] > 1) systemMajor = [[systemVersionArray objectAtIndex:1] intValue]; + if ([systemVersionArray count] > 2) systemMinor = [[systemVersionArray objectAtIndex:2] intValue]; + if (systemPrefix == 10 && systemMajor > 4) { + NSAlert *alert = [NSAlert alertWithMessageText:@"This is the Tiger (10.4) version of Sequel Pro" defaultButton:@"Quit and open website" alternateButton:@"Run anyway" otherButton:@"Quit" informativeTextWithFormat:@"This version of Sequel Pro is only intended for use with Mac OS X Tiger (10.4.x). When run on your system, the interface will show incorrectly and buttons will be out of place. We recommend you visit the website to download a current version of Sequel Pro."]; + int returncode = [alert runModal]; - NSMutableDictionary *tempDictionary = [NSMutableDictionary dictionaryWithDictionary:[favorites objectAtIndex:[tableView selectedRow]]]; - [tempDictionary setObject:[NSString stringWithFormat:@"%@Copy", [tempDictionary objectForKey:@"name"]] forKey:@"name"]; -// [tempDictionary setObject:[NSString stringWithFormat:@"%@Copy", [tempDictionary objectForKey:@"user"]] forKey:@"user"]; - - [favorites insertObject:tempDictionary atIndex:[tableView selectedRow]+1]; - [tableView selectRow:[tableView selectedRow]+1 byExtendingSelection:NO]; + // Quit and open website button selected + if (returncode == NSAlertDefaultReturn) { + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.sequelpro.com/"]]; + [NSApp terminate:self]; - [tableView reloadData]; -} + // Quit + } else if (returncode == NSAlertOtherReturn) { + [[NSApplication sharedApplication] terminate:self]; -/* -enables or disables limitRowsField (depending on the state of limitRowsSwitch) -*/ -- (IBAction)chooseLimitRows:(id)sender -{ - if ( [limitRowsSwitch state] == NSOnState ) { - [limitRowsField setEnabled:YES]; - [limitRowsField selectText:self]; - } else { - [limitRowsField setEnabled:NO]; - } -} - -/* -close the favoriteSheet and save favorite if user hit save -*/ -- (IBAction)closeFavoriteSheet:(id)sender -{ - NSEnumerator *enumerator = [favorites objectEnumerator]; - id favorite; - int count; + // Run normally, opening a window manually + } else { + TableDocument *tableDocument; - //test if user has entered at least name and host/socket - if ( [sender tag] && - ([[nameField stringValue] isEqualToString:@""] || ([[hostField stringValue] isEqualToString:@""] && [[socketField stringValue] isEqualToString:@""])) ) { - NSRunAlertPanel(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"Please enter at least name and host or socket!", @"message of panel when name/host/socket are missing"), NSLocalizedString(@"OK", @"OK button"), nil, nil); - return; - } - - //test if favorite name isn't used by another favorite - count = 0; - if ( [sender tag] ) { - while ( (favorite = [enumerator nextObject]) ) { - if ( [[favorite objectForKey:@"name"] isEqualToString:[nameField stringValue]] ) - { - if ( isNewFavorite || (!isNewFavorite && (count != [tableView selectedRow])) ) { - NSRunAlertPanel(NSLocalizedString(@"Error", @"error"), [NSString stringWithFormat:NSLocalizedString(@"Favorite %@ has already been saved!\nPlease specify another name.", @"message of panel when favorite name has already been used"), [nameField stringValue]], NSLocalizedString(@"OK", @"OK button"), nil, nil); - return; + if (tableDocument = [[NSDocumentController sharedDocumentController] makeUntitledDocumentOfType:@"DocumentType" error:nil]) { + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AutoConnectToDefault"]) { + [tableDocument setShouldAutomaticallyConnect:YES]; } + [[NSDocumentController sharedDocumentController] addDocument:tableDocument]; + [tableDocument makeWindowControllers]; + [tableDocument showWindows]; } -/* - if ( [[favorite objectForKey:@"host"] isEqualToString:[hostField stringValue]] && - [[favorite objectForKey:@"user"] isEqualToString:[userField stringValue]] && - [[favorite objectForKey:@"database"] isEqualToString:[databaseField stringValue]] ) { - if ( isNewFavorite || (!isNewFavorite && (count != [tableView selectedRow])) ) { - NSRunAlertPanel(@"Error", @"There is already a favorite with the same host, user and database!", @"OK", nil, nil); - return; - } - } -*/ - count++; } } +} + +#pragma mark - +#pragma mark IBAction methods - [NSApp stopModalWithCode:[sender tag]]; +/** + * Opens the preferences window + */ +- (IBAction)openPreferences:(id)sender +{ + [prefsController showWindow:self]; } -/* -enables/disables ssh tunneling -*/ -- (IBAction)toggleUseSSH:(id)sender +#pragma mark - +#pragma mark Getters + +/** + * Provide a method to retrieve the prefs controller + */ +- (SPPreferenceController *)preferenceController { - if ( [sshCheckbox state] == NSOnState ) { - [sshUserField setEnabled:YES]; - [sshPasswordField setEnabled:YES]; - [sshHostField setEnabled:YES]; - [sshPortField setEnabled:YES]; - } else { - [sshUserField setEnabled:NO]; - [sshPasswordField setEnabled:NO]; - [sshHostField setEnabled:NO]; - [sshPortField setEnabled:NO]; - } + return prefsController; } + +#pragma mark - #pragma mark Services menu methods -/* -passes the query to the last created document -*/ +/** + * Passes the query to the last created document + */ - (void)doPerformQueryService:(NSPasteboard *)pboard userData:(NSString *)data error:(NSString **)error { NSString *pboardString; - NSArray *types; - - types = [pboard types]; - - if (![types containsObject:NSStringPboardType] || !(pboardString = [pboard stringForType:NSStringPboardType])) { + + NSArray *types = [pboard types]; + + if ((![types containsObject:NSStringPboardType]) || (!(pboardString = [pboard stringForType:NSStringPboardType]))) { *error = @"Pasteboard couldn't give string."; + return; } - - //check if there exists a document - if ( ![[[NSDocumentController sharedDocumentController] documents] count] ) { + + // Check if at least one document exists + if (![[[NSDocumentController sharedDocumentController] documents] count]) { *error = @"No Documents open!"; + return; } - - //pass query to last created document -// [[[NSDocumentController sharedDocumentController] currentDocument] doPerformQueryService:pboardString]; - [[[[NSDocumentController sharedDocumentController] documents] objectAtIndex:[[[NSDocumentController sharedDocumentController] documents] count]-1] doPerformQueryService:pboardString]; - + + // Pass query to last created document + [[[[NSDocumentController sharedDocumentController] documents] objectAtIndex:([[[NSDocumentController sharedDocumentController] documents] count] - 1)] doPerformQueryService:pboardString]; + return; } - +#pragma mark - #pragma mark Sequel Pro menu methods -/* -opens donate link in default browser -*/ +/** + * Opens donate link in default browser + */ - (IBAction)donate:(id)sender { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.sequelpro.com/donate.html"]]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:SEQUEL_PRO_DONATIONS_URL]]; } -/* -opens website link in default browser -*/ +/** + * Opens website link in default browser + */ - (IBAction)visitWebsite:(id)sender { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.sequelpro.com/"]]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:SEQUEL_PRO_HOME_PAGE_URL]]; } -/* -opens help link in default browser -*/ +/** + * Opens help link in default browser + */ - (IBAction)visitHelpWebsite:(id)sender { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.sequelpro.com/frequently-asked-questions.html"]]; -} - -/* -checks for updates and opens download page in default browser -*/ -- (IBAction)checkForUpdates:(id)sender -{ - NSLog(@"[MainController checkForUpdates:] is not currently functional."); + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:SEQUEL_PRO_FAQ_URL]]; } +#pragma mark - +#pragma mark Other methods -#pragma mark TableView datasource methods - -- (int)numberOfRowsInTableView:(NSTableView *)aTableView -{ - return [favorites count]; -} - -- (id)tableView:(NSTableView *)aTableView - objectValueForTableColumn:(NSTableColumn *)aTableColumn - row:(int)rowIndex -{ - return [[favorites objectAtIndex:rowIndex] objectForKey:[aTableColumn identifier]]; -} - - -#pragma mark TableView drag & drop datasource methods - -- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard -{ - int originalRow; - NSArray *pboardTypes; - - if ( [rows count] == 1 ) { - pboardTypes=[NSArray arrayWithObjects:@"SequelProPreferencesPasteboard", nil]; - originalRow = [[rows objectAtIndex:0] intValue]; - - [pboard declareTypes:pboardTypes owner:nil]; - [pboard setString:[[NSNumber numberWithInt:originalRow] stringValue] forType:@"SequelProPreferencesPasteboard"]; - - return YES; - } else { - return NO; - } -} - -- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(int)row - proposedDropOperation:(NSTableViewDropOperation)operation -{ - NSArray *pboardTypes = [[info draggingPasteboard] types]; - int originalRow; - - if ([pboardTypes count] == 1 && row != -1) - { - if ([[pboardTypes objectAtIndex:0] isEqualToString:@"SequelProPreferencesPasteboard"]==YES && operation==NSTableViewDropAbove) - { - originalRow = [[[info draggingPasteboard] stringForType:@"SequelProPreferencesPasteboard"] intValue]; - - if (row != originalRow && row != (originalRow+1)) - { - return NSDragOperationMove; - } - } - } - - return NSDragOperationNone; -} - -- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id )info row:(int)row dropOperation:(NSTableViewDropOperation)operation -{ - int originalRow; - int destinationRow; - NSMutableDictionary *draggedRow; - - originalRow = [[[info draggingPasteboard] stringForType:@"SequelProPreferencesPasteboard"] intValue]; - destinationRow = row; - - if ( destinationRow > originalRow ) - destinationRow--; - - draggedRow = [NSMutableDictionary dictionaryWithDictionary:[favorites objectAtIndex:originalRow]]; - [favorites removeObjectAtIndex:originalRow]; - [favorites insertObject:draggedRow atIndex:destinationRow]; - - [tableView reloadData]; - [tableView selectRow:destinationRow byExtendingSelection:NO]; - - return YES; -} - -/* - opens sheet to edit favorite and saves favorite if user hit OK +/** + * Override the default open-blank-document methods to automatically connect + * automatically opened windows. */ -- (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex +- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender { - int code; - NSDictionary *favorite = [favorites objectAtIndex:rowIndex]; - - // set up fields - [nameField setStringValue:[favorite objectForKey:@"name"]]; - [hostField setStringValue:[favorite objectForKey:@"host"]]; - [socketField setStringValue:[favorite objectForKey:@"socket"]]; - [userField setStringValue:[favorite objectForKey:@"user"]]; - [portField setStringValue:[favorite objectForKey:@"port"]]; - [databaseField setStringValue:[favorite objectForKey:@"database"]]; - [passwordField setStringValue:[keyChainInstance getPasswordForName:[NSString stringWithFormat:@"Sequel Pro : %@", [nameField stringValue]] - account:[NSString stringWithFormat:@"%@@%@/%@", [userField stringValue], [hostField stringValue], [databaseField stringValue]]]]; - - // set up ssh fields - if ( [[favorite objectForKey:@"useSSH"] intValue] == 1 ) { - [sshCheckbox setState:NSOnState]; - [sshUserField setEnabled:YES]; - [sshPasswordField setEnabled:YES]; - [sshHostField setEnabled:YES]; - [sshPortField setEnabled:YES]; - [sshHostField setStringValue:[favorite objectForKey:@"sshHost"]]; - [sshUserField setStringValue:[favorite objectForKey:@"sshUser"]]; - [sshPortField setStringValue:[favorite objectForKey:@"sshPort"]]; - [sshPasswordField setStringValue:[keyChainInstance getPasswordForName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@", [nameField stringValue]] - account:[NSString stringWithFormat:@"%@@%@/%@", [userField stringValue], [hostField stringValue], [databaseField stringValue]]]]; - } else { - [sshCheckbox setState:NSOffState]; - [sshUserField setEnabled:NO]; - [sshPasswordField setEnabled:NO]; - [sshHostField setEnabled:NO]; - [sshPortField setEnabled:NO]; - [sshHostField setStringValue:@""]; - [sshUserField setStringValue:@""]; - [sshPortField setStringValue:@""]; - [sshPasswordField setStringValue:@""]; - } - - // run sheet - [NSApp beginSheet:favoriteSheet - modalForWindow:preferencesWindow - modalDelegate:self - didEndSelector:nil - contextInfo:nil]; + TableDocument *firstTableDocument; - code = [NSApp runModalForWindow:favoriteSheet]; - - [NSApp endSheet:favoriteSheet]; - [favoriteSheet orderOut:nil]; - - if ( code == 1 ) { - if ( ![[socketField stringValue] isEqualToString:@""] ) { - //set host to localhost if socket is used - [hostField setStringValue:@"localhost"]; + // Manually open a new document, setting MainController as sender to trigger autoconnection + if (firstTableDocument = [[NSDocumentController sharedDocumentController] makeUntitledDocumentOfType:@"DocumentType" error:nil]) { + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AutoConnectToDefault"]) { + [firstTableDocument setShouldAutomaticallyConnect:YES]; } - - //get ssh settings - NSString *sshHost, *sshUser, *sshPassword, *sshPort; - NSNumber *ssh; - if ( [sshCheckbox state] == NSOnState ) { - if ( [[sshHostField stringValue] isEqualToString:@""] ) { - sshHost = [hostField stringValue]; - } else { - sshHost = [sshHostField stringValue]; - } - if ( [[sshUserField stringValue] isEqualToString:@""] ) { - sshUser = [userField stringValue]; - } else { - sshUser = [sshUserField stringValue]; - } - if ( [[sshPasswordField stringValue] isEqualToString:@""] ) { - sshPassword = [passwordField stringValue]; - } else { - sshPassword = [sshPasswordField stringValue]; - } - if ( [[sshPortField stringValue] isEqualToString:@""] ) { - sshPort = [portField stringValue]; - } else { - sshPort = [sshPortField stringValue]; - } - ssh = [NSNumber numberWithInt:1]; - } else { - sshHost = @""; - sshUser = @""; - sshPassword = @""; - sshPort = @""; - ssh = [NSNumber numberWithInt:0]; - } - - //replace password - [keyChainInstance deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro : %@", [favorite objectForKey:@"name"]] - account:[NSString stringWithFormat:@"%@@%@/%@", [favorite objectForKey:@"user"], [favorite objectForKey:@"host"], [favorite objectForKey:@"database"]]]; - - if ( ![[passwordField stringValue] isEqualToString:@""] ) - [keyChainInstance addPassword:[passwordField stringValue] - forName:[NSString stringWithFormat:@"Sequel Pro : %@", [nameField stringValue]] - account:[NSString stringWithFormat:@"%@@%@/%@", [userField stringValue], [hostField stringValue], [databaseField stringValue]]]; - - //replace ssh password - [keyChainInstance deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@", [favorite objectForKey:@"name"]] - account:[NSString stringWithFormat:@"%@@%@/%@", [favorite objectForKey:@"user"], [favorite objectForKey:@"host"], [favorite objectForKey:@"database"]]]; - - if ( ([sshCheckbox state] == NSOnState) && ![sshPassword isEqualToString:@""] ) { - [keyChainInstance addPassword:sshPassword - forName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@", [nameField stringValue]] - account:[NSString stringWithFormat:@"%@@%@/%@", [userField stringValue], [hostField stringValue], - [databaseField stringValue]]]; - } - - //replace favorite - favorite = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[nameField stringValue], [hostField stringValue], [socketField stringValue], [userField stringValue], [portField stringValue], [databaseField stringValue], ssh, sshHost, sshUser, sshPort, nil] - forKeys:[NSArray arrayWithObjects:@"name", @"host", @"socket", @"user", @"port", @"database", @"useSSH", @"sshHost", @"sshUser", @"sshPort", nil]]; - [favorites replaceObjectAtIndex:rowIndex withObject:favorite]; - [tableView reloadData]; + [[NSDocumentController sharedDocumentController] addDocument:firstTableDocument]; + [firstTableDocument makeWindowControllers]; + [firstTableDocument showWindows]; } + // Return NO to the automatic opening return NO; } - -#pragma mark Window delegate methods - -/* - saves the preferences +/** + * What exactly is this for? */ -- (BOOL)windowShouldClose:(id)sender -{ - if ( sender == preferencesWindow ) { - if ( [reloadAfterAddingSwitch state] == NSOnState ) { - [prefs setBool:YES forKey:@"reloadAfterAdding"]; - } else { - [prefs setBool:NO forKey:@"reloadAfterAdding"]; - } - if ( [reloadAfterEditingSwitch state] == NSOnState ) { - [prefs setBool:YES forKey:@"reloadAfterEditing"]; - } else { - [prefs setBool:NO forKey:@"reloadAfterEditing"]; - } - if ( [reloadAfterRemovingSwitch state] == NSOnState ) { - [prefs setBool:YES forKey:@"reloadAfterRemoving"]; - } else { - [prefs setBool:NO forKey:@"reloadAfterRemoving"]; - } - if ( [showErrorSwitch state] == NSOnState ) { - [prefs setBool:YES forKey:@"showError"]; - } else { - [prefs setBool:NO forKey:@"showError"]; - } - if ( [dontShowBlobSwitch state] == NSOnState ) { - [prefs setBool:YES forKey:@"dontShowBlob"]; - } else { - [prefs setBool:NO forKey:@"dontShowBlob"]; - } - if ( [limitRowsSwitch state] == NSOnState ) { - [prefs setBool:YES forKey:@"limitRows"]; - } else { - [prefs setBool:NO forKey:@"limitRows"]; - } - if ( [useMonospacedFontsSwitch state] == NSOnState ) { - [prefs setBool:YES forKey:@"useMonospacedFonts"]; - } else { - [prefs setBool:NO forKey:@"useMonospacedFonts"]; - } - if ( [fetchRowCountSwitch state] == NSOnState ) { - [prefs setBool:YES forKey:@"fetchRowCount"]; - } else { - [prefs setBool:NO forKey:@"fetchRowCount"]; - } - [prefs setObject:[nullValueField stringValue] forKey:@"nullValue"]; - if ( [limitRowsField intValue] > 0 ) { - [prefs setInteger:[limitRowsField intValue] forKey:@"limitRowsValue"]; - } else { - [prefs setInteger:1 forKey:@"limitRowsValue"]; - } - [prefs setObject:[encodingPopUpButton titleOfSelectedItem] forKey:@"encoding"]; - - [prefs setObject:favorites forKey:@"favorites"]; - } - return YES; -} - - -#pragma mark Other methods - -- (void)awakeFromNib -{ - int currentVersionNumber; - - // Register MainController as services provider - [NSApp setServicesProvider:self]; - - // Register MainController for AppleScript events - [[NSScriptExecutionContext sharedScriptExecutionContext] setTopLevelObject:self]; - - // Get the current bundle version number (the SVN build number) for per-version upgrades - currentVersionNumber = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]; - - prefs = [[NSUserDefaults standardUserDefaults] retain]; - isNewFavorite = NO; - [prefs registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], @"reloadAfterAdding", - [NSNumber numberWithBool:YES], @"reloadAfterEditing", - [NSNumber numberWithBool:NO], @"reloadAfterRemoving", - [NSString stringWithString:@"NULL"], @"nullValue", - [NSNumber numberWithBool:YES], @"showError", - [NSNumber numberWithBool:NO], @"dontShowBlob", - [NSString stringWithString:NSHomeDirectory()], @"savePath", - [NSString stringWithString:NSHomeDirectory()], @"openPath", - [NSString stringWithString:@"Autodetect"], @"encoding", - [NSNumber numberWithBool:NO], @"useMonospacedFonts", - [NSNumber numberWithBool:YES], @"fetchRowCount", - [NSNumber numberWithBool:YES], @"limitRows", - [NSNumber numberWithInt:1000], @"limitRowsValue", - [NSNumber numberWithInt:60], @"keepAliveInterval", - [NSNumber numberWithInt:0], @"lastUsedVersion", - nil]]; - - // For versions prior to r336, where column widths have been saved, walk through them and remove - // any table widths set to 15 or less (fix for mangled columns caused by Issue #140) - if ([[prefs objectForKey:@"lastUsedVersion"] intValue] < 336 && [prefs objectForKey:@"tableColumnWidths"] != nil) { - NSEnumerator *databaseEnumerator, *tableEnumerator, *columnEnumerator; - NSString *databaseKey, *tableKey, *columnKey; - NSMutableDictionary *newDatabase, *newTable; - float columnWidth; - NSMutableDictionary *newTableColumnWidths = [[NSMutableDictionary alloc] init]; - - databaseEnumerator = [[prefs objectForKey:@"tableColumnWidths"] keyEnumerator]; - while (databaseKey = [databaseEnumerator nextObject]) { - newDatabase = [[NSMutableDictionary alloc] init]; - tableEnumerator = [[[prefs objectForKey:@"tableColumnWidths"] objectForKey:databaseKey] keyEnumerator]; - while (tableKey = [tableEnumerator nextObject]) { - newTable = [[NSMutableDictionary alloc] init]; - columnEnumerator = [[[[prefs objectForKey:@"tableColumnWidths"] objectForKey:databaseKey] objectForKey:tableKey] keyEnumerator]; - while (columnKey = [columnEnumerator nextObject]) { - columnWidth = [[[[[prefs objectForKey:@"tableColumnWidths"] objectForKey:databaseKey] objectForKey:tableKey] objectForKey:columnKey] floatValue]; - if (columnWidth >= 15) { - [newTable setObject:[NSNumber numberWithFloat:columnWidth] forKey:[NSString stringWithString:columnKey]]; - } - } - if ([newTable count]) { - [newDatabase setObject:[NSDictionary dictionaryWithDictionary:newTable] forKey:[NSString stringWithString:tableKey]]; - } - [newTable release]; - } - if ([newDatabase count]) { - [newTableColumnWidths setObject:[NSDictionary dictionaryWithDictionary:newDatabase] forKey:[NSString stringWithString:databaseKey]]; - } - [newDatabase release]; - } - [prefs setObject:[NSDictionary dictionaryWithDictionary:newTableColumnWidths] forKey:@"tableColumnWidths"]; - [newTableColumnWidths release]; - } - - // Write the current bundle version to the prefs - [prefs setObject:[NSNumber numberWithInt:currentVersionNumber] forKey:@"lastUsedVersion"]; - - [tableView registerForDraggedTypes:[NSArray arrayWithObjects:@"SequelProPreferencesPasteboard", nil]]; - [tableView reloadData]; -} - - -// SSHTunnel methods -- (id)authenticate:(NSScriptCommand *)command { - NSDictionary *args = [command evaluatedArguments]; - NSString *givenQuery = [ args objectForKey:@"query"]; - NSString *tunnelName = [ args objectForKey:@"tunnelName"]; - NSString *fifo = [ args objectForKey:@"fifo"]; - - NSLog(@"tunnel: %@ / query: %@ / fifo: %@",tunnelName,givenQuery,fifo); - NSFileHandle *fh = [ NSFileHandle fileHandleForWritingAtPath: fifo ]; - [ fh writeData: [ @"xy" dataUsingEncoding: NSASCIIStringEncoding]]; - [ fh closeFile ]; - - NSLog(@"password written"); - return @"OK"; - -/* - [ query setStringValue: givenQuery ]; - [NSApp beginSheet: alertSheet - modalForWindow: mainWindow - modalDelegate: nil - didEndSelector: nil - contextInfo: nil]; - [NSApp runModalForWindow: alertSheet]; - // Sheet is up here. - [NSApp endSheet: alertSheet]; - [alertSheet orderOut: self]; - if ( sheetStatus == 0) - { - password = [ passwd stringValue ]; - [ passwd setStringValue: @"" ]; - return password ; - } - else - { - [[tunnelTask objectForKey: @"task" ] terminate ]; - } - sheetStatus = nil; - return @""; -*/ -} - -// Method used for Applescript hooks to quit the application - (id)handleQuitScriptCommand:(NSScriptCommand *)command { - [ NSApp terminate: self ]; + [NSApp terminate:self]; + + // Suppress warning return nil; } diff --git a/Source/NoodleLineNumberView.h b/Source/NoodleLineNumberView.h new file mode 100644 index 00000000..ca734a56 --- /dev/null +++ b/Source/NoodleLineNumberView.h @@ -0,0 +1,60 @@ +// +// NoodleLineNumberView.h +// Line View Test +// +// Created by Paul Kim on 9/28/08. +// Copyright (c) 2008 Noodlesoft, LLC. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@class NoodleLineNumberMarker; + +@interface NoodleLineNumberView : NSRulerView +{ + // Array of character indices for the beginning of each line + NSMutableArray *lineIndices; + NSFont *font; + NSColor *textColor; + NSColor *alternateTextColor; + NSColor *backgroundColor; +} + +- (id)initWithScrollView:(NSScrollView *)aScrollView; + +- (void)setFont:(NSFont *)aFont; +- (NSFont *)font; + +- (void)setTextColor:(NSColor *)color; +- (NSColor *)textColor; + +- (void)setAlternateTextColor:(NSColor *)color; +- (NSColor *)alternateTextColor; + +- (void)setBackgroundColor:(NSColor *)color; +- (NSColor *)backgroundColor; + +- (unsigned)lineNumberForLocation:(float)location; + +@end diff --git a/Source/NoodleLineNumberView.m b/Source/NoodleLineNumberView.m new file mode 100644 index 00000000..c5d76187 --- /dev/null +++ b/Source/NoodleLineNumberView.m @@ -0,0 +1,493 @@ +// +// NoodleLineNumberView.m +// Line View Test +// +// Created by Paul Kim on 9/28/08. +// Copyright (c) 2008 Noodlesoft, LLC. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +// This version of the NoodleLineNumberView for Sequel Pro removes marker +// functionality. + +#import "NoodleLineNumberView.h" + +#define DEFAULT_THICKNESS 22.0 +#define RULER_MARGIN 5.0 + +@interface NoodleLineNumberView (Private) + +- (NSMutableArray *)lineIndices; +- (void)invalidateLineIndices; +- (void)calculateLines; +- (unsigned)lineNumberForCharacterIndex:(unsigned)index inText:(NSString *)text; +- (NSDictionary *)textAttributes; + +@end + +@implementation NoodleLineNumberView + +- (id)initWithScrollView:(NSScrollView *)aScrollView +{ + if ((self = [super initWithScrollView:aScrollView orientation:NSVerticalRuler]) != nil) + { + [self setClientView:[aScrollView documentView]]; + } + return self; +} + +- (void)awakeFromNib +{ + [self setClientView:[[self scrollView] documentView]]; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [lineIndices release]; + [font release]; + + [super dealloc]; +} + +- (void)setFont:(NSFont *)aFont +{ + if (font != aFont) + { + [font autorelease]; + font = [aFont retain]; + } +} + +- (NSFont *)font +{ + if (font == nil) + { + return [NSFont labelFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]; + } + return font; +} + +- (void)setTextColor:(NSColor *)color +{ + if (textColor != color) + { + [textColor autorelease]; + textColor = [color retain]; + } +} + +- (NSColor *)textColor +{ + if (textColor == nil) + { + return [NSColor colorWithCalibratedWhite:0.42 alpha:1.0]; + } + return textColor; +} + +- (void)setAlternateTextColor:(NSColor *)color +{ + if (alternateTextColor != color) + { + [alternateTextColor autorelease]; + alternateTextColor = [color retain]; + } +} + +- (NSColor *)alternateTextColor +{ + if (alternateTextColor == nil) + { + return [NSColor whiteColor]; + } + return alternateTextColor; +} + +- (void)setBackgroundColor:(NSColor *)color +{ + if (backgroundColor != color) + { + [backgroundColor autorelease]; + backgroundColor = [color retain]; + } +} + +- (NSColor *)backgroundColor +{ + return backgroundColor; +} + +- (void)setClientView:(NSView *)aView +{ + id oldClientView; + + oldClientView = [self clientView]; + + if ((oldClientView != aView) && [oldClientView isKindOfClass:[NSTextView class]]) + { + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSTextStorageDidProcessEditingNotification object:[(NSTextView *)oldClientView textStorage]]; + } + [super setClientView:aView]; + if ((aView != nil) && [aView isKindOfClass:[NSTextView class]]) + { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:NSTextStorageDidProcessEditingNotification object:[(NSTextView *)aView textStorage]]; + + [self invalidateLineIndices]; + } +} + +- (NSMutableArray *)lineIndices +{ + if (lineIndices == nil) + { + [self calculateLines]; + } + return lineIndices; +} + +- (void)invalidateLineIndices +{ + [lineIndices release]; + lineIndices = nil; +} + +- (void)textDidChange:(NSNotification *)notification +{ + // Invalidate the line indices. They will be recalculated and recached on demand. + [self invalidateLineIndices]; + + [self setNeedsDisplay:YES]; +} + +- (unsigned)lineNumberForLocation:(float)location +{ + unsigned line, count, index, rectCount, i; + NSRectArray rects; + NSRect visibleRect; + NSLayoutManager *layoutManager; + NSTextContainer *container; + NSRange nullRange; + NSMutableArray *lines; + id view; + + view = [self clientView]; + visibleRect = [[[self scrollView] contentView] bounds]; + + lines = [self lineIndices]; + + location += NSMinY(visibleRect); + + if ([view isKindOfClass:[NSTextView class]]) + { + nullRange = NSMakeRange(NSNotFound, 0); + layoutManager = [view layoutManager]; + container = [view textContainer]; + count = [lines count]; + + for (line = 0; line < count; line++) + { + index = [[lines objectAtIndex:line] unsignedIntValue]; + + rects = [layoutManager rectArrayForCharacterRange:NSMakeRange(index, 0) + withinSelectedCharacterRange:nullRange + inTextContainer:container + rectCount:&rectCount]; + + for (i = 0; i < rectCount; i++) + { + if ((location >= NSMinY(rects[i])) && (location < NSMaxY(rects[i]))) + { + return line + 1; + } + } + } + } + return NSNotFound; +} + +- (void)calculateLines +{ + id view; + + view = [self clientView]; + + if ([view isKindOfClass:[NSTextView class]]) + { + unsigned index, numberOfLines, stringLength, lineEnd, contentEnd; + NSString *text; + float oldThickness, newThickness; + + text = [view string]; + stringLength = [text length]; + [lineIndices release]; + lineIndices = [[NSMutableArray alloc] init]; + + index = 0; + numberOfLines = 0; + + do + { + [lineIndices addObject:[NSNumber numberWithUnsignedInt:index]]; + + index = NSMaxRange([text lineRangeForRange:NSMakeRange(index, 0)]); + numberOfLines++; + } + while (index < stringLength); + + // Check if text ends with a new line. + [text getLineStart:NULL end:&lineEnd contentsEnd:&contentEnd forRange:NSMakeRange([[lineIndices lastObject] unsignedIntValue], 0)]; + if (contentEnd < lineEnd) + { + [lineIndices addObject:[NSNumber numberWithUnsignedInt:index]]; + } + + oldThickness = [self ruleThickness]; + newThickness = [self requiredThickness]; + if (fabs(oldThickness - newThickness) > 1) + { + NSInvocation *invocation; + + // 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). + invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(setRuleThickness:)]]; + [invocation setSelector:@selector(setRuleThickness:)]; + [invocation setTarget:self]; + [invocation setArgument:&newThickness atIndex:2]; + + [invocation performSelector:@selector(invoke) withObject:nil afterDelay:0.0]; + } + } +} + +- (unsigned)lineNumberForCharacterIndex:(unsigned)index inText:(NSString *)text +{ + unsigned left, right, mid, lineStart; + NSMutableArray *lines; + + lines = [self lineIndices]; + + // Binary search + left = 0; + right = [lines count]; + + while ((right - left) > 1) + { + mid = (right + left) / 2; + lineStart = [[lines objectAtIndex:mid] unsignedIntValue]; + + if (index < lineStart) + { + right = mid; + } + else if (index > lineStart) + { + left = mid; + } + else + { + return mid; + } + } + return left; +} + +- (NSDictionary *)textAttributes +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + [self font], NSFontAttributeName, + [self textColor], NSForegroundColorAttributeName, + nil]; +} + +- (float)requiredThickness +{ + unsigned lineCount, digits, i; + NSMutableString *sampleString; + NSSize stringSize; + + lineCount = [[self lineIndices] count]; + digits = (unsigned)log10(lineCount) + 1; + sampleString = [NSMutableString string]; + for (i = 0; i < digits; i++) + { + // Use "8" since it is one of the fatter numbers. Anything but "1" + // will probably be ok here. I could be pedantic and actually find the fattest + // number for the current font but nah. + [sampleString appendString:@"8"]; + } + + stringSize = [sampleString sizeWithAttributes:[self textAttributes]]; + + // Round up the value. There is a bug on 10.4 where the display gets all wonky when scrolling if you don't + // return an integral value here. + return ceilf(MAX(DEFAULT_THICKNESS, stringSize.width + RULER_MARGIN * 2)); +} + +- (void)drawHashMarksAndLabelsInRect:(NSRect)aRect +{ + id view; + NSRect bounds; + + 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))]; + } + + view = [self clientView]; + + if ([view isKindOfClass:[NSTextView class]]) + { + NSLayoutManager *layoutManager; + NSTextContainer *container; + NSRect visibleRect; + NSRange range, glyphRange, nullRange; + NSString *text, *labelText; + unsigned rectCount, index, line, count; + NSRectArray rects; + float ypos, yinset; + NSDictionary *textAttributes, *currentTextAttributes; + NSSize stringSize; + NSMutableArray *lines; + + layoutManager = [view layoutManager]; + container = [view textContainer]; + text = [view string]; + nullRange = NSMakeRange(NSNotFound, 0); + + yinset = [view textContainerInset].height; + visibleRect = [[[self scrollView] contentView] bounds]; + + textAttributes = [self textAttributes]; + + lines = [self lineIndices]; + + // Find the characters that are currently visible + glyphRange = [layoutManager glyphRangeForBoundingRect:visibleRect inTextContainer:container]; + range = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; + + // Fudge the range a tad in case there is an extra new line at end. + // It doesn't show up in the glyphs so would not be accounted for. + range.length++; + + count = [lines count]; + index = 0; + + for (line = [self lineNumberForCharacterIndex:range.location inText:text]; line < count; line++) + { + index = [[lines objectAtIndex:line] unsignedIntValue]; + + if (NSLocationInRange(index, range)) + { + rects = [layoutManager rectArrayForCharacterRange:NSMakeRange(index, 0) + withinSelectedCharacterRange:nullRange + inTextContainer:container + rectCount:&rectCount]; + + if (rectCount > 0) + { + // Note that the ruler view is only as tall as the visible + // portion. Need to compensate for the clipview's coordinates. + ypos = yinset + NSMinY(rects[0]) - NSMinY(visibleRect); + + // Line numbers are internally stored starting at 0 + labelText = [NSString stringWithFormat:@"%d", line + 1]; + + stringSize = [labelText sizeWithAttributes:textAttributes]; + + currentTextAttributes = textAttributes; + + // Draw string flush right, centered vertically within the line + [labelText drawInRect: + NSMakeRect(NSWidth(bounds) - stringSize.width - RULER_MARGIN, + ypos + (NSHeight(rects[0]) - stringSize.height) / 2.0, + NSWidth(bounds) - RULER_MARGIN * 2.0, NSHeight(rects[0])) + withAttributes:currentTextAttributes]; + } + } + if (index > NSMaxRange(range)) + { + break; + } + } + } +} + + +#pragma mark NSCoding methods + +#define NOODLE_FONT_CODING_KEY @"font" +#define NOODLE_TEXT_COLOR_CODING_KEY @"textColor" +#define NOODLE_ALT_TEXT_COLOR_CODING_KEY @"alternateTextColor" +#define NOODLE_BACKGROUND_COLOR_CODING_KEY @"backgroundColor" + +- (id)initWithCoder:(NSCoder *)decoder +{ + if ((self = [super initWithCoder:decoder]) != nil) + { + if ([decoder allowsKeyedCoding]) + { + font = [[decoder decodeObjectForKey:NOODLE_FONT_CODING_KEY] retain]; + textColor = [[decoder decodeObjectForKey:NOODLE_TEXT_COLOR_CODING_KEY] retain]; + alternateTextColor = [[decoder decodeObjectForKey:NOODLE_ALT_TEXT_COLOR_CODING_KEY] retain]; + backgroundColor = [[decoder decodeObjectForKey:NOODLE_BACKGROUND_COLOR_CODING_KEY] retain]; + } + else + { + font = [[decoder decodeObject] retain]; + textColor = [[decoder decodeObject] retain]; + alternateTextColor = [[decoder decodeObject] retain]; + backgroundColor = [[decoder decodeObject] retain]; + } + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + [super encodeWithCoder:encoder]; + + if ([encoder allowsKeyedCoding]) + { + [encoder encodeObject:font forKey:NOODLE_FONT_CODING_KEY]; + [encoder encodeObject:textColor forKey:NOODLE_TEXT_COLOR_CODING_KEY]; + [encoder encodeObject:alternateTextColor forKey:NOODLE_ALT_TEXT_COLOR_CODING_KEY]; + [encoder encodeObject:backgroundColor forKey:NOODLE_BACKGROUND_COLOR_CODING_KEY]; + } + else + { + [encoder encodeObject:font]; + [encoder encodeObject:textColor]; + [encoder encodeObject:alternateTextColor]; + [encoder encodeObject:backgroundColor]; + } +} + +@end diff --git a/Source/SPArrayAdditions.h b/Source/SPArrayAdditions.h new file mode 100644 index 00000000..d1084ad7 --- /dev/null +++ b/Source/SPArrayAdditions.h @@ -0,0 +1,29 @@ +// +// SPArrayAdditions.h +// sequel-pro +// +// Created by Jakob Egger on March 24, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import + +@interface NSArray (SPArrayAdditions) + +- (NSString *)componentsJoinedAndBacktickQuoted; + +@end diff --git a/Source/SPArrayAdditions.m b/Source/SPArrayAdditions.m new file mode 100644 index 00000000..3115eb47 --- /dev/null +++ b/Source/SPArrayAdditions.m @@ -0,0 +1,45 @@ +// +// SPArrayAdditions.m +// sequel-pro +// +// Created by Jakob Egger on March 24, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPArrayAdditions.h" +#import "SPStringAdditions.h" + +@implementation NSArray (SPArrayAdditions) + +- (NSString *)componentsJoinedAndBacktickQuoted; +/* + * This method quotes all elements with backticks and then joins them with + * commas. Use it for field lists as in "SELECT (...) FROM somewhere" + */ +{ + NSString *result = [NSString string]; + int i; + for (i = 0; i < [self count]; i++) + { + NSString *component = [self objectAtIndex:i]; + if ([result length]) result = [result stringByAppendingString: @","]; + result = [result stringByAppendingString: [component backtickQuotedString] ]; + } + return result; +} + +@end diff --git a/Source/SPConsoleMessage.h b/Source/SPConsoleMessage.h new file mode 100644 index 00000000..233c19b8 --- /dev/null +++ b/Source/SPConsoleMessage.h @@ -0,0 +1,44 @@ +// +// SPConsoleMessage.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on Mar 12, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import + +@interface SPConsoleMessage : NSObject +{ + BOOL isError; + NSDate *messageDate; + NSString *message; +} + ++ (SPConsoleMessage *)consoleMessageWithMessage:(NSString *)consoleMessage date:(NSDate *)date; + +- (id)initWithMessage:(NSString *)message date:(NSDate *)date; + +- (BOOL)isError; +- (NSDate *)messageDate; +- (NSString *)message; + +- (void)setIsError:(BOOL)error; +- (void)setMessageDate:(NSDate *)theDate; +- (void)setMessage:(NSString *)theMessage; + +@end diff --git a/Source/SPConsoleMessage.m b/Source/SPConsoleMessage.m new file mode 100644 index 00000000..d5bdfc41 --- /dev/null +++ b/Source/SPConsoleMessage.m @@ -0,0 +1,84 @@ +// +// SPConsoleMessage.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on Mar 12, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPConsoleMessage.h" + +@implementation SPConsoleMessage + ++ (SPConsoleMessage *)consoleMessageWithMessage:(NSString *)message date:(NSDate *)date +{ + return [[[SPConsoleMessage alloc] initWithMessage:message date:date] autorelease]; +} + +- (id)initWithMessage:(NSString *)consoleMessage date:(NSDate *)date +{ + if ((self = [super init])) { + isError = NO; + messageDate = [date copy]; + message = [[NSString alloc] initWithString:consoleMessage]; + } + + return self; +} + + +- (BOOL)isError +{ + return isError; +} + +- (NSDate *)messageDate +{ + return messageDate; +} + +- (NSString *)message +{ + return message; +} + +- (void)setIsError:(BOOL)error +{ + isError = error; +} + +- (void)setMessageDate:(NSDate *)theDate +{ + if (messageDate) [messageDate release]; + messageDate = [theDate copy]; +} + +- (void)setMessage:(NSString *)theMessage +{ + if (message) [message release]; + message = [[NSString alloc] initWithString:theMessage]; +} + +- (void)dealloc +{ + [message release], message = nil; + [messageDate release], messageDate = nil; + + [super dealloc]; +} + +@end diff --git a/Source/SPEditorTokens.h b/Source/SPEditorTokens.h new file mode 100644 index 00000000..44d0340f --- /dev/null +++ b/Source/SPEditorTokens.h @@ -0,0 +1,19 @@ +/* + * SPEditorTokens.h + * sequel-pro + * + * Created by Jakob on 3/15/09. + * + * This file defines all the tokens used for parsing the source code + */ + +#define SPT_DOUBLE_QUOTED_TEXT 1 +#define SPT_SINGLE_QUOTED_TEXT 2 +#define SPT_BACKTICK_QUOTED_TEXT 3 +#define SPT_RESERVED_WORD 4 +#define SPT_COMMENT 5 +#define SPT_WHITESPACE 6 +#define SPT_WORD 7 +#define SPT_OTHER 8 +#define SPT_NUMERIC 9 +#define SPT_VARIABLE 10 diff --git a/Source/SPEditorTokens.l b/Source/SPEditorTokens.l new file mode 100644 index 00000000..04cf1660 --- /dev/null +++ b/Source/SPEditorTokens.l @@ -0,0 +1,666 @@ +%{ + +/* + * SPEditorTokens.l - created by Jakob on 3/15/09 for Sequel Pro + * + * This is the lex file used for syntax coloring. + * To add new keywords, just add a line where the other + * keywords are and replace spaces with {s} + * + * If you're new to lex and interested what the code below does, I found + * "The Lex And Yacc Page" at http://dinosaur.compilertools.net/ to be + * very helpful. Keep in mind that Xcode actually uses flex, the GNU + * version of lex. There's a very thorough Texinfo manual for flex + * available. (type 'info flex' in the Terminal) + */ + +#import "SPEditorTokens.h" +int utf8strlen(const char * _s); +int yyuoffset, yyuleng; + +#define YY_NO_UNPUT + +//keep track of the current utf-8 character (not byte) offset and token length +#define YY_USER_ACTION { yyuoffset += yyuleng; yyuleng = utf8strlen(yytext); } +%} +%option noyywrap +%option case-insensitive + +s [ \t\n]+ +alpha [a-z_\.À-゚] +numeric ([+-]?(([0-9]+\.[0-9]+)|([0-9]*\.[0-9]+)|([0-9]+))(e[+-]?[0-9]+)?) +ops "+"|"-"|"*"|"/" +word [a-z_\.0-9À-゚@] +variable @{1,2}[a-z_\.0-9À-゚$]+ +nonword [^a-z_0-9À-゚#\n\t] +keyworda (G(R(OUP{s}BY|ANT(S)?)|E(T_FORMAT|OMETRY(COLLECTION)?)|LOBAL)|B(Y(TE)?|TREE|I(GINT|N(LOG|ARY)|T)|O(TH|OL(EAN)?)|E(GIN|TWEEN|FORE)|LOB|ACKUP{s}TABLE)|H(IGH_PRIORITY|O(STS|UR(_(MI(NUTE|CROSECOND)|SECOND))?)|ELP|A(SH|NDLER|VING))|C(R(OSS|EATE)|H(ECK(SUM)?|A(R(SET|ACTER)?|NGE(D)?|IN))|IPHER|O(M(M(IT(TED)?|ENT)|P(RESSED|LETION|ACT))|N(S(TRAINT|ISTENT)|NECTION|CURRENT|T(RIBUTORS|INUE|AINS)|DITION|VERT)|DE|L(UMN(_FORMAT)?|LATE)|ALESCE{s}PARTITION)|U(R(RENT_(TIME(STAMP)?|DATE|USER)|SOR)|BE)|L(IENT|OSE)|A(S(CADE(D)?|E)|CHE{s}INDEX|LL))|I(GNORE|MPORT{s}TABLESPACE|S(SUER|OLATION)?|N(S(TALL|E(RT(_METHOD)?|NSITIVE))|N(O(BASE|DB)|ER)|T(1|2|8|3|O({s}(DUMP|OUT)FILE)?|4|E(RVAL|GER))?|ITIAL_SIZE|OUT|DEX(ES)?|VOKER|FILE)?|TERATE|O_THREAD|DENTIFIED|F)|D(ROP|YNAMIC|I(RECTORY|S(CARD{s}TABLESPACE|TINCT(ROW)?|K|ABLE{s}KEYS)|V)|O(UBLE)?|U(MPFILE|PLICATE|AL)|E(S(C(RIBE)?|_KEY_FILE)|C(IMAL|LARE)?|TERMINISTIC|F(INER|AULT)|L(ETE|AY(_KEY_WRITE|ED))|ALLOCATE)|A(Y(_(MI(NUTE|CROSECOND)|SECOND|HOUR))?|T(E(TIME)?|A(BASE(S)?|FILE)?)))|JOIN|E(RRORS|X(TEN(T_SIZE|DED)|I(STS|T)|P(LAIN|ANSION)|ECUTE)|SCAPE(D{s}BY)?|N(GINE(S)?|CLOSED{s}BY|D(S)?|UM|ABLE{s}KEYS)|VE(RY|NT)|LSE(IF)?|ACH)|K(ILL({s}(CONNECTION|QUERY))?|EY(S|_BLOCK_SIZE)?)|F(R(OM|AC_SECOND)|I(RST|XED|LE)|O(R(CE|EIGN)?|UND)|U(NCTION|LL(TEXT)?)|ETCH|L(OAT(8|4)?|USH)|A(ST|LSE))|A(G(GREGATE|AINST)|S(C(II)?|ENSITIVE)?|N(Y|D|ALYZE)|C(CESSIBLE|TION)|T|DD|UT(HORS|O(_INCREMENT|EXTEND_SIZE))|VG(_ROW_LENGTH)?|FTER|L(GORITHM|TER|L))) +keywordl (R(TREE|IGHT|O(UTINE|W(S|_FORMAT)?|LL(BACK|UP))|E(GEXP|MOVE{s}PARTITIONING|BUILD{s}PARTITION|S(T(RICT|ORE{s}TABLE)|UME|ET)|NAME|COVER|TURN(S)?|ORGANIZE{s}PARTITION|D(O(_BUFFER_SIZE|FILE)|UNDANT)|P(EAT(ABLE)?|L(ICATION|ACE)|AIR)|VOKE|QUIRE|FERENCES|L(OAD|EASE|AY_(THREAD|LOG_(POS|FILE)))|A(D(S|_(ONLY|WRITE))?|L))|LIKE|ANGE)|M(I(GRATE|N(_ROWS|UTE(_(MICROSECOND|SECOND))?)|CROSECOND|DDLEINT)|O(NTH|D(IF(Y|IES)|E)?)|U(TEX|LTI(PO(INT|LYGON)|LINESTRING))|E(RGE|MORY|DIUM(BLOB|TEXT|INT)?)|A(X(_(ROWS|SIZE|CONNECTIONS_PER_HOUR|U(SER_CONNECTIONS|PDATES_PER_HOUR)|QUERIES_PER_HOUR)|VALUE)|STER(_(S(SL(_(C(IPHER|ERT|A(PATH)?)|VERIFY_SERVER_CERT|KEY))?|ERVER_ID)|HOST|CONNECT_RETRY|USER|P(ORT|ASSWORD)|LOG_(POS|FILE)))?|TCH))|N(CHAR|O(NE|_W(RITE_TO_BINLOG|AIT)|T|DEGROUP)?|DB(CLUSTER)?|U(MERIC|LL)|E(XT|W)|VARCHAR|A(ME(S)?|T(IONAL|URAL)))|O(R(DER{s}BY)?|N({s}(DUPLICATE{s}KEY{s}UPDATE)?|E(_SHOT)?|LINE)|UT(ER|FILE)?|P(TI(MIZE|ON(S|ALLY)?)|EN)|FF(SET|LINE)|LD_PASSWORD)|P(R(I(MARY|VILEGES)|OCE(SS|DURE)|E(SERVE|CISION|PARE|V))|HASE|O(INT|LYGON)|URGE|A(R(SER|TI(TION(S|ING)?|AL))|SSWORD|CK_KEYS))|QU(ICK|ERY|ARTER)|L(I(MIT|ST|NE(S(TRING)?|AR)|KE)|O(G(S|FILE({s}GROUP))|NG(BLOB|TEXT)?|C(K(S)?|AL(TIME(STAMP)?)?)|OP|W_PRIORITY|AD{s}(DATA|INDEX{s}INTO{s}CACHE))|E(SS|VEL|FT|A(DING|VE(S)?))|A(ST|NGUAGE))) +keywords (X(OR|509|A)|S(MALLINT|SL|H(OW({s}(E(NGINE(S)?|RRORS)|M(ASTER|UTEX)|BINLOG|GRANTS|INNODB|P(RIVILEGES|ROFILE(S)?|ROCEDURE{s}CODE)|SLAVE{s}(HOSTS|STATUS)|TRIGGERS|VARIABLES|WARNINGS|PROCESSLIST|FIELDS|PLUGIN(S)?|STORAGE{s}ENGINES|TABLE{s}TYPES|CO(LUMNS|LLATION)|BINLOG{s}EVENTS))?|UTDOWN|ARE)|NAPSHOT|CHE(MA(S)?|DULE(R)?)|T(R(ING|AIGHT_JOIN)|O(RAGE|P)|A(RT(S|ING{s}BY)?|TUS))|I(GNED|MPLE)|O(ME|NAME|UNDS)|U(B(JECT|PARTITION(S)?)|SPEND|PER)|P(ECIFIC|ATIAL)|E(RIAL(IZABLE)?|SSION|NSITIVE|C(OND(_MICROSECOND)?|URITY)|T({s}(PASSWORD|NAMES|ONE_SHOT))?|PARATOR|LECT)|QL(STATE|_(B(IG_RESULT|UFFER_RESULT)|SMALL_RESULT|NO_CACHE|CA(CHE|LC_FOUND_ROWS)|T(SI_(M(INUTE|ONTH)|SECOND|HOUR|YEAR|DAY|QUARTER|FRAC_SECOND|WEEK)|HREAD))|EXCEPTION|WARNING)?|LAVE|AVEPOINT)|YEAR(_MONTH)?|T(R(IGGER(S)?|U(NCATE|E)|A(NSACTION|ILING))|H(EN|AN)|YPE|I(ME(STAMP(DIFF|ADD)?)?|NY(BLOB|TEXT|INT))|O|E(RMINATED{s}BY|XT|MP(TABLE|ORARY))|ABLE(S(PACE)?)?)|ZEROFILL|U(S(ING|E(R(_RESOURCES)?|_FRM)?|AGE)|N(SIGNED|COMMITTED|TIL|I(NSTALL|CODE|ON|QUE)|D(O(_BUFFER_SIZE|FILE)?|EFINED)|KNOWN|LOCK)|TC_(TIME(STAMP)?|DATE)|P(GRADE|DATE))|V(IEW|A(R(BINARY|YING|CHAR(ACTER)?|IABLES)|LUE(S)?))|W(RITE|H(ILE|E(RE|N))|ITH({s}PARSER)?|ORK|EEK|A(RNINGS|IT))) + + +%x comment +%x equation +%x varequation +%% +\"([^"\\]|\\(.|\n))*\"? { return SPT_DOUBLE_QUOTED_TEXT; } /* double quoted strings */ +'([^'\\]|\\(.|\n))*'? { return SPT_SINGLE_QUOTED_TEXT; } /* single quoted strings */ +`[^`]*`? { return SPT_BACKTICK_QUOTED_TEXT; } /* identifier quoting */ + +"/*" { BEGIN(comment); return SPT_COMMENT; } /* beginning of a c style comment */ +[^*]* { return SPT_COMMENT; } /* anything except * in a c cmnt */ +"*"+ { return SPT_COMMENT; } /* a range of * */ +"*"+"/" { BEGIN(INITIAL); return SPT_COMMENT; } /* a range of * with trailing / + Thanks to John Dickinson for publishing + this method of parsing C comments on + http://www.stillhq.com/pdfdb/000561/data.pdf + */ + +#[^\n]*\n? | /* # Comments */ +--[ \t][^\n]*\n? { return SPT_COMMENT; } /* -- Comments */ + +{variable}/{ops} { BEGIN(varequation); return SPT_VARIABLE; }/* SQL variables before operator*/ +{ops} { BEGIN(INITIAL); return SPT_OTHER; } +{variable} { return SPT_VARIABLE; } /* SQL variables */ + +{numeric}/{ops} { BEGIN(equation); return SPT_NUMERIC; } /* numeric before operator */ +{ops} { BEGIN(INITIAL); return SPT_OTHER; } /* set operator after a numeric */ +{numeric}/{alpha} { return SPT_WORD; } /* catch numeric followed by char */ + +{s}+ { return SPT_WHITESPACE; } /* ignore spaces */ + +{keyworda} { return SPT_RESERVED_WORD; } /* all the mysql reserved words */ +{keywordl} { return SPT_RESERVED_WORD; } /* all the mysql reserved words */ +{keywords} { return SPT_RESERVED_WORD; } /* all the mysql reserved words */ + + +{numeric} { return SPT_NUMERIC; } /* single numeric value */ + +{word}+ { return SPT_WORD; } /* return any word */ + +{nonword} { return SPT_OTHER; } /* return anything else */ + + + +<> { + BEGIN(INITIAL); /* make sure we return to initial state when finished! */ + yy_delete_buffer(YY_CURRENT_BUFFER); + return 0; + } +%% + +#define ONEMASK ((size_t)(-1) / 0xFF) +// adapted from http://www.daemonology.net/blog/2008-06-05-faster-utf8-strlen.html +int utf8strlen(const char * _s) +{ + const char * s; + size_t count = 0; + size_t u; + unsigned char b; + + /* Handle any initial misaligned bytes. */ + for (s = _s; (uintptr_t)(s) & (sizeof(size_t) - 1); s++) { + b = *s; + + /* Exit if we hit a zero byte. */ + if (b == '\0') + goto done; + + /* Is this byte NOT the first byte of a character? */ + count += (b >> 7) & ((~b) >> 6); + } + + /* Handle complete blocks. */ + for (; ; s += sizeof(size_t)) { + /* Prefetch 256 bytes ahead. */ + __builtin_prefetch(&s[256], 0, 0); + + /* Grab 4 or 8 bytes of UTF-8 data. */ + u = *(size_t *)(s); + + /* Exit the loop if there are any zero bytes. */ + if ((u - ONEMASK) & (~u) & (ONEMASK * 0x80)) + break; + + /* Count bytes which are NOT the first byte of a character. */ + u = ((u & (ONEMASK * 0x80)) >> 7) & ((~u) >> 6); + count += (u * ONEMASK) >> ((sizeof(size_t) - 1) * 8); + } + + /* Take care of any left-over bytes. */ + for (; ; s++) { + b = *s; + + /* Exit if we hit a zero byte. */ + if (b == '\0') + break; + + /* Is this byte NOT the first byte of a character? */ + count += (b >> 7) & ((~b) >> 6); + } + +done: + return ((s - _s) - count); +} + +/* un-optimized keywords: +ACCESSIBLE +ACTION +ADD +AFTER +AGAINST +AGGREGATE +ALGORITHM +ALL +ALTER +ANALYZE +AND +ANY +AS +ASC +ASCII +ASENSITIVE +AT +AUTHORS +AUTOEXTEND_SIZE +AUTO_INCREMENT +AVG +AVG_ROW_LENGTH +BACKUP{s}TABLE +BEFORE +BEGIN +BETWEEN +BIGINT +BINARY +BINLOG +BIT +BLOB +BOOL +BOOLEAN +BOTH +BTREE +BY +BYTE +CACHE{s}INDEX +CALL +CASCADE +CASCADED +CASE +CHAIN +CHANGE +CHANGED +CHAR +CHARACTER +CHARSET +CHECK +CHECKSUM +CIPHER +CLIENT +CLOSE +COALESCE{s}PARTITION +CODE +COLLATE +COLUMN +COLUMN_FORMAT +COMMENT +COMMIT +COMMITTED +COMPACT +COMPLETION +COMPRESSED +CONCURRENT +CONDITION +CONNECTION +CONSISTENT +CONSTRAINT +CONTAINS +CONTINUE +CONTRIBUTORS +CONVERT +CREATE +CROSS +CUBE +CURRENT_DATE +CURRENT_TIME +CURRENT_TIMESTAMP +CURRENT_USER +CURSOR +DATA +DATABASE +DATABASES +DATAFILE +DATE +DATETIME +DAY +DAY_HOUR +DAY_MICROSECOND +DAY_MINUTE +DAY_SECOND +DEALLOCATE +DEC +DECIMAL +DECLARE +DEFAULT +DEFINER +DELAYED +DELAY_KEY_WRITE +DELETE +DESC +DESCRIBE +DES_KEY_FILE +DETERMINISTIC +DIRECTORY +DISABLE{s}KEYS +DISCARD{s}TABLESPACE +DISK +DISTINCT +DISTINCTROW +DIV +DO +DOUBLE +DROP +DUAL +DUMPFILE +DUPLICATE +DYNAMIC +EACH +ELSE +ELSEIF +ENABLE{s}KEYS +ENCLOSED{s}BY +END +ENDS +ENGINE +ENGINES +ENUM +ERRORS +ESCAPE +ESCAPED{s}BY +EVENT +EVERY +EXECUTE +EXISTS +EXIT +EXPANSION +EXPLAIN +EXTENDED +EXTENT_SIZE +FALSE +FAST +FETCH +FILE +FIRST +FIXED +FLOAT +FLOAT4 +FLOAT8 +FLUSH +FOR +FORCE +FOREIGN +FOUND +FRAC_SECOND +FROM +FULL +FULLTEXT +FUNCTION +GEOMETRY +GEOMETRYCOLLECTION +GET_FORMAT +GLOBAL +GRANT +GRANTS +GROUP{s}BY +HANDLER +HASH +HAVING +HELP +HIGH_PRIORITY +HOSTS +HOUR +HOUR_MICROSECOND +HOUR_MINUTE +HOUR_SECOND +IDENTIFIED +IF +IGNORE +IMPORT{s}TABLESPACE +IN +INDEX +INDEXES +INFILE +INITIAL_SIZE +INNER +INNOBASE +INNODB +INOUT +INSENSITIVE +INSERT +INSERT_METHOD +INSTALL +INT +INT1 +INT2 +INT3 +INT4 +INT8 +INTEGER +INTERVAL +INTO({s}(DUMP|OUT)FILE)? +INVOKER +IO_THREAD +IS +ISOLATION +ISSUER +ITERATE +JOIN +KEY +KEYS +KEY_BLOCK_SIZE +KILL({s}(CONNECTION|QUERY))? +LANGUAGE +LAST +LEADING +LEAVE +LEAVES +LEFT +LESS +LEVEL +LIKE +LIMIT +LINEAR +LINES +LINESTRING +LIST +LOAD{s}(DATA|INDEX{s}INTO{s}CACHE) +LOCAL +LOCALTIME +LOCALTIMESTAMP +LOCK +LOCKS +LOGFILE({s}GROUP) +LOGS +LONG +LONGBLOB +LONGTEXT +LOOP +LOW_PRIORITY +MASTER +MASTER_CONNECT_RETRY +MASTER_HOST +MASTER_LOG_FILE +MASTER_LOG_POS +MASTER_PASSWORD +MASTER_PORT +MASTER_SERVER_ID +MASTER_SSL +MASTER_SSL_CA +MASTER_SSL_CAPATH +MASTER_SSL_CERT +MASTER_SSL_CIPHER +MASTER_SSL_KEY +MASTER_SSL_VERIFY_SERVER_CERT +MASTER_USER +MATCH +MAXVALUE +MAX_CONNECTIONS_PER_HOUR +MAX_QUERIES_PER_HOUR +MAX_ROWS +MAX_SIZE +MAX_UPDATES_PER_HOUR +MAX_USER_CONNECTIONS +MEDIUM +MEDIUMBLOB +MEDIUMINT +MEDIUMTEXT +MEMORY +MERGE +MICROSECOND +MIDDLEINT +MIGRATE +MINUTE +MINUTE_MICROSECOND +MINUTE_SECOND +MIN_ROWS +MOD +MODE +MODIFIES +MODIFY +MONTH +MULTILINESTRING +MULTIPOINT +MULTIPOLYGON +MUTEX +NAME +NAMES +NATIONAL +NATURAL +NCHAR +NDB +NDBCLUSTER +NEW +NEXT +NO +NODEGROUP +NONE +NOT +NO_WAIT +NO_WRITE_TO_BINLOG +NULL +NUMERIC +NVARCHAR +OFFLINE +OFFSET +OLD_PASSWORD +ONE +ONE_SHOT +ONLINE +ON{s}(DUPLICATE{s}KEY{s}UPDATE)? +OPEN +OPTIMIZE +OPTION +OPTIONS +OPTIONALLY +OR +ORDER{s}BY +OUT +OUTER +OUTFILE +PACK_KEYS +PARSER +PARTIAL +PARTITION +PARTITIONING +PARTITIONS +PASSWORD +PHASE +POINT +POLYGON +PRECISION +PREPARE +PRESERVE +PREV +PRIMARY +PRIVILEGES +PROCEDURE +PROCESS +PURGE +QUARTER +QUERY +QUICK +RANGE +READ +READS +READ_ONLY +READ_WRITE +REAL +REBUILD{s}PARTITION +RECOVER +REDOFILE +REDO_BUFFER_SIZE +REDUNDANT +REFERENCES +REGEXP +RELAY_LOG_FILE +RELAY_LOG_POS +RELAY_THREAD +RELEASE +RELOAD +REMOVE{s}PARTITIONING +RENAME +REORGANIZE{s}PARTITION +REPAIR +REPEAT +REPEATABLE +REPLACE +REPLICATION +REQUIRE +RESET +RESTORE{s}TABLE +RESTRICT +RESUME +RETURN +RETURNS +REVOKE +RIGHT +RLIKE +ROLLBACK +ROLLUP +ROUTINE +ROW +ROWS +ROW_FORMAT +RTREE +SAVEPOINT +SCHEDULE +SCHEDULER +SCHEMA +SCHEMAS +SECOND +SECOND_MICROSECOND +SECURITY +SELECT +SENSITIVE +SEPARATOR +SERIAL +SERIALIZABLE +SESSION +SET({s}(PASSWORD|NAMES|ONE_SHOT))? +SHARE +SHOW({s}(E(NGINE(S)?|RRORS)|M(ASTER|UTEX)|BINLOG|GRANTS|INNODB|P(RIVILEGES|ROFILE(S)?|ROCEDURE{s}CODE)|SLAVE{s}(HOSTS|STATUS)|TRIGGERS|VARIABLES|WARNINGS|PROCESSLIST|FIELDS|PLUGIN(S)?|STORAGE{s}ENGINES|TABLE{s}TYPES|CO(LUMNS|LLATION)|BINLOG{s}EVENTS))? +SHUTDOWN +SIGNED +SIMPLE +SLAVE +SMALLINT +SNAPSHOT +SOME +SONAME +SOUNDS +SPATIAL +SPECIFIC +SQL +SQLEXCEPTION +SQLSTATE +SQLWARNING +SQL_BIG_RESULT +SQL_BUFFER_RESULT +SQL_CACHE +SQL_CALC_FOUND_ROWS +SQL_NO_CACHE +SQL_SMALL_RESULT +SQL_THREAD +SQL_TSI_DAY +SQL_TSI_FRAC_SECOND +SQL_TSI_HOUR +SQL_TSI_MINUTE +SQL_TSI_MONTH +SQL_TSI_QUARTER +SQL_TSI_SECOND +SQL_TSI_WEEK +SQL_TSI_YEAR +SSL +START +STARTING{s}BY +STARTS +STATUS +STOP +STORAGE +STRAIGHT_JOIN +STRING +SUBJECT +SUBPARTITION +SUBPARTITIONS +SUPER +SUSPEND +TABLE +TABLES +TABLESPACE +TEMPORARY +TEMPTABLE +TERMINATED{s}BY +TEXT +THAN +THEN +TIME +TIMESTAMP +TIMESTAMPADD +TIMESTAMPDIFF +TINYBLOB +TINYINT +TINYTEXT +TO +TRAILING +TRANSACTION +TRIGGER +TRIGGERS +TRUE +TRUNCATE +TYPE +UNCOMMITTED +UNDEFINED +UNDO +UNDOFILE +UNDO_BUFFER_SIZE +UNICODE +UNINSTALL +UNION +UNIQUE +UNKNOWN +UNLOCK +UNSIGNED +UNTIL +UPDATE +UPGRADE +USAGE +USE +USER +USER_RESOURCES +USE_FRM +USING +UTC_DATE +UTC_TIME +UTC_TIMESTAMP +VALUE +VALUES +VARBINARY +VARCHAR +VARCHARACTER +VARIABLES +VARYING +VIEW +WAIT +WARNINGS +WEEK +WHEN +WHERE +WHILE +WITH({s}PARSER)? +WORK +WRITE +X509 +XA +XOR +YEAR +YEAR_MONTH +ZEROFILL +*/ diff --git a/Source/SPExportController.h b/Source/SPExportController.h new file mode 100644 index 00000000..ee2b651e --- /dev/null +++ b/Source/SPExportController.h @@ -0,0 +1,91 @@ +// +// SPExportController.h +// sequel-pro +// +// Created by Ben Perry (benperry.com.au) on 21/02/09. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import +#import "CMMCPConnection.h" +#import "CMMCPResult.h" + +@interface SPExportController : NSObject { + + // Table Document + IBOutlet id tableDocumentInstance; + IBOutlet id tableWindow; + + // Tables List + IBOutlet id tablesListInstance; + + // Export Window + IBOutlet id exportWindow; + IBOutlet id exportToolbar; + IBOutlet id exportTableList; + IBOutlet id exportTabBar; + IBOutlet id exportInputMatrix; + IBOutlet id exportFilePerTableCheck; + IBOutlet id exportFilePerTableNote; + + // SQL + IBOutlet id exportSQLIncludeStructureCheck; + IBOutlet id exportSQLIncludeDropSyntaxCheck; + IBOutlet id exportSQLIncludeErrorsCheck; + + // Excel + IBOutlet id exportExcelSheetOrFilePerTableMatrix; + + // CSV + IBOutlet id exportCSVIncludeFieldNamesCheck; + IBOutlet id exportCSVFieldsTerminatedField; + IBOutlet id exportCSVFieldsWrappedField; + IBOutlet id exportCSVFieldsEscapedField; + IBOutlet id exportCSVLinesTerminatedField; + + // HTML + IBOutlet id exportHTMLIncludeStructureCheck; + IBOutlet id exportHTMLIncludeHeadAndBodyTagsCheck; + + // XML + IBOutlet id exportXMLIncludeStructureCheck; + + // PDF + IBOutlet id exportPDFIncludeStructureCheck; + + // Token Name View + IBOutlet id tokenNameView; + IBOutlet id tokenNameField; + IBOutlet id tokenNameTokensField; + IBOutlet id exampleNameLabel; + + // Local Variables + CMMCPConnection *mySQLConnection; + NSMutableArray *tables; +} + +// Export Methods +- (void)export; +- (IBAction)closeSheet:(id)sender; + +// Utility Methods +- (void)setConnection:(CMMCPConnection *)theConnection; +- (void)loadTables; +- (IBAction)switchTab:(id)sender; +- (IBAction)switchInput:(id)sender; + +@end diff --git a/Source/SPExportController.m b/Source/SPExportController.m new file mode 100644 index 00000000..5979d166 --- /dev/null +++ b/Source/SPExportController.m @@ -0,0 +1,178 @@ +// +// SPExportController.m +// sequel-pro +// +// Created by Ben Perry (benperry.com.au) on 21/02/09. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPExportController.h" +#import "TablesList.h" + +@implementation SPExportController + +#pragma mark - +#pragma mark Export Methods + +-(void)export +{ + if ([NSBundle loadNibNamed:@"ExportDialog" owner:self]) { + [self loadTables]; + [NSApp beginSheet:exportWindow modalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; + } +} + +- (IBAction)closeSheet:(id)sender +{ + [NSApp endSheet:exportWindow]; + [NSApp stopModalWithCode:[sender tag]]; +} + +- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + [sheet orderOut:self]; +} + +#pragma mark - +#pragma mark Utility Methods + +- (void)setConnection:(CMMCPConnection *)theConnection +{ + mySQLConnection = theConnection; +} + +- (void)loadTables +{ + CMMCPResult *queryResult; + int i; + + [tables removeAllObjects]; + queryResult = (CMMCPResult *)[mySQLConnection listTables]; + + if ([queryResult numOfRows]) + [queryResult dataSeek:0]; + + for ( i = 0 ; i < [queryResult numOfRows] ; i++ ) { + [tables addObject:[NSMutableArray arrayWithObjects: + [NSNumber numberWithBool:YES], + [[queryResult fetchRowAsArray] objectAtIndex:0], + nil + ]]; + } + + [exportTableList reloadData]; +} + +- (IBAction)switchTab:(id)sender +{ + if ([sender isKindOfClass:[NSToolbarItem class]]) { + [exportTabBar selectTabViewItemWithIdentifier:[[sender label] lowercaseString]]; + + [exportFilePerTableCheck setHidden:[[sender label] isEqualToString:@"Excel"]]; + [exportFilePerTableNote setHidden:[[sender label] isEqualToString:@"Excel"]]; + } +} + +- (IBAction)switchInput:(id)sender +{ + if ([sender isKindOfClass:[NSMatrix class]]) { + [exportTableList setEnabled:([[sender selectedCell] tag] == 3)]; + } +} + +#pragma mark - +#pragma mark Table View Datasource methods + +- (int)numberOfRowsInTableView:(NSTableView *)aTableView; +{ + return [tables count]; +} + +- (id)tableView:(NSTableView *)aTableView +objectValueForTableColumn:(NSTableColumn *)aTableColumn + row:(int)rowIndex +{ + id returnObject = nil; + + if ( [[aTableColumn identifier] isEqualToString:@"switch"] ) { + returnObject = [[tables objectAtIndex:rowIndex] objectAtIndex:0]; + } else { + returnObject = [[tables objectAtIndex:rowIndex] objectAtIndex:1]; + } + + return returnObject; +} + +- (void)tableView:(NSTableView *)aTableView + setObjectValue:(id)anObject + forTableColumn:(NSTableColumn *)aTableColumn + row:(int)rowIndex +{ + [[tables objectAtIndex:rowIndex] replaceObjectAtIndex:0 withObject:anObject]; +} + +#pragma mark - +#pragma mark Table View Delegate methods + +- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex +{ + return (aTableView != exportTableList); +} + +- (BOOL)tableView:(NSTableView *)aTableView shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row +{ + return (aTableView == exportTableList); +} + +- (void)tableView:(NSTableView *)aTableView + willDisplayCell:(id)aCell + forTableColumn:(NSTableColumn *)aTableColumn + row:(int)rowIndex +{ + [aCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; +} + +#pragma mark - +#pragma mark Toolbar Delegate Methods + +- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar +{ + NSArray *array = [toolbar items]; + NSMutableArray *items = [NSMutableArray arrayWithCapacity:6]; + int i; + + for (i = 0; i < [array count]; i++) { + NSToolbarItem *item = [array objectAtIndex:i]; + [items addObject:[item itemIdentifier]]; + } + + return items; +} + +- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem +{ + return YES; +} + +- (id)init; +{ + self = [super init]; + tables = [[NSMutableArray alloc] init]; + return self; +} + +@end diff --git a/Source/SPFavoriteTextFieldCell.h b/Source/SPFavoriteTextFieldCell.h new file mode 100644 index 00000000..c6c597fa --- /dev/null +++ b/Source/SPFavoriteTextFieldCell.h @@ -0,0 +1,44 @@ +// +// SPFavoriteTextFieldCell.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on Dec 29, 2008 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import +#import "ImageAndTextCell.h" + +@interface SPFavoriteTextFieldCell : ImageAndTextCell +{ + NSString *favoriteName; + NSString *favoriteHost; + + NSColor *mainStringColor; + NSColor *subStringColor; +} + +- (NSString *)favoriteName; +- (void)setFavoriteName:(NSString *)name; + +- (NSString *)favoriteHost; +- (void)setFavoriteHost:(NSString *)host; + +- (void)invertFontColors; +- (void)restoreFontColors; + +@end diff --git a/Source/SPFavoriteTextFieldCell.m b/Source/SPFavoriteTextFieldCell.m new file mode 100644 index 00000000..897ad278 --- /dev/null +++ b/Source/SPFavoriteTextFieldCell.m @@ -0,0 +1,239 @@ +// +// SPFavoriteTextFieldCell.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on Dec 29, 2008 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPFavoriteTextFieldCell.h" + +#define FAVORITE_NAME_FONT_SIZE 12.0 + +@interface SPFavoriteTextFieldCell (PrivateAPI) + +- (NSAttributedString *)constructSubStringAttributedString; +- (NSAttributedString *)attributedStringForFavoriteName; +- (NSDictionary *)mainStringAttributedStringAttributes; +- (NSDictionary *)subStringAttributedStringAttributes; + +@end + +@implementation SPFavoriteTextFieldCell + +// ------------------------------------------------------------------------------- +// init +// ------------------------------------------------------------------------------- +- (id)init +{ + if ((self = [super init])) { + mainStringColor = [NSColor blackColor]; + subStringColor = [NSColor grayColor]; + } + + return self; +} + +// ------------------------------------------------------------------------------- +// copyWithZone: +// ------------------------------------------------------------------------------- +- (id)copyWithZone:(NSZone *)zone +{ + SPFavoriteTextFieldCell *cell = (SPFavoriteTextFieldCell *)[super copyWithZone:zone]; + + cell->favoriteName = nil; + + cell->favoriteName = [favoriteName retain]; + + return cell; +} + +// ------------------------------------------------------------------------------- +// favoriteName +// +// Get the cell's favorite name. +// ------------------------------------------------------------------------------- +- (NSString *)favoriteName +{ + return favoriteName; +} + +// ------------------------------------------------------------------------------- +// setFavoriteName: +// +// Set the cell's favorite name to the supplied name. +// ------------------------------------------------------------------------------- +- (void)setFavoriteName:(NSString *)name +{ + if (favoriteName != name) { + [favoriteName release]; + favoriteName = [name retain]; + } +} + +// ------------------------------------------------------------------------------- +// favoriteHost +// +// Get the cell's favorite host. +// ------------------------------------------------------------------------------- +- (NSString *)favoriteHost +{ + return favoriteHost; +} + +// ------------------------------------------------------------------------------- +// setFavoriteHost: +// +// Set the cell's favorite host to the supplied name. +// ------------------------------------------------------------------------------- +- (void)setFavoriteHost:(NSString *)host +{ + if (favoriteHost != host) { + [favoriteHost release]; + favoriteHost = [host retain]; + } +} + +// ------------------------------------------------------------------------------- +// drawInteriorWithFrame:inView: +// +// Draws the actual cell. +// ------------------------------------------------------------------------------- +- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + (([self isHighlighted]) && (![[self highlightColorWithFrame:cellFrame inView:controlView] isEqualTo:[NSColor secondarySelectedControlColor]])) ? [self invertFontColors] : [self restoreFontColors]; + + // Construct and get the sub text attributed string + NSAttributedString *mainString = [[self attributedStringForFavoriteName] autorelease]; + NSAttributedString *subString = [[self constructSubStringAttributedString] autorelease]; + + NSRect subFrame = NSMakeRect(0.0, 0.0, [subString size].width, [subString size].height); + + // Total height of both strings with a 2 pixel separation space + float totalHeight = [mainString size].height + [subString size].height + 1.0; + + cellFrame.origin.y += (cellFrame.size.height - totalHeight) / 2.0; + cellFrame.origin.x += 10.0; // Indent main string from image + + // Position the sub text's frame rect + subFrame.origin.y = [mainString size].height + cellFrame.origin.y + 1.0; + subFrame.origin.x = cellFrame.origin.x; + + cellFrame.size.height = totalHeight; + + int i; + float maxWidth = cellFrame.size.width; + float mainStringWidth = [mainString size].width; + float subStringWidth = [subString size].width; + + if (maxWidth < mainStringWidth) { + for (i = 0; i <= [mainString length]; i++) { + if ([[mainString attributedSubstringFromRange:NSMakeRange(0, i)] size].width >= maxWidth) { + mainString = [[[NSMutableAttributedString alloc] initWithString:[[[mainString attributedSubstringFromRange:NSMakeRange(0, i - 3)] string] stringByAppendingString:@"..."] attributes:[self mainStringAttributedStringAttributes]] autorelease]; + } + } + } + + if (maxWidth < subStringWidth) { + for (i = 0; i <= [subString length]; i++) { + if ([[subString attributedSubstringFromRange:NSMakeRange(0, i)] size].width >= maxWidth) { + subString = [[[NSMutableAttributedString alloc] initWithString:[[[subString attributedSubstringFromRange:NSMakeRange(0, i - 3)] string] stringByAppendingString:@"..."] attributes:[self subStringAttributedStringAttributes]] autorelease]; + } + } + } + + [mainString drawInRect:cellFrame]; + [subString drawInRect:subFrame]; +} + +// ------------------------------------------------------------------------------- +// invertFontColors +// +// Inverts the displayed font colors when the cell is selected. +// ------------------------------------------------------------------------------- +- (void)invertFontColors +{ + mainStringColor = [NSColor whiteColor]; + subStringColor = [NSColor whiteColor]; +} + +// ------------------------------------------------------------------------------- +// restoreFontColors +// +// Restores the displayed font colors once the cell is no longer selected. +// ------------------------------------------------------------------------------- +- (void)restoreFontColors +{ + mainStringColor = [NSColor blackColor]; + subStringColor = [NSColor grayColor]; +} + +// ------------------------------------------------------------------------------- +// dealloc +// ------------------------------------------------------------------------------- +- (void)dealloc +{ + [favoriteName release], favoriteName = nil; + + [super dealloc]; +} + +@end + +@implementation SPFavoriteTextFieldCell (PrivateAPI) + +// ------------------------------------------------------------------------------- +// constructSubStringAttributedString +// +// Constructs the attributed string to be used as the cell's substring. +// ------------------------------------------------------------------------------- +- (NSAttributedString *)constructSubStringAttributedString +{ + return [[NSAttributedString alloc] initWithString:favoriteHost attributes:[self subStringAttributedStringAttributes]]; +} + +// ------------------------------------------------------------------------------- +// attributedStringForFavoriteName +// +// Constructs the attributed string for the cell's favorite name. +// ------------------------------------------------------------------------------- +- (NSAttributedString *)attributedStringForFavoriteName +{ + return [[NSAttributedString alloc] initWithString:favoriteName attributes:[self mainStringAttributedStringAttributes]]; +} + +// ------------------------------------------------------------------------------- +// mainStringAttributedStringAttributes +// +// Returns the attributes of the cell's main string. +// ------------------------------------------------------------------------------- +- (NSDictionary *)mainStringAttributedStringAttributes +{ + return [NSDictionary dictionaryWithObjectsAndKeys:mainStringColor, NSForegroundColorAttributeName, [NSFont systemFontOfSize:FAVORITE_NAME_FONT_SIZE], NSFontAttributeName, nil]; +} + +// ------------------------------------------------------------------------------- +// subStringAttributedStringAttributes +// +// Returns the attributes of the cell's sub string. +// ------------------------------------------------------------------------------- +- (NSDictionary *)subStringAttributedStringAttributes +{ + return [NSDictionary dictionaryWithObjectsAndKeys:subStringColor, NSForegroundColorAttributeName, [NSFont systemFontOfSize:[NSFont smallSystemFontSize]], NSFontAttributeName, nil]; +} + +@end diff --git a/Source/SPGrowlController.h b/Source/SPGrowlController.h index 2ad73e8b..7b604e30 100644 --- a/Source/SPGrowlController.h +++ b/Source/SPGrowlController.h @@ -30,5 +30,6 @@ // Post notification - (void)notifyWithTitle:(NSString *)title description:(NSString *)description notificationName:(NSString *)name; +- (void)notifyWithTitle:(NSString *)title description:(NSString *)description notificationName:(NSString *)name iconData:(NSData *)data priority:(int)priority isSticky:(BOOL)sticky clickContext:(id)clickContext; @end diff --git a/Source/SPGrowlController.m b/Source/SPGrowlController.m index 3f429067..853619ff 100644 --- a/Source/SPGrowlController.m +++ b/Source/SPGrowlController.m @@ -26,11 +26,9 @@ static SPGrowlController *sharedGrowlController = nil; @implementation SPGrowlController -// ------------------------------------------------------------------------------- -// sharedGrowlController -// -// Returns the shared Growl controller. -// ------------------------------------------------------------------------------- +/* + * Returns the shared Growl controller. + */ + (SPGrowlController *)sharedGrowlController { @synchronized(self) { @@ -42,9 +40,6 @@ static SPGrowlController *sharedGrowlController = nil; return sharedGrowlController; } -// ------------------------------------------------------------------------------- -// allocWithZone: -// ------------------------------------------------------------------------------- + (id)allocWithZone:(NSZone *)zone { @synchronized(self) { @@ -58,9 +53,6 @@ static SPGrowlController *sharedGrowlController = nil; return nil; // On subsequent allocation attempts return nil } -// ------------------------------------------------------------------------------- -// init -// ------------------------------------------------------------------------------- - (id)init { if (self = [super init]) { @@ -70,10 +62,9 @@ static SPGrowlController *sharedGrowlController = nil; return self; } -// ------------------------------------------------------------------------------- -// The following base protocol methods are implemented to ensure the singleton -// status of this class. -// ------------------------------------------------------------------------------- +/* + * The following base protocol methods are implemented to ensure the singleton status of this class. + */ - (id)copyWithZone:(NSZone *)zone { return self; } @@ -85,39 +76,35 @@ static SPGrowlController *sharedGrowlController = nil; - (void)release { } -// ------------------------------------------------------------------------------- -// notifyWithTitle:description:notificationName: -// -// Posts a Growl notification using the supplied details and default values. -// ------------------------------------------------------------------------------- +/* + * Posts a Growl notification using the supplied details and default values. + */ - (void)notifyWithTitle:(NSString *)title description:(NSString *)description notificationName:(NSString *)name { - // Post notification - [GrowlApplicationBridge notifyWithTitle:title - description:description - notificationName:name - iconData:nil - priority:0 - isSticky:NO - clickContext:nil]; + [self notifyWithTitle:title + description:description + notificationName:name + iconData:nil + priority:0 + isSticky:NO + clickContext:nil]; } -// ------------------------------------------------------------------------------- -// notifyWithTitle:description:notificationName: -// -// Posts a Growl notification using the supplied details and effectively ignoring -// the default values. -// ------------------------------------------------------------------------------- +/* + * Posts a Growl notification using the supplied details and effectively ignoring the default values. + */ - (void)notifyWithTitle:(NSString *)title description:(NSString *)description notificationName:(NSString *)name iconData:(NSData *)data priority:(int)priority isSticky:(BOOL)sticky clickContext:(id)clickContext { - // Post notification - [GrowlApplicationBridge notifyWithTitle:title - description:description - notificationName:name - iconData:data - priority:priority - isSticky:sticky - clickContext:clickContext]; + // Post notification only if preference is set + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GrowlEnabled"]) { + [GrowlApplicationBridge notifyWithTitle:title + description:description + notificationName:name + iconData:data + priority:priority + isSticky:sticky + clickContext:clickContext]; + } } @end diff --git a/Source/SPPreferenceController.h b/Source/SPPreferenceController.h new file mode 100644 index 00000000..cb12cde1 --- /dev/null +++ b/Source/SPPreferenceController.h @@ -0,0 +1,84 @@ +// +// SPPreferenceController.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on Dec 10, 2008 +// Modified by Ben Perry (benperry.com.au) on Mar 28, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import + +@class KeyChain; + +@interface SPPreferenceController : NSWindowController +{ + IBOutlet NSWindow *preferencesWindow; + + IBOutlet NSView *generalView; + IBOutlet NSView *notificationsView; + IBOutlet NSView *tablesView; + IBOutlet NSView *favoritesView; + IBOutlet NSView *autoUpdateView; + IBOutlet NSView *networkView; + + IBOutlet NSPopUpButton *defaultFavoritePopup; + + IBOutlet NSTableView *favoritesTableView; + IBOutlet NSArrayController *favoritesController; + + IBOutlet NSTextField *nameField; + IBOutlet NSTextField *hostField; + IBOutlet NSTextField *userField; + IBOutlet NSTextField *databaseField; + IBOutlet NSSecureTextField *passwordField; + KeyChain *keychain; + + NSToolbar *toolbar; + + NSToolbarItem *generalItem; + NSToolbarItem *notificationsItem; + NSToolbarItem *tablesItem; + NSToolbarItem *favoritesItem; + NSToolbarItem *autoUpdateItem; + NSToolbarItem *networkItem; + + NSUserDefaults *prefs; +} + +- (void)applyRevisionChanges; + +// IBAction methods +- (IBAction)addFavorite:(id)sender; +- (IBAction)removeFavorite:(id)sender; +- (IBAction)duplicateFavorite:(id)sender; +- (IBAction)saveFavorite:(id)sender; +- (IBAction)updateDefaultFavorite:(id)sender; + +// Toolbar item IBAction methods +- (IBAction)displayGeneralPreferences:(id)sender; +- (IBAction)displayTablePreferences:(id)sender; +- (IBAction)displayFavoritePreferences:(id)sender; +- (IBAction)displayNotificationPreferences:(id)sender; +- (IBAction)displayAutoUpdatePreferences:(id)sender; +- (IBAction)displayNetworkPreferences:(id)sender; + +// Other +- (void)updateDefaultFavoritePopup; +- (void)selectFavorites:(NSArray *)favorite; + +@end diff --git a/Source/SPPreferenceController.m b/Source/SPPreferenceController.m new file mode 100644 index 00000000..dbe9fb82 --- /dev/null +++ b/Source/SPPreferenceController.m @@ -0,0 +1,842 @@ +// +// SPPreferenceController.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on Dec 10, 2008 +// Modified by Ben Perry (benperry.com.au) on Mar 28, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPPreferenceController.h" +#import "SPWindowAdditions.h" +#import "SPFavoriteTextFieldCell.h" +#import "KeyChain.h" + +#define FAVORITES_PB_DRAG_TYPE @"SequelProPreferencesPasteboard" + +#define PREFERENCE_TOOLBAR_GENERAL @"Preference Toolbar General" +#define PREFERENCE_TOOLBAR_TABLES @"Preference Toolbar Tables" +#define PREFERENCE_TOOLBAR_FAVORITES @"Preference Toolbar Favorites" +#define PREFERENCE_TOOLBAR_NOTIFICATIONS @"Preference Toolbar Notifications" +#define PREFERENCE_TOOLBAR_AUTOUPDATE @"Preference Toolbar Auto Update" +#define PREFERENCE_TOOLBAR_NETWORK @"Preference Toolbar Network" + +#pragma mark - + +@interface SPPreferenceController (PrivateAPI) + +- (void)_setupToolbar; +- (void)_resizeWindowForContentView:(NSView *)view; + +@end + +#pragma mark - + +@implementation SPPreferenceController + +// ------------------------------------------------------------------------------- +// init +// ------------------------------------------------------------------------------- +- (id)init +{ + if (self = [super initWithWindowNibName:@"Preferences"]) { + prefs = [NSUserDefaults standardUserDefaults]; + [self applyRevisionChanges]; + } + return self; +} + +// ------------------------------------------------------------------------------- +// windowDidLoad +// ------------------------------------------------------------------------------- +- (void)windowDidLoad +{ + [self _setupToolbar]; + + keychain = [[KeyChain alloc] init]; + + SPFavoriteTextFieldCell *tableCell = [[[SPFavoriteTextFieldCell alloc] init] autorelease]; + + [tableCell setImage:[NSImage imageNamed:@"database"]]; + + // Replace column's NSTextFieldCell with custom SWProfileTextFieldCell + [[[favoritesTableView tableColumns] objectAtIndex:0] setDataCell:tableCell]; + + [favoritesTableView registerForDraggedTypes:[NSArray arrayWithObject:FAVORITES_PB_DRAG_TYPE]]; + + [favoritesTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + [favoritesTableView reloadData]; + + [self updateDefaultFavoritePopup]; +} + +#pragma mark - +#pragma mark Preferences upgrade routine + +// ------------------------------------------------------------------------------- +// applyRevisionChanges +// Checks the revision number, applies any preference upgrades, and updates to +// latest revision. +// Currently uses both lastUsedVersion and LastUsedVersion for <0.9.5 compatibility. +// ------------------------------------------------------------------------------- +- (void)applyRevisionChanges +{ + int currentVersionNumber, recordedVersionNumber = 0; + + // Get the current bundle version number (the SVN build number) for per-version upgrades + currentVersionNumber = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]; + + // Get the current revision + if ([prefs objectForKey:@"lastUsedVersion"]) recordedVersionNumber = [[prefs objectForKey:@"lastUsedVersion"] intValue]; + if ([prefs objectForKey:@"LastUsedVersion"]) recordedVersionNumber = [[prefs objectForKey:@"LastUsedVersion"] intValue]; + + // Skip processing if the current version matches or is less than recorded version + if (currentVersionNumber <= recordedVersionNumber) return; + + // If no recorded version, update to current revision and skip processing + if (!recordedVersionNumber) { + [prefs setObject:[NSNumber numberWithInt:currentVersionNumber] forKey:@"LastUsedVersion"]; + return; + } + + // For versions prior to r336 (0.9.4), where column widths have been saved, walk through them and remove + // any table widths set to 15 or less (fix for mangled columns caused by Issue #140) + if (recordedVersionNumber < 336 && [prefs objectForKey:@"tableColumnWidths"] != nil) { + NSEnumerator *databaseEnumerator, *tableEnumerator, *columnEnumerator; + NSString *databaseKey, *tableKey, *columnKey; + NSMutableDictionary *newDatabase, *newTable; + float columnWidth; + NSMutableDictionary *newTableColumnWidths = [[NSMutableDictionary alloc] init]; + + databaseEnumerator = [[prefs objectForKey:@"tableColumnWidths"] keyEnumerator]; + while (databaseKey = [databaseEnumerator nextObject]) { + newDatabase = [[NSMutableDictionary alloc] init]; + tableEnumerator = [[[prefs objectForKey:@"tableColumnWidths"] objectForKey:databaseKey] keyEnumerator]; + while (tableKey = [tableEnumerator nextObject]) { + newTable = [[NSMutableDictionary alloc] init]; + columnEnumerator = [[[[prefs objectForKey:@"tableColumnWidths"] objectForKey:databaseKey] objectForKey:tableKey] keyEnumerator]; + while (columnKey = [columnEnumerator nextObject]) { + columnWidth = [[[[[prefs objectForKey:@"tableColumnWidths"] objectForKey:databaseKey] objectForKey:tableKey] objectForKey:columnKey] floatValue]; + if (columnWidth >= 15) { + [newTable setObject:[NSNumber numberWithFloat:columnWidth] forKey:[NSString stringWithString:columnKey]]; + } + } + if ([newTable count]) { + [newDatabase setObject:[NSDictionary dictionaryWithDictionary:newTable] forKey:[NSString stringWithString:tableKey]]; + } + [newTable release]; + } + if ([newDatabase count]) { + [newTableColumnWidths setObject:[NSDictionary dictionaryWithDictionary:newDatabase] forKey:[NSString stringWithString:databaseKey]]; + } + [newDatabase release]; + } + [prefs setObject:[NSDictionary dictionaryWithDictionary:newTableColumnWidths] forKey:@"tableColumnWidths"]; + [newTableColumnWidths release]; + } + + // For versions prior to r561 (0.9.5), migrate old pref keys where they exist to the new pref keys + if (recordedVersionNumber < 561) { + NSEnumerator *keyEnumerator; + NSString *oldKey, *newKey; + NSDictionary *keysToUpgrade = [NSDictionary dictionaryWithObjectsAndKeys: + @"encoding", @"DefaultEncoding", + @"useMonospacedFonts", @"UseMonospacedFonts", + @"reloadAfterAdding", @"ReloadAfterAddingRow", + @"reloadAfterEditing", @"ReloadAfterEditingRow", + @"reloadAfterRemoving", @"ReloadAfterRemovingRow", + @"dontShowBlob", @"LoadBlobsAsNeeded", + @"fetchRowCount", @"FetchCorrectRowCount", + @"limitRows", @"LimitResults", + @"limitRowsValue", @"LimitResultsValue", + @"nullValue", @"NullValue", + @"showError", @"ShowNoAffectedRowsError", + @"connectionTimeout", @"ConnectionTimeoutValue", + @"keepAliveInterval", @"KeepAliveInterval", + @"lastFavoriteIndex", @"LastFavoriteIndex", + nil]; + + keyEnumerator = [keysToUpgrade keyEnumerator]; + while (newKey = [keyEnumerator nextObject]) { + oldKey = [keysToUpgrade objectForKey:newKey]; + if ([prefs objectForKey:oldKey]) { + [prefs setObject:[prefs objectForKey:oldKey] forKey:newKey]; + [prefs removeObjectForKey:oldKey]; + } + } + + // Remove outdated keys + [prefs removeObjectForKey:@"lastUsedVersion"]; + [prefs removeObjectForKey:@"version"]; + } + + // For versions prior to r567 (0.9.5), add a timestamp-based identifier to favorites and keychain entries + if (recordedVersionNumber < 567 && [prefs objectForKey:@"favorites"]) { + int i; + NSMutableArray *favoritesArray = [NSMutableArray arrayWithArray:[prefs objectForKey:@"favorites"]]; + NSMutableDictionary *favorite; + NSString *password, *keychainName, *keychainAccount; + KeyChain *upgradeKeychain = [[KeyChain alloc] init]; + + // Cycle through the favorites, generating a timestamp-derived ID for each and renaming associated keychain items. + for (i = 0; i < [favoritesArray count]; i++) { + favorite = [NSMutableDictionary dictionaryWithDictionary:[favoritesArray objectAtIndex:i]]; + if ([favorite objectForKey:@"id"]) continue; + [favorite setObject:[NSNumber numberWithInt:[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]] forKey:@"id"]; + keychainName = [NSString stringWithFormat:@"Sequel Pro : %@", [favorite objectForKey:@"name"]]; + keychainAccount = [NSString stringWithFormat:@"%@@%@/%@", + [favorite objectForKey:@"user"], [favorite objectForKey:@"host"], [favorite objectForKey:@"database"]]; + password = [upgradeKeychain getPasswordForName:keychainName account:keychainAccount]; + [upgradeKeychain deletePasswordForName:keychainName account:keychainAccount]; + if (password && [password length]) { + keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]]; + [upgradeKeychain addPassword:password forName:keychainName account:keychainAccount]; + } + [favoritesArray replaceObjectAtIndex:i withObject:[NSDictionary dictionaryWithDictionary:favorite]]; + } + [prefs setObject:[NSArray arrayWithArray:favoritesArray] forKey:@"favorites"]; + [upgradeKeychain release]; + password = nil; + } + + // Update the prefs revision + [prefs setObject:[NSNumber numberWithInt:currentVersionNumber] forKey:@"LastUsedVersion"]; +} + +#pragma mark - +#pragma mark IBAction methods + +// ------------------------------------------------------------------------------- +// addFavorite: +// ------------------------------------------------------------------------------- +- (IBAction)addFavorite:(id)sender +{ + NSNumber *favoriteid = [NSNumber numberWithInt:[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; + + // Create default favorite + NSMutableDictionary *favorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"New Favorite", @"", @"", @"", @"", @"", favoriteid, nil] + forKeys:[NSArray arrayWithObjects:@"name", @"host", @"socket", @"user", @"port", @"database", @"id", nil]]; + + [favoritesController addObject:favorite]; + + [favoritesTableView reloadData]; + [self updateDefaultFavoritePopup]; +} + +// ------------------------------------------------------------------------------- +// removeFavorite: +// ------------------------------------------------------------------------------- +- (IBAction)removeFavorite:(id)sender +{ + if ([favoritesTableView numberOfSelectedRows] == 1) { + + // Get selected favorite's details + NSString *name = [favoritesController valueForKeyPath:@"selection.name"]; + NSString *user = [favoritesController valueForKeyPath:@"selection.user"]; + NSString *host = [favoritesController valueForKeyPath:@"selection.host"]; + NSString *database = [favoritesController valueForKeyPath:@"selection.database"]; + int favoriteid = [[favoritesController valueForKeyPath:@"selection.id"] intValue]; + + // Remove passwords from the Keychain + [keychain deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro : %@ (%i)", name, favoriteid] + account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]]; + [keychain deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", name, favoriteid] + account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]]; + + // Reset last used favorite + if ([favoritesTableView selectedRow] == [prefs integerForKey:@"LastFavoriteIndex"]) { + [prefs setInteger:0 forKey:@"LastFavoriteIndex"]; + } + + // Reset default favorite + if ([favoritesTableView selectedRow] == [prefs integerForKey:@"DefaultFavorite"]) { + [prefs setInteger:[prefs integerForKey:@"LastFavoriteIndex"] forKey:@"DefaultFavorite"]; + } + + [favoritesController removeObjectAtArrangedObjectIndex:[favoritesTableView selectedRow]]; + + [favoritesTableView reloadData]; + [self updateDefaultFavoritePopup]; + } +} + +// ------------------------------------------------------------------------------- +// duplicateFavorite: +// ------------------------------------------------------------------------------- +- (IBAction)duplicateFavorite:(id)sender +{ + if ([favoritesTableView numberOfSelectedRows] == 1) { + NSString *keychainName, *keychainAccount, *password; + NSMutableDictionary *favorite = [NSMutableDictionary dictionaryWithDictionary:[[favoritesController arrangedObjects] objectAtIndex:[favoritesTableView selectedRow]]]; + NSNumber *favoriteid = [NSNumber numberWithInt:[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; + + // Select the keychain password for duplication + keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]]; + keychainAccount = [NSString stringWithFormat:@"%@@%@/%@", + [favorite objectForKey:@"user"], [favorite objectForKey:@"host"], [favorite objectForKey:@"database"]]; + password = [keychain getPasswordForName:keychainName account:keychainAccount]; + + // Update the unique ID + [favorite setObject:favoriteid forKey:@"id"]; + + // Alter the name for clarity + [favorite setObject:[NSString stringWithFormat:@"%@ Copy", [favorite objectForKey:@"name"]] forKey:@"name"]; + + // Create a new keychain item if appropriate + if (password && [password length]) { + keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]]; + [keychain addPassword:password forName:keychainName account:keychainAccount]; + } + password = nil; + + [favoritesController addObject:favorite]; + + [favoritesTableView reloadData]; + [self updateDefaultFavoritePopup]; + } +} + +// ------------------------------------------------------------------------------- +// saveFavorite: +// ------------------------------------------------------------------------------- +- (IBAction)saveFavorite:(id)sender +{ + +} + +// ------------------------------------------------------------------------------- +// updateDefaultFavorite: +// ------------------------------------------------------------------------------- +- (IBAction)updateDefaultFavorite:(id)sender +{ + if ([defaultFavoritePopup indexOfSelectedItem] == 0) { + [prefs setBool:YES forKey:@"SelectLastFavoriteUsed"]; + } else { + [prefs setBool:NO forKey:@"SelectLastFavoriteUsed"]; + + // Minus 2 from index to account for the "Last Used" and separator items + [prefs setInteger:[defaultFavoritePopup indexOfSelectedItem]-2 forKey:@"DefaultFavorite"]; + } +} + +#pragma mark - +#pragma mark Toolbar item IBAction methods + +// ------------------------------------------------------------------------------- +// displayGeneralPreferences: +// ------------------------------------------------------------------------------- +- (IBAction)displayGeneralPreferences:(id)sender +{ + [toolbar setSelectedItemIdentifier:PREFERENCE_TOOLBAR_GENERAL]; + [self _resizeWindowForContentView:generalView]; +} + +// ------------------------------------------------------------------------------- +// displayTablePreferences: +// ------------------------------------------------------------------------------- +- (IBAction)displayTablePreferences:(id)sender +{ + [toolbar setSelectedItemIdentifier:PREFERENCE_TOOLBAR_TABLES]; + [self _resizeWindowForContentView:tablesView]; +} + +// ------------------------------------------------------------------------------- +// displayFavoritePreferences: +// ------------------------------------------------------------------------------- +- (IBAction)displayFavoritePreferences:(id)sender +{ + [toolbar setSelectedItemIdentifier:PREFERENCE_TOOLBAR_FAVORITES]; + [self _resizeWindowForContentView:favoritesView]; + + // Set the default favorite popup back to preference + if (sender == [defaultFavoritePopup lastItem]) { + if (![prefs boolForKey:@"SelectLastFavoriteUsed"]) { + [defaultFavoritePopup selectItemAtIndex:[prefs integerForKey:@"DefaultFavorite"]+2]; + } else { + [defaultFavoritePopup selectItemAtIndex:0]; + } + } +} + +// ------------------------------------------------------------------------------- +// displayNotificationPreferences: +// ------------------------------------------------------------------------------- +- (IBAction)displayNotificationPreferences:(id)sender +{ + [toolbar setSelectedItemIdentifier:PREFERENCE_TOOLBAR_NOTIFICATIONS]; + [self _resizeWindowForContentView:notificationsView]; +} + +// ------------------------------------------------------------------------------- +// displayAutoUpdatePreferences: +// ------------------------------------------------------------------------------- +- (IBAction)displayAutoUpdatePreferences:(id)sender +{ + [toolbar setSelectedItemIdentifier:PREFERENCE_TOOLBAR_AUTOUPDATE]; + [self _resizeWindowForContentView:autoUpdateView]; +} + +// ------------------------------------------------------------------------------- +// displayNetworkPreferences: +// ------------------------------------------------------------------------------- +- (IBAction)displayNetworkPreferences:(id)sender +{ + [toolbar setSelectedItemIdentifier:PREFERENCE_TOOLBAR_NETWORK]; + [self _resizeWindowForContentView:networkView]; +} + +#pragma mark - +#pragma mark TableView datasource methods + +// ------------------------------------------------------------------------------- +// numberOfRowsInTableView: +// ------------------------------------------------------------------------------- +- (int)numberOfRowsInTableView:(NSTableView *)aTableView +{ + return [[favoritesController arrangedObjects] count]; +} + +// ------------------------------------------------------------------------------- +// tableView:objectValueForTableColumn:row: +// ------------------------------------------------------------------------------- +- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex +{ + return [[[favoritesController arrangedObjects] objectAtIndex:rowIndex] objectForKey:[aTableColumn identifier]]; +} + +#pragma mark - +#pragma mark TableView drag & drop datasource methods + +// ------------------------------------------------------------------------------- +// tableView:writeRows:toPasteboard: +// ------------------------------------------------------------------------------- +- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray *)rows toPasteboard:(NSPasteboard *)pboard +{ + int originalRow; + NSArray *pboardTypes; + + if ([rows count] == 1) { + pboardTypes = [NSArray arrayWithObject:FAVORITES_PB_DRAG_TYPE]; + originalRow = [[rows objectAtIndex:0] intValue]; + + [pboard declareTypes:pboardTypes owner:nil]; + [pboard setString:[[NSNumber numberWithInt:originalRow] stringValue] forType:FAVORITES_PB_DRAG_TYPE]; + + return YES; + } + else { + return NO; + } +} + +// ------------------------------------------------------------------------------- +// tableView:validateDrop:proposedRow:proposedDropOperation: +// ------------------------------------------------------------------------------- +- (NSDragOperation)tableView:(NSTableView *)tv validateDrop:(id )info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation +{ + int originalRow; + NSArray *pboardTypes = [[info draggingPasteboard] types]; + + if (([pboardTypes count] > 1) && (row != -1)) { + if (([pboardTypes containsObject:FAVORITES_PB_DRAG_TYPE]) && (operation == NSTableViewDropAbove)) { + originalRow = [[[info draggingPasteboard] stringForType:FAVORITES_PB_DRAG_TYPE] intValue]; + + if ((row != originalRow) && (row != (originalRow + 1))) { + return NSDragOperationMove; + } + } + } + + return NSDragOperationNone; +} + +// ------------------------------------------------------------------------------- +// tableView:acceptDrop:row:dropOperation: +// ------------------------------------------------------------------------------- +- (BOOL)tableView:(NSTableView *)tv acceptDrop:(id )info row:(int)row dropOperation:(NSTableViewDropOperation)operation +{ + int originalRow; + int destinationRow; + NSMutableDictionary *draggedRow; + + originalRow = [[[info draggingPasteboard] stringForType:FAVORITES_PB_DRAG_TYPE] intValue]; + destinationRow = row; + + if (destinationRow > originalRow) { + destinationRow--; + } + + draggedRow = [NSMutableDictionary dictionaryWithDictionary:[[favoritesController arrangedObjects] objectAtIndex:originalRow]]; + + [favoritesController removeObjectAtArrangedObjectIndex:originalRow]; + [favoritesController insertObject:draggedRow atArrangedObjectIndex:destinationRow]; + + [favoritesTableView reloadData]; + [favoritesTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:destinationRow] byExtendingSelection:NO]; + + // Update default favorite to take on new value + if ([prefs integerForKey:@"LastFavoriteIndex"] == originalRow) { + [prefs setInteger:destinationRow forKey:@"LastFavoriteIndex"]; + } + + // Update default favorite to take on new value + if ([prefs integerForKey:@"DefaultFavorite"] == originalRow) { + [prefs setInteger:destinationRow forKey:@"DefaultFavorite"]; + } + [self updateDefaultFavoritePopup]; + + return YES; +} + + +#pragma mark - +#pragma mark TableView delegate methods + +// ------------------------------------------------------------------------------- +// tableView:willDisplayCell:forTableColumn:row: +// ------------------------------------------------------------------------------- +- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)index +{ + if ([cell isKindOfClass:[SPFavoriteTextFieldCell class]]) { + [cell setFavoriteName:[[[favoritesController arrangedObjects] objectAtIndex:index] objectForKey:@"name"]]; + [cell setFavoriteHost:[[[favoritesController arrangedObjects] objectAtIndex:index] objectForKey:@"host"]]; + } +} + +// ------------------------------------------------------------------------------- +// tableViewSelectionDidChange: +// ------------------------------------------------------------------------------- +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + if ([[favoritesTableView selectedRowIndexes] count] > 0) { + [favoritesController setSelectionIndexes:[favoritesTableView selectedRowIndexes]]; + } + + // If no selection is present, blank the field. + if ([[favoritesTableView selectedRowIndexes] count] == 0) { + [passwordField setStringValue:@""]; + return; + } + + // Otherwise retrieve and set the password. + NSString *keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favoritesController valueForKeyPath:@"selection.name"], [[favoritesController valueForKeyPath:@"selection.id"] intValue]]; + NSString *keychainAccount = [NSString stringWithFormat:@"%@@%@/%@", + [favoritesController valueForKeyPath:@"selection.user"], + [favoritesController valueForKeyPath:@"selection.host"], + [favoritesController valueForKeyPath:@"selection.database"]]; + + [passwordField setStringValue:[keychain getPasswordForName:keychainName account:keychainAccount]]; +} + +#pragma mark - +#pragma mark Toolbar delegate methods + +// ------------------------------------------------------------------------------- +// toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: +// ------------------------------------------------------------------------------- +- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag +{ + if ([itemIdentifier isEqualToString:PREFERENCE_TOOLBAR_GENERAL]) { + return generalItem; + } + else if ([itemIdentifier isEqualToString:PREFERENCE_TOOLBAR_TABLES]) { + return tablesItem; + } + else if ([itemIdentifier isEqualToString:PREFERENCE_TOOLBAR_FAVORITES]) { + return favoritesItem; + } + else if ([itemIdentifier isEqualToString:PREFERENCE_TOOLBAR_NOTIFICATIONS]) { + return notificationsItem; + } + else if ([itemIdentifier isEqualToString:PREFERENCE_TOOLBAR_AUTOUPDATE]) { + return autoUpdateItem; + } + else if ([itemIdentifier isEqualToString:PREFERENCE_TOOLBAR_NETWORK]) { + return networkItem; + } + + return [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease]; +} + +// ------------------------------------------------------------------------------- +// toolbarAllowedItemIdentifiers: +// ------------------------------------------------------------------------------- +- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar +{ + return [NSArray arrayWithObjects:PREFERENCE_TOOLBAR_GENERAL, PREFERENCE_TOOLBAR_TABLES, PREFERENCE_TOOLBAR_FAVORITES, PREFERENCE_TOOLBAR_NOTIFICATIONS, PREFERENCE_TOOLBAR_AUTOUPDATE, PREFERENCE_TOOLBAR_NETWORK, nil]; +} + +// ------------------------------------------------------------------------------- +// toolbarDefaultItemIdentifiers: +// ------------------------------------------------------------------------------- +- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar +{ + return [NSArray arrayWithObjects:PREFERENCE_TOOLBAR_GENERAL, PREFERENCE_TOOLBAR_TABLES, PREFERENCE_TOOLBAR_FAVORITES, PREFERENCE_TOOLBAR_NOTIFICATIONS, PREFERENCE_TOOLBAR_AUTOUPDATE, PREFERENCE_TOOLBAR_NETWORK, nil]; +} + +// ------------------------------------------------------------------------------- +// toolbarSelectableItemIdentifiers: +// ------------------------------------------------------------------------------- +- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar +{ + return [NSArray arrayWithObjects:PREFERENCE_TOOLBAR_GENERAL, PREFERENCE_TOOLBAR_TABLES, PREFERENCE_TOOLBAR_FAVORITES, PREFERENCE_TOOLBAR_NOTIFICATIONS, PREFERENCE_TOOLBAR_AUTOUPDATE, PREFERENCE_TOOLBAR_NETWORK, nil]; +} + +#pragma mark - +#pragma mark SplitView delegate methods + +// ------------------------------------------------------------------------------- +// splitView:constrainMaxCoordinate:ofSubviewAt: +// ------------------------------------------------------------------------------- +- (float)splitView:(NSSplitView *)sender constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)offset +{ + return (proposedMax - 220); +} + +// ------------------------------------------------------------------------------- +// splitView:constrainMinCoordinate:ofSubviewAt: +// ------------------------------------------------------------------------------- +- (float)splitView:(NSSplitView *)sender constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)offset +{ + return (proposedMin + 100); +} + + +#pragma mark - +#pragma mark TextField delegate methods + +// ------------------------------------------------------------------------------- +// control:textShouldEndEditing: +// Trap editing end notifications and use them to update the keychain password +// appropriately when name, host, user, password or database changes. +// ------------------------------------------------------------------------------- +- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor +{ + NSString *oldKeychainName, *newKeychainName; + NSString *oldKeychainAccount, *newKeychainAccount; + NSString *oldPassword; + + // Only proceed for name, host, user or database changes + if (control != nameField && control != hostField && control != userField && control != passwordField && control != databaseField) + return YES; + + // Set the current keychain name and account strings + oldKeychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favoritesController valueForKeyPath:@"selection.name"], [[favoritesController valueForKeyPath:@"selection.id"] intValue]]; + oldKeychainAccount = [NSString stringWithFormat:@"%@@%@/%@", + [favoritesController valueForKeyPath:@"selection.user"], + [favoritesController valueForKeyPath:@"selection.host"], + [favoritesController valueForKeyPath:@"selection.database"]]; + + // Retrieve the old password + oldPassword = [keychain getPasswordForName:oldKeychainName account:oldKeychainAccount]; + + // If no details have changed, skip processing + if ([nameField stringValue] == [favoritesController valueForKeyPath:@"selection.name"] + && [hostField stringValue] == [favoritesController valueForKeyPath:@"selection.host"] + && [userField stringValue] == [favoritesController valueForKeyPath:@"selection.user"] + && [databaseField stringValue] == [favoritesController valueForKeyPath:@"selection.database"] + && [passwordField stringValue] == oldPassword) { + oldPassword = nil; + return YES; + } + oldPassword = nil; + + // Set up the new keychain name and account strings + newKeychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [nameField stringValue], [[favoritesController valueForKeyPath:@"selection.id"] intValue]]; + newKeychainAccount = [NSString stringWithFormat:@"%@@%@/%@", + [userField stringValue], + [hostField stringValue], + [databaseField stringValue]]; + + // Delete the old keychain item + [keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount]; + + // Add the new keychain item if the password field has a value + if ([[passwordField stringValue] length]) + [keychain addPassword:[passwordField stringValue] forName:newKeychainName account:newKeychainAccount]; + + // Proceed with editing + return YES; +} + +#pragma mark - +#pragma mark Window delegate methods + +// ------------------------------------------------------------------------------- +// windowWillClose: +// Trap window close notifications and use them to ensure changes are saved. +// ------------------------------------------------------------------------------- +- (void)windowWillClose:(NSNotification *)notification +{ + // Mark the currently selected field in the window as having finished editing, to trigger saves. + if ([preferencesWindow firstResponder]) + [preferencesWindow endEditingFor:[preferencesWindow firstResponder]]; +} + +#pragma mark - +#pragma mark Other + +// ------------------------------------------------------------------------------- +// updateDefaultFavoritePopup: +// +// Build the default favorite popup button +// ------------------------------------------------------------------------------- +- (void)updateDefaultFavoritePopup; +{ + [defaultFavoritePopup removeAllItems]; + + // Use the last used favorite + [defaultFavoritePopup addItemWithTitle:@"Last Used"]; + [[defaultFavoritePopup menu] addItem:[NSMenuItem separatorItem]]; + + // Load in current favorites + [defaultFavoritePopup addItemsWithTitles:[[favoritesController arrangedObjects] valueForKeyPath:@"name"]]; + + // Add item to switch to edit favorites pane + [[defaultFavoritePopup menu] addItem:[NSMenuItem separatorItem]]; + [defaultFavoritePopup addItemWithTitle:@"Edit Favorites…"]; + [[[defaultFavoritePopup menu] itemWithTitle:@"Edit Favorites…"] setAction:@selector(displayFavoritePreferences:)]; + [[[defaultFavoritePopup menu] itemWithTitle:@"Edit Favorites…"] setTarget:self]; + + // Select the default favorite from prefs + if (![prefs boolForKey:@"SelectLastFavoriteUsed"]) { + [defaultFavoritePopup selectItemAtIndex:[prefs integerForKey:@"DefaultFavorite"] + 2]; + } else { + [defaultFavoritePopup selectItemAtIndex:0]; + } +} + +// ------------------------------------------------------------------------------- +// selectFavorite: +// +// Selects the specified favorite(s) in the favorites list +// ------------------------------------------------------------------------------- +- (void)selectFavorites:(NSArray *)favorites +{ + [favoritesController setSelectedObjects:favorites]; +} + +// ------------------------------------------------------------------------------- +// dealloc +// ------------------------------------------------------------------------------- +- (void)dealloc +{ + [keychain release], keychain = nil; + + [super dealloc]; +} + +@end + + + +#pragma mark - + +@implementation SPPreferenceController (PrivateAPI) + +// ------------------------------------------------------------------------------- +// _setupToolbar +// +// Constructs the preferences' window toolbar. +// ------------------------------------------------------------------------------- +- (void)_setupToolbar +{ + toolbar = [[[NSToolbar alloc] initWithIdentifier:@"Preference Toolbar"] autorelease]; + + // General preferences + generalItem = [[NSToolbarItem alloc] initWithItemIdentifier:PREFERENCE_TOOLBAR_GENERAL]; + + [generalItem setLabel:NSLocalizedString(@"General", @"")]; + [generalItem setImage:[NSImage imageNamed:@"toolbar-preferences-general"]]; + [generalItem setTarget:self]; + [generalItem setAction:@selector(displayGeneralPreferences:)]; + + // Table preferences + tablesItem = [[NSToolbarItem alloc] initWithItemIdentifier:PREFERENCE_TOOLBAR_TABLES]; + + [tablesItem setLabel:NSLocalizedString(@"Tables", @"")]; + [tablesItem setImage:[NSImage imageNamed:@"toolbar-preferences-tables"]]; + [tablesItem setTarget:self]; + [tablesItem setAction:@selector(displayTablePreferences:)]; + + // Favorite preferences + favoritesItem = [[NSToolbarItem alloc] initWithItemIdentifier:PREFERENCE_TOOLBAR_FAVORITES]; + + [favoritesItem setLabel:NSLocalizedString(@"Favorites", @"")]; + [favoritesItem setImage:[NSImage imageNamed:@"toolbar-preferences-favorites"]]; + [favoritesItem setTarget:self]; + [favoritesItem setAction:@selector(displayFavoritePreferences:)]; + + // Notification preferences + notificationsItem = [[NSToolbarItem alloc] initWithItemIdentifier:PREFERENCE_TOOLBAR_NOTIFICATIONS]; + + [notificationsItem setLabel:NSLocalizedString(@"Notifications", @"")]; + [notificationsItem setImage:[NSImage imageNamed:@"toolbar-preferences-notifications"]]; + [notificationsItem setTarget:self]; + [notificationsItem setAction:@selector(displayNotificationPreferences:)]; + + // AutoUpdate preferences + autoUpdateItem = [[NSToolbarItem alloc] initWithItemIdentifier:PREFERENCE_TOOLBAR_AUTOUPDATE]; + + [autoUpdateItem setLabel:NSLocalizedString(@"Auto Update", @"")]; + [autoUpdateItem setImage:[NSImage imageNamed:@"toolbar-preferences-autoupdate"]]; + [autoUpdateItem setTarget:self]; + [autoUpdateItem setAction:@selector(displayAutoUpdatePreferences:)]; + + // Network preferences + networkItem = [[NSToolbarItem alloc] initWithItemIdentifier:PREFERENCE_TOOLBAR_NETWORK]; + + [networkItem setLabel:NSLocalizedString(@"Network", @"")]; + [networkItem setImage:[NSImage imageNamed:@"toolbar-preferences-network"]]; + [networkItem setTarget:self]; + [networkItem setAction:@selector(displayNetworkPreferences:)]; + + [toolbar setDelegate:self]; + [toolbar setSelectedItemIdentifier:PREFERENCE_TOOLBAR_GENERAL]; + [toolbar setAllowsUserCustomization:NO]; + + [preferencesWindow setToolbar:toolbar]; + [preferencesWindow setShowsToolbarButton:NO]; + + [self displayGeneralPreferences:nil]; +} + +// ------------------------------------------------------------------------------- +// _resizeWindowForContentView: +// +// Resizes the window to the size of the supplied view. +// ------------------------------------------------------------------------------- +- (void)_resizeWindowForContentView:(NSView *)view +{ + // remove all current views + NSEnumerator *en = [[[preferencesWindow contentView] subviews] objectEnumerator]; + NSView *subview; + + while (subview = [en nextObject]) + { + [subview removeFromSuperview]; + } + + // resize window + [preferencesWindow resizeForContentView:view titleBarVisible:YES]; + + // add view + [[preferencesWindow contentView] addSubview:view]; + [view setFrameOrigin:NSMakePoint(0, 0)]; +} + +@end diff --git a/Source/SPQueryConsole.h b/Source/SPQueryConsole.h index e1072904..4a2326c6 100644 --- a/Source/SPQueryConsole.h +++ b/Source/SPQueryConsole.h @@ -24,15 +24,33 @@ @interface SPQueryConsole : NSWindowController { - IBOutlet NSTextView *consoleTextView; + IBOutlet NSView *saveLogView; + IBOutlet NSTableView *consoleTableView; + IBOutlet NSSearchField *consoleSearchField; + IBOutlet NSProgressIndicator *progressIndicator; + IBOutlet NSButton *includeTimeStampsButton, *saveConsoleButton, *clearConsoleButton; + + NSFont *consoleFont; + NSMutableArray *messagesFullSet, *messagesFilteredSet, *messagesVisibleSet; + BOOL showSelectStatementsAreDisabled; + BOOL filterIsActive; + NSMutableString *activeFilterString; + + float uncollapsedDateColumnWidth; } ++ (SPQueryConsole *)sharedQueryConsole; + +- (IBAction)copy:(id)sender; - (IBAction)clearConsole:(id)sender; - (IBAction)saveConsoleAs:(id)sender; +- (IBAction)toggleShowTimeStamps:(id)sender; +- (IBAction)toggleShowSelectShowStatements:(id)sender; - (void)showMessageInConsole:(NSString *)message; - (void)showErrorInConsole:(NSString *)error; -- (NSTextView *)consoleTextView; - +- (int)consoleMessageCount; +- (NSFont *)consoleFont; +- (void)setConsoleFont:(NSFont *)theFont; @end diff --git a/Source/SPQueryConsole.m b/Source/SPQueryConsole.m index 1fe62d65..ac96e51f 100644 --- a/Source/SPQueryConsole.m +++ b/Source/SPQueryConsole.m @@ -21,127 +21,501 @@ // More info at #import "SPQueryConsole.h" +#import "SPConsoleMessage.h" + +#define MESSAGE_TRUNCATE_CHARACTER_LENGTH 256 +#define MESSAGE_TIME_STAMP_FORMAT @"%H:%M:%S" #define DEFAULT_CONSOLE_LOG_FILENAME @"untitled" #define DEFAULT_CONSOLE_LOG_FILE_EXTENSION @"log" #define CONSOLE_WINDOW_AUTO_SAVE_NAME @"QueryConsole" +// Table view column identifiers +#define TABLEVIEW_MESSAGE_COLUMN_IDENTIFIER @"message" +#define TABLEVIEW_DATE_COLUMN_IDENTIFIER @"messageDate" + @interface SPQueryConsole (PrivateAPI) -- (void)_appendMessageToConsole:(NSString *)message withColor:(NSColor *)color; +- (NSString *)_getConsoleStringWithTimeStamps:(BOOL)timeStamps; + +- (void)_updateFilterState; +- (void)_addMessageToConsole:(NSString *)message isError:(BOOL)error; +- (BOOL)_messageMatchesCurrentFilters:(NSString *)message; @end +static SPQueryConsole *sharedQueryConsole = nil; + @implementation SPQueryConsole -// ------------------------------------------------------------------------------- -// awakeFromNib -// -// Set the window's auto save name. -// ------------------------------------------------------------------------------- +/* + * Returns the shared query console. + */ ++ (SPQueryConsole *)sharedQueryConsole +{ + @synchronized(self) { + if (sharedQueryConsole == nil) { + [[self alloc] init]; + } + } + + return sharedQueryConsole; +} + ++ (id)allocWithZone:(NSZone *)zone +{ + @synchronized(self) { + if (sharedQueryConsole == nil) { + sharedQueryConsole = [super allocWithZone:zone]; + + return sharedQueryConsole; + } + } + + return nil; // On subsequent allocation attempts return nil +} + +- (id)init +{ + if ((self = [super initWithWindowNibName:@"Console"])) { + messagesFullSet = [[NSMutableArray alloc] init]; + messagesFilteredSet = [[NSMutableArray alloc] init]; + consoleFont = [[NSFont systemFontOfSize:[NSFont smallSystemFontSize]] retain]; + + showSelectStatementsAreDisabled = NO; + filterIsActive = NO; + activeFilterString = [[NSMutableString alloc] init]; + + // Weak reference to active messages set - starts off as full set + messagesVisibleSet = messagesFullSet; + + uncollapsedDateColumnWidth = [[consoleTableView tableColumnWithIdentifier:TABLEVIEW_DATE_COLUMN_IDENTIFIER] width]; + } + + return self; +} + +/* + * The following base protocol methods are implemented to ensure the singleton status of this class. + */ + +- (id)copyWithZone:(NSZone *)zone { return self; } + +- (id)retain { return self; } + +- (unsigned)retainCount { return UINT_MAX; } + +- (id)autorelease { return self; } + +- (void)release { } + +/** + * Set the window's auto save name. + */ - (void)awakeFromNib { [self setWindowFrameAutosaveName:CONSOLE_WINDOW_AUTO_SAVE_NAME]; } -// ------------------------------------------------------------------------------- -// clearConsole: -// -// Clears the console by setting its displayed text to an empty string. -// ------------------------------------------------------------------------------- +/** + * Copy implementation for console table view. + */ +- (void)copy:(id)sender +{ + NSResponder *firstResponder = [[self window] firstResponder]; + + if ((firstResponder == consoleTableView) && ([consoleTableView numberOfSelectedRows] > 0)) { + + NSString *string = @""; + NSIndexSet *rows = [consoleTableView selectedRowIndexes]; + + int i = [rows firstIndex]; + + while (i != NSNotFound) + { + if (i < [messagesVisibleSet count]) { + SPConsoleMessage *message = [messagesVisibleSet objectAtIndex:i]; + + NSString *consoleMessage = [message message]; + + // If the timestamp column is not hidden we need to include them in the copy + if ([[consoleTableView tableColumnWithIdentifier:TABLEVIEW_DATE_COLUMN_IDENTIFIER] width] > 0) { + + NSString *dateString = [[message messageDate] descriptionWithCalendarFormat:MESSAGE_TIME_STAMP_FORMAT timeZone:nil locale:nil]; + + consoleMessage = [NSString stringWithFormat:@"/* MySQL %@ */ %@", dateString, consoleMessage]; + } + + string = [string stringByAppendingFormat:@"%@\n", consoleMessage]; + } + + i = [rows indexGreaterThanIndex:i]; + } + + NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; + + // Copy the string to the pasteboard + [pasteBoard declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] owner:nil]; + [pasteBoard setString:string forType:NSStringPboardType]; + } +} + +/** + * Clears the console by removing all of its messages. + */ - (IBAction)clearConsole:(id)sender { - [consoleTextView setString:@""]; + [messagesFullSet removeAllObjects]; + [messagesFilteredSet removeAllObjects]; + + [consoleTableView reloadData]; } -// ------------------------------------------------------------------------------- -// saveConsoleAs: -// -// Presents the user with a save panel to the save the current console to a log file. -// ------------------------------------------------------------------------------- +/** + * Presents the user with a save panel to the save the current console to a log file. + */ - (IBAction)saveConsoleAs:(id)sender { NSSavePanel *panel = [NSSavePanel savePanel]; - + [panel setRequiredFileType:DEFAULT_CONSOLE_LOG_FILE_EXTENSION]; - + [panel setExtensionHidden:NO]; [panel setAllowsOtherFileTypes:YES]; [panel setCanSelectHiddenExtension:YES]; - + + [panel setAccessoryView:saveLogView]; + [panel beginSheetForDirectory:nil file:DEFAULT_CONSOLE_LOG_FILENAME modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; + [panel beginSheetForDirectory:nil + file:DEFAULT_CONSOLE_LOG_FILENAME + modalForWindow:[self window] + modalDelegate:self + didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) + contextInfo:NULL]; } -// ------------------------------------------------------------------------------- -// showMessageInConsole: -// -// Shows the supplied message in the console. -// ------------------------------------------------------------------------------- +/** + * Toggles the display of the message time stamp column in the table view. + */ +- (IBAction)toggleShowTimeStamps:(id)sender +{ + if ([sender intValue]) { + [[consoleTableView tableColumnWithIdentifier:TABLEVIEW_DATE_COLUMN_IDENTIFIER] setMinWidth:50.0]; + [[consoleTableView tableColumnWithIdentifier:TABLEVIEW_DATE_COLUMN_IDENTIFIER] setWidth:uncollapsedDateColumnWidth]; + } else { + uncollapsedDateColumnWidth = [[consoleTableView tableColumnWithIdentifier:TABLEVIEW_DATE_COLUMN_IDENTIFIER] width]; + [[consoleTableView tableColumnWithIdentifier:TABLEVIEW_DATE_COLUMN_IDENTIFIER] setMinWidth:0.0]; + [[consoleTableView tableColumnWithIdentifier:TABLEVIEW_DATE_COLUMN_IDENTIFIER] setWidth: 0.0]; + } +} + +/** + * Toggles the hiding of messages containing SELECT and SHOW statements + */ +- (IBAction)toggleShowSelectShowStatements:(id)sender +{ + // Store the state of the toggle for later quick reference + showSelectStatementsAreDisabled = ![sender intValue]; + + [self _updateFilterState]; +} + +/** + * Shows the supplied message in the console. + */ - (void)showMessageInConsole:(NSString *)message { - [self _appendMessageToConsole:message withColor:[NSColor blackColor]]; + [self _addMessageToConsole:message isError:NO]; } -// ------------------------------------------------------------------------------- -// showErrorInConsole: -// -// Shows the supplied error in the console. -// ------------------------------------------------------------------------------- +/** + * Shows the supplied error in the console. + */ - (void)showErrorInConsole:(NSString *)error { - [self _appendMessageToConsole:error withColor:[NSColor redColor]]; + [self _addMessageToConsole:error isError:YES]; } -// ------------------------------------------------------------------------------- -// consoleTextView -// -// Return a reference to the console's text view. -// ------------------------------------------------------------------------------- -- (NSTextView *)consoleTextView +/** + * Returns the number of messages currently in the console. + */ +- (int)consoleMessageCount { - return consoleTextView; + return [messagesFullSet count]; } -// ------------------------------------------------------------------------------- -// savePanelDidEnd:returnCode:contextInfo: -// -// Called when the NSSavePanel sheet ends. -// ------------------------------------------------------------------------------- +/** + * Called when the NSSavePanel sheet ends. Writes the console's current content to the selected file if required. + */ - (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo { if (returnCode == NSOKButton) { - [[[consoleTextView textStorage] string] writeToFile:[sheet filename] atomically:YES encoding:NSUTF8StringEncoding error:NULL]; + [[self _getConsoleStringWithTimeStamps:[includeTimeStampsButton intValue]] writeToFile:[sheet filename] atomically:YES encoding:NSUTF8StringEncoding error:NULL]; + } +} + +#pragma mark - +#pragma mark Tableview delegate methods + +/** + * Table view delegate method. Returns the number of rows in the table veiw. + */ +- (int)numberOfRowsInTableView:(NSTableView *)tableView +{ + return [messagesVisibleSet count]; +} + +/** + * Table view delegate method. Returns the specific object for the request column and row. + */ +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row +{ + NSString *returnValue = nil; + + id object = [[messagesVisibleSet objectAtIndex:row] valueForKey:[tableColumn identifier]]; + + if ([[tableColumn identifier] isEqualToString:TABLEVIEW_DATE_COLUMN_IDENTIFIER]) { + + NSString *dateString = [(NSDate *)object descriptionWithCalendarFormat:MESSAGE_TIME_STAMP_FORMAT timeZone:nil locale:nil]; + + returnValue = [NSString stringWithFormat:@"/* MySQL %@ */", dateString]; + } + else { + if ([(NSString *)object length] > MESSAGE_TRUNCATE_CHARACTER_LENGTH) { + object = [NSString stringWithFormat:@"%@...", [object substringToIndex:MESSAGE_TRUNCATE_CHARACTER_LENGTH]]; + } + + returnValue = object; + } + + NSMutableDictionary *stringAtributes = nil; + + if (consoleFont) { + stringAtributes = [NSMutableDictionary dictionaryWithObject:consoleFont forKey:NSFontAttributeName]; + } + + // If this is an error message give it a red colour + if ([(SPConsoleMessage *)[messagesVisibleSet objectAtIndex:row] isError]) { + if (stringAtributes) { + [stringAtributes setObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; + } + else { + stringAtributes = [NSMutableDictionary dictionaryWithObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; + } } + + return [[[NSAttributedString alloc] initWithString:returnValue attributes:stringAtributes] autorelease]; +} + +#pragma mark - +#pragma mark Other + +/** + * Called whenver the test within the search field changes. + */ +- (void)controlTextDidChange:(NSNotification *)notification +{ + id object = [notification object]; + + if ([object isEqualTo:consoleSearchField]) { + + // Store the state of the text filter and the current filter string for later quick reference + [activeFilterString setString:[[object stringValue] lowercaseString]]; + filterIsActive = [activeFilterString length]?YES:NO; + + [self _updateFilterState]; + } +} + +/** + * Menu item validation for console table view contextual menu. + */ +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + if ([menuItem action] == @selector(copy:)) { + return ([consoleTableView numberOfSelectedRows] > 0); + } + + if ([menuItem action] == @selector(clearConsole:)) { + return ([self consoleMessageCount] > 0); + } + + return [[self window] validateMenuItem:menuItem]; +} + +- (NSFont *)consoleFont +{ + return consoleFont; +} + +- (void)setConsoleFont:(NSFont *)theFont +{ + if (consoleFont) [consoleFont release]; + consoleFont = [theFont copy]; +} + +/** + * Standard dealloc. + */ +- (void)dealloc +{ + messagesVisibleSet = nil; + + [messagesFullSet release], messagesFullSet = nil; + [messagesFilteredSet release], messagesFilteredSet = nil; + [activeFilterString release], activeFilterString = nil; + [consoleFont release], consoleFont = nil; + + [super dealloc]; } @end @implementation SPQueryConsole (PrivateAPI) -// ------------------------------------------------------------------------------- -// _appendMessageToConsole:withColor: -// -// Appeds the supplied string to the query console, coloring the text using the -// supplied color. -// ------------------------------------------------------------------------------- -- (void)_appendMessageToConsole:(NSString *)message withColor:(NSColor *)color -{ - int begin, end; - - // Set the selected range of the text view to be the very last character - [consoleTextView setSelectedRange:NSMakeRange([[consoleTextView string] length], 0)]; - begin = [[consoleTextView string] length]; - - // Apped the message to the current text storage using the text view's current typing attributes - [[consoleTextView textStorage] appendAttributedString:[[NSAttributedString alloc] initWithString:message attributes:[consoleTextView typingAttributes]]]; - end = [[consoleTextView string] length]; - - // Color the text we just added - [consoleTextView setTextColor:color range:NSMakeRange(begin, (end - begin))]; - - // Scroll to the text we just added - [consoleTextView scrollRangeToVisible:[consoleTextView selectedRange]]; +/** + * Creates and returns a string made entirely of all of the console's messages and includes the message + * time stamps if specified. + */ +- (NSString *)_getConsoleStringWithTimeStamps:(BOOL)timeStamps +{ + NSMutableString *consoleString = [[[NSMutableString alloc] init] autorelease]; + int i; + + for (i = 0; i < [messagesVisibleSet count]; i++) { + SPConsoleMessage *message = [messagesVisibleSet objectAtIndex:i]; + if (timeStamps) { + NSString *dateString = [[message messageDate] descriptionWithCalendarFormat:MESSAGE_TIME_STAMP_FORMAT timeZone:nil locale:nil]; + + [consoleString appendString:[NSString stringWithFormat:@"/* MySQL %@ */ ", dateString]]; + } + + [consoleString appendString:[NSString stringWithFormat:@"%@\n", [message message]]]; + } + + return consoleString; +} + + +/** + * Updates the filtered result set based on any filter string and whether or not + * all SELECT nd SHOW statements should be shown within the console. + */ +- (void)_updateFilterState +{ + int i; + + // Display start progress spinner + [progressIndicator setHidden:NO]; + [progressIndicator startAnimation:self]; + + // Don't allow clearing the console while filtering its content + [saveConsoleButton setEnabled:NO]; + [clearConsoleButton setEnabled:NO]; + + [messagesFilteredSet removeAllObjects]; + + // If filtering is disabled and all show/selects are shown, empty the filtered + // result set and set the full set to visible. + if (!filterIsActive && !showSelectStatementsAreDisabled) { + messagesVisibleSet = messagesFullSet; + + [consoleTableView reloadData]; + [consoleTableView scrollRowToVisible:([messagesVisibleSet count] - 1)]; + + [saveConsoleButton setEnabled:YES]; + [clearConsoleButton setEnabled:YES]; + + [saveConsoleButton setTitle:@"Save As..."]; + + // Hide progress spinner + [progressIndicator setHidden:YES]; + [progressIndicator stopAnimation:self]; + return; + } + + // Cache frequently used selector, avoiding dynamic binding overhead + IMP messageMatchesFilters = [self methodForSelector:@selector(_messageMatchesCurrentFilters:)]; + + // Loop through all the messages in the full set to determine which should be + // added to the filtered set. + for (i = 0; i < [messagesFullSet count]; i++) { + SPConsoleMessage *message = [messagesFullSet objectAtIndex:i]; + + // Add a reference to the message to the filtered set if filters are active and the + // current message matches them + if ((messageMatchesFilters)(self, @selector(_messageMatchesCurrentFilters:), [message message])) { + [messagesFilteredSet addObject:message]; + } + } + + // Ensure that the filtered set is marked as the currently visible set. + messagesVisibleSet = messagesFilteredSet; + + [consoleTableView reloadData]; + [consoleTableView scrollRowToVisible:([messagesVisibleSet count] - 1)]; + + if ([messagesVisibleSet count] > 0) { + [saveConsoleButton setEnabled:YES]; + [clearConsoleButton setEnabled:YES]; + } + + [saveConsoleButton setTitle:@"Save View As..."]; + + // Hide progress spinner + [progressIndicator setHidden:YES]; + [progressIndicator stopAnimation:self]; +} + +/** + * Adds the supplied message to the query console. + */ +- (void)_addMessageToConsole:(NSString *)message isError:(BOOL)error +{ + SPConsoleMessage *consoleMessage = [SPConsoleMessage consoleMessageWithMessage:[[message stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] stringByAppendingString:@";"] date:[NSDate date]]; + + [consoleMessage setIsError:error]; + + [messagesFullSet addObject:consoleMessage]; + + // If filtering is active, determine whether to add a reference to the filtered set + if ((showSelectStatementsAreDisabled || filterIsActive) + && [self _messageMatchesCurrentFilters:[consoleMessage message]]) + { + [messagesFilteredSet addObject:[messagesFullSet lastObject]]; + } + + // Reload the table and scroll to the new message + [consoleTableView reloadData]; + [consoleTableView scrollRowToVisible:([messagesVisibleSet count] - 1)]; +} + +/** + * Checks whether the supplied message text matches the current filter text, if any, + * and whether it should be hidden if the SELECT/SHOW toggle is off. + */ +- (BOOL)_messageMatchesCurrentFilters:(NSString *)message +{ + BOOL messageMatchesCurrentFilters = YES; + + // Check whether to hide the message based on the current filter text, if any + if (filterIsActive + && [message rangeOfString:activeFilterString options:NSCaseInsensitiveSearch].location == NSNotFound) + { + messageMatchesCurrentFilters = NO; + } + + // If hiding SELECTs and SHOWs is toggled to on, check whether the message is a SELECT or SHOW + if (messageMatchesCurrentFilters + && showSelectStatementsAreDisabled + && ([message hasPrefix:@"SELECT"] || [message hasPrefix:@"SHOW"])) + { + messageMatchesCurrentFilters = NO; + } + + return messageMatchesCurrentFilters; } @end diff --git a/Source/SPSQLParser.h b/Source/SPSQLParser.h index 14ac6f9d..be068c16 100644 --- a/Source/SPSQLParser.h +++ b/Source/SPSQLParser.h @@ -23,6 +23,12 @@ #import +/* + * Define the length of the character cache to use when parsing instead of accessing + * via characterAtIndex:. There is a balance here between updating the cache very + * often and access penalties; 1500 appears a reasonable compromise. + */ +#define CHARACTER_CACHE_LENGTH 1500 /* * This class provides a string class intended for SQL parsing. It extends NSMutableString, @@ -53,6 +59,9 @@ @interface SPSQLParser : NSMutableString { id string; + unichar *stringCharCache; + long charCacheStart; + long charCacheEnd; } @@ -210,6 +219,15 @@ typedef enum _SPCommentTypes { - (long) endIndexOfStringQuotedByCharacter:(unichar)quoteCharacter startingAtIndex:(long)index; - (long) endIndexOfCommentOfType:(SPCommentType)commentType startingAtIndex:(long)index; +/* + * Cacheing methods to enable a faster alternative to characterAtIndex: when walking strings, and overrides to update. + */ +- (unichar) charAtIndex:(long)index; +- (void) clearCharCache; +- (void) deleteCharactersInRange:(NSRange)aRange; +- (void) insertString:(NSString *)aString atIndex:(int)anIndex; + + /* Required and primitive methods to allow subclassing class cluster */ #pragma mark - diff --git a/Source/SPSQLParser.m b/Source/SPSQLParser.m index 9828f529..ad749fc7 100644 --- a/Source/SPSQLParser.m +++ b/Source/SPSQLParser.m @@ -60,11 +60,11 @@ case '-': if (stringLength < currentStringIndex + 2) break; if ([string characterAtIndex:currentStringIndex+1] != '-') break; - if (![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[string characterAtIndex:currentStringIndex+2]]) break; + if (![[NSCharacterSet whitespaceCharacterSet] characterIsMember:[string characterAtIndex:currentStringIndex+2]]) break; commentEndIndex = [self endIndexOfCommentOfType:SPDoubleDashComment startingAtIndex:currentStringIndex]; // Remove the comment - [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + [self deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; stringLength -= commentEndIndex - currentStringIndex + 1; currentStringIndex--; break; @@ -73,7 +73,7 @@ commentEndIndex = [self endIndexOfCommentOfType:SPHashComment startingAtIndex:currentStringIndex]; // Remove the comment - [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + [self deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; stringLength -= commentEndIndex - currentStringIndex + 1; currentStringIndex--; break; @@ -85,7 +85,7 @@ commentEndIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex]; // Remove the comment - [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + [self deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; stringLength -= commentEndIndex - currentStringIndex + 1; currentStringIndex--; break; @@ -158,7 +158,7 @@ if (stringIndex == NSNotFound) return NO; // If it has been found, trim the string appropriately and return YES - [string deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusive?1:0))]; + [self deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusive?1:0))]; return YES; } @@ -213,7 +213,7 @@ // Select the appropriate string range, truncate the current string, and return the selected string resultString = [NSString stringWithString:[string substringWithRange:NSMakeRange(0, stringIndex + (inclusiveReturn?1:0))]]; - [string deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusiveTrim?1:0))]; + [self deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusiveTrim?1:0))]; return resultString; } @@ -255,7 +255,7 @@ - (NSString *) stringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter inclusively:(BOOL)inclusive skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings { long fromCharacterIndex, toCharacterIndex; - + // Look for the first occurrence of the from: character fromCharacterIndex = [self firstOccurrenceOfCharacter:fromCharacter afterIndex:-1 skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; if (fromCharacterIndex == NSNotFound) return nil; @@ -318,7 +318,7 @@ // Select the correct part of the string, truncate the current string, and return the selected string. resultString = [string substringWithRange:NSMakeRange(fromCharacterIndex + (inclusiveReturn?0:1), toCharacterIndex + (inclusiveReturn?1:-1) - fromCharacterIndex)]; - [string deleteCharactersInRange:NSMakeRange(fromCharacterIndex + (inclusiveTrim?0:1), toCharacterIndex + (inclusiveTrim?1:-1) - fromCharacterIndex)]; + [self deleteCharactersInRange:NSMakeRange(fromCharacterIndex + (inclusiveTrim?0:1), toCharacterIndex + (inclusiveTrim?1:-1) - fromCharacterIndex)]; return resultString; } @@ -358,16 +358,14 @@ NSMutableArray *resultsArray = [NSMutableArray array]; long stringIndex = -1, nextIndex = 0; - // Walk through the string finding the character to split by, and add non-zero length strings. + // Walk through the string finding the character to split by, and add all strings to the array. while (1) { nextIndex = [self firstOccurrenceOfCharacter:character afterIndex:stringIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; if (nextIndex == NSNotFound) { break; } - if (nextIndex - stringIndex - 1 > 0) { - [resultsArray addObject:[string substringWithRange:NSMakeRange(stringIndex+1, nextIndex - stringIndex - 1)]]; - } + [resultsArray addObject:[string substringWithRange:NSMakeRange(stringIndex+1, nextIndex - stringIndex - 1)]]; stringIndex = nextIndex; } @@ -408,12 +406,16 @@ long stringLength = [string length]; int bracketingLevel = 0; + // Cache frequently used selectors, avoiding dynamic binding overhead + IMP charAtIndex = [self methodForSelector:@selector(charAtIndex:)]; + IMP endIndex = [self methodForSelector:@selector(endIndexOfStringQuotedByCharacter:startingAtIndex:)]; + // Sanity check inputs if (startIndex < -1) startIndex = -1; // Walk along the string, processing characters for (currentStringIndex = startIndex + 1; currentStringIndex < stringLength; currentStringIndex++) { - currentCharacter = [string characterAtIndex:currentStringIndex]; + currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex); // Check for the ending character, and if it has been found and quoting/brackets is valid, return. if (currentCharacter == character) { @@ -430,7 +432,7 @@ case '"': case '`': if (!ignoreQuotedStrings) break; - quotedStringEndIndex = [self endIndexOfStringQuotedByCharacter:currentCharacter startingAtIndex:currentStringIndex+1]; + quotedStringEndIndex = (long)(*endIndex)(self, @selector(endIndexOfStringQuotedByCharacter:startingAtIndex:), currentCharacter, currentStringIndex+1); if (quotedStringEndIndex == NSNotFound) { return NSNotFound; } @@ -449,8 +451,8 @@ // For comments starting "--[\s]", ensure the start syntax is valid before proceeding. case '-': if (stringLength < currentStringIndex + 2) break; - if ([string characterAtIndex:currentStringIndex+1] != '-') break; - if (![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[string characterAtIndex: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; currentStringIndex = [self endIndexOfCommentOfType:SPDoubleDashComment startingAtIndex:currentStringIndex]; break; @@ -461,7 +463,7 @@ // For comments starting "/*", ensure the start syntax is valid before proceeding. case '/': if (stringLength < currentStringIndex + 1) break; - if ([string characterAtIndex:currentStringIndex+1] != '*') break; + if ((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) != '*') break; currentStringIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex]; break; } @@ -480,17 +482,20 @@ BOOL characterIsEscaped; unichar currentCharacter; + // Cache the charAtIndex selector, avoiding dynamic binding overhead + IMP charAtIndex = [self methodForSelector:@selector(charAtIndex:)]; + stringLength = [string length]; // Walk the string looking for the string end for ( currentStringIndex = index; currentStringIndex < stringLength; currentStringIndex++) { - currentCharacter = [string characterAtIndex:currentStringIndex]; + currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), 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 && [string characterAtIndex:currentStringIndex+1] == '`') { + if ((currentStringIndex + 1) < stringLength && (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) == '`') { currentStringIndex++; continue; } @@ -504,7 +509,7 @@ characterIsEscaped = NO; i = 1; quotedStringLength = currentStringIndex - 1; - while ((quotedStringLength - i) > 0 && [string characterAtIndex:currentStringIndex - i] == '\\') { + while ((quotedStringLength - i) > 0 && (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex - i) == '\\') { characterIsEscaped = !characterIsEscaped; i++; } @@ -512,7 +517,7 @@ // 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 && [string characterAtIndex:currentStringIndex+1] == quoteCharacter) { + if ((currentStringIndex + 1) < stringLength && (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) == quoteCharacter) { currentStringIndex++; continue; } @@ -534,6 +539,9 @@ long stringLength = [string length]; unichar currentCharacter; + // Cache the charAtIndex selector, avoiding dynamic binding overhead + IMP charAtIndex = [self methodForSelector:@selector(charAtIndex:)]; + switch (commentType) { // For comments of type "--[\s]", start the comment processing two characters in to match the start syntax, @@ -545,7 +553,7 @@ case SPHashComment: index++; for ( ; index < stringLength; index++ ) { - currentCharacter = [string characterAtIndex:index]; + currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), index); if (currentCharacter == '\r' || currentCharacter == '\n') { return index-1; } @@ -557,8 +565,8 @@ case SPCStyleComment: index = index+2; for ( ; index < stringLength; index++ ) { - if ([string characterAtIndex:index] == '*') { - if ((stringLength > index + 1) && [string characterAtIndex:index+1] == '/') { + if ((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), index) == '*') { + if ((stringLength > index + 1) && (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), index+1) == '/') { return (index+1); } } @@ -569,6 +577,52 @@ return (stringLength-1); } +/* + * Provide a method to retrieve a character from the local cache. + * Does no bounds checking on the underlying string, and so is kept + * separate for characterAtIndex:. + */ +- (unichar) charAtIndex:(long)index +{ + + // If the current cache doesn't include the current character, update it. + if (index > charCacheEnd || index < charCacheStart) { + if (charCacheEnd > -1) { + free(stringCharCache); + } + unsigned int remainingStringLength = [string length] - index; + unsigned int newcachelength = (CHARACTER_CACHE_LENGTH < remainingStringLength)?CHARACTER_CACHE_LENGTH:remainingStringLength; + stringCharCache = (unichar *)calloc(newcachelength, sizeof(unichar)); + [string getCharacters:stringCharCache range:NSMakeRange(index, newcachelength)]; + charCacheEnd = index + newcachelength - 1; + charCacheStart = index; + } + return stringCharCache[index - charCacheStart]; +} + +/* + * Provide a method to clear the cache, and use it when updating the string. + */ +- (void) clearCharCache +{ + if (charCacheEnd > -1) { + free(stringCharCache); + } + charCacheEnd = -1; + charCacheStart = 0; +} +- (void) deleteCharactersInRange:(NSRange)aRange +{ + [super deleteCharactersInRange:aRange]; + [self clearCharCache]; +} +- (void) insertString:(NSString *)aString atIndex:(int)anIndex +{ + [super insertString:aString atIndex:anIndex]; + [self clearCharCache]; +} + + /* Required and primitive methods to allow subclassing class cluster */ #pragma mark - @@ -576,45 +630,53 @@ if (self = [super init]) { string = [[NSMutableString string] retain]; } + charCacheEnd = -1; return self; } - (id) initWithBytes:(const void *)bytes length:(unsigned int)length encoding:(NSStringEncoding)encoding { if (self = [super init]) { string = [[NSMutableString alloc] initWithBytes:bytes length:length encoding:encoding]; } + charCacheEnd = -1; return self; } - (id) initWithBytesNoCopy:(void *)bytes length:(unsigned int)length encoding:(NSStringEncoding)encoding freeWhenDone:(BOOL)flag { if (self = [super init]) { string = [[NSMutableString alloc] initWithBytesNoCopy:bytes length:length encoding:encoding freeWhenDone:flag]; } + charCacheEnd = -1; return self; } - (id) initWithCapacity:(unsigned int)capacity { if (self = [super init]) { string = [[NSMutableString stringWithCapacity:capacity] retain]; } + charCacheEnd = -1; return self; } - (id) initWithCharactersNoCopy:(unichar *)characters length:(unsigned int)length freeWhenDone:(BOOL)flag { if (self = [super init]) { string = [[NSMutableString alloc] initWithCharactersNoCopy:characters length:length freeWhenDone:flag]; } + charCacheEnd = -1; return self; } - (id) initWithContentsOfFile:(id)path { + charCacheEnd = -1; return [self initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; } - (id) initWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)encoding error:(NSError **)error { if (self = [super init]) { string = [[NSMutableString alloc] initWithContentsOfFile:path encoding:encoding error:error]; } + charCacheEnd = -1; return self; } - (id) initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding { if (self = [super init]) { string = [[NSMutableString alloc] initWithCString:nullTerminatedCString encoding:encoding]; } + charCacheEnd = -1; return self; } - (id) initWithFormat:(NSString *)format, ... { @@ -622,12 +684,14 @@ va_start(argList, format); id str = [self initWithFormat:format arguments:argList]; va_end(argList); + charCacheEnd = -1; return str; } - (id) initWithFormat:(NSString *)format arguments:(va_list)argList { if (self = [super init]) { string = [[NSMutableString alloc] initWithFormat:format arguments:argList]; } + charCacheEnd = -1; return self; } - (unsigned int) length { @@ -641,15 +705,19 @@ } - (unsigned int) replaceOccurrencesOfString:(NSString *)target withString:(NSString *)replacement options:(unsigned)options range:(NSRange)searchRange { return [string replaceOccurrencesOfString:target withString:replacement options:options range:searchRange]; + [self clearCharCache]; } - (void) setString:(NSString *)aString { [string setString:aString]; + [self clearCharCache]; } - (void) replaceCharactersInRange:(NSRange)range withString:(NSString *)aString { [string replaceCharactersInRange:range withString:aString]; + [self clearCharCache]; } - (void) dealloc { [string release]; + if (charCacheEnd != -1) free(stringCharCache); [super dealloc]; } @end \ No newline at end of file diff --git a/Source/SPStringAdditions.h b/Source/SPStringAdditions.h index 4acd748c..1d706666 100644 --- a/Source/SPStringAdditions.h +++ b/Source/SPStringAdditions.h @@ -25,6 +25,10 @@ @interface NSString (SPStringAdditions) + (NSString *)stringForByteSize:(int)byteSize; ++ (NSString *)stringForTimeInterval:(float)timeInterval; + +- (NSString *)backtickQuotedString; +- (NSArray *)lineRangesForRange:(NSRange)aRange; #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 - (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)set; diff --git a/Source/SPStringAdditions.m b/Source/SPStringAdditions.m index 2916611d..e0bf65cf 100644 --- a/Source/SPStringAdditions.m +++ b/Source/SPStringAdditions.m @@ -24,11 +24,9 @@ @implementation NSString (SPStringAdditions) -// ------------------------------------------------------------------------------- -// stringForByteSize: -// -// Returns a human readable version string of the supplied byte size. -// ------------------------------------------------------------------------------- +/* + * Returns a human readable version string of the supplied byte size. + */ + (NSString *)stringForByteSize:(int)byteSize { float size = byteSize; @@ -66,37 +64,148 @@ return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:size]]; } + +// ------------------------------------------------------------------------------- +// stringForTimeInterval: +// +// Returns a human readable version string of the supplied time interval. +// ------------------------------------------------------------------------------- ++ (NSString *)stringForTimeInterval:(float)timeInterval +{ + NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease]; + + [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; + + if (timeInterval < 1) { + timeInterval = (timeInterval * 1000); + [numberFormatter setFormat:@"#,##0 ms"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + if (timeInterval < 10) { + [numberFormatter setFormat:@"#,##0.00 s"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + if (timeInterval < 100) { + [numberFormatter setFormat:@"#,##0.0 s"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + if (timeInterval < 300) { + [numberFormatter setFormat:@"#,##0 s"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + if (timeInterval < 3600) { + timeInterval = (timeInterval / 60); + [numberFormatter setFormat:@"#,##0 min"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + timeInterval = (timeInterval / 3600); + [numberFormatter setFormat:@"#,##0 hours"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; +} + + +// ------------------------------------------------------------------------------- +// backtickQuotedString +// +// Returns the string quoted with backticks as required for MySQL identifiers +// eg.: tablename => `tablename` +// my`table => `my``table` +// ------------------------------------------------------------------------------- +- (NSString *)backtickQuotedString +{ + // mutableCopy automatically retains the returned string, so don't forget to release it later... + NSMutableString *workingCopy = [self mutableCopy]; + + // First double all backticks in the string to escape them + // I don't want to use "stringByReplacingOccurrencesOfString:withString:" because it's only available in 10.5 + [workingCopy replaceOccurrencesOfString: @"`" + withString: @"``" + options: NSLiteralSearch + range: NSMakeRange(0, [workingCopy length]) ]; + + // Add the quotes around the string + NSString *quotedString = [NSString stringWithFormat: @"`%@`", workingCopy]; + + [workingCopy release]; + + return quotedString; +} + +// ------------------------------------------------------------------------------- +// lineRangesForRange +// +// Returns an array of serialised NSRanges, each representing a line within the string +// which is at least partially covered by the NSRange supplied. +// Each line includes the line termination character(s) for the line. As per +// lineRangeForRange, lines are split by CR, LF, CRLF, U+2028 (Unicode line separator), +// or U+2029 (Unicode paragraph separator). +// ------------------------------------------------------------------------------- +- (NSArray *)lineRangesForRange:(NSRange)aRange +{ + NSMutableArray *lineRangesArray = [NSMutableArray array]; + NSRange currentLineRange; + + // Check that the range supplied is valid - if not return an empty array. + if (aRange.location == NSNotFound || aRange.location + aRange.length > [self length]) + return lineRangesArray; + + // Get the range of the first string covered by the specified range, and add it to the array + currentLineRange = [self lineRangeForRange:NSMakeRange(aRange.location, 0)]; + [lineRangesArray addObject:NSStringFromRange(currentLineRange)]; + + // Loop through until the line end matches or surpasses the end of the specified range + while (currentLineRange.location + currentLineRange.length < aRange.location + aRange.length) { + currentLineRange = [self lineRangeForRange:NSMakeRange(currentLineRange.location + currentLineRange.length, 0)]; + [lineRangesArray addObject:NSStringFromRange(currentLineRange)]; + } + + // Return the constructed array of ranges + return lineRangesArray; +} + + #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 - // ------------------------------------------------------------------------------- - // componentsSeparatedByCharactersInSet: - // Credit - Greg Hulands - // Needed for 10.4+ compatibility - // ------------------------------------------------------------------------------- - - (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)set // 10.5 adds this to NSString, but we are 10.4+ - { - NSMutableArray *result = [NSMutableArray array]; - NSScanner *scanner = [NSScanner scannerWithString:self]; - NSString *chunk = nil; - - [scanner setCharactersToBeSkipped:nil]; - BOOL sepFound = [scanner scanCharactersFromSet:set intoString:(NSString **)nil]; // skip any preceding separators - - if (sepFound) { // if initial separator, start with empty component - [result addObject:@""]; - } - - while ([scanner scanUpToCharactersFromSet:set intoString:&chunk]) { - [result addObject:chunk]; - sepFound = [scanner scanCharactersFromSet: set intoString: (NSString **) nil]; - } - - if (sepFound) { // if final separator, end with empty component - [result addObject: @""]; - } - - result = [result copy]; - return [result autorelease]; +/* + * componentsSeparatedByCharactersInSet: + * Credit - Greg Hulands + * Needed for 10.4+ compatibility + */ +- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)set // 10.5 adds this to NSString, but we are 10.4+ +{ + NSMutableArray *result = [NSMutableArray array]; + NSScanner *scanner = [NSScanner scannerWithString:self]; + NSString *chunk = nil; + + [scanner setCharactersToBeSkipped:nil]; + BOOL sepFound = [scanner scanCharactersFromSet:set intoString:(NSString **)nil]; // skip any preceding separators + + if (sepFound) { // if initial separator, start with empty component + [result addObject:@""]; + } + + while ([scanner scanUpToCharactersFromSet:set intoString:&chunk]) { + [result addObject:chunk]; + sepFound = [scanner scanCharactersFromSet: set intoString: (NSString **) nil]; } + + if (sepFound) { // if final separator, end with empty component + [result addObject: @""]; + } + + result = [result copy]; + return [result autorelease]; +} #endif @end diff --git a/Source/SPTableData.h b/Source/SPTableData.h index 0a3a8883..099b1e22 100644 --- a/Source/SPTableData.h +++ b/Source/SPTableData.h @@ -42,6 +42,7 @@ - (NSDictionary *) columnWithName:(NSString *)colName; - (NSArray *) columnNames; - (NSDictionary *) columnAtIndex:(int)index; +- (BOOL) columnIsBlobOrText:(NSString *)colName; - (NSString *) statusValueForKey:(NSString *)aKey; - (NSDictionary *) statusValues; - (void) resetAllData; diff --git a/Source/SPTableData.m b/Source/SPTableData.m index 1ff4917c..b791563b 100644 --- a/Source/SPTableData.m +++ b/Source/SPTableData.m @@ -28,6 +28,7 @@ #import "SPSQLParser.h" #import "TableDocument.h" #import "TablesList.h" +#import "SPStringAdditions.h" @implementation SPTableData @@ -130,6 +131,24 @@ return [columns objectAtIndex:index]; } +/* + * Checks if this column is type text or blob. + * Used to determine if we have to show a popup when we edit a value from this column. + */ + +- (BOOL) columnIsBlobOrText:(NSString *)colName +{ + if ([columns count] == 0) { + if ([tableListInstance tableType] == SP_TABLETYPE_VIEW) { + [self updateInformationForCurrentView]; + } else { + [self updateInformationForCurrentTable]; + } + } + + return (BOOL) ([[[self columnWithName:colName] objectForKey:@"typegrouping"] isEqualToString:@"textdata" ] || [[[self columnWithName:colName] objectForKey:@"typegrouping"] isEqualToString:@"blobdata"]); +} + /* * Retrieve the table status value for a supplied key, using or refreshing the cache as appropriate. @@ -241,11 +260,13 @@ if ([tableName isEqualToString:@""] || !tableName) return nil; // Retrieve the CREATE TABLE syntax for the table - CMMCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", tableName]]; + CMMCPResult *theResult = [mySQLConnection queryString: [NSString stringWithFormat: @"SHOW CREATE TABLE %@", + [tableName backtickQuotedString] + ]]; // Check for any errors, but only display them if a connection still exists if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - if (![mySQLConnection isConnected]) { + if ([mySQLConnection isConnected]) { NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while retrieving table information:\n\n%@", [mySQLConnection getLastErrorMessage]], @"OK", nil, nil); } return nil; @@ -284,13 +305,34 @@ // If the first character is a backtick, this is a field definition. if ([fieldsParser characterAtIndex:0] =='`') { - - // Capture the area between the two backticks as the name - [tableColumn setObject:[fieldsParser trimAndReturnStringFromCharacter:'`' toCharacter:'`' trimmingInclusively:YES returningInclusively:NO ignoringQuotedStrings:NO] forKey:@"name"]; + + // Capture the area between the two backticks as the name + NSString *fieldName = [fieldsParser trimAndReturnStringFromCharacter: '`' + toCharacter: '`' + trimmingInclusively: YES + returningInclusively: NO + ignoringQuotedStrings: NO]; + //if the next character is again a backtick, we stumbled across an escaped backtick. we have to continue parsing. + while ([fieldsParser characterAtIndex:0] =='`') { + fieldName = [fieldName stringByAppendingFormat: @"`%@", + [fieldsParser trimAndReturnStringFromCharacter: '`' + toCharacter: '`' + trimmingInclusively: YES + returningInclusively: NO + ignoringQuotedStrings: NO] + ]; + } + + [tableColumn setObject:fieldName forKey:@"name"]; // Split the remaining field definition string by spaces and process [tableColumn addEntriesFromDictionary:[self parseFieldDefinitionStringParts:[fieldsParser splitStringByCharacter:' ' skippingBrackets:YES]]]; - + + //if column is not null, but doesn't have a default value, set empty string + if([[tableColumn objectForKey:@"null"] intValue] == 0 && [[tableColumn objectForKey:@"autoincrement"] intValue] == 0 && ![tableColumn objectForKey:@"default"]) { + [tableColumn setObject:@"" forKey:@"default"]; + } + // Store the column. [tableColumns addObject:[NSDictionary dictionaryWithDictionary:tableColumn]]; @@ -393,11 +435,11 @@ if ([viewName isEqualToString:@""] || !viewName) return nil; // Retrieve the SHOW COLUMNS syntax for the table - CMMCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM `%@`", viewName]]; + CMMCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [viewName backtickQuotedString]]]; // Check for any errors, but only display them if a connection still exists if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - if (![mySQLConnection isConnected]) { + if ([mySQLConnection isConnected]) { NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while retrieving view information:\n\n%@", [mySQLConnection getLastErrorMessage]], @"OK", nil, nil); } return nil; @@ -431,7 +473,7 @@ // Select the column default if available if ([resultRow objectForKey:@"Default"]) { if ([[resultRow objectForKey:@"Default"] isNSNull]) { - [tableColumn setValue:[NSString stringWithString:[[NSUserDefaults standardUserDefaults] objectForKey:@"nullValue"]] forKey:@"default"]; + [tableColumn setValue:[NSString stringWithString:[[NSUserDefaults standardUserDefaults] objectForKey:@"NullValue"]] forKey:@"default"]; } else { [tableColumn setValue:[NSString stringWithString:[resultRow objectForKey:@"Default"]] forKey:@"default"]; } @@ -477,9 +519,11 @@ // Run the status query and retrieve as a dictionary. CMMCPResult *tableStatusResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW TABLE STATUS LIKE '%@'", [tableListInstance tableName]]]; - // Check for any errors + // Check for any errors, only displaying them if the connection hasn't been terminated if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while retrieving table status:\n\n%@", [mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + if ([mySQLConnection isConnected]) { + NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while retrieving table status:\n\n%@", [mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + } return FALSE; } @@ -508,12 +552,18 @@ NSMutableDictionary *fieldDetails = [[NSMutableDictionary alloc] init]; NSMutableArray *detailParts; NSString *detailString; - int i, partsArrayLength; + int i, definitionPartsIndex = 0, partsArrayLength; if (![definitionParts count]) return [NSDictionary dictionary]; + // Skip blank items within the definition parts + while (definitionPartsIndex < [definitionParts count] + && ![[[definitionParts objectAtIndex:definitionPartsIndex] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length]) + definitionPartsIndex++; + // The first item is always the data type. - [fieldParser setString:[definitionParts objectAtIndex:0]]; + [fieldParser setString:[definitionParts objectAtIndex:definitionPartsIndex]]; + definitionPartsIndex++; // If no field length definition is present, store only the type if ([fieldParser firstOccurrenceOfCharacter:'(' ignoringQuotedStrings:YES] == NSNotFound) { @@ -554,6 +604,7 @@ } [detailParser release]; } + [fieldParser release]; // Also capture a general column type "group" to allow behavioural switches detailString = [[NSString alloc] initWithString:[fieldDetails objectForKey:@"type"]]; @@ -583,6 +634,7 @@ } else { [fieldDetails setObject:@"blobdata" forKey:@"typegrouping"]; } + [detailString release]; // Set up some column defaults for all columns [fieldDetails setValue:[NSNumber numberWithBool:YES] forKey:@"null"]; @@ -593,8 +645,8 @@ // Walk through the remaining column definition parts storing recognised details partsArrayLength = [definitionParts count]; - for (i = 1; i < partsArrayLength; i++) { - detailString = [[NSString alloc] initWithString:[[definitionParts objectAtIndex:i] uppercaseString]]; + for ( ; definitionPartsIndex < partsArrayLength; definitionPartsIndex++) { + detailString = [[NSString alloc] initWithString:[[definitionParts objectAtIndex:definitionPartsIndex] uppercaseString]]; // Whether numeric fields are unsigned if ([detailString isEqualToString:@"UNSIGNED"]) { @@ -609,30 +661,30 @@ [fieldDetails setValue:[NSNumber numberWithBool:YES] forKey:@"binary"]; // Whether text types have a different encoding to the table - } else if ([detailString isEqualToString:@"CHARSET"] && (i + 1 < partsArrayLength)) { - if (![[[definitionParts objectAtIndex:i+1] uppercaseString] isEqualToString:@"DEFAULT"]) { - [fieldDetails setValue:[definitionParts objectAtIndex:i+1] forKey:@"encoding"]; + } else if ([detailString isEqualToString:@"CHARSET"] && (definitionPartsIndex + 1 < partsArrayLength)) { + if (![[[definitionParts objectAtIndex:definitionPartsIndex+1] uppercaseString] isEqualToString:@"DEFAULT"]) { + [fieldDetails setValue:[definitionParts objectAtIndex:definitionPartsIndex+1] forKey:@"encoding"]; } - i++; - } else if ([detailString isEqualToString:@"CHARACTER"] && (i + 2 < partsArrayLength) - && [[[definitionParts objectAtIndex:i+1] uppercaseString] isEqualToString:@"SET"]) { - if (![[[definitionParts objectAtIndex:i+2] uppercaseString] isEqualToString:@"DEFAULT"]) {; - [fieldDetails setValue:[definitionParts objectAtIndex:i+2] forKey:@"encoding"]; + definitionPartsIndex++; + } else if ([detailString isEqualToString:@"CHARACTER"] && (definitionPartsIndex + 2 < partsArrayLength) + && [[[definitionParts objectAtIndex:definitionPartsIndex+1] uppercaseString] isEqualToString:@"SET"]) { + if (![[[definitionParts objectAtIndex:definitionPartsIndex+2] uppercaseString] isEqualToString:@"DEFAULT"]) {; + [fieldDetails setValue:[definitionParts objectAtIndex:definitionPartsIndex+2] forKey:@"encoding"]; } - i = i + 2; + definitionPartsIndex += 2; // Whether text types have a different collation to the table - } else if ([detailString isEqualToString:@"COLLATE"] && (i + 1 < partsArrayLength)) { - if (![[[definitionParts objectAtIndex:i+1] uppercaseString] isEqualToString:@"DEFAULT"]) { - [fieldDetails setValue:[definitionParts objectAtIndex:i+1] forKey:@"collation"]; + } else if ([detailString isEqualToString:@"COLLATE"] && (definitionPartsIndex + 1 < partsArrayLength)) { + if (![[[definitionParts objectAtIndex:definitionPartsIndex+1] uppercaseString] isEqualToString:@"DEFAULT"]) { + [fieldDetails setValue:[definitionParts objectAtIndex:definitionPartsIndex+1] forKey:@"collation"]; } - i++; + definitionPartsIndex++; // Whether fields are NOT NULL - } else if ([detailString isEqualToString:@"NOT"] && (i + 1 < partsArrayLength) - && [[[definitionParts objectAtIndex:i+1] uppercaseString] isEqualToString:@"NULL"]) { + } else if ([detailString isEqualToString:@"NOT"] && (definitionPartsIndex + 1 < partsArrayLength) + && [[[definitionParts objectAtIndex:definitionPartsIndex+1] uppercaseString] isEqualToString:@"NULL"]) { [fieldDetails setValue:[NSNumber numberWithBool:NO] forKey:@"null"]; - i++; + definitionPartsIndex++; // Whether fields are NULL } else if ([detailString isEqualToString:@"NULL"]) { @@ -643,11 +695,11 @@ [fieldDetails setValue:[NSNumber numberWithBool:YES] forKey:@"autoincrement"]; // Field defaults - } else if ([detailString isEqualToString:@"DEFAULT"] && (i + 1 < partsArrayLength)) { - detailParser = [[SPSQLParser alloc] initWithString:[definitionParts objectAtIndex:i+1]]; + } else if ([detailString isEqualToString:@"DEFAULT"] && (definitionPartsIndex + 1 < partsArrayLength)) { + detailParser = [[SPSQLParser alloc] initWithString:[definitionParts objectAtIndex:definitionPartsIndex+1]]; [fieldDetails setValue:[detailParser unquotedString] forKey:@"default"]; [detailParser release]; - i++; + definitionPartsIndex++; } // TODO: Currently unhandled: [UNIQUE | PRIMARY] KEY | COMMENT 'foo' | COLUMN_FORMAT bar | STORAGE q | REFERENCES... diff --git a/Source/SPTableInfo.m b/Source/SPTableInfo.m index cb494ea1..c87a337d 100644 --- a/Source/SPTableInfo.m +++ b/Source/SPTableInfo.m @@ -155,7 +155,7 @@ - (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { if ((rowIndex > 0) && [[aTableColumn identifier] isEqualToString:@"info"]) { - [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"TablePropertyIcon"]]; + [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"table-property"]]; [(ImageAndTextCell*)aCell setIndentationLevel:1]; } else { [(ImageAndTextCell*)aCell setImage:nil]; diff --git a/Source/SPTextViewAdditions.h b/Source/SPTextViewAdditions.h new file mode 100644 index 00000000..95075165 --- /dev/null +++ b/Source/SPTextViewAdditions.h @@ -0,0 +1,39 @@ +// +// SPTextViewAdditions.h +// sequel-pro +// +// Created by Hans-Jörg Bibiko on April 05, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import + +@interface NSTextView (SPTextViewAdditions) + +- (IBAction)selectCurrentWord:(id)sender; +- (IBAction)selectCurrentLine:(id)sender; +- (IBAction)doSelectionUpperCase:(id)sender; +- (IBAction)doSelectionLowerCase:(id)sender; +- (IBAction)doSelectionTitleCase:(id)sender; +- (IBAction)doDecomposedStringWithCanonicalMapping:(id)sender; +- (IBAction)doDecomposedStringWithCompatibilityMapping:(id)sender; +- (IBAction)doPrecomposedStringWithCanonicalMapping:(id)sender; +- (IBAction)doPrecomposedStringWithCompatibilityMapping:(id)sender; +- (IBAction)doTranspose:(id)sender; +- (IBAction)doRemoveDiacritics:(id)sender; + +@end \ No newline at end of file diff --git a/Source/SPTextViewAdditions.m b/Source/SPTextViewAdditions.m new file mode 100644 index 00000000..885e51df --- /dev/null +++ b/Source/SPTextViewAdditions.m @@ -0,0 +1,287 @@ +// +// SPTextViewAdditions.m +// sequel-pro +// +// Created by Hans-Jörg Bibiko on April 05, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPStringAdditions.h" + +@implementation NSTextView (SPTextViewAdditions) + +/* + * Returns the range of the current word. + * finds: [| := caret] |word wo|rd word| + * If | is in between whitespaces nothing will be selected. + */ +- (NSRange)getRangeForCurrentWord +{ + + NSRange curRange = [self selectedRange]; + unsigned long curLocation = curRange.location; + + [self moveWordLeft:self]; + [self moveWordRightAndModifySelection:self]; + + unsigned long newStartRange = [self selectedRange].location; + unsigned long newEndRange = newStartRange + [self selectedRange].length; + + // if current location does not intersect with found range + // then caret is at the begin of a word -> change strategy + if(curLocation < newStartRange || curLocation > newEndRange) + { + [self setSelectedRange:curRange]; + [self moveWordRightAndModifySelection:self]; + } + + if([[[self string] substringWithRange:[self selectedRange]] rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].location != NSNotFound) + [self setSelectedRange:curRange]; + + NSRange wordRange = [self selectedRange]; + + [self setSelectedRange:curRange]; + + return(wordRange); + +} + +/* + * Select current word. + * finds: [| := caret] |word wo|rd word| + * If | is in between whitespaces nothing will be selected. + */ +- (IBAction)selectCurrentWord:(id)sender +{ + [self setSelectedRange:[self getRangeForCurrentWord]]; +} + +/* + * Select current line. + */ +- (IBAction)selectCurrentLine:(id)sender +{ + [self doCommandBySelector:@selector(moveToBeginningOfLine:)]; + [self doCommandBySelector:@selector(moveToEndOfLineAndModifySelection:)]; +} + +/* + * Change selection or current word to upper case and preserves the selection. + */ +- (IBAction)doSelectionUpperCase:(id)sender +{ + NSRange curRange = [self selectedRange]; + NSRange selRange = (curRange.length) ? curRange : [self getRangeForCurrentWord]; + [self setSelectedRange:selRange]; + [self insertText:[[[self string] substringWithRange:selRange] uppercaseString]]; + [self setSelectedRange:curRange]; +} + +/* + * Change selection or current word to lower case and preserves the selection. + */ +- (IBAction)doSelectionLowerCase:(id)sender +{ + NSRange curRange = [self selectedRange]; + NSRange selRange = (curRange.length) ? curRange : [self getRangeForCurrentWord]; + [self setSelectedRange:selRange]; + [self insertText:[[[self string] substringWithRange:selRange] lowercaseString]]; + [self setSelectedRange:curRange]; +} + +/* + * Change selection or current word to title case and preserves the selection. + */ +- (IBAction)doSelectionTitleCase:(id)sender +{ + NSRange curRange = [self selectedRange]; + NSRange selRange = (curRange.length) ? curRange : [self getRangeForCurrentWord]; + [self setSelectedRange:selRange]; + [self insertText:[[[self string] substringWithRange:selRange] capitalizedString]]; + [self setSelectedRange:curRange]; +} + +/* + * Change selection or current word according to Unicode's NFD and preserves the selection. + */ +- (IBAction)doDecomposedStringWithCanonicalMapping:(id)sender +{ + NSRange curRange = [self selectedRange]; + NSRange selRange = (curRange.length) ? curRange : [self getRangeForCurrentWord]; + [self setSelectedRange:selRange]; + NSString* convString = [[[self string] substringWithRange:selRange] decomposedStringWithCanonicalMapping]; + [self insertText:convString]; + + // correct range for combining characters + if(curRange.length) + [self setSelectedRange:NSMakeRange(selRange.location, [convString length])]; + else + // if no selection place the caret at the end of the current word + { + NSRange newRange = [self getRangeForCurrentWord]; + [self setSelectedRange:NSMakeRange(newRange.location + newRange.length, 0)]; + } +} + +/* + * Change selection or current word according to Unicode's NFKD and preserves the selection. + */ +- (IBAction)doDecomposedStringWithCompatibilityMapping:(id)sender +{ + NSRange curRange = [self selectedRange]; + NSRange selRange = (curRange.length) ? curRange : [self getRangeForCurrentWord]; + [self setSelectedRange:selRange]; + NSString* convString = [[[self string] substringWithRange:selRange] decomposedStringWithCompatibilityMapping]; + [self insertText:convString]; + + // correct range for combining characters + if(curRange.length) + [self setSelectedRange:NSMakeRange(selRange.location, [convString length])]; + else + // if no selection place the caret at the end of the current word + { + NSRange newRange = [self getRangeForCurrentWord]; + [self setSelectedRange:NSMakeRange(newRange.location + newRange.length, 0)]; + } +} + +/* + * Change selection or current word according to Unicode's NFC and preserves the selection. + */ +- (IBAction)doPrecomposedStringWithCanonicalMapping:(id)sender +{ + NSRange curRange = [self selectedRange]; + NSRange selRange = (curRange.length) ? curRange : [self getRangeForCurrentWord]; + [self setSelectedRange:selRange]; + NSString* convString = [[[self string] substringWithRange:selRange] precomposedStringWithCanonicalMapping]; + [self insertText:convString]; + + // correct range for combining characters + if(curRange.length) + [self setSelectedRange:NSMakeRange(selRange.location, [convString length])]; + else + // if no selection place the caret at the end of the current word + { + NSRange newRange = [self getRangeForCurrentWord]; + [self setSelectedRange:NSMakeRange(newRange.location + newRange.length, 0)]; + } +} + +- (IBAction)doRemoveDiacritics:(id)sender +{ + + NSRange curRange = [self selectedRange]; + NSRange selRange = (curRange.length) ? curRange : [self getRangeForCurrentWord]; + [self setSelectedRange:selRange]; + NSString* convString = [[[self string] substringWithRange:selRange] decomposedStringWithCanonicalMapping]; + NSArray* chars; + chars = [convString componentsSeparatedByCharactersInSet:[NSCharacterSet nonBaseCharacterSet]]; + NSString* cleanString = [chars componentsJoinedByString:@""]; + [self insertText:cleanString]; + if(curRange.length) + [self setSelectedRange:NSMakeRange(selRange.location, [cleanString length])]; + else + // if no selection place the caret at the end of the current word + { + NSRange newRange = [self getRangeForCurrentWord]; + [self setSelectedRange:NSMakeRange(newRange.location + newRange.length, 0)]; + } + +} + +/* + * Change selection or current word according to Unicode's NFKC to title case and preserves the selection. + */ +- (IBAction)doPrecomposedStringWithCompatibilityMapping:(id)sender +{ + NSRange curRange = [self selectedRange]; + NSRange selRange = (curRange.length) ? curRange : [self getRangeForCurrentWord]; + [self setSelectedRange:selRange]; + NSString* convString = [[[self string] substringWithRange:selRange] precomposedStringWithCompatibilityMapping]; + [self insertText:convString]; + + // correct range for combining characters + if(curRange.length) + [self setSelectedRange:NSMakeRange(selRange.location, [convString length])]; + else + // if no selection place the caret at the end of the current word + { + NSRange newRange = [self getRangeForCurrentWord]; + [self setSelectedRange:NSMakeRange(newRange.location + newRange.length, 0)]; + } +} + + +/* + * Transpose adjacent characters, or if a selection is given reverse the selected characters. + * If the caret is at the absolute end of the text field it transpose the two last charaters. + * If the caret is at the absolute beginnng of the text field do nothing. + * TODO: not yet combining-diacritics-safe + */ +- (IBAction)doTranspose:(id)sender +{ + NSRange curRange = [self selectedRange]; + NSRange workingRange = curRange; + + if(!curRange.length) + @try // caret is in between two chars + { + if(curRange.location+1 > [[self string] length]) + { + // caret is at the end of a text field + // transpose last two characters + [self moveLeftAndModifySelection:self]; + [self moveLeftAndModifySelection:self]; + workingRange = [self selectedRange]; + } + else if(curRange.location == 0) + { + // caret is at the beginning of the text field + // do nothing + workingRange.length = 0; + } + else + { + // caret is in between two characters + // reverse adjacent characters + NSRange twoCharRange = NSMakeRange(curRange.location-1, 2); + [self setSelectedRange:twoCharRange]; + workingRange = twoCharRange; + } + } + @catch(id ae) + { workingRange.length = 0; } + + + + // reverse string : TODO not yet combining diacritics safe! + if(workingRange.length > 1) + { + NSMutableString *reversedStr; + unsigned long len = workingRange.length; + reversedStr = [NSMutableString stringWithCapacity:len]; + while (len > 0) + [reversedStr appendString: + [NSString stringWithFormat:@"%C", [[self string] characterAtIndex:--len+workingRange.location]]]; + + [self insertText:reversedStr]; + [self setSelectedRange:curRange]; + } +} + +@end + diff --git a/Source/SPWindowAdditions.h b/Source/SPWindowAdditions.h new file mode 100644 index 00000000..af247427 --- /dev/null +++ b/Source/SPWindowAdditions.h @@ -0,0 +1,30 @@ +// +// SPWindowAdditions.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on Dec 10, 2008 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import + +@interface NSWindow (SPWindowAdditions) + +- (float)toolbarHeight; +- (void)resizeForContentView:(NSView *)view titleBarVisible:(BOOL)visible; + +@end diff --git a/Source/SPWindowAdditions.m b/Source/SPWindowAdditions.m new file mode 100644 index 00000000..d5992a86 --- /dev/null +++ b/Source/SPWindowAdditions.m @@ -0,0 +1,75 @@ +// +// SPWindowAdditions.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on Dec 10, 2008 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPWindowAdditions.h" + +@implementation NSWindow (SPWindowAdditions) + +// ------------------------------------------------------------------------------- +// toolbarHeight +// +// Returns the height of the currently visible toolbar. +// ------------------------------------------------------------------------------- +- (float)toolbarHeight +{ + NSRect windowFrame; + float toolbarHeight = 0.0; + + if (([self toolbar]) && ([[self toolbar] isVisible])) { + windowFrame = [NSWindow contentRectForFrameRect:[self frame] styleMask:[self styleMask]]; + + toolbarHeight = NSHeight(windowFrame) - NSHeight([[self contentView] frame]); + } + + return toolbarHeight; +} + +// ------------------------------------------------------------------------------- +// resizeForContentView:titleBarVisible +// +// Resizes this window to the size of the supplied view. +// ------------------------------------------------------------------------------- +- (void)resizeForContentView:(NSView *)view titleBarVisible:(BOOL)visible +{ + NSSize viewSize = [view frame].size; + NSRect frame = [self frame]; + + if ((viewSize.height) < [self contentMinSize].height) { + viewSize.height = [self contentMinSize].height; + } + + float newHeight = (viewSize.height + [self toolbarHeight]); + + // If the title bar is visible add 22 pixels to new height of window. + if (visible) { + newHeight += 22; + } + + frame.origin.y += frame.size.height - newHeight; + + frame.size.height = newHeight; + frame.size.width = viewSize.width; + + [self setFrame:frame display:YES animate:YES]; +} + +@end diff --git a/Source/TableContent.h b/Source/TableContent.h index f18659a0..cc242aba 100644 --- a/Source/TableContent.h +++ b/Source/TableContent.h @@ -25,16 +25,14 @@ #import #import -#import "CMCopyTable.h" -#import "CMMCPConnection.h" -#import "CMMCPResult.h" + +@class CMMCPConnection, CMMCPResult, CMCopyTable; @interface TableContent : NSObject { IBOutlet id tableDocumentInstance; IBOutlet id tablesListInstance; IBOutlet id tableDataInstance; - IBOutlet id queryConsoleInstance; IBOutlet id tableWindow; IBOutlet CMCopyTable *tableContentView; @@ -66,7 +64,7 @@ NSString *compareType, *sortField; BOOL isEditingRow, isEditingNewRow, isSavingRow, isDesc, setLimit; NSUserDefaults *prefs; - int numRows, currentlyEditingRow; + int numRows, currentlyEditingRow, maxNumRowsOfCurrentTable; bool areShowingAllRows; } diff --git a/Source/TableContent.m b/Source/TableContent.m index a25a2cf4..d52a2dda 100644 --- a/Source/TableContent.m +++ b/Source/TableContent.m @@ -27,9 +27,14 @@ #import "TableDocument.h" #import "TablesList.h" #import "CMImageView.h" +#import "CMCopyTable.h" +#import "CMMCPConnection.h" +#import "CMMCPResult.h" #import "SPDataCellFormatter.h" #import "SPTableData.h" #import "SPQueryConsole.h" +#import "SPStringAdditions.h" +#import "SPArrayAdditions.h" @implementation TableContent @@ -45,7 +50,7 @@ sortField = nil; areShowingAllRows = false; currentlyEditingRow = -1; - + return self; } @@ -83,6 +88,7 @@ // Remove existing columns from the table theColumns = [tableContentView tableColumns]; + while ([theColumns count]) { [tableContentView removeTableColumn:[theColumns objectAtIndex:0]]; } @@ -90,7 +96,6 @@ // If no table has been supplied, reset the view to a blank table and disabled elements if ( [aTable isEqualToString:@""] || !aTable ) { - // Empty the stored data arrays [fullResult removeAllObjects]; [filteredResult removeAllObjects]; @@ -127,10 +132,14 @@ // Post a notification that a query will be performed [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; - // Retrieve the field names and types for this table from the data cache. This is used when requesting all data as part + // Retrieve the field names and types for this table from the data cache. This is used when requesting all data as part // of the fieldListForQuery method, and also to decide whether or not to preserve the current filter/sort settings. theColumns = [tableDataInstance columns]; columnNames = [tableDataInstance columnNames]; + + // Retrieve the total number of rows of the current table + // to adjustify "Limit From:" + maxNumRowsOfCurrentTable = [[[tableDataInstance statusValues] objectForKey:@"Rows"] intValue]; // Retrieve the number of rows in the table and initially mark all as being visible. numRows = [self getNumberOfRows]; @@ -171,7 +180,7 @@ } // Set the data cell font according to the preferences - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { + if ( [prefs boolForKey:@"UseMonospacedFonts"] ) { [dataCell setFont:[NSFont fontWithName:@"Monaco" size:10]]; } else { [dataCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; @@ -203,7 +212,10 @@ // Otherwise, clear sorting } else { - sortField = nil; + if (sortField) { + [sortField release]; + sortField = nil; + } isDesc = NO; } @@ -232,6 +244,7 @@ [fieldField selectItemWithTitle:preservedFilterField]; [self setCompareTypes:self]; } + if (preserveCurrentView && preservedFilterField != nil && [fieldField itemWithTitle:preservedFilterField] && [compareField itemWithTitle:preservedFilterComparison]) { @@ -241,7 +254,7 @@ } // Enable or disable the limit fields according to preference setting - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { // Attempt to preserve the limit value if it's still valid if (!preserveCurrentView || [limitRowsField intValue] < 1 || [limitRowsField intValue] >= numRows) { @@ -251,8 +264,8 @@ [limitRowsButton setEnabled:YES]; [limitRowsStepper setEnabled:YES]; [limitRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Limited to %d rows starting with row", @"text showing the number of rows the result is limited to"), - [prefs integerForKey:@"limitRowsValue"]]]; - if ([prefs integerForKey:@"limitRowsValue"] < numRows) + [prefs integerForKey:@"LimitResultsValue"]]]; + if ([prefs integerForKey:@"LimitResultsValue"] < numRows) areShowingAllRows = NO; } else { [limitRowsField setEnabled:NO]; @@ -262,26 +275,26 @@ [limitRowsText setStringValue:NSLocalizedString(@"No limit", @"text showing that the result isn't limited")]; } - // Enable the table buttons + // set the state of the table buttons [addButton setEnabled:YES]; - [copyButton setEnabled:YES]; - [removeButton setEnabled:YES]; + [copyButton setEnabled:NO]; + [removeButton setEnabled:NO]; // Perform the data query and store the result as an array containing a dictionary per result row - query = [NSString stringWithFormat:@"SELECT %@ FROM `%@`", [self fieldListForQuery], selectedTable]; + query = [NSString stringWithFormat:@"SELECT %@ FROM %@", [self fieldListForQuery], [selectedTable backtickQuotedString]]; if ( sortField ) { - query = [NSString stringWithFormat:@"%@ ORDER BY `%@`", query, sortField]; + query = [NSString stringWithFormat:@"%@ ORDER BY %@", query, [sortField backtickQuotedString]]; if ( isDesc ) query = [query stringByAppendingString:@" DESC"]; } - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { if ( [limitRowsField intValue] <= 0 ) { [limitRowsField setStringValue:@"1"]; } query = [query stringByAppendingString: [NSString stringWithFormat:@" LIMIT %d,%d", - [limitRowsField intValue]-1, [prefs integerForKey:@"limitRowsValue"]]]; + [limitRowsField intValue]-1, [prefs integerForKey:@"LimitResultsValue"]]]; } queryResult = [mySQLConnection queryString:query]; @@ -291,17 +304,25 @@ } [fullResult setArray:[self fetchResultAsArray:queryResult]]; + + // This to fix an issue where by areShowingAllRows is set to NO above during the restore of the filter options + // leading the code to believe that the result set is filtered. If the filtered result set count is the same as the + // maximum rows in the table then filtering is currently not in use and we set areShowingAllRows back to YES. + if ([filteredResult count] == maxNumRowsOfCurrentTable) { + areShowingAllRows = YES; + } // Apply any filtering and update the row count if (!areShowingAllRows) { [self filterTable:self]; [countText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%d rows of %d selected", @"text showing how many rows are in the filtered result"), [filteredResult count], numRows]]; - } else { + } + else { [filteredResult setArray:fullResult]; [countText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%d rows in table", @"text showing how many rows are in the result"), [fullResult count]]]; } - // Reload the table data. + // Reload the table data [tableContentView reloadData]; // Post the notification that the query is finished @@ -309,13 +330,12 @@ } /* - * Reloads the current table data, performing a new SQL query. Now attempts to preserve sort order, filters, and viewport. + * Reloads the current table data, performing a new SQL query. Now attempts to preserve sort order, filters, and viewport. */ - (IBAction)reloadTable:(id)sender { - // Check whether a save of the current row is required. - if ( ![self saveRowOnDeselect] ) return; + if (![self saveRowOnDeselect]) return; // Store the current viewport location NSRect viewRect = [tableContentView visibleRect]; @@ -323,13 +343,13 @@ // Clear the table data column cache [tableDataInstance resetColumnData]; + // Load the table's data [self loadTable:selectedTable]; // Restore the viewport [tableContentView scrollRectToVisible:viewRect]; } - /* * Reload the table values without reconfiguring the tableView (with filter and limit if set) */ @@ -342,12 +362,12 @@ [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; //enable or disable limit fields - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { [limitRowsField setEnabled:YES]; [limitRowsButton setEnabled:YES]; [limitRowsStepper setEnabled:YES]; [limitRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Limited to %d rows starting with row", @"text showing the number of rows the result is limited to"), - [prefs integerForKey:@"limitRowsValue"]]]; + [prefs integerForKey:@"LimitResultsValue"]]]; } else { [limitRowsField setEnabled:NO]; [limitRowsButton setEnabled:NO]; @@ -357,20 +377,20 @@ } // queryString = [@"SELECT * FROM " stringByAppendingString:selectedTable]; - queryString = [NSString stringWithFormat:@"SELECT %@ FROM `%@`", [self fieldListForQuery], selectedTable]; + queryString = [NSString stringWithFormat:@"SELECT %@ FROM %@", [self fieldListForQuery], [selectedTable backtickQuotedString]]; if ( sortField ) { - queryString = [NSString stringWithFormat:@"%@ ORDER BY `%@`", queryString, sortField]; - // queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@" ORDER BY `%@`", sortField]]; + queryString = [NSString stringWithFormat:@"%@ ORDER BY %@", queryString, [sortField backtickQuotedString]]; + // queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@" ORDER BY %@", [sortField backtickQuotedString]]]; if ( isDesc ) queryString = [queryString stringByAppendingString:@" DESC"]; } - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { if ( [limitRowsField intValue] <= 0 ) { [limitRowsField setStringValue:@"1"]; } queryString = [queryString stringByAppendingString: [NSString stringWithFormat:@" LIMIT %d,%d", - [limitRowsField intValue]-1, [prefs integerForKey:@"limitRowsValue"]]]; + [limitRowsField intValue]-1, [prefs integerForKey:@"LimitResultsValue"]]]; [limitRowsField selectText:self]; } queryResult = [mySQLConnection queryString:queryString]; @@ -413,10 +433,17 @@ if ( [limitRowsField intValue] <= 0 ) { [limitRowsField setStringValue:@"1"]; } + + // If limitRowsField > number of total found rows show the last limitRowsValue rows + if ( [prefs boolForKey:@"LimitResults"] && [limitRowsField intValue] >= maxNumRowsOfCurrentTable ) { + int newLimit = maxNumRowsOfCurrentTable - [prefs integerForKey:@"LimitResultsValue"]; + [limitRowsField setStringValue:[[NSNumber numberWithInt:(newLimit<1)?1:newLimit] stringValue]]; + } + // If the filter field is empty, the limit field is at 1, and the selected filter is not looking // for NULLs or NOT NULLs, then don't allow filtering. - if (([argument length] == 0) && (![[[compareField selectedItem] title] hasSuffix:@"NULL"]) && (![prefs boolForKey:@"limitRows"] || [limitRowsField intValue] == 1)) { + if (([argument length] == 0) && (![[[compareField selectedItem] title] hasSuffix:@"NULL"]) && (![prefs boolForKey:@"LimitResults"] || [limitRowsField intValue] == 1)) { [argument release]; [self showAll:sender]; return; @@ -429,7 +456,7 @@ BOOL ignoreArgument = NO; // Start building the query string - queryString = [NSString stringWithFormat:@"SELECT %@ FROM `%@`", [self fieldListForQuery], selectedTable]; + queryString = [NSString stringWithFormat:@"SELECT %@ FROM %@", [self fieldListForQuery], [selectedTable backtickQuotedString]]; // Add filter if appropriate if (([argument length] > 0) || [[[compareField selectedItem] title] hasSuffix:@"NULL"]) { @@ -454,7 +481,7 @@ case 4: compareOperator = @"IN"; doQuote = NO; - [argument setString:[[@"('" stringByAppendingString:argument] stringByAppendingString:@"')"]]; + [argument setString:[[@"(" stringByAppendingString:argument] stringByAppendingString:@")"]]; break; case 5: compareOperator = @"IS NULL"; @@ -551,11 +578,11 @@ } } [argument setString:[mySQLConnection prepareString:argument]]; - queryString = [NSString stringWithFormat:@"%@ WHERE `%@` %@ \"%@\"", - queryString, [fieldField titleOfSelectedItem], compareOperator, argument]; + queryString = [NSString stringWithFormat:@"%@ WHERE %@ %@ \"%@\"", + queryString, [[fieldField titleOfSelectedItem] backtickQuotedString], compareOperator, argument]; } else { - queryString = [NSString stringWithFormat:@"%@ WHERE `%@` %@ %@", - queryString, [fieldField titleOfSelectedItem], + queryString = [NSString stringWithFormat:@"%@ WHERE %@ %@ %@", + queryString, [[fieldField titleOfSelectedItem] backtickQuotedString], compareOperator, (ignoreArgument) ? @"" : argument]; } } @@ -563,20 +590,33 @@ // Add sorting details if appropriate if ( sortField ) { - queryString = [NSString stringWithFormat:@"%@ ORDER BY `%@`", queryString, sortField]; + queryString = [NSString stringWithFormat:@"%@ ORDER BY %@", queryString, [sortField backtickQuotedString]]; if ( isDesc ) queryString = [queryString stringByAppendingString:@" DESC"]; } + // retain the query before LIMIT + // to redo the query if nothing found for LIMIT > 1 + NSString* tempQueryString; // LIMIT if appropriate - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { + tempQueryString = [NSString stringWithString:queryString]; queryString = [NSString stringWithFormat:@"%@ LIMIT %d,%d", queryString, - [limitRowsField intValue]-1, [prefs integerForKey:@"limitRowsValue"]]; + [limitRowsField intValue]-1, [prefs integerForKey:@"LimitResultsValue"]]; } - + theResult = [mySQLConnection queryString:queryString]; [filteredResult setArray:[self fetchResultAsArray:theResult]]; + // try it again if theResult is empty and limitRowsField > 1 by setting LIMIT to 0, limitRowsValue + if([prefs boolForKey:@"LimitResults"] && [limitRowsField intValue] > 1 && [filteredResult count] == 0) { + queryString = [NSString stringWithFormat:@"%@ LIMIT %d,%d", tempQueryString, + 0, [prefs integerForKey:@"LimitResultsValue"]]; + theResult = [mySQLConnection queryString:queryString]; + [limitRowsField setStringValue:@"1"]; + [filteredResult setArray:[self fetchResultAsArray:theResult]]; + } + [countText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%d rows of %d selected", @"text showing how many rows are in the filtered result"), [filteredResult count], numRows]]; // Reset the table view @@ -632,8 +672,8 @@ columns = [[NSArray alloc] initWithArray:[tableDataInstance columns]]; for ( i = 0 ; i < [columns count] ; i++ ) { column = [columns objectAtIndex:i]; - if ([column objectForKey:@"default"] == nil) { - [newRow setObject:[prefs stringForKey:@"nullValue"] forKey:[column objectForKey:@"name"]]; + if ([column objectForKey:@"default"] == nil || [[column objectForKey:@"default"] isEqualToString:@"NULL"]) { + [newRow setObject:[prefs stringForKey:@"NullValue"] forKey:[column objectForKey:@"name"]]; } else { [newRow setObject:[column objectForKey:@"default"] forKey:[column objectForKey:@"name"]]; } @@ -657,7 +697,7 @@ { NSMutableDictionary *tempRow; CMMCPResult *queryResult; - NSDictionary *row; + NSDictionary *row, *dbDataRow; int i; // Check whether a save of the current row is required. @@ -673,15 +713,37 @@ //copy row tempRow = [NSMutableDictionary dictionaryWithDictionary:[filteredResult objectAtIndex:[tableContentView selectedRow]]]; [filteredResult insertObject:tempRow atIndex:[tableContentView selectedRow]+1]; + + //if we don't show blobs, read data for this duplicate column from db + if ([prefs boolForKey:@"LoadBlobsAsNeeded"]) { + // Abort if there are no indices on this table - argumentForRow will display an error. + if (![[self argumentForRow:[tableContentView selectedRow]] length]){ + return; + } + //if we have indexes, use argumentForRow + queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@", [selectedTable backtickQuotedString], [self argumentForRow:[tableContentView selectedRow]]]]; + dbDataRow = [queryResult fetchRowAsDictionary]; + } + + //set autoincrement fields to NULL - queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM `%@`", selectedTable]]; + queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [selectedTable backtickQuotedString]]]; if ([queryResult numOfRows]) [queryResult dataSeek:0]; for ( i = 0 ; i < [queryResult numOfRows] ; i++ ) { row = [queryResult fetchRowAsDictionary]; if ( [[row objectForKey:@"Extra"] isEqualToString:@"auto_increment"] ) { - [tempRow setObject:[prefs stringForKey:@"nullValue"] forKey:[row objectForKey:@"Field"]]; + [tempRow setObject:[prefs stringForKey:@"NullValue"] forKey:[row objectForKey:@"Field"]]; + } else if ( [tableDataInstance columnIsBlobOrText:[row objectForKey:@"Field"]] && [prefs boolForKey:@"LoadBlobsAsNeeded"] && dbDataRow) { + NSString *valueString = nil; + //if what we read from DB is NULL (NSNull), we replace it with the string NULL + if([[dbDataRow objectForKey:[row objectForKey:@"Field"]] isKindOfClass:[NSNull class]]) + valueString = [prefs objectForKey:@"NullValue"]; + else + valueString = [dbDataRow objectForKey:[row objectForKey:@"Field"]]; + [tempRow setObject:valueString forKey:[row objectForKey:@"Field"]]; } } + //select row and go in edit mode [tableContentView reloadData]; [tableContentView selectRow:[tableContentView selectedRow]+1 byExtendingSelection:NO]; @@ -705,11 +767,11 @@ /* if ( ([tableContentView numberOfSelectedRows] == [self numberOfRowsInTableView:tableContentView]) && areShowingAllRows && - (![prefs boolForKey:@"limitRows"] || ([tableContentView numberOfSelectedRows] < [prefs integerForKey:@"limitRowsValue"])) ) { + (![prefs boolForKey:@"LimitResults"] || ([tableContentView numberOfSelectedRows] < [prefs integerForKey:@"LimitResultsValue"])) ) { */ if ( ([tableContentView numberOfSelectedRows] == [tableContentView numberOfRows]) && - (([prefs boolForKey:@"limitRows"] && [tableContentView numberOfSelectedRows] == [self fetchNumberOfRows]) || - (![prefs boolForKey:@"limitRows"] && [tableContentView numberOfSelectedRows] == [self getNumberOfRows])) ) { + (([prefs boolForKey:@"LimitResults"] && [tableContentView numberOfSelectedRows] == [self fetchNumberOfRows]) || + (![prefs boolForKey:@"LimitResults"] && [tableContentView numberOfSelectedRows] == [self getNumberOfRows])) ) { NSBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"Delete", @"delete button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, tableWindow, self, @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"removeallrows", NSLocalizedString(@"Do you really want to delete all rows?", @"message of panel asking for confirmation for deleting all rows")); } else if ( [tableContentView numberOfSelectedRows] == 1 ) { @@ -989,7 +1051,7 @@ [tableContentView setVerticalMotionCanBeginDrag:NO]; prefs = [[NSUserDefaults standardUserDefaults] retain]; - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { + if ( [prefs boolForKey:@"UseMonospacedFonts"] ) { [argumentField setFont:[NSFont fontWithName:@"Monaco" size:10]]; [limitRowsField setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; [editTextView setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; @@ -1000,9 +1062,9 @@ } [hexTextView setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; [limitRowsStepper setEnabled:NO]; - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { [limitRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Limited to %d rows starting with row", @"text showing the number of rows the result is limited to"), - [prefs integerForKey:@"limitRowsValue"]]]; + [prefs integerForKey:@"LimitResultsValue"]]]; } else { [limitRowsText setStringValue:NSLocalizedString(@"No limit", @"text showing that the result isn't limited")]; [limitRowsField setStringValue:@""]; @@ -1016,7 +1078,7 @@ { NSArray *stringTypes = [NSArray arrayWithObjects:NSLocalizedString(@"is", @"popup menuitem for field IS value"), NSLocalizedString(@"is not", @"popup menuitem for field IS NOT value"), NSLocalizedString(@"contains", @"popup menuitem for field CONTAINS value"), NSLocalizedString(@"contains not", @"popup menuitem for field CONTAINS NOT value"), @"IN", nil]; NSArray *numberTypes = [NSArray arrayWithObjects:@"=", @"≠", @">", @"<", @"≥", @"≤", @"IN", nil]; - NSArray *dateTypes = [NSArray arrayWithObjects:NSLocalizedString(@"is", @"popup menuitem for field IS value"), NSLocalizedString(@"is not", @"popup menuitem for field IS NOT value"), NSLocalizedString(@"older than", @"popup menuitem for field OLDER THAN value"), NSLocalizedString(@"younger than", @"popup menuitem for field YOUNGER THAN value"), NSLocalizedString(@"older than or equal to", @"popup menuitem for field OLDER THAN OR EQUAL TO value"), NSLocalizedString(@"younger than or equal to", @"popup menuitem for field YOUNGER THAN OR EQUAL TO value"), nil]; + NSArray *dateTypes = [NSArray arrayWithObjects:NSLocalizedString(@"is", @"popup menuitem for field IS value"), NSLocalizedString(@"is not", @"popup menuitem for field IS NOT value"), NSLocalizedString(@"is after", @"popup menuitem for field AFTER DATE value"), NSLocalizedString(@"is before", @"popup menuitem for field BEFORE DATE value"), NSLocalizedString(@"is after or equal to", @"popup menuitem for field AFTER OR EQUAL TO value"), NSLocalizedString(@"is before or equal to", @"popup menuitem for field BEFORE OR EQUAL TO value"), nil]; NSString *fieldTypeGrouping = [NSString stringWithString:[[tableDataInstance columnWithName:[[fieldField selectedItem] title]] objectForKey:@"typegrouping"]]; int i; @@ -1083,12 +1145,14 @@ */ { if ( [limitRowsStepper intValue] > 0 ) { - [limitRowsField setIntValue:[limitRowsField intValue]+[prefs integerForKey:@"limitRowsValue"]]; + int newStep = [limitRowsField intValue]+[prefs integerForKey:@"LimitResultsValue"]; + // if newStep > the total number of rows in the current table retain the old value + [limitRowsField setIntValue:(newStep>maxNumRowsOfCurrentTable)?[limitRowsField intValue]:newStep]; } else { - if ( ([limitRowsField intValue]-[prefs integerForKey:@"limitRowsValue"]) < 1 ) { + if ( ([limitRowsField intValue]-[prefs integerForKey:@"LimitResultsValue"]) < 1 ) { [limitRowsField setIntValue:1]; } else { - [limitRowsField setIntValue:[limitRowsField intValue]-[prefs integerForKey:@"limitRowsValue"]]; + [limitRowsField setIntValue:[limitRowsField intValue]-[prefs integerForKey:@"LimitResultsValue"]]; } } [limitRowsStepper setIntValue:0]; @@ -1100,23 +1164,15 @@ - (NSArray *)fetchResultAsArray:(CMMCPResult *)theResult { NSArray *columns; - NSString *columnTypeGrouping; - NSMutableArray *columnsBlobStatus, *tempResult = [NSMutableArray array]; + NSMutableArray *tempResult = [NSMutableArray array]; + NSDictionary *tempRow; NSMutableDictionary *modifiedRow = [NSMutableDictionary dictionary]; NSEnumerator *enumerator; id key; int i, j; - BOOL columnIsBlobOrText; - + columns = [tableDataInstance columns]; - columnsBlobStatus = [[NSMutableArray alloc] init]; - for ( i = 0 ; i < [columns count]; i++ ) { - columnTypeGrouping = [[columns objectAtIndex:i] objectForKey:@"typegrouping"]; - columnIsBlobOrText = ([columnTypeGrouping isEqualToString:@"textdata"] || [columnTypeGrouping isEqualToString:@"blobdata"]); - [columnsBlobStatus addObject:[NSNumber numberWithBool:columnIsBlobOrText]]; - } - if ([theResult numOfRows]) [theResult dataSeek:0]; for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { tempRow = [theResult fetchRowAsDictionary]; @@ -1124,17 +1180,17 @@ while ( key = [enumerator nextObject] ) { if ( [[tempRow objectForKey:key] isMemberOfClass:[NSNull class]] ) { - [modifiedRow setObject:[prefs stringForKey:@"nullValue"] forKey:key]; + [modifiedRow setObject:[prefs stringForKey:@"NullValue"] forKey:key]; } else { [modifiedRow setObject:[tempRow objectForKey:key] forKey:key]; } } // Add values for hidden blob and text fields if appropriate - if ( [prefs boolForKey:@"dontShowBlob"] ) { + if ( [prefs boolForKey:@"LoadBlobsAsNeeded"] ) { for ( j = 0 ; j < [columns count] ; j++ ) { - if ( [[columnsBlobStatus objectAtIndex:j] boolValue] ) { - [modifiedRow setObject:NSLocalizedString(@"- blob or text -", @"value shown for hidden blob and text fields") forKey:[[columns objectAtIndex:j] objectForKey:@"name"]]; + if ( [tableDataInstance columnIsBlobOrText:[[columns objectAtIndex:j] objectForKey:@"name"] ] ) { + [modifiedRow setObject:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields") forKey:[[columns objectAtIndex:j] objectForKey:@"name"]]; } } } @@ -1142,8 +1198,6 @@ [tempResult addObject:[NSMutableDictionary dictionaryWithDictionary:modifiedRow]]; } - [columnsBlobStatus release]; - return tempResult; } @@ -1156,7 +1210,6 @@ - (BOOL)addRowToDB { NSArray *theColumns, *columnNames; - NSMutableArray *fieldValues = [[NSMutableArray alloc] init]; NSMutableString *queryString; NSString *query; CMMCPResult *queryResult; @@ -1166,7 +1219,6 @@ int i; if ( !isEditingRow || currentlyEditingRow == -1) { - [fieldValues release]; return YES; } @@ -1183,12 +1235,12 @@ theColumns = [tableDataInstance columns]; columnNames = [tableDataInstance columnNames]; + NSMutableArray *fieldValues = [[NSMutableArray alloc] init]; // Get the field values for ( i = 0 ; i < [columnNames count] ; i++ ) { rowObject = [[filteredResult objectAtIndex:currentlyEditingRow] objectForKey:[columnNames objectAtIndex:i]]; - // Convert the object to a string (here we can add special treatment for date-, number- and data-fields) - if ( [[rowObject description] isEqualToString:[prefs stringForKey:@"nullValue"]] + if ( [[rowObject description] isEqualToString:[prefs stringForKey:@"NullValue"]] || ([rowObject isMemberOfClass:[NSString class]] && [[rowObject description] isEqualToString:@""]) ) { //NULL when user entered the nullValue string defined in the prefs or when a number field isn't set @@ -1219,18 +1271,18 @@ // Use INSERT syntax when creating new rows if ( isEditingNewRow ) { - queryString = [NSString stringWithFormat:@"INSERT INTO `%@` (`%@`) VALUES (%@)", - selectedTable, [columnNames componentsJoinedByString:@"`,`"], [fieldValues componentsJoinedByString:@","]]; + queryString = [NSString stringWithFormat:@"INSERT INTO %@ (%@) VALUES (%@)", + [selectedTable backtickQuotedString], [columnNames componentsJoinedAndBacktickQuoted], [fieldValues componentsJoinedByString:@","]]; // Use UPDATE syntax otherwise } else { - queryString = [NSMutableString stringWithFormat:@"UPDATE `%@` SET ", selectedTable]; + queryString = [NSMutableString stringWithFormat:@"UPDATE %@ SET ", [selectedTable backtickQuotedString]]; for ( i = 0 ; i < [columnNames count] ; i++ ) { if ( i > 0 ) { [queryString appendString:@", "]; } - [queryString appendString:[NSString stringWithFormat:@"`%@`=%@", - [columnNames objectAtIndex:i], [fieldValues objectAtIndex:i]]]; + [queryString appendString:[NSString stringWithFormat:@"%@=%@", + [[columnNames objectAtIndex:i] backtickQuotedString], [fieldValues objectAtIndex:i]]]; } [queryString appendString:[NSString stringWithFormat:@" WHERE %@", [self argumentForRow:-2]]]; } @@ -1239,7 +1291,7 @@ // If no rows have been changed, show error if appropriate. if ( ![mySQLConnection affectedRows] ) { - if ( [prefs boolForKey:@"showError"] ) { + if ( [prefs boolForKey:@"ShowNoAffectedRowsError"] ) { NSBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"The row was not written to the MySQL database. You probably haven't changed anything.\nReload the table to be sure that the row exists and use a primary key for your table.\n(This error can be turned off in the preferences.)", @"message of panel when no rows have been affected after writing to the db")); } else { @@ -1249,7 +1301,7 @@ isEditingRow = NO; isEditingNewRow = NO; currentlyEditingRow = -1; - [queryConsoleInstance showErrorInConsole:[NSString stringWithFormat:NSLocalizedString(@"/* WARNING %@ No rows have been affected */\n", @"warning shown in the console when no rows have been affected after writing to the db"), currentTime]]; + [[SPQueryConsole sharedQueryConsole] showErrorInConsole:[NSString stringWithFormat:NSLocalizedString(@"/* WARNING %@ No rows have been affected */\n", @"warning shown in the console when no rows have been affected after writing to the db"), currentTime]]; return YES; // On success... @@ -1258,7 +1310,7 @@ // New row created successfully if ( isEditingNewRow ) { - if ( [prefs boolForKey:@"reloadAfterAdding"] ) { + if ( [prefs boolForKey:@"ReloadAfterAddingRow"] ) { [self reloadTableValues:self]; [tableContentView deselectAll:self]; [tableWindow endEditingFor:nil]; @@ -1277,26 +1329,26 @@ // Existing row edited successfully } else { - if ( [prefs boolForKey:@"reloadAfterEditing"] ) { + if ( [prefs boolForKey:@"ReloadAfterEditingRow"] ) { [self reloadTableValues:self]; [tableContentView deselectAll:self]; [tableWindow endEditingFor:nil]; // TODO: this probably needs looking at... it's reloading it all itself? } else { - query = [NSString stringWithFormat:@"SELECT %@ FROM `%@`", [self fieldListForQuery], selectedTable]; + query = [NSString stringWithFormat:@"SELECT %@ FROM %@", [self fieldListForQuery], [selectedTable backtickQuotedString]]; if ( sortField ) { - query = [NSString stringWithFormat:@"%@ ORDER BY `%@`", query, sortField]; + query = [NSString stringWithFormat:@"%@ ORDER BY %@", query, [sortField backtickQuotedString]]; if ( isDesc ) query = [query stringByAppendingString:@" DESC"]; } - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { if ( [limitRowsField intValue] <= 0 ) { [limitRowsField setStringValue:@"1"]; } query = [query stringByAppendingString: [NSString stringWithFormat:@" LIMIT %d,%d", - [limitRowsField intValue]-1, [prefs integerForKey:@"limitRowsValue"]]]; + [limitRowsField intValue]-1, [prefs integerForKey:@"LimitResultsValue"]]]; } queryResult = [mySQLConnection queryString:query]; [fullResult setArray:[self fetchResultAsArray:queryResult]]; @@ -1369,7 +1421,7 @@ if ( !keys ) { setLimit = NO; keys = [[NSMutableArray alloc] init]; - theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM `%@`", selectedTable]]; + theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [selectedTable backtickQuotedString]]]; if ([theResult numOfRows]) [theResult dataSeek:0]; for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { theRow = [theResult fetchRowAsDictionary]; @@ -1382,11 +1434,11 @@ // If there is no primary key, all the fields are used in the argument. if ( ![keys count] ) { [keys setArray:columnNames]; - setLimit = YES; - + setLimit = YES; + // When the option to not show blob or text options is set, we have a problem - we don't have // the right values to use in the WHERE statement. Throw an error if this is the case. - if ( [prefs boolForKey:@"dontShowBlob"] && [self tableContainsBlobOrTextColumns] ) { + if ( [prefs boolForKey:@"LoadBlobsAsNeeded"] && [self tableContainsBlobOrTextColumns] ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"You can't hide blob and text fields when working with tables without index.", @"message of panel when trying to edit tables without index and with hidden blob/text fields")); [keys removeAllObjects]; @@ -1419,8 +1471,8 @@ [value setString:[tempValue description]]; } - if ( [value isEqualToString:[prefs stringForKey:@"nullValue"]] ) { - [argument appendString:[NSString stringWithFormat:@"`%@` IS NULL", [keys objectAtIndex:i]]]; + if ( [value isEqualToString:[prefs stringForKey:@"NullValue"]] ) { + [argument appendString:[NSString stringWithFormat:@"%@ IS NULL", [[keys objectAtIndex:i] backtickQuotedString]]]; } else { // Escape special characters (in WHERE statement!) @@ -1442,9 +1494,9 @@ columnType = [[tableDataInstance columnWithName:[keys objectAtIndex:i]] objectForKey:@"typegrouping"]; if ( [columnType isEqualToString:@"integer"] || [columnType isEqualToString:@"float"] || [columnType isEqualToString:@"bit"] ) { - [argument appendString:[NSString stringWithFormat:@"`%@` = %@", [keys objectAtIndex:i], value]]; + [argument appendString:[NSString stringWithFormat:@"%@ = %@", [[keys objectAtIndex:i] backtickQuotedString], value]]; } else { - [argument appendString:[NSString stringWithFormat:@"`%@` LIKE %@", [keys objectAtIndex:i], value]]; + [argument appendString:[NSString stringWithFormat:@"%@ LIKE %@", [[keys objectAtIndex:i] backtickQuotedString], value]]; } } } @@ -1462,11 +1514,9 @@ { int i; NSArray *tableColumns = [tableDataInstance columns]; - NSString *columnTypeGrouping; for ( i = 0 ; i < [tableColumns count]; i++ ) { - columnTypeGrouping = [[tableColumns objectAtIndex:i] objectForKey:@"typegrouping"]; - if ([columnTypeGrouping isEqualToString:@"textdata"] || [columnTypeGrouping isEqualToString:@"blobdata"]) { + if ( [tableDataInstance columnIsBlobOrText:[[tableColumns objectAtIndex:i] objectForKey:@"name"]] ) { return YES; } } @@ -1484,21 +1534,19 @@ NSMutableArray *fields = [NSMutableArray array]; NSArray *columns = [tableDataInstance columns]; NSArray *columnNames = [tableDataInstance columnNames]; - NSString *columnTypeGrouping; - if ( [prefs boolForKey:@"dontShowBlob"] ) { + if ( [prefs boolForKey:@"LoadBlobsAsNeeded"] ) { for ( i = 0 ; i < [columnNames count] ; i++ ) { - columnTypeGrouping = [[columns objectAtIndex:i] objectForKey:@"typegrouping"]; - if (![columnTypeGrouping isEqualToString:@"textdata"] && ![columnTypeGrouping isEqualToString:@"blobdata"]) { + if (![tableDataInstance columnIsBlobOrText:[[columns objectAtIndex:i] objectForKey:@"name"]] ) { [fields addObject:[columnNames objectAtIndex:i]]; } } // Always select at least one field - the first if there are no non-blob fields. if ( [fields count] == 0 ) { - return [NSString stringWithFormat:@"`%@`", [columnNames objectAtIndex:0]]; + return [[columnNames objectAtIndex:0] backtickQuotedString]; } else { - return [NSString stringWithFormat:@"`%@`", [fields componentsJoinedByString:@"`,`"]]; + return [fields componentsJoinedAndBacktickQuoted]; } } else { return @"*"; @@ -1515,7 +1563,7 @@ NSNumber *index; NSMutableArray *tempArray = [NSMutableArray array]; NSMutableArray *tempResult = [NSMutableArray array]; - NSString *queryString; + NSString *queryString, *wherePart; CMMCPResult *queryResult; int i, errors; @@ -1543,9 +1591,9 @@ /* if ( ([tableContentView numberOfSelectedRows] == [self numberOfRowsInTableView:tableContentView]) && areShowingAllRows && - ([tableContentView numberOfSelectedRows] < [prefs integerForKey:@"limitRowsValue"]) ) { + ([tableContentView numberOfSelectedRows] < [prefs integerForKey:@"LimitResultsValue"]) ) { */ - [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM `%@`", selectedTable]]; + [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@", [selectedTable backtickQuotedString]]]; if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [self reloadTable:self]; } else { @@ -1559,18 +1607,24 @@ errors = 0; while ( (index = [enumerator nextObject]) ) { - [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM `%@` WHERE %@", - selectedTable, [self argumentForRow:[index intValue]]]]; - if ( ![mySQLConnection affectedRows] ) { - //no rows deleted - errors++; - } else if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { - //rows deleted with success - [tempArray addObject:index]; + wherePart = [NSString stringWithString:[self argumentForRow:[index intValue]]]; + //argumentForRow might return empty query, in which case we shouldn't execute the partial query + if([wherePart length] > 0) { + [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@", [selectedTable backtickQuotedString], wherePart]]; + if ( ![mySQLConnection affectedRows] ) { + //no rows deleted + errors++; + } else if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { + //rows deleted with success + [tempArray addObject:index]; + } else { + //error in mysql-query + errors++; + } } else { - //error in mysql-query errors++; } + } if ( errors ) { @@ -1578,7 +1632,7 @@ } //do deleting (after enumerating) - if ( [prefs boolForKey:@"reloadAfterRemoving"] ) { + if ( [prefs boolForKey:@"ReloadAfterRemovingRow"] ) { [self reloadTableValues:self]; } else { for ( i = 0 ; i < [filteredResult count] ; i++ ) { @@ -1589,21 +1643,22 @@ numRows = [self getNumberOfRows]; if ( !areShowingAllRows ) { // queryString = [@"SELECT * FROM " stringByAppendingString:selectedTable]; - queryString = [NSString stringWithFormat:@"SELECT %@ FROM `%@`", [self fieldListForQuery], selectedTable]; + queryString = [NSString stringWithFormat:@"SELECT %@ FROM %@", [self fieldListForQuery], [selectedTable backtickQuotedString]]; if ( sortField ) { - // queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@" ORDER BY `%@`", sortField]]; - queryString = [NSString stringWithFormat:@"%@ ORDER BY `%@`", queryString, sortField]; + // queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@" ORDER BY %@", [sortField backtickQuotedString]]]; + queryString = [NSString stringWithFormat:@"%@ ORDER BY %@", queryString, [sortField backtickQuotedString]]; if ( isDesc ) queryString = [queryString stringByAppendingString:@" DESC"]; } - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { if ( [limitRowsField intValue] <= 0 ) { [limitRowsField setStringValue:@"1"]; } queryString = [queryString stringByAppendingString: [NSString stringWithFormat:@" LIMIT %d,%d", - [limitRowsField intValue]-1, [prefs integerForKey:@"limitRowsValue"]]]; + [limitRowsField intValue]-1, [prefs integerForKey:@"LimitResultsValue"]]]; } + queryResult = [mySQLConnection queryString:queryString]; // [fullResult setArray:[[self fetchResultAsArray:queryResult] retain]]; [fullResult setArray:[self fetchResultAsArray:queryResult]]; @@ -1627,7 +1682,7 @@ */ - (int)getNumberOfRows { - if ([prefs boolForKey:@"limitRows"] && [prefs boolForKey:@"fetchRowCount"]) { + if ([prefs boolForKey:@"LimitResults"] && [prefs boolForKey:@"FetchCorrectRowCount"]) { numRows = [self fetchNumberOfRows]; } else { numRows = [fullResult count]; @@ -1641,7 +1696,7 @@ */ - (int)fetchNumberOfRows { - return [[[[mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM `%@`", selectedTable]] fetchRowAsArray] objectAtIndex:0] intValue]; + return [[[[mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [selectedTable backtickQuotedString]]] fetchRowAsArray] objectAtIndex:0] intValue]; } //tableView datasource methods @@ -1655,10 +1710,10 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { id theRow, theValue; - + theRow = [filteredResult objectAtIndex:rowIndex]; theValue = [theRow objectForKey:[aTableColumn identifier]]; - + // Convert data objects to their string representation in the current encoding, falling back to ascii if ( [theValue isKindOfClass:[NSData class]] ) { NSString *dataRepresentation = [[NSString alloc] initWithData:theValue encoding:[mySQLConnection encoding]]; @@ -1668,8 +1723,59 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn else theValue = [NSString stringWithString:dataRepresentation]; if (dataRepresentation) [dataRepresentation release]; } - - return theValue; + return theValue; +} + +- (void)tableView: (CMCopyTable *)aTableView + willDisplayCell: (id)cell + forTableColumn: (NSTableColumn*)aTableColumn + row: (int)row +/* + * This function changes the text color of + * text/blob fields which are not yet loaded to gray + */ +{ + // Check if loading of text/blob fields is disabled + // If not, all text fields are loaded and we don't have to make them gray + if ([prefs boolForKey:@"LoadBlobsAsNeeded"]) + { + // Make sure that the cell actually responds to setTextColor: + // In the future, we might use different cells for the table view + // that don't support this selector + if ([cell respondsToSelector:@selector(setTextColor:)]) + { + NSArray *columns = [tableDataInstance columns]; + NSArray *columnNames = [tableDataInstance columnNames]; + NSString *columnTypeGrouping; + int indexOfColumn; + + // We have to find the index of the current column + // Make sure we find it, otherwise return (We might decide in the future + // to add a column to the TableView that doesn't correspond to a column + // of the Mysql table...) + indexOfColumn = [columnNames indexOfObject:[aTableColumn identifier]]; + if (indexOfColumn == NSNotFound) return; + + // Test if the current column is a text or a blob field + columnTypeGrouping = [[columns objectAtIndex:indexOfColumn] objectForKey:@"typegrouping"]; + if ([columnTypeGrouping isEqualToString:@"textdata"] || [columnTypeGrouping isEqualToString:@"blobdata"]) { + + // now check if the field has been loaded already or not + if ([[cell stringValue] isEqualToString:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]) + { + // change the text color of the cell to gray + [cell setTextColor: [NSColor grayColor]]; + } + else + { + // Change the text color back to black + // This is necessary because NSTableView reuses + // the NSCell to draw further rows in the column + [cell setTextColor: [NSColor blackColor]]; + } + } + } + } } - (void)tableView:(NSTableView *)aTableView @@ -1724,20 +1830,21 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn isDesc = NO; [tableContentView setIndicatorImage:nil inTableColumn:[tableContentView tableColumnWithIdentifier:sortField]]; } - sortField = [tableColumn identifier]; + if (sortField) [sortField release]; + sortField = [[NSString alloc] initWithString:[tableColumn identifier]]; //make queryString and perform query - queryString = [NSString stringWithFormat:@"SELECT %@ FROM `%@` ORDER BY `%@`", [self fieldListForQuery], - selectedTable, sortField]; + queryString = [NSString stringWithFormat:@"SELECT %@ FROM %@ ORDER BY %@", [self fieldListForQuery], + [selectedTable backtickQuotedString], [sortField backtickQuotedString]]; if ( isDesc ) queryString = [queryString stringByAppendingString:@" DESC"]; - if ( [prefs boolForKey:@"limitRows"] ) { + if ( [prefs boolForKey:@"LimitResults"] ) { if ( [limitRowsField intValue] <= 0 ) { [limitRowsField setStringValue:@"1"]; } queryString = [queryString stringByAppendingString: [NSString stringWithFormat:@" LIMIT %d,%d", - [limitRowsField intValue]-1, [prefs integerForKey:@"limitRowsValue"]]]; + [limitRowsField intValue]-1, [prefs integerForKey:@"LimitResultsValue"]]]; } queryResult = [mySQLConnection queryString:queryString]; @@ -1780,9 +1887,14 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn if ( isEditingRow && [tableContentView selectedRow] != currentlyEditingRow && ![self saveRowOnDeselect] ) return; // Update the row selection count + // and update the status of the delete/duplicate buttons if ( [tableContentView numberOfSelectedRows] > 0 ) { + [copyButton setEnabled:YES]; + [removeButton setEnabled:YES]; [countText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%d of %d rows selected", @"Text showing how many rows are selected"), [tableContentView numberOfSelectedRows], [tableContentView numberOfRows]]]; } else { + [copyButton setEnabled:NO]; + [removeButton setEnabled:NO]; [countText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%d rows", @"Text showing how many rows are in the result"), [tableContentView numberOfRows]]]; } } @@ -1824,13 +1936,15 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn if ( [tableColumnWidths objectForKey:database] == nil ) { [tableColumnWidths setObject:[NSMutableDictionary dictionary] forKey:database]; } else { - [tableColumnWidths setObject:[[tableColumnWidths objectForKey:database] mutableCopy] forKey:database]; + [tableColumnWidths setObject:[NSMutableDictionary dictionaryWithDictionary:[tableColumnWidths objectForKey:database]] forKey:database]; + } // get table object if ( [[tableColumnWidths objectForKey:database] objectForKey:table] == nil ) { [[tableColumnWidths objectForKey:database] setObject:[NSMutableDictionary dictionary] forKey:table]; } else { - [[tableColumnWidths objectForKey:database] setObject:[[[tableColumnWidths objectForKey:database] objectForKey:table] mutableCopy] forKey:table]; + [[tableColumnWidths objectForKey:database] setObject:[NSMutableDictionary dictionaryWithDictionary:[[tableColumnWidths objectForKey:database] objectForKey:table]] forKey:table]; + } // save column size [[[tableColumnWidths objectForKey:database] objectForKey:table] setObject:[NSNumber numberWithFloat:[[[aNotification userInfo] objectForKey:@"NSTableColumn"] width]] forKey:[[[aNotification userInfo] objectForKey:@"NSTableColumn"] identifier]]; @@ -1844,21 +1958,23 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn - (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { int code; - NSString *columnTypeGrouping, *query, *stringValue = nil; + NSString *query, *stringValue = nil, *wherePart = nil; + NSEnumerator *enumerator; NSDictionary *tempRow; NSMutableDictionary *modifiedRow = [NSMutableDictionary dictionary]; id key, theValue; CMMCPResult *tempResult; - BOOL columnIsBlobOrText = NO; // If not isEditingRow and the preference value for not showing blobs is set, check whether the row contains any blobs. - if ( [prefs boolForKey:@"dontShowBlob"] && !isEditingRow ) { + if ( [prefs boolForKey:@"LoadBlobsAsNeeded"] && !isEditingRow ) { // If the table does contain blob or text fields, load the values ready for editing. if ( [self tableContainsBlobOrTextColumns] ) { - query = [NSString stringWithFormat:@"SELECT * FROM `%@` WHERE %@", - selectedTable, [self argumentForRow:[tableContentView selectedRow]]]; + wherePart = [NSString stringWithString:[self argumentForRow:[tableContentView selectedRow]]]; + if([wherePart length]==0) + return NO; + query = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@", [selectedTable backtickQuotedString], wherePart]; tempResult = [mySQLConnection queryString:query]; if ( ![tempResult numOfRows] ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, @@ -1869,7 +1985,7 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn enumerator = [tempRow keyEnumerator]; while ( key = [enumerator nextObject] ) { if ( [[tempRow objectForKey:key] isMemberOfClass:[NSNull class]] ) { - [modifiedRow setObject:[prefs stringForKey:@"nullValue"] forKey:key]; + [modifiedRow setObject:[prefs stringForKey:@"NullValue"] forKey:key]; } else { [modifiedRow setObject:[tempRow objectForKey:key] forKey:key]; } @@ -1879,14 +1995,8 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn } } - // If the selected column is a blob/text type, force sheet editing. - columnTypeGrouping = [[tableDataInstance columnWithName:[aTableColumn identifier]] objectForKey:@"typegrouping"]; - if ([columnTypeGrouping isEqualToString:@"textdata"] || [columnTypeGrouping isEqualToString:@"blobdata"]) { - columnIsBlobOrText = YES; - } - // Open the sheet if the multipleLineEditingButton is enabled or the column was a blob or a text. - if ( [multipleLineEditingButton state] == NSOnState || columnIsBlobOrText ) { + if ( [multipleLineEditingButton state] == NSOnState || [tableDataInstance columnIsBlobOrText:[aTableColumn identifier]] ) { theValue = [[filteredResult objectAtIndex:rowIndex] objectForKey:[aTableColumn identifier]]; NSImage *image = nil; editData = [theValue retain]; @@ -2070,16 +2180,14 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn //last but not least - (void)dealloc -{ - // NSLog(@"TableContent dealloc"); - +{ [editData release]; [fullResult release]; [filteredResult release]; [keys release]; [oldRow release]; [compareType release]; - [sortField release]; + if (sortField) [sortField release]; [prefs release]; [super dealloc]; diff --git a/Source/TableDocument.h b/Source/TableDocument.h index 7227f0dd..a0c099f9 100644 --- a/Source/TableDocument.h +++ b/Source/TableDocument.h @@ -25,8 +25,8 @@ #import #import -#import "CMMCPConnection.h" -#import "CMMCPResult.h" + +@class CMMCPConnection, CMMCPResult; /** * The TableDocument class controls the primary database view window. @@ -42,7 +42,7 @@ IBOutlet id tableDumpInstance; IBOutlet id tableDataInstance; IBOutlet id tableStatusInstance; - IBOutlet id queryConsoleInstance; + IBOutlet id spExportControllerInstance; IBOutlet id tableWindow; IBOutlet id connectSheet; @@ -53,6 +53,7 @@ IBOutlet id favoritesButton; IBOutlet NSTableView *connectFavoritesTableView; IBOutlet NSArrayController *favoritesController; + IBOutlet id nameField; IBOutlet id hostField; IBOutlet id socketField; IBOutlet id userField; @@ -61,7 +62,7 @@ IBOutlet id databaseField; IBOutlet id connectProgressBar; - IBOutlet id connectProgressStatusText; + IBOutlet NSTextField *connectProgressStatusText; IBOutlet id databaseNameField; IBOutlet id databaseEncodingButton; IBOutlet id addDatabaseButton; @@ -71,6 +72,8 @@ IBOutlet id sidebarGrabber; + IBOutlet NSTextView *customQueryTextView; + IBOutlet NSTableView *dbTablesTableView; IBOutlet id syntaxView; @@ -79,7 +82,6 @@ CMMCPConnection *mySQLConnection; - NSMutableArray *favorites; NSArray *variables; NSString *selectedDatabase; NSString *mySQLVersion; @@ -88,22 +90,25 @@ NSMenu *selectEncodingMenu; BOOL _supportsEncoding; NSString *_encoding; + BOOL _encodingViaLatin1; + BOOL _shouldOpenConnectionAutomatically; NSToolbar *mainToolbar; NSToolbarItem *chooseDatabaseToolbarItem; } //start sheet +- (void)setShouldAutomaticallyConnect:(BOOL)shouldAutomaticallyConnect; - (IBAction)connectToDB:(id)sender; - (IBAction)connect:(id)sender; - (IBAction)cancelConnectSheet:(id)sender; - (IBAction)closeSheet:(id)sender; - (IBAction)chooseFavorite:(id)sender; -- (IBAction)removeFavorite:(id)sender; +- (IBAction)editFavorites:(id)sender; - (id)selectedFavorite; - (NSString *)selectedFavoritePassword; - (void)connectSheetAddToFavorites:(id)sender; -- (void)addToFavoritesHost:(NSString *)host socket:(NSString *)socket +- (void)addToFavoritesName:(NSString *)name host:(NSString *)host socket:(NSString *)socket user:(NSString *)user password:(NSString *)password port:(NSString *)port database:(NSString *)database useSSH:(BOOL)useSSH // no-longer in use @@ -111,7 +116,6 @@ sshUser:(NSString *)sshUser // no-longer in use sshPassword:(NSString *)sshPassword // no-longer in use sshPort:(NSString *)sshPort; // no-longer in use -- (NSMutableArray *)favorites; //alert sheets method - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo; @@ -130,6 +134,7 @@ - (void)setConnectionEncoding:(NSString *)mysqlEncoding reloadingViews:(BOOL)reloadViews; - (NSString *)databaseEncoding; - (NSString *)connectionEncoding; +- (BOOL)connectionEncodingViaLatin1; - (IBAction)chooseEncoding:(id)sender; - (BOOL)supportsEncoding; - (void)updateEncodingMenuWithSelectedEncoding:(NSString *)encoding; @@ -154,6 +159,7 @@ - (void)closeConnection; //getter methods +- (NSString *)name; - (NSString *)database; - (NSString *)table; - (NSString *)mySQLVersion; @@ -175,6 +181,7 @@ - (IBAction)viewContent:(id)sender; - (IBAction)viewQuery:(id)sender; - (IBAction)viewStatus:(id)sender; +- (IBAction)addConnectionToFavorites:(id)sender; //toolbar methods - (void)setupToolbar; @@ -184,34 +191,10 @@ - (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem; - (void)updateChooseDatabaseToolbarItemWidth; -//NSDocument methods -- (NSString *)windowNibName; -- (void)windowControllerDidLoadNib:(NSWindowController *)aController; -- (void)windowWillClose:(NSNotification *)aNotification; - -//NSWindow delegate methods -- (BOOL)windowShouldClose:(id)sender; - //SMySQL delegate methods - (void)willQueryString:(NSString *)query; - (void)queryGaveError:(NSString *)error; -// Connection sheet delegate methods -- (void) controlTextDidChange:(NSNotification *)aNotification; - -//splitView delegate methods -- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview; -- (float)splitView:(NSSplitView *)sender constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)offset; -- (float)splitView:(NSSplitView *)sender constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)offset; -- (NSRect)splitView:(NSSplitView *)splitView additionalEffectiveRectOfDividerAtIndex:(int)dividerIndex; - - -//tableView datasource methods -- (int)numberOfRowsInTableView:(NSTableView *)aTableView; -- (id)tableView:(NSTableView *)aTableView - objectValueForTableColumn:(NSTableColumn *)aTableColumn - row:(int)rowIndex; - @end extern NSString *TableDocumentFavoritesControllerSelectionIndexDidChange; diff --git a/Source/TableDocument.m b/Source/TableDocument.m index 6d936800..b6cab9e4 100644 --- a/Source/TableDocument.m +++ b/Source/TableDocument.m @@ -33,24 +33,35 @@ #import "TableStatus.h" #import "ImageAndTextCell.h" #import "SPGrowlController.h" +#import "SPExportController.h" #import "SPQueryConsole.h" #import "SPSQLParser.h" #import "SPTableData.h" +#import "SPStringAdditions.h" +#import "SPQueryConsole.h" +#import "CMMCPConnection.h" +#import "CMMCPResult.h" +#import "MainController.h" +#import "SPPreferenceController.h" NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocumentFavoritesControllerSelectionIndexDidChange"; -NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFavoritesControllerFavoritesDidChange"; + +@interface TableDocument (PrivateAPI) + +- (BOOL)_favoriteAlreadyExists:(NSString *)database host:(NSString *)host user:(NSString *)user; + +@end @implementation TableDocument - (id)init { - if (![super init]) - return nil; - - _encoding = [@"utf8" retain]; - chooseDatabaseButton = nil; - chooseDatabaseToolbarItem = nil; - + if ((self = [super init])) { + _encoding = [@"utf8" retain]; + chooseDatabaseButton = nil; + chooseDatabaseToolbarItem = nil; + } + return self; } @@ -59,11 +70,9 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa // register selection did change handler for favorites controller (used in connect sheet) [favoritesController addObserver:self forKeyPath:@"selectionIndex" options:NSKeyValueChangeInsertion context:TableDocumentFavoritesControllerSelectionIndexDidChange]; - // register value change handler for favourites, so we can save them to preferences - [self addObserver:self forKeyPath:@"favorites" options:0 context:TableDocumentFavoritesControllerFavoritesDidChange]; - // register double click for the favorites view (double click favorite to connect) [connectFavoritesTableView setTarget:self]; + [connectFavoritesTableView setDoubleAction:@selector(connect:)]; // find the Database -> Database Encoding menu (it's not in our nib, so we can't use interface builder) selectEncodingMenu = [[[[[NSApp mainMenu] itemWithTag:1] submenu] itemWithTag:1] submenu]; @@ -78,32 +87,34 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [self chooseFavorite:self]; return; } - - if (context == TableDocumentFavoritesControllerFavoritesDidChange) { - [prefs setObject:[self favorites] forKey:@"favorites"]; - return; - } - + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } - (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)ps error:(NSError **)e -{ - +{ NSPrintInfo *printInfo = [self printInfo]; + [printInfo setHorizontalPagination:NSFitPagination]; + [printInfo setVerticalPagination:NSAutoPagination]; NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:[[tableTabView selectedTabViewItem] view] printInfo:printInfo]; return printOp; } - - (CMMCPConnection *)sharedConnection { return mySQLConnection; } - //start sheet +/** + * Set whether the connection sheet should automaticall start connecting + */ +- (void)setShouldAutomaticallyConnect:(BOOL)shouldAutomaticallyConnect +{ + _shouldOpenConnectionAutomatically = shouldAutomaticallyConnect; +} + /** * tries to connect to a database server, shows connect sheet prompting user to * enter details/select favorite and shoows alert sheets on failure. @@ -111,7 +122,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa - (IBAction)connectToDB:(id)sender { - // load the details of the curretnly selected favorite into the text boxes in connect sheet + // load the details of the currently selected favorite into the text boxes in connect sheet [self chooseFavorite:self]; // run the connect sheet (modal) @@ -120,6 +131,13 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa modalDelegate:self didEndSelector:@selector(connectSheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; + + // Connect automatically to the last used or default favourite + // connectSheet must open first. + if (_shouldOpenConnectionAutomatically) { + _shouldOpenConnectionAutomatically = false; + [self connect:self]; + } } @@ -193,7 +211,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa //register as delegate [mySQLConnection setDelegate:self]; // set encoding - NSString *encodingName = [prefs objectForKey:@"encoding"]; + NSString *encodingName = [prefs objectForKey:@"DefaultEncoding"]; if ( [encodingName isEqualToString:@"Autodetect"] ) { [self setConnectionEncoding:[self databaseEncoding] reloadingViews:NO]; } else { @@ -214,12 +232,12 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [tableContentInstance setConnection:mySQLConnection]; [customQueryInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; + [spExportControllerInstance setConnection:mySQLConnection]; [tableStatusInstance setConnection:mySQLConnection]; [tableDataInstance setConnection:mySQLConnection]; [self setFileName:[NSString stringWithFormat:@"(MySQL %@) %@@%@ %@", mySQLVersion, [userField stringValue], [hostField stringValue], [databaseField stringValue]]]; - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@", mySQLVersion, [userField stringValue], - [hostField stringValue], [databaseField stringValue]]]; + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", mySQLVersion, [self name], [databaseField stringValue]]]; // Connected Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Connected" @@ -230,12 +248,12 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa //can't connect to host NSBeginAlertSheet(NSLocalizedString(@"Connection failed!", @"connection failed"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, @selector(sheetDidEnd:returnCode:contextInfo:), @"connect", - [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to host %@.\nBe sure that the address is correct and that you have the necessary privileges.\nMySQL said: %@", @"message of panel when connection to host failed"), [hostField stringValue], [mySQLConnection getLastErrorMessage]]); + [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to host %@, or the request timed out.\n\nBe sure that the address is correct and that you have the necessary privileges, or try increasing the connection timeout (currently %i seconds).\n\nMySQL said: %@", @"message of panel when connection to host failed"), [hostField stringValue], [[prefs objectForKey:@"ConnectionTimeoutValue"] intValue], [mySQLConnection getLastErrorMessage]]); } else if (code == 3) { //can't connect to db NSBeginAlertSheet(NSLocalizedString(@"Connection failed!", @"connection failed"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, @selector(sheetDidEnd:returnCode:contextInfo:), @"connect", - [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that the database exists and that you have the necessary privileges.\nMySQL said: %@", @"message of panel when connection to db failed"), [databaseField stringValue], [mySQLConnection getLastErrorMessage]]); + [NSString stringWithFormat:NSLocalizedString(@"Connected to host, but unable to connect to database %@.\n\nBe sure that the database exists and that you have the necessary privileges.\n\nMySQL said: %@", @"message of panel when connection to db failed"), [databaseField stringValue], [mySQLConnection getLastErrorMessage]]); } else if (code == 4) { //no host is given NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, @@ -250,12 +268,12 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [tableWindow close]; } -- (IBAction)closeSheet:(id)sender -/* - invoked when user hits the cancel button of the connectSheet - stops modal session with code 0 - reused when user hits the close button of the variablseSheet or of the createTableSyntaxSheet +/** + * Invoked when user hits the cancel button of the connectSheet + * stops modal session with code 0 + * reused when user hits the close button of the variablseSheet or of the createTableSyntaxSheet */ +- (IBAction)closeSheet:(id)sender { [NSApp stopModalWithCode:0]; } @@ -268,55 +286,28 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa if (![self selectedFavorite]) return; + [nameField setStringValue:[self valueForKeyPath:@"selectedFavorite.name"]]; [hostField setStringValue:[self valueForKeyPath:@"selectedFavorite.host"]]; [socketField setStringValue:[self valueForKeyPath:@"selectedFavorite.socket"]]; [userField setStringValue:[self valueForKeyPath:@"selectedFavorite.user"]]; [portField setStringValue:[self valueForKeyPath:@"selectedFavorite.port"]]; [databaseField setStringValue:[self valueForKeyPath:@"selectedFavorite.database"]]; [passwordField setStringValue:[self selectedFavoritePassword]]; -} - -/** - * Remove the selected favourite. Instead of calling the remove: method of the Favorites NSArrayController - * directly in the XIB we do it here because we also need to remove the keychain password. - */ -- (IBAction)removeFavorite:(id)sender -{ - if (![self selectedFavorite]) { - return; - } - - NSString *name = [self valueForKeyPath:@"selectedFavorite.name"]; - NSString *user = [self valueForKeyPath:@"selectedFavorite.user"]; - NSString *host = [self valueForKeyPath:@"selectedFavorite.host"]; - NSString *database = [self valueForKeyPath:@"selectedFavorite.database"]; - - [keyChainInstance deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro : %@", name] - account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]]; - [keyChainInstance deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@", name] - account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]]; - // Remove from favorites array controller - [favoritesController remove:[self selectedFavorite]]; - + [prefs setInteger:[favoritesController selectionIndex] forKey:@"LastFavoriteIndex"]; } /** - * Return the favorites array. + * Opens the preferences window, or brings it to the front, and switch to the favorites tab. + * If a favorite is selected in the connection sheet, it is also select in the prefs window. */ -- (NSMutableArray *)favorites +- (IBAction)editFavorites:(id)sender { - // if no favorites, load from user defaults - if (!favorites) { - favorites = [[NSMutableArray alloc] initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:@"favorites"]]; - } - - // if no favorites in user defaults, load empty ones - if (!favorites) { - favorites = [[NSMutableArray array] retain]; - } + SPPreferenceController *prefsController = [[NSApp delegate] preferenceController]; - return favorites; + [prefsController showWindow:self]; + [prefsController displayFavoritePreferences:self]; + [prefsController selectFavorites:[favoritesController selectedObjects]]; } /** @@ -340,7 +331,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa if (![self selectedFavorite]) return nil; - NSString *keychainName = [NSString stringWithFormat:@"Sequel Pro : %@", [self valueForKeyPath:@"selectedFavorite.name"]]; + NSString *keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [self valueForKeyPath:@"selectedFavorite.name"], [[self valueForKeyPath:@"selectedFavorite.id"] intValue]]; NSString *keychainAccount = [NSString stringWithFormat:@"%@@%@/%@", [self valueForKeyPath:@"selectedFavorite.user"], [self valueForKeyPath:@"selectedFavorite.host"], @@ -351,23 +342,24 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa - (void)connectSheetAddToFavorites:(id)sender { - [self addToFavoritesHost:[hostField stringValue] socket:[socketField stringValue] user:[userField stringValue] password:[passwordField stringValue] port:[portField stringValue] database:[databaseField stringValue] useSSH:false sshHost:@"" sshUser:@"" sshPassword:@"" sshPort:@""]; + [self addToFavoritesName:[nameField stringValue] host:[hostField stringValue] socket:[socketField stringValue] user:[userField stringValue] password:[passwordField stringValue] port:[portField stringValue] database:[databaseField stringValue] useSSH:false sshHost:@"" sshUser:@"" sshPassword:@"" sshPort:@""]; } /** * add actual connection to favorites */ -- (void)addToFavoritesHost:(NSString *)host socket:(NSString *)socket - user:(NSString *)user password:(NSString *)password - port:(NSString *)port database:(NSString *)database - useSSH:(BOOL)useSSH // no-longer in use - sshHost:(NSString *)sshHost // no-longer in use - sshUser:(NSString *)sshUser // no-longer in use - sshPassword:(NSString *)sshPassword // no-longer in use - sshPort:(NSString *)sshPort // no-longer in use -{ - NSString *favoriteName = [NSString stringWithFormat:@"%@@%@", user, host]; - if (![database isEqualToString:@""]) +- (void)addToFavoritesName:(NSString *)name host:(NSString *)host socket:(NSString *)socket + user:(NSString *)user password:(NSString *)password + port:(NSString *)port database:(NSString *)database + useSSH:(BOOL)useSSH // no-longer in use + sshHost:(NSString *)sshHost // no-longer in use + sshUser:(NSString *)sshUser // no-longer in use + sshPassword:(NSString *)sshPassword // no-longer in use + sshPort:(NSString *)sshPort // no-longer in use +{ + NSString *favoriteName = [name length]?name:[NSString stringWithFormat:@"%@@%@", user, host]; + NSNumber *favoriteid = [NSNumber numberWithInt:[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; + if (![name length] && ![database isEqualToString:@""]) favoriteName = [NSString stringWithFormat:@"%@ %@", database, favoriteName]; // test if host and socket are not nil @@ -376,21 +368,18 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa return; } - [self willChangeValueForKey:@"favorites"]; - // write favorites and password - NSMutableDictionary *newFavorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:favoriteName, host, socket, user, port, database, nil] - forKeys:[NSArray arrayWithObjects:@"name", @"host", @"socket", @"user", @"port", @"database", nil]]; - [favorites addObject:newFavorite]; - + NSMutableDictionary *newFavorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:favoriteName, host, socket, user, port, database, favoriteid, nil] + forKeys:[NSArray arrayWithObjects:@"name", @"host", @"socket", @"user", @"port", @"database", @"id", nil]]; if (![password isEqualToString:@""]) { [keyChainInstance addPassword:password - forName:[NSString stringWithFormat:@"Sequel Pro : %@", favoriteName] + forName:[NSString stringWithFormat:@"Sequel Pro : %@ (%i)", favoriteName, [favoriteid intValue]] account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]]; } - [self didChangeValueForKey:@"favorites"]; + [favoritesController addObject:newFavorite]; [favoritesController setSelectedObjects:[NSArray arrayWithObject:newFavorite]]; + [[[NSApp delegate] preferenceController] updateDefaultFavoritePopup]; } /** @@ -400,10 +389,9 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa * if contextInfo == removedatabase -> tries to remove the selected database */ - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo -{ - [sheet orderOut:self]; - +{ if ([contextInfo isEqualToString:@"connect"]) { + [sheet orderOut:self]; [self connectToDB:nil]; return; } @@ -412,7 +400,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa if (returnCode != NSAlertDefaultReturn) return; - [mySQLConnection queryString:[NSString stringWithFormat:@"DROP DATABASE `%@`", [self database]]]; + [mySQLConnection queryString:[NSString stringWithFormat:@"DROP DATABASE %@", [[self database] backtickQuotedString]]]; if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { // error while deleting db NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't remove database.\nMySQL said: %@", @"message of panel when removing db failed"), [mySQLConnection getLastErrorMessage]]); @@ -424,7 +412,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [self setDatabases:self]; [tablesListInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/", mySQLVersion, [userField stringValue], [hostField stringValue]]]; + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/", mySQLVersion, [self name]]]; } } @@ -496,7 +484,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa selectedDatabase = [[chooseDatabaseButton titleOfSelectedItem] retain]; [tablesListInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@", mySQLVersion, [userField stringValue], [hostField stringValue], [self database]]]; + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", mySQLVersion, [self name], [self database]]]; } /** @@ -535,11 +523,11 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa return; } - NSString *createStatement = [NSString stringWithFormat:@"CREATE DATABASE `%@`", [databaseNameField stringValue]]; + NSString *createStatement = [NSString stringWithFormat:@"CREATE DATABASE %@", [[databaseNameField stringValue] backtickQuotedString]]; // If there is an encoding selected other than the default we must specify it in CREATE DATABASE statement if ([databaseEncodingButton indexOfSelectedItem] > 0) { - createStatement = [NSString stringWithFormat:@"%@ DEFAULT CHARACTER SET `%@`", createStatement, [self mysqlEncodingFromDisplayEncoding:[databaseEncodingButton title]]]; + createStatement = [NSString stringWithFormat:@"%@ DEFAULT CHARACTER SET %@", createStatement, [[self mysqlEncodingFromDisplayEncoding:[databaseEncodingButton title]] backtickQuotedString]]; } // Create the database @@ -565,7 +553,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [self setDatabases:self]; [tablesListInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@", mySQLVersion, [userField stringValue], [hostField stringValue], selectedDatabase]]; + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", mySQLVersion, [self name], selectedDatabase]]; } /** @@ -583,10 +571,19 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa { if ([chooseDatabaseButton indexOfSelectedItem] == 0) return; + if (![tablesListInstance selectionShouldChangeInTableView:nil]) return; - NSBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"Delete", @"delete button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, tableWindow, self, nil, @selector(sheetDidEnd:returnCode:contextInfo:), @"removedatabase", [NSString stringWithFormat:NSLocalizedString(@"Do you really want to delete the database %@?", @"message of panel asking for confirmation for deleting db"), [self database]]); + NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Delete database '%@'?", @"delete database message"), [self database]] + defaultButton:NSLocalizedString(@"Delete", @"delete button") + alternateButton:NSLocalizedString(@"Cancel", @"cancel button") + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the database '%@'. This operation cannot be undone.", @"delete database informative message"), [self database]]]; + + [alert setAlertStyle:NSCriticalAlertStyle]; + + [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removedatabase"]; } #pragma mark Console methods @@ -596,7 +593,25 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa */ - (void)toggleConsole:(id)sender { - [[queryConsoleInstance window] setIsVisible:![[queryConsoleInstance window] isVisible]]; + BOOL isConsoleVisible = [[[SPQueryConsole sharedQueryConsole] window] isVisible]; + + // Show or hide the console + [[[SPQueryConsole sharedQueryConsole] window] setIsVisible:(!isConsoleVisible)]; + + // Get the menu item for showing and hiding the console. This is isn't the best way to get it as any + // changes to the menu structure will result in the wrong item being selected. + NSMenuItem *menuItem = [[[[NSApp mainMenu] itemAtIndex:3] submenu] itemAtIndex:5]; + + // Only update the menu item title if its the menu item and not the toolbar + [menuItem setTitle:(!isConsoleVisible) ? NSLocalizedString(@"Hide Console", @"Hide Console") : NSLocalizedString(@"Show Console", @"Show Console")]; +} + +/** + * Clears the console by removing all of its messages + */ +- (void)clearConsole:(id)sender +{ + [[SPQueryConsole sharedQueryConsole] clearConsole:sender]; } #pragma mark Encoding Methods @@ -606,11 +621,11 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa */ - (void)setConnectionEncoding:(NSString *)mysqlEncoding reloadingViews:(BOOL)reloadViews { - BOOL uselatin1results = NO; + _encodingViaLatin1 = NO; // Special-case UTF-8 over latin 1 to allow viewing/editing of mangled data. if ([mysqlEncoding isEqualToString:@"utf8-"]) { - uselatin1results = YES; + _encodingViaLatin1 = YES; mysqlEncoding = @"utf8"; } @@ -618,13 +633,14 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [mySQLConnection queryString:[NSString stringWithFormat:@"SET NAMES '%@'", mysqlEncoding]]; if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { - if (uselatin1results) + if (_encodingViaLatin1) [mySQLConnection queryString:@"SET CHARACTER_SET_RESULTS=latin1"]; [mySQLConnection setEncoding:[CMMCPConnection encodingForMySQLEncoding:[mysqlEncoding UTF8String]]]; [_encoding autorelease]; _encoding = [mysqlEncoding retain]; } else { [mySQLConnection queryString:[NSString stringWithFormat:@"SET NAMES '%@'", [self databaseEncoding]]]; + _encodingViaLatin1 = NO; if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { NSLog(@"Error: could not set encoding to %@ nor fall back to database encoding on MySQL %@", mysqlEncoding, [self mySQLVersion]); return; @@ -632,7 +648,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa } // update the selected menu item - if (uselatin1results) { + if (_encodingViaLatin1) { [self updateEncodingMenuWithSelectedEncoding:[self encodingNameFromMySQLEncoding:[NSString stringWithFormat:@"%@-", mysqlEncoding]]]; } else { [self updateEncodingMenuWithSelectedEncoding:[self encodingNameFromMySQLEncoding:mysqlEncoding]]; @@ -655,6 +671,14 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa return _encoding; } +/** + * Returns whether the current encoding should display results via Latin1 transport for backwards compatibility + */ +- (BOOL)connectionEncodingViaLatin1 +{ + return _encodingViaLatin1; +} + /** * updates the currently selected item in the encoding menu * @@ -781,18 +805,22 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa return _supportsEncoding; } - #pragma mark Table Methods +/** + * Displays the CREATE TABLE syntax of the selected table to the user via a HUD panel. + */ - (IBAction)showCreateTableSyntax:(id)sender { //Create the query and get results - NSString *query = [NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", [self table]]; + NSString *query = [NSString stringWithFormat:@"SHOW CREATE TABLE %@", [[self table] backtickQuotedString]]; CMMCPResult *theResult = [mySQLConnection queryString:query]; - // Check for errors + // Check for errors, only displaying if the connection hasn't been terminated if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while creating table syntax.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + if ([mySQLConnection isConnected]) { + NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while creating table syntax.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + } return; } @@ -805,15 +833,20 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [createTableSyntaxWindow makeKeyAndOrderFront:self]; } +/** + * Copies the CREATE TABLE syntax of the selected table to the pasteboard. + */ - (IBAction)copyCreateTableSyntax:(id)sender { // Create the query and get results - NSString *query = [NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", [self table]]; + NSString *query = [NSString stringWithFormat:@"SHOW CREATE TABLE %@", [[self table] backtickQuotedString]]; CMMCPResult *theResult = [mySQLConnection queryString:query]; - // Check for errors + // Check for errors, only displaying if the connection hasn't been terminated if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while creating table syntax.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + if ([mySQLConnection isConnected]) { + NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while creating table syntax.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + } return; } @@ -833,131 +866,264 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa notificationName:@"Table Syntax Copied"]; } +/** + * Performs a MySQL check table on the selected table and presents the result to the user via an alert sheet. + */ - (IBAction)checkTable:(id)sender -{ - NSString *query; - CMMCPResult *theResult; - NSDictionary *theRow; +{ + CMMCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"CHECK TABLE %@", [[self table] backtickQuotedString]]]; - //Create the query and get results - query = [NSString stringWithFormat:@"CHECK TABLE `%@`", [self table]]; - theResult = [mySQLConnection queryString:query]; - - // Check for errors + // Check for errors, only displaying if the connection hasn't been terminated if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while checking table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + if ([mySQLConnection isConnected]) { + + [[NSAlert alertWithMessageText:@"Unable to check table" + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:@"An error occurred while trying to check the table '%@'. Please try again.\n\n%@", [self table], [mySQLConnection getLastErrorMessage]]] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; + } + return; } // Process result - theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; - NSRunInformationalAlertPanel([NSString stringWithFormat:@"Check '%@' table", [self table]], [NSString stringWithFormat:@"Check: %@", [theRow objectForKey:@"Msg_text"]], @"OK", nil, nil); + NSDictionary *result = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; + + NSString *message = @""; + + message = ([[result objectForKey:@"Msg_type"] isEqualToString:@"status"]) ? @"Check table successfully passed." : @"Check table failed."; + + message = [NSString stringWithFormat:@"%@\n\nMySQL said: %@", message, [result objectForKey:@"Msg_text"]]; + + [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Check table '%@'", [self table]] + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:message] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; } +/** + * Analyzes the selected table and presents the result to the user via an alert sheet. + */ - (IBAction)analyzeTable:(id)sender { - NSString *query; - CMMCPResult *theResult; - NSDictionary *theRow; - - //Create the query and get results - query = [NSString stringWithFormat:@"ANALYZE TABLE `%@`", [self table]]; - theResult = [mySQLConnection queryString:query]; + CMMCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"ANALYZE TABLE %@", [[self table] backtickQuotedString]]]; - // Check for errors + // Check for errors, only displaying if the connection hasn't been terminated if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while analyzing table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + if ([mySQLConnection isConnected]) { + + [[NSAlert alertWithMessageText:@"Unable to analyze table" + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:@"An error occurred while trying to analyze the table '%@'. Please try again.\n\n%@", [self table], [mySQLConnection getLastErrorMessage]]] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; + } + return; } // Process result - theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; - NSRunInformationalAlertPanel([NSString stringWithFormat:@"Analyze '%@' table", [self table]], [NSString stringWithFormat:@"Analyze: %@", [theRow objectForKey:@"Msg_text"]], @"OK", nil, nil); + NSDictionary *result = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; + + NSString *message = @""; + + message = ([[result objectForKey:@"Msg_type"] isEqualToString:@"status"]) ? @"Successfully analyzed table" : @"Analyze table failed."; + + message = [NSString stringWithFormat:@"%@\n\nMySQL said: %@", message, [result objectForKey:@"Msg_text"]]; + + [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Analyze table '%@'", [self table]] + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:message] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; } +/** + * Optimizes the selected table and presents the result to the user via an alert sheet. + */ - (IBAction)optimizeTable:(id)sender { - NSString *query; - CMMCPResult *theResult; - NSDictionary *theRow; - - //Create the query and get results - query = [NSString stringWithFormat:@"OPTIMIZE TABLE `%@`", [self table]]; - theResult = [mySQLConnection queryString:query]; + CMMCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"OPTIMIZE TABLE %@", [[self table] backtickQuotedString]]]; - // Check for errors - if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while optimizing table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + // Check for errors, only displaying if the connection hasn't been terminated + if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { + if ([mySQLConnection isConnected]) { + + [[NSAlert alertWithMessageText:@"Unable to optimize table" + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:@"An error occurred while trying to optimize the table '%@'. Please try again.\n\n%@", [self table], [mySQLConnection getLastErrorMessage]]] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; + } + + return; } // Process result - theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; - NSRunInformationalAlertPanel([NSString stringWithFormat:@"Optimize '%@' table", [self table]], [NSString stringWithFormat:@"Optimize: %@", [theRow objectForKey:@"Msg_text"]], @"OK", nil, nil); + NSDictionary *result = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; + + NSString *message = @""; + + message = ([[result objectForKey:@"Msg_type"] isEqualToString:@"status"]) ? @"Successfully optimized table" : @"Optimize table failed."; + + message = [NSString stringWithFormat:@"%@\n\nMySQL said: %@", message, [result objectForKey:@"Msg_text"]]; + + [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Optimize table '%@'", [self table]] + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:message] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; } +/** + * Repairs the selected table and presents the result to the user via an alert sheet. + */ - (IBAction)repairTable:(id)sender { - NSString *query; - CMMCPResult *theResult; - NSDictionary *theRow; - - //Create the query and get results - query = [NSString stringWithFormat:@"REPAIR TABLE `%@`", [self table]]; - theResult = [mySQLConnection queryString:query]; + CMMCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"REPAIR TABLE %@", [[self table] backtickQuotedString]]]; - // Check for errors + // Check for errors, only displaying if the connection hasn't been terminated if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while repairing table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + if ([mySQLConnection isConnected]) { + + [[NSAlert alertWithMessageText:@"Unable to repair table" + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:@"An error occurred while trying to repair the table '%@'. Please try again.\n\n%@", [self table], [mySQLConnection getLastErrorMessage]]] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; + } + + return; } // Process result - theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; - NSRunInformationalAlertPanel([NSString stringWithFormat:@"Repair '%@' table", [self table]], [NSString stringWithFormat:@"Repair: %@", [theRow objectForKey:@"Msg_text"]], @"OK", nil, nil); + NSDictionary *result = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; + + NSString *message = @""; + + message = ([[result objectForKey:@"Msg_type"] isEqualToString:@"status"]) ? @"Successfully repaired table" : @"Repair table failed."; + + message = [NSString stringWithFormat:@"%@\n\nMySQL said: %@", message, [result objectForKey:@"Msg_text"]]; + + [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Repair table '%@'", [self table]] + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:message] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; } +/** + * Flush the selected table and inform the user via a dialog sheet. + */ - (IBAction)flushTable:(id)sender { - NSString *query; - CMMCPResult *theResult; - - //Create the query and get results - query = [NSString stringWithFormat:@"FLUSH TABLE `%@`", [self table]]; - theResult = [mySQLConnection queryString:query]; + [mySQLConnection queryString:[NSString stringWithFormat:@"FLUSH TABLE %@", [[self table] backtickQuotedString]]]; - // Check for errors + // Check for errors, only displaying if the connection hasn't been terminated if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while flushing table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + if ([mySQLConnection isConnected]) { + + [[NSAlert alertWithMessageText:@"Unable to flush table" + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:@"An error occurred while trying to flush the table '%@'. Please try again.\n\n%@", [self table], [mySQLConnection getLastErrorMessage]]] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; + } + return; } - - // Process result - NSRunInformationalAlertPanel([NSString stringWithFormat:@"Flush '%@' table", [self table]], @"Flushed", @"OK", nil, nil); + + [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Flush table '%@'", [self table]] + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:@"Table was successfully flushed"] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; } +/** + * Runs a MySQL checksum on the selected table and present the result to the user via an alert sheet. + */ - (IBAction)checksumTable:(id)sender -{ - NSString *query; - CMMCPResult *theResult; - NSDictionary *theRow; - - //Create the query and get results - query = [NSString stringWithFormat:@"CHECKSUM TABLE `%@`", [self table]]; - theResult = [mySQLConnection queryString:query]; +{ + CMMCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"CHECKSUM TABLE %@", [[self table] backtickQuotedString]]]; - // Check for errors + // Check for errors, only displaying if the connection hasn't been terminated if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { - NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while performming checksum on table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil); + if ([mySQLConnection isConnected]) { + + [[NSAlert alertWithMessageText:@"Unable to perform checksum" + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:@"An error occurred while performing the checksum on table '%@'. Please try again.\n\n%@", [self table], [mySQLConnection getLastErrorMessage]]] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; + } + return; } // Process result - theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; - NSRunInformationalAlertPanel([NSString stringWithFormat:@"Checksum '%@' table", [self table]], [NSString stringWithFormat:@"Checksum: %@", [theRow objectForKey:@"Checksum"]], @"OK", nil, nil); + NSString *result = [[[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject] objectForKey:@"Checksum"]; + + [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Checksum table '%@'", [self table]] + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:@"Table checksum: %@", result]] + beginSheetModalForWindow:tableWindow + modalDelegate:self + didEndSelector:NULL + contextInfo:NULL]; } - #pragma mark Other Methods + /** - * returns the host + * Returns the host */ - (NSString *)host { @@ -965,7 +1131,18 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa } /** - * passes query to tablesListInstance + * Returns the name + */ +- (NSString *)name +{ + if ([[nameField stringValue] length]) { + return [nameField stringValue]; + } + return [NSString stringWithFormat:@"%@@%@", [userField stringValue], [hostField stringValue]]; +} + +/** + * Passes query to tablesListInstance */ - (void)doPerformQueryService:(NSString *)query { @@ -974,7 +1151,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa } /** - * flushes the mysql privileges + * Flushes the mysql privileges */ - (void)flushPrivileges:(id)sender { @@ -990,10 +1167,10 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa } } -- (void)showVariables:(id)sender -/* - shows the mysql variables +/** + * Shows the MySQL server variables */ +- (void)showVariables:(id)sender { CMMCPResult *theResult; NSMutableArray *tempResult = [NSMutableArray array]; @@ -1031,48 +1208,48 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa notificationName:@"Disconnected"]; } +// Getter methods -//getter methods -- (NSString *)database -/* - returns the currently selected database +/** + * Returns the currently selected database */ +- (NSString *)database { return selectedDatabase; } -- (NSString *)table -/* - returns the currently selected table (passing the request to TablesList) +/** + * Returns the currently selected table (passing the request to TablesList) */ +- (NSString *)table { return [tablesListInstance tableName]; } -- (NSString *)mySQLVersion -/* - returns the mysql version +/** + * Returns the MySQL version */ +- (NSString *)mySQLVersion { return mySQLVersion; } -- (NSString *)user -/* - returns the mysql version +/** + * Returns the current user */ +- (NSString *)user { return [userField stringValue]; } +// Notification center methods -//notification center methods -- (void)willPerformQuery:(NSNotification *)notification -/* - invoked before a query is performed +/** + * Invoked before a query is performed */ +- (void)willPerformQuery:(NSNotification *)notification { - // Only start the progress indicator is this document window is key. + // Only start the progress indicator if this document window is key. // Because we are starting the progress indicator based on the notification // of a query being started, we have to prevent other windows from // starting theirs. The same is also true for the below hasPerformedQuery: @@ -1085,46 +1262,53 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa } } -- (void)hasPerformedQuery:(NSNotification *)notification -/* - invoked after a query has been performed +/** + * Invoked after a query has been performed */ +- (void)hasPerformedQuery:(NSNotification *)notification { if ([tableWindow isKeyWindow]) { [queryProgressBar stopAnimation:self]; } } -- (void)applicationWillTerminate:(NSNotification *)notification -/* - invoked when the application will terminate +/** + * Invoked when the application will terminate */ +- (void)applicationWillTerminate:(NSNotification *)notification { [tablesListInstance selectionShouldChangeInTableView:nil]; } -- (void)tunnelStatusChanged:(NSNotification *)notification -/* - the status of the tunnel has changed +/** + * The status of the tunnel has changed */ +- (void)tunnelStatusChanged:(NSNotification *)notification { } -//menu methods -- (IBAction)import:(id)sender -/* - passes the request to the tableDump object +// Menu methods + +/** + * Passes the request to the tableDump object */ +- (IBAction)import:(id)sender { [tableDumpInstance importFile]; } -- (IBAction)export:(id)sender -/* - passes the request to the tableDump object +/** + * Passes the request to the tableDump object */ +- (IBAction)export:(id)sender { - [tableDumpInstance exportFile:[sender tag]]; + if ([sender tag] == -1) { + //[tableDumpInstance export]; + + [spExportControllerInstance export]; + } else { + [tableDumpInstance exportFile:[sender tag]]; + } } - (IBAction)exportTable:(id)sender @@ -1141,7 +1325,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa * Menu validation */ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem -{ +{ if ([menuItem action] == @selector(import:) || [menuItem action] == @selector(export:) || [menuItem action] == @selector(exportMultipleTables:) || @@ -1172,6 +1356,10 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa return ([self table] != nil && [[self table] isNotEqualTo:@""]); } + if ([menuItem action] == @selector(addConnectionToFavorites:)) { + return (![self _favoriteAlreadyExists:[self database] host:[self host] user:[self user]]); + } + return [super validateMenuItem:menuItem]; } @@ -1219,6 +1407,9 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [tableTabView selectTabViewItemAtIndex:2]; [mainToolbar setSelectedItemIdentifier:@"SwitchToRunQueryToolbarItemIdentifier"]; + + // Set the focus on the text field if no query has been run + if (![[customQueryTextView string] length]) [tableWindow makeFirstResponder:customQueryTextView]; } - (IBAction)viewStatus:(id)sender @@ -1241,6 +1432,21 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [mainToolbar setSelectedItemIdentifier:@"SwitchToTableStatusToolbarItemIdentifier"]; } +/** + * Adds the current database connection details to the user's favorites if it doesn't already exist. + */ +- (IBAction)addConnectionToFavorites:(id)sender +{ + // Obviously don't add if it already exists. We shouldn't really need this as the menu item validation + // enables or disables the menu item based on the same method. Although to be safe do the check anyway + // as we don't know what's calling this method. + if ([self _favoriteAlreadyExists:[self database] host:[self host] user:[self user]]) { + return; + } + + // Add current connection to favorites using the same method as used on the connection sheet to provide consistency. + [self connectSheetAddToFavorites:self]; +} #pragma mark Toolbar Methods @@ -1253,8 +1459,8 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa mainToolbar = [[[NSToolbar alloc] initWithIdentifier:@"TableWindowToolbar"] autorelease]; // set up toolbar properties - [mainToolbar setAllowsUserCustomization: YES]; - [mainToolbar setAutosavesConfiguration: YES]; + [mainToolbar setAllowsUserCustomization:YES]; + [mainToolbar setAutosavesConfiguration:YES]; [mainToolbar setDisplayMode:NSToolbarDisplayModeIconAndLabel]; // set ourself as the delegate @@ -1297,11 +1503,11 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa //set up tooltip and image [toolbarItem setToolTip:NSLocalizedString(@"Show or hide the console which shows all MySQL commands performed by Sequel Pro", @"tooltip for toolbar item for show/hide console")]; - if ([[queryConsoleInstance window] isVisible]) { - [toolbarItem setLabel:NSLocalizedString(@"Hide Console", @"toolbar item for hide console")]; + if ([[[SPQueryConsole sharedQueryConsole] window] isVisible]) { + [toolbarItem setLabel:NSLocalizedString(@"Hide Console", @"Hide Console")]; [toolbarItem setImage:[NSImage imageNamed:@"hideconsole"]]; } else { - [toolbarItem setLabel:NSLocalizedString(@"Show Console", @"toolbar item for showconsole")]; + [toolbarItem setLabel:NSLocalizedString(@"Show Console", @"Show Console")]; [toolbarItem setImage:[NSImage imageNamed:@"showconsole"]]; } @@ -1317,12 +1523,12 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [toolbarItem setToolTip:NSLocalizedString(@"Clear the console which shows all MySQL commands performed by Sequel Pro", @"tooltip for toolbar item for clear console")]; [toolbarItem setImage:[NSImage imageNamed:@"clearconsole"]]; //set up the target action - [toolbarItem setTarget:queryConsoleInstance]; + [toolbarItem setTarget:self]; [toolbarItem setAction:@selector(clearConsole:)]; } else if ([itemIdentifier isEqualToString:@"SwitchToTableStructureToolbarItemIdentifier"]) { - [toolbarItem setLabel:NSLocalizedString(@"Table", @"toolbar item label for switching to the Table Structure tab")]; - [toolbarItem setPaletteLabel:NSLocalizedString(@"Table Structure", @"toolbar item label for switching to the Table Structure tab")]; + [toolbarItem setLabel:NSLocalizedString(@"Structure", @"toolbar item label for switching to the Table Structure tab")]; + [toolbarItem setPaletteLabel:NSLocalizedString(@"Edit Table Structure", @"toolbar item label for switching to the Table Structure tab")]; //set up tooltip and image [toolbarItem setToolTip:NSLocalizedString(@"Switch to the Table Structure tab", @"tooltip for toolbar item for switching to the Table Structure tab")]; [toolbarItem setImage:[NSImage imageNamed:@"toolbar-switch-to-structure"]]; @@ -1331,8 +1537,8 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [toolbarItem setAction:@selector(viewStructure:)]; } else if ([itemIdentifier isEqualToString:@"SwitchToTableContentToolbarItemIdentifier"]) { - [toolbarItem setLabel:NSLocalizedString(@"Browse", @"toolbar item label for switching to the Table Content tab")]; - [toolbarItem setPaletteLabel:NSLocalizedString(@"Table Content", @"toolbar item label for switching to the Table Content tab")]; + [toolbarItem setLabel:NSLocalizedString(@"Content", @"toolbar item label for switching to the Table Content tab")]; + [toolbarItem setPaletteLabel:NSLocalizedString(@"Browse & Edit Table Content", @"toolbar item label for switching to the Table Content tab")]; //set up tooltip and image [toolbarItem setToolTip:NSLocalizedString(@"Switch to the Table Content tab", @"tooltip for toolbar item for switching to the Table Content tab")]; [toolbarItem setImage:[NSImage imageNamed:@"toolbar-switch-to-browse"]]; @@ -1341,8 +1547,8 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [toolbarItem setAction:@selector(viewContent:)]; } else if ([itemIdentifier isEqualToString:@"SwitchToRunQueryToolbarItemIdentifier"]) { - [toolbarItem setLabel:NSLocalizedString(@"SQL", @"toolbar item label for switching to the Run Query tab")]; - [toolbarItem setPaletteLabel:NSLocalizedString(@"Run Query", @"toolbar item label for switching to the Run Query tab")]; + [toolbarItem setLabel:NSLocalizedString(@"Query", @"toolbar item label for switching to the Run Query tab")]; + [toolbarItem setPaletteLabel:NSLocalizedString(@"Run Custom Query", @"toolbar item label for switching to the Run Query tab")]; //set up tooltip and image [toolbarItem setToolTip:NSLocalizedString(@"Switch to the Run Query tab", @"tooltip for toolbar item for switching to the Run Query tab")]; [toolbarItem setImage:[NSImage imageNamed:@"toolbar-switch-to-sql"]]; @@ -1396,7 +1602,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa { return [NSArray arrayWithObjects: @"DatabaseSelectToolbarItemIdentifier", - NSToolbarFlexibleSpaceItemIdentifier, + NSToolbarSeparatorItemIdentifier, @"SwitchToTableStructureToolbarItemIdentifier", @"SwitchToTableContentToolbarItemIdentifier", @"SwitchToRunQueryToolbarItemIdentifier", @@ -1404,6 +1610,9 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa nil]; } +/** + * toolbar delegate method + */ - (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar { return [NSArray arrayWithObjects: @@ -1416,20 +1625,29 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa } /** - * validates the toolbar items + * Validates the toolbar items */ - (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem; { - if ([[toolbarItem itemIdentifier] isEqualToString:@"ToggleConsoleIdentifier"]) { - if ([[queryConsoleInstance window] isVisible]) { + NSString *identifier = [toolbarItem itemIdentifier]; + + // Toggle console item + if ([identifier isEqualToString:@"ToggleConsoleIdentifier"]) { + if ([[[SPQueryConsole sharedQueryConsole] window] isVisible]) { [toolbarItem setLabel:@"Hide Console"]; [toolbarItem setImage:[NSImage imageNamed:@"hideconsole"]]; - } else { + } + else { [toolbarItem setLabel:@"Show Console"]; [toolbarItem setImage:[NSImage imageNamed:@"showconsole"]]; } } + // Clear console item + if ([identifier isEqualToString:@"ClearConsoleIdentifier"]) { + return ([[SPQueryConsole sharedQueryConsole] consoleMessageCount] > 0); + } + return YES; } @@ -1443,11 +1661,11 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa return @"DBView"; } -- (void)windowControllerDidLoadNib:(NSWindowController *) aController -/* - code that need to be executed once the windowController has loaded the document's window - sets upt the interface (small fonts) +/** + * Code that need to be executed once the windowController has loaded the document's window + * sets upt the interface (small fonts). */ +- (void)windowControllerDidLoadNib:(NSWindowController *) aController { [aController setShouldCascadeWindows:YES]; [super windowControllerDidLoadNib:aController]; @@ -1455,8 +1673,6 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa NSEnumerator *theCols = [[variablesTableView tableColumns] objectEnumerator]; NSTableColumn *theCol; - // [tableWindow makeKeyAndOrderFront:self]; - prefs = [[NSUserDefaults standardUserDefaults] retain]; //register for notifications @@ -1468,15 +1684,15 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa name:@"NSApplicationWillTerminateNotification" object:nil]; //set up interface - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { - [[queryConsoleInstance consoleTextView] setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; + if ( [prefs boolForKey:@"UseMonospacedFonts"] ) { + [[SPQueryConsole sharedQueryConsole] setConsoleFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; [syntaxViewContent setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; while ( (theCol = [theCols nextObject]) ) { [[theCol dataCell] setFont:[NSFont fontWithName:@"Monaco" size:10]]; } } else { - [[queryConsoleInstance consoleTextView] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + [[SPQueryConsole sharedQueryConsole] setConsoleFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; [syntaxViewContent setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; while ( (theCol = [theCols nextObject]) ) { [[theCol dataCell] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; @@ -1487,21 +1703,33 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [self setupToolbar]; // [self connectToDB:nil]; [self performSelector:@selector(connectToDB:) withObject:tableWindow afterDelay:0.0f]; + + if([prefs boolForKey:@"SelectLastFavoriteUsed"] == YES){ + [favoritesController setSelectionIndex:[prefs integerForKey:@"LastFavoriteIndex"]]; + } else { + [favoritesController setSelectionIndex:[prefs integerForKey:@"DefaultFavorite"]]; + } } +// NSWindow delegate methods + +/** + * Invoked when the document window is about to close + */ - (void)windowWillClose:(NSNotification *)aNotification { + //reset print settings, so we're not prompted about saving them + [self setPrintInfo:[NSPrintInfo sharedPrintInfo]]; + if ([mySQLConnection isConnected]) [self closeConnection]; - if ([[queryConsoleInstance window] isVisible]) [self toggleConsole:self]; + if ([[[SPQueryConsole sharedQueryConsole] window] isVisible]) [self toggleConsole:self]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } - -//NSWindow delegate methods -- (BOOL)windowShouldClose:(id)sender -/* - invoked when the document window should close +/** + * Invoked when the document window should close */ +- (BOOL)windowShouldClose:(id)sender { if ( ![tablesListInstance selectionShouldChangeInTableView:nil] ) { return NO; @@ -1516,34 +1744,32 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa * Invoked when framework will perform a query */ - (void)willQueryString:(NSString *)query -{ - NSString *currentTime = [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:nil]; - - [queryConsoleInstance showMessageInConsole:[NSString stringWithFormat:@"/* MySQL %@ */ %@;\n", currentTime, query]]; +{ + [[SPQueryConsole sharedQueryConsole] showMessageInConsole:query]; } /** * Invoked when query gave an error */ - (void)queryGaveError:(NSString *)error -{ - NSString *currentTime = [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:nil]; - - [queryConsoleInstance showErrorInConsole:[NSString stringWithFormat:@"/* ERROR %@ */ %@;\n", currentTime, error]]; +{ + [[SPQueryConsole sharedQueryConsole] showErrorInConsole:error]; } +#pragma mark - #pragma mark Connection sheet delegate methods /** * When a favorite is selected, and the connection details are edited, deselect the favorite; * this is clearer and also prevents a failed connection from being repopulated with the * favorite's details instead of the last used details. - * This method allows the password to be changed without altering the selection. */ - (void) controlTextDidChange:(NSNotification *)aNotification { - if ([aNotification object] == hostField || [aNotification object] == userField || [aNotification object] == databaseField - || [aNotification object] == socketField || [aNotification object] == portField) { + if ([aNotification object] == nameField || [aNotification object] == hostField + || [aNotification object] == userField || [aNotification object] == passwordField + || [aNotification object] == databaseField || [aNotification object] == socketField + || [aNotification object] == portField) { [favoritesController setSelectionIndexes:[NSIndexSet indexSet]]; } else if ([aNotification object] == databaseNameField) { @@ -1615,15 +1841,14 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa } -//tableView datasource methods +#pragma mark TableView datasource methods + - (int)numberOfRowsInTableView:(NSTableView *)aTableView { return [variables count]; } -- (id)tableView:(NSTableView *)aTableView -objectValueForTableColumn:(NSTableColumn *)aTableColumn - row:(int)rowIndex +- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { id theValue; @@ -1642,10 +1867,6 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn - (IBAction)terminate:(id)sender { [[NSApp orderedDocuments] makeObjectsPerformSelector:@selector(cancelConnectSheet:) withObject:nil]; - - // Save the favourites - commits any unsaved changes ie favourite renames - [prefs setObject:[self favorites] forKey:@"favorites"]; - [NSApp terminate:sender]; } @@ -1653,7 +1874,6 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn { [chooseDatabaseButton release]; [mySQLConnection release]; - [favorites release]; [variables release]; [selectedDatabase release]; [mySQLVersion release]; @@ -1663,3 +1883,33 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn } @end + +@implementation TableDocument (PrivateAPI) + +/** + * Checks to see if a favorite with the supplied details already exists. + */ +- (BOOL)_favoriteAlreadyExists:(NSString *)database host:(NSString *)host user:(NSString *)user +{ + NSArray *favorites = [favoritesController arrangedObjects]; + int i; + + // Ensure database, host, and user match prefs format + if (!database) database = @""; + if (!host) host = @""; + if (!user) user = @""; + + // Loop the favorites and check their details + for (i = 0; i < [favorites count]; i++) { + NSDictionary *favorite = [favorites objectAtIndex:i]; + if ([[favorite objectForKey:@"database"] isEqualToString:database] && + [[favorite objectForKey:@"host"] isEqualToString:host] && + [[favorite objectForKey:@"user"] isEqualToString:user]) { + return YES; + } + } + + return NO; +} + +@end diff --git a/Source/TableDump.h b/Source/TableDump.h index 3f89ab68..d35b40c4 100644 --- a/Source/TableDump.h +++ b/Source/TableDump.h @@ -58,6 +58,12 @@ IBOutlet id exportMultipleFieldsEscapedField; IBOutlet id exportMultipleLinesTerminatedField; + // New Export Window + IBOutlet id exportWindow; + IBOutlet id exportTabBar; + IBOutlet id exportToolbar; + IBOutlet id exportTableList; + IBOutlet id importCSVView; IBOutlet NSPopUpButton *importFormatPopup; IBOutlet id importCSVBox; @@ -104,8 +110,9 @@ - (IBAction)closeSheet:(id)sender; - (IBAction)stepRow:(id)sender; - (IBAction)cancelProgressBar:(id)sender; + //export methods -//- (IBAction)saveDump:(id)sender; +- (void)export; - (void)exportFile:(int)tag; - (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo; @@ -132,9 +139,14 @@ toFileHandle:(NSFileHandle *)fileHandle tableName:(NSString *)table withHeader:(BOOL)header silently:(BOOL)silently; - (NSString *)htmlEscapeString:(NSString *)string; -- (BOOL)exportTables:(NSArray *)selectedTables toFileHandle:(NSFileHandle *)fileHandle usingFormat:(NSString *)type; + +- (BOOL)exportTables:(NSArray *)selectedTables toFileHandle:(NSFileHandle *)fileHandle usingFormat:(NSString *)type usingMulti:(BOOL)multi; - (BOOL)exportSelectedTablesToFileHandle:(NSFileHandle *)fileHandle usingFormat:(NSString *)type; +// New Export methods +- (IBAction)switchTab:(id)sender; +- (IBAction)switchInput:(id)sender; + //additional methods - (void)setConnection:(CMMCPConnection *)theConnection; diff --git a/Source/TableDump.m b/Source/TableDump.m index fb0504a8..958969e9 100644 --- a/Source/TableDump.m +++ b/Source/TableDump.m @@ -31,6 +31,8 @@ #import "SPGrowlController.h" #import "SPSQLParser.h" #import "SPTableData.h" +#import "SPStringAdditions.h" +#import "SPArrayAdditions.h" @implementation TableDump @@ -88,12 +90,24 @@ ends the modal session */ { + [NSApp endSheet:exportWindow]; [NSApp stopModalWithCode:[sender tag]]; } #pragma mark - #pragma mark export methods +- (void)export +{ + [self reloadTables:self]; + [NSApp beginSheet:exportWindow modalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; +} + +- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + [sheet orderOut:self]; +} + - (void)exportFile:(int)tag /* invoked when user clicks on an export menuItem @@ -104,13 +118,13 @@ NSSavePanel *savePanel = [NSSavePanel savePanel]; [savePanel setAllowsOtherFileTypes:YES]; [savePanel setExtensionHidden:NO]; - NSString *currentDate = [[NSDate date] descriptionWithCalendarFormat:@"%d.%m.%Y" timeZone:nil locale:nil]; + NSString *currentDate = [[NSDate date] descriptionWithCalendarFormat:@"%Y-%m-%d" timeZone:nil locale:nil]; switch ( tag ) { case 5: // export dump [self reloadTables:self]; - file = [NSString stringWithFormat:@"%@_dump %@.sql", [tableDocumentInstance database], currentDate]; + file = [NSString stringWithFormat:@"%@_%@.sql", [tableDocumentInstance database], currentDate]; [savePanel setRequiredFileType:@"sql"]; [savePanel setAccessoryView:exportDumpView]; contextInfo = @"exportDump"; @@ -242,11 +256,11 @@ // Export the full resultset for the currently selected table to a file in CSV format } else if ( [contextInfo isEqualToString:@"exportTableContentAsCSV"] ) { - success = [self exportTables:[NSArray arrayWithObject:[tableDocumentInstance table]] toFileHandle:fileHandle usingFormat:@"csv"]; + success = [self exportTables:[NSArray arrayWithObject:[tableDocumentInstance table]] toFileHandle:fileHandle usingFormat:@"csv" usingMulti:NO]; // Export the full resultset for the currently selected table to a file in XML format } else if ( [contextInfo isEqualToString:@"exportTableContentAsXML"] ) { - success = [self exportTables:[NSArray arrayWithObject:[tableDocumentInstance table]] toFileHandle:fileHandle usingFormat:@"xml"]; + success = [self exportTables:[NSArray arrayWithObject:[tableDocumentInstance table]] toFileHandle:fileHandle usingFormat:@"xml" usingMulti:NO]; // Export the current "browse" view to a file in CSV format } else if ( [contextInfo isEqualToString:@"exportBrowseViewAsCSV"] ) { @@ -373,14 +387,30 @@ NSError *errorStr = nil; NSMutableString *errors = [NSMutableString string]; NSString *fileType = [[importFormatPopup selectedItem] title]; + BOOL importSQLAsUTF8 = YES; + + // Load file into string. For SQL imports, try UTF8 file encoding before the current encoding. + if ([fileType isEqualToString:@"SQL"]) { + NSLog(@"Reading as utf8"); + dumpFile = [SPSQLParser stringWithContentsOfFile:filename + encoding:NSUTF8StringEncoding + error:&errorStr]; + NSLog(dumpFile); + if (errorStr) { + importSQLAsUTF8 = NO; + errorStr = nil; + } + } - //load file into string - dumpFile = [SPSQLParser stringWithContentsOfFile:filename - encoding:[CMMCPConnection encodingForMySQLEncoding:[[tableDocumentInstance connectionEncoding] UTF8String]] - error:&errorStr]; + // If the SQL-as-UTF8 read failed, and for CSVs, use the current connection encoding. + if (!importSQLAsUTF8 || [fileType isEqualToString:@"CSV"]) { + dumpFile = [SPSQLParser stringWithContentsOfFile:filename + encoding:[CMMCPConnection encodingForMySQLEncoding:[[tableDocumentInstance connectionEncoding] UTF8String]] + error:&errorStr]; + } if (errorStr) { - NSBeginAlertSheet(NSLocalizedString(@"Error", @"Title of error alert"), + NSBeginAlertSheet(NSLocalizedString(@"Error", @"Error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, @@ -428,7 +458,16 @@ for ( i = 0 ; i < [queries count] ; i++ ) { [singleProgressBar setDoubleValue:((i+1)*100/[queries count])]; [singleProgressBar displayIfNeeded]; - [mySQLConnection queryString:[queries objectAtIndex:i]]; + + // Skip blank or whitespace-only queries to avoid errors + if ([[[queries objectAtIndex:i] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) + continue; + + if (importSQLAsUTF8) { + [mySQLConnection queryString:[queries objectAtIndex:i] usingEncoding:NSUTF8StringEncoding]; + } else { + [mySQLConnection queryString:[queries objectAtIndex:i]]; + } if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""] && ![[mySQLConnection getLastErrorMessage] isEqualToString:@"Query was empty"]) { [errors appendString:[NSString stringWithFormat:NSLocalizedString(@"[ERROR in query %d] %@\n", @"error text when multiple custom query failed"), (i+1),[mySQLConnection getLastErrorMessage]]]; @@ -497,7 +536,7 @@ [singleProgressBar setIndeterminate:NO]; if([importArray count] == 0){ - NSBeginAlertSheet(NSLocalizedString(@"Error", @"Title of error alert"), + NSBeginAlertSheet(NSLocalizedString(@"Error", @"Error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, @@ -584,7 +623,7 @@ if ( [fNames length] ) [fNames appendString:@","]; - [fNames appendString:[NSString stringWithFormat:@"`%@`", [[tableSourceInstance fieldNames] objectAtIndex:i]]]; + [fNames appendString:[[[tableSourceInstance fieldNames] objectAtIndex:i] backtickQuotedString]]; } } @@ -613,8 +652,8 @@ } //perform query - [mySQLConnection queryString:[NSString stringWithFormat:@"INSERT INTO `%@` (%@) VALUES (%@)", - [fieldMappingPopup titleOfSelectedItem], + [mySQLConnection queryString:[NSString stringWithFormat:@"INSERT INTO %@ (%@) VALUES (%@)", + [[fieldMappingPopup titleOfSelectedItem] backtickQuotedString], fNames, fValues]]; @@ -708,7 +747,7 @@ [fieldMappingButtonOptions setArray:[importArray objectAtIndex:currentRow]]; for (i = 0; i < [fieldMappingButtonOptions count]; i++) { if ([[fieldMappingButtonOptions objectAtIndex:i] isNSNull]) { - [fieldMappingButtonOptions replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%i. %@", i+1, [prefs objectForKey:@"nullValue"]]]; + [fieldMappingButtonOptions replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%i. %@", i+1, [prefs objectForKey:@"NullValue"]]]; } else { [fieldMappingButtonOptions replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%i. %@", i+1, [fieldMappingButtonOptions objectAtIndex:i]]]; } @@ -746,20 +785,21 @@ */ - (BOOL)dumpSelectedTablesAsSqlToFileHandle:(NSFileHandle *)fileHandle { - int i,j,t,rowCount, colCount, progressBarWidth, lastProgressValue, queryLength; + int i,j,t,rowCount, colCount, progressBarWidth, lastProgressValue, queryLength, tableType; CMMCPResult *queryResult; - NSString *tableName, *tableColumnTypeGrouping; + NSString *tableName, *tableColumnTypeGrouping, *previousConnectionEncoding; NSArray *fieldNames; NSArray *theRow; NSMutableArray *selectedTables = [NSMutableArray array]; - NSMutableString *headerString = [NSMutableString string]; + NSMutableString *metaString = [NSMutableString string]; NSMutableString *cellValue = [NSMutableString string]; NSMutableString *sqlString = [NSMutableString string]; NSMutableString *errors = [NSMutableString string]; NSDictionary *tableDetails; NSMutableArray *tableColumnNumericStatus; NSStringEncoding connectionEncoding = [mySQLConnection encoding]; - id createTableSyntax; + id createTableSyntax = nil; + BOOL previousConnectionEncodingViaLatin1; // Reset the interface [errorsView setString:@""]; @@ -783,16 +823,38 @@ } // Add the dump header to the dump file. - [headerString setString:@"# Sequel Pro dump\n"]; - [headerString appendString:[NSString stringWithFormat:@"# Version %@\n", - [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; - [headerString appendString:@"# http://code.google.com/p/sequel-pro\n#\n"]; - [headerString appendString:[NSString stringWithFormat:@"# Host: %@ (MySQL %@)\n", - [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; - [headerString appendString:[NSString stringWithFormat:@"# Database: %@\n", [tableDocumentInstance database]]]; - [headerString appendString:[NSString stringWithFormat:@"# Generation Time: %@\n", [NSDate date]]]; - [headerString appendString:@"# ************************************************************\n\n"]; - [fileHandle writeData:[headerString dataUsingEncoding:connectionEncoding]]; + [metaString setString:@"# Sequel Pro dump\n"]; + [metaString appendString:[NSString stringWithFormat:@"# Version %@\n", + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; + [metaString appendString:@"# http://code.google.com/p/sequel-pro\n#\n"]; + [metaString appendString:[NSString stringWithFormat:@"# Host: %@ (MySQL %@)\n", + [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; + [metaString appendString:[NSString stringWithFormat:@"# Database: %@\n", [tableDocumentInstance database]]]; + [metaString appendString:[NSString stringWithFormat:@"# Generation Time: %@\n", [NSDate date]]]; + [metaString appendString:@"# ************************************************************\n\n"]; + + // Add commands to store the client encodings used when importing and set to UTF8 to preserve data + [metaString appendString:@"/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n"]; + [metaString appendString:@"/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n"]; + [metaString appendString:@"/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n"]; + [metaString appendString:@"/*!40101 SET NAMES utf8 */;\n"]; + + // Add commands to store and disable unique checks, foreign key checks, mode and notes where supported. + // Include trailing semicolons to ensure they're run individually. Use mysql-version based comments. + if ( [addDropTableSwitch state] == NSOnState ) + [metaString appendString:@"/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n"]; + [metaString appendString:@"/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n"]; + [metaString appendString:@"/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n"]; + [metaString appendString:@"/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n\n\n"]; + + [fileHandle writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; + + // Store the current connection encoding so it can be restored after the dump. + previousConnectionEncoding = [tableDocumentInstance connectionEncoding]; + previousConnectionEncodingViaLatin1 = [tableDocumentInstance connectionEncodingViaLatin1]; + + // Set the connection to UTF8 to be able to export correctly. + [tableDocumentInstance setConnectionEncoding:@"utf8" reloadingViews:NO]; // Loop through the selected tables for ( i = 0 ; i < [selectedTables count] ; i++ ) { @@ -808,36 +870,48 @@ // Add the name of table [fileHandle writeData:[[NSString stringWithFormat:@"# Dump of table %@\n# ------------------------------------------------------------\n\n", tableName] - dataUsingEncoding:connectionEncoding]]; + dataUsingEncoding:NSUTF8StringEncoding]]; + // Determine whether this table is a table or a view via the create table command, and keep the create table syntax + queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE %@", [tableName backtickQuotedString]]]; + if ( [queryResult numOfRows] ) { + tableDetails = [[NSDictionary alloc] initWithDictionary:[queryResult fetchRowAsDictionary]]; + if ([tableDetails objectForKey:@"Create View"]) { + createTableSyntax = [[[tableDetails objectForKey:@"Create View"] copy] autorelease]; + tableType = SP_TABLETYPE_VIEW; + } else { + createTableSyntax = [[[tableDetails objectForKey:@"Create Table"] copy] autorelease]; + tableType = SP_TABLETYPE_TABLE; + } + [tableDetails release]; + } + if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { + [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; + if ( [addErrorsSwitch state] == NSOnState ) { + [fileHandle writeData:[[NSString stringWithFormat:@"# Error: %@\n", [mySQLConnection getLastErrorMessage]] dataUsingEncoding:NSUTF8StringEncoding]]; + } + } + + // Add a "drop table" command if specified in the export dialog if ( [addDropTableSwitch state] == NSOnState ) - [fileHandle writeData:[[NSString stringWithFormat:@"DROP TABLE IF EXISTS `%@`;\n\n", tableName] - dataUsingEncoding:connectionEncoding]]; + [fileHandle writeData:[[NSString stringWithFormat:@"DROP %@ IF EXISTS %@;\n\n", ((tableType == SP_TABLETYPE_TABLE)?@"TABLE":@"VIEW"), [tableName backtickQuotedString]] + dataUsingEncoding:NSUTF8StringEncoding]]; + // Add the create syntax for the table if specified in the export dialog - if ( [addCreateTableSwitch state] == NSOnState ) { - queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", tableName]]; - if ( [queryResult numOfRows] ) { - createTableSyntax = [[queryResult fetchRowAsDictionary] objectForKey:@"Create Table"]; - if ( [createTableSyntax isKindOfClass:[NSData class]] ) { - createTableSyntax = [[[NSString alloc] initWithData:createTableSyntax encoding:connectionEncoding] autorelease]; - } - [fileHandle writeData:[createTableSyntax dataUsingEncoding:connectionEncoding]]; - [fileHandle writeData:[[NSString stringWithString:@";\n\n"] dataUsingEncoding:connectionEncoding]]; - } - if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { - [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; - if ( [addErrorsSwitch state] == NSOnState ) { - [fileHandle writeData:[[NSString stringWithFormat:@"# Error: %@\n", [mySQLConnection getLastErrorMessage]] dataUsingEncoding:connectionEncoding]]; - } + if ( [addCreateTableSwitch state] == NSOnState && createTableSyntax) { + if ( [createTableSyntax isKindOfClass:[NSData class]] ) { + createTableSyntax = [[[NSString alloc] initWithData:createTableSyntax encoding:connectionEncoding] autorelease]; } + [fileHandle writeData:[createTableSyntax dataUsingEncoding:NSUTF8StringEncoding]]; + [fileHandle writeData:[[NSString stringWithString:@";\n\n"] dataUsingEncoding:NSUTF8StringEncoding]]; } // Add the table content if required - if ( [addTableContentSwitch state] == NSOnState ) { - queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM `%@`", tableName]]; + if ( [addTableContentSwitch state] == NSOnState && tableType == SP_TABLETYPE_TABLE ) { + queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@", [tableName backtickQuotedString]]]; fieldNames = [queryResult fetchFieldNames]; rowCount = [queryResult numOfRows]; @@ -868,9 +942,15 @@ [queryResult dataSeek:0]; queryLength = 0; + // Lock the table for writing and disable keys if supported + [metaString setString:@""]; + [metaString appendString:[NSString stringWithFormat:@"LOCK TABLES %@ WRITE;\n", [tableName backtickQuotedString]]]; + [metaString appendString:[NSString stringWithFormat:@"/*!40000 ALTER TABLE %@ DISABLE KEYS */;\n", [tableName backtickQuotedString]]]; + [fileHandle writeData:[metaString dataUsingEncoding:connectionEncoding]]; + // Construct the start of the insertion command - [fileHandle writeData:[[NSString stringWithFormat:@"INSERT INTO `%@` (`%@`)\nVALUES\n\t(", - tableName, [fieldNames componentsJoinedByString:@"`,`"]] dataUsingEncoding:connectionEncoding]]; + [fileHandle writeData:[[NSString stringWithFormat:@"INSERT INTO %@ (%@)\nVALUES\n\t(", + [tableName backtickQuotedString], [fieldNames componentsJoinedAndBacktickQuoted]] dataUsingEncoding:NSUTF8StringEncoding]]; // Iterate through the rows to construct a VALUES group for each for ( j = 0 ; j < rowCount ; j++ ) { @@ -929,8 +1009,8 @@ // Add a new INSERT starter command every ~250k of data. if (queryLength > 250000) { - [sqlString appendString:[NSString stringWithFormat:@");\n\nINSERT INTO `%@` (`%@`)\nVALUES\n\t(", - tableName, [fieldNames componentsJoinedByString:@"`,`"]]]; + [sqlString appendString:[NSString stringWithFormat:@");\n\nINSERT INTO %@ (%@)\nVALUES\n\t(", + [tableName backtickQuotedString], [fieldNames componentsJoinedAndBacktickQuoted]]]; queryLength = 0; } else { [sqlString appendString:@"),\n\t("]; @@ -940,26 +1020,53 @@ } // Write this row to the file - [fileHandle writeData:[sqlString dataUsingEncoding:connectionEncoding]]; + [fileHandle writeData:[sqlString dataUsingEncoding:NSUTF8StringEncoding]]; } // Complete the command - [fileHandle writeData:[[NSString stringWithString:@";\n\n"] dataUsingEncoding:connectionEncoding]]; + [fileHandle writeData:[[NSString stringWithString:@";\n\n"] dataUsingEncoding:NSUTF8StringEncoding]]; + + // Unlock the table and re-enable keys if supported + [metaString setString:@""]; + [metaString appendString:[NSString stringWithFormat:@"/*!40000 ALTER TABLE %@ ENABLE KEYS */;\n", [tableName backtickQuotedString]]]; + [metaString appendString:@"UNLOCK TABLES;\n"]; + [fileHandle writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; if ( [addErrorsSwitch state] == NSOnState ) { [fileHandle writeData:[[NSString stringWithFormat:@"# Error: %@\n", [mySQLConnection getLastErrorMessage]] - dataUsingEncoding:connectionEncoding]]; + dataUsingEncoding:NSUTF8StringEncoding]]; } } } } // Add an additional separator between tables - [fileHandle writeData:[[NSString stringWithString:@"\n\n"] dataUsingEncoding:connectionEncoding]]; + [fileHandle writeData:[[NSString stringWithString:@"\n\n"] dataUsingEncoding:NSUTF8StringEncoding]]; } + // Restore unique checks, foreign key checks, and other settings saved at the start + [metaString setString:@"\n\n\n"]; + [metaString appendString:@"/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n"]; + [metaString appendString:@"/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n"]; + [metaString appendString:@"/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n"]; + if ( [addDropTableSwitch state] == NSOnState ) + [metaString appendString:@"/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n"]; + + // Restore the client encoding to the original encoding before import + [metaString appendString:@"/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n"]; + [metaString appendString:@"/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n"]; + [metaString appendString:@"/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n"]; + + // Write footer-type information to the file + [fileHandle writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; + + // Restore the connection character set to pre-export details + [tableDocumentInstance + setConnectionEncoding:[NSString stringWithFormat:@"%@%@", previousConnectionEncoding, previousConnectionEncodingViaLatin1?@"-":@""] + reloadingViews:NO]; + // Close the progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; @@ -995,7 +1102,7 @@ NSMutableString *csvCell = [NSMutableString string]; NSMutableArray *csvRow = [NSMutableArray array]; NSMutableString *csvString = [NSMutableString string]; - NSString *nullString = [NSString stringWithString:[prefs objectForKey:@"nullValue"]]; + NSString *nullString = [NSString stringWithString:[prefs objectForKey:@"NullValue"]]; NSString *escapedEscapeString, *escapedFieldSeparatorString, *escapedEnclosingString, *escapedLineEndString; NSString *dataConversionString; NSScanner *csvNumericTester; @@ -1286,14 +1393,14 @@ fieldCount = [tempRowArray count]; } else { while ( [tempRowArray count] < fieldCount ) { - [tempRowArray addObject:[NSString stringWithString:[prefs objectForKey:@"nullValue"]]]; + [tempRowArray addObject:[NSString stringWithString:[prefs objectForKey:@"NullValue"]]]; } } for ( i = 0 ; i < [tempRowArray count] ; i++ ) { // Insert a NSNull object if the cell contains an unescaped null character or an unescaped string // which matches the NULL string set in preferences. - if ( [[tempRowArray objectAtIndex:i] isEqualToString:@"\\N"] || [[tempRowArray objectAtIndex:i] isEqualToString:[prefs objectForKey:@"nullValue"]] ) { + if ( [[tempRowArray objectAtIndex:i] isEqualToString:@"\\N"] || [[tempRowArray objectAtIndex:i] isEqualToString:[prefs objectForKey:@"NullValue"]] ) { [tempRowArray replaceObjectAtIndex:i withObject:[NSNull null]]; } else { @@ -1490,14 +1597,14 @@ } } - return [self exportTables:selectedTables toFileHandle:fileHandle usingFormat:type]; + return [self exportTables:selectedTables toFileHandle:fileHandle usingFormat:type usingMulti:YES]; } /* Walks through the selected tables and exports them to a file handle. The export type must be "csv" for CSV format, and "xml" for XML format. */ -- (BOOL)exportTables:(NSArray *)selectedTables toFileHandle:(NSFileHandle *)fileHandle usingFormat:(NSString *)type +- (BOOL)exportTables:(NSArray *)selectedTables toFileHandle:(NSFileHandle *)fileHandle usingFormat:(NSString *)type usingMulti:(BOOL)multi { int i, j; CMMCPResult *queryResult; @@ -1586,7 +1693,7 @@ } // Retrieve all the content within this table - queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM `%@`", tableName]]; + queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@", [tableName backtickQuotedString]]]; // Note any errors during retrieval if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { @@ -1604,15 +1711,27 @@ // Use the appropriate export method to write the data to file if ( [type isEqualToString:@"csv"] ) { - [self writeCsvForArray:nil orQueryResult:queryResult - toFileHandle:fileHandle - outputFieldNames:[exportMultipleFieldNamesSwitch state] - terminatedBy:[exportMultipleFieldsTerminatedField stringValue] - enclosedBy:[exportMultipleFieldsEnclosedField stringValue] - escapedBy:[exportMultipleFieldsEscapedField stringValue] - lineEnds:[exportMultipleLinesTerminatedField stringValue] - withNumericColumns:tableColumnNumericStatus - silently:YES]; + if (multi) { + [self writeCsvForArray:nil orQueryResult:queryResult + toFileHandle:fileHandle + outputFieldNames:[exportMultipleFieldNamesSwitch state] + terminatedBy:[exportMultipleFieldsTerminatedField stringValue] + enclosedBy:[exportMultipleFieldsEnclosedField stringValue] + escapedBy:[exportMultipleFieldsEscapedField stringValue] + lineEnds:[exportMultipleLinesTerminatedField stringValue] + withNumericColumns:tableColumnNumericStatus + silently:YES]; + } else { + [self writeCsvForArray:nil orQueryResult:queryResult + toFileHandle:fileHandle + outputFieldNames:[exportFieldNamesSwitch state] + terminatedBy:[exportFieldsTerminatedField stringValue] + enclosedBy:[exportFieldsEnclosedField stringValue] + escapedBy:[exportFieldsEscapedField stringValue] + lineEnds:[exportLinesTerminatedField stringValue] + withNumericColumns:tableColumnNumericStatus + silently:YES]; + } // Add a spacer to the file [fileHandle writeData:[[NSString stringWithFormat:@"%@%@%@", csvLineEnd, csvLineEnd, csvLineEnd] dataUsingEncoding:connectionEncoding]]; @@ -1782,7 +1901,7 @@ [[exportDumpTableView tableColumnWithIdentifier:@"switch"] setDataCell:switchButton]; [[exportMultipleCSVTableView tableColumnWithIdentifier:@"switch"] setDataCell:switchButton]; [[exportMultipleXMLTableView tableColumnWithIdentifier:@"switch"] setDataCell:switchButton]; - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { + if ( [prefs boolForKey:@"UseMonospacedFonts"] ) { [[[exportDumpTableView tableColumnWithIdentifier:@"tables"] dataCell] setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; [[[exportMultipleCSVTableView tableColumnWithIdentifier:@"tables"] dataCell] @@ -1823,7 +1942,7 @@ forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { - if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"useMonospacedFonts"] ) { + if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"UseMonospacedFonts"] ) { [aCell setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; } else @@ -1900,6 +2019,13 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn #pragma mark - #pragma mark other + +- (void)awakeFromNib +{ + [self switchTab:[[exportToolbar items] objectAtIndex:0]]; + [exportToolbar setSelectedItemIdentifier:[[[exportToolbar items] objectAtIndex:0] itemIdentifier]]; +} + //last but not least - (id)init; { @@ -1921,7 +2047,7 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn [fieldMappingArray release]; [savePath release]; [openPath release]; - [prefs release]; + [prefs release]; [super dealloc]; } @@ -1931,4 +2057,41 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn progressCancelled = YES; } +- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar +{ + NSArray *array = [toolbar items]; + NSMutableArray *items = [NSMutableArray arrayWithCapacity:6]; + int i; + + for (i = 0; i < [array count]; i++) + { + NSToolbarItem *item = [array objectAtIndex:i]; + [items addObject:[item itemIdentifier]]; + } + + return items; +} + +#pragma mark New Export methods + +- (IBAction)switchTab:(id)sender +{ + if ([sender isKindOfClass:[NSToolbarItem class]]) { + [exportTabBar selectTabViewItemWithIdentifier:[[sender label] lowercaseString]]; + } +} + +- (IBAction)switchInput:(id)sender +{ + if ([sender isKindOfClass:[NSMatrix class]]) { + [exportTableList setEnabled:([[sender selectedCell] tag] == 3)]; + } +} + + +- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem +{ + return YES; +} + @end diff --git a/Source/TableSource.m b/Source/TableSource.m index 5d1c8e43..92aa9811 100644 --- a/Source/TableSource.m +++ b/Source/TableSource.m @@ -25,6 +25,8 @@ #import "TableSource.h" #import "TablesList.h" #import "SPTableData.h" +#import "SPStringAdditions.h" +#import "SPArrayAdditions.h" @implementation TableSource @@ -48,6 +50,7 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab selectedTable = aTable; [tableSourceView deselectAll:self]; + [indexView deselectAll:self]; if ( isEditingRow ) return; @@ -80,7 +83,7 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; //perform queries and load results in array (each row as a dictionary) - tableSourceResult = [[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM `%@`", selectedTable]] retain]; + tableSourceResult = [[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [selectedTable backtickQuotedString]]] retain]; // listFieldsFromTable is broken in the current version of the framework (no back-ticks for table name)! // tableSourceResult = [[mySQLConnection listFieldsFromTable:selectedTable] retain]; @@ -88,7 +91,7 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab [tableFields setArray:[self fetchResultAsArray:tableSourceResult]]; [tableSourceResult release]; - indexResult = [[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW INDEX FROM `%@`", selectedTable]] retain]; + indexResult = [[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW INDEX FROM %@", [selectedTable backtickQuotedString]]] retain]; // [indexes setArray:[[self fetchResultAsArray:indexResult] retain]]; [indexes setArray:[self fetchResultAsArray:indexResult]]; [indexResult release]; @@ -179,10 +182,12 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab // If a view is selected, disable the buttons; otherwise enable. BOOL editingEnabled = ([tablesListInstance tableType] == SP_TABLETYPE_TABLE); [addFieldButton setEnabled:editingEnabled]; - [copyFieldButton setEnabled:editingEnabled]; - [removeFieldButton setEnabled:editingEnabled]; [addIndexButton setEnabled:editingEnabled]; - [removeIndexButton setEnabled:editingEnabled]; + + //the following three buttons will only be enabled if a row field/index is selected! + [copyFieldButton setEnabled:NO]; + [removeFieldButton setEnabled:NO]; + [removeIndexButton setEnabled:NO]; //add columns to indexedColumnsField [indexedColumnsField removeAllItems]; @@ -231,7 +236,7 @@ adds an empty row to the tableSource-array and goes into edit mode if ( ![self saveRowOnDeselect] ) return; [tableFields addObject:[NSMutableDictionary - dictionaryWithObjects:[NSArray arrayWithObjects:@"",@"int",@"",@"0",@"0",@"0",@"YES",@"",[prefs stringForKey:@"nullValue"],@"None",nil] + dictionaryWithObjects:[NSArray arrayWithObjects:@"",@"int",@"",@"0",@"0",@"0",@"YES",@"",[prefs stringForKey:@"NullValue"],@"None",nil] forKeys:[NSArray arrayWithObjects:@"Field",@"Type",@"Length",@"unsigned",@"zerofill",@"binary",@"Null",@"Key",@"Default",@"Extra",nil]]]; [tableSourceView reloadData]; @@ -293,7 +298,7 @@ adds the index to the mysql-db and stops modal session with code 1 when success, { indexName = @""; } else { - indexName = [NSString stringWithFormat:@"`%@`", [indexNameField stringValue]]; + indexName = [[indexNameField stringValue] backtickQuotedString]; } } indexedColumns = [[indexedColumnsField stringValue] componentsSeparatedByString:@","]; @@ -306,14 +311,14 @@ adds the index to the mysql-db and stops modal session with code 1 when success, } } - [mySQLConnection queryString:[NSString stringWithFormat:@"ALTER TABLE `%@` ADD %@ %@ (`%@`)", - selectedTable, [indexTypeField titleOfSelectedItem], indexName, - [tempIndexedColumns componentsJoinedByString:@"`,`"]]]; + [mySQLConnection queryString:[NSString stringWithFormat:@"ALTER TABLE %@ ADD %@ %@ (%@)", + [selectedTable backtickQuotedString], [indexTypeField titleOfSelectedItem], indexName, + [tempIndexedColumns componentsJoinedAndBacktickQuoted]]]; /* -NSLog([NSString stringWithFormat:@"ALTER TABLE `%@` ADD %@ %@ (`%@`)", - selectedTable, [indexTypeField titleOfSelectedItem], indexName, - [[[indexedColumnsField stringValue] componentsSeparatedByString:@","] componentsJoinedByString:@"`,`"]]); +NSLog([NSString stringWithFormat:@"ALTER TABLE %@ ADD %@ %@ (%@)", + [selectedTable backtickQuotedString], [indexTypeField titleOfSelectedItem], indexName, + [tempIndexedColumns componentsJoinedAndBacktickQuoted]]); */ if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { @@ -372,7 +377,7 @@ opens alertsheet and asks for confirmation // alert any listeners that we are about to perform a query. [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; - NSString *query = [NSString stringWithFormat:@"ALTER TABLE `%@` TYPE = %@",selectedTable,selectedItem]; + NSString *query = [NSString stringWithFormat:@"ALTER TABLE %@ TYPE = %@",[selectedTable backtickQuotedString],selectedItem]; [mySQLConnection queryString:query]; // The query is now complete. @@ -498,7 +503,7 @@ sets the connection (received from TableDocument) and makes things that have to [tableSourceView registerForDraggedTypes:[NSArray arrayWithObjects:@"SequelProPasteboard", nil]]; while ( (indexColumn = [indexColumnsEnumerator nextObject]) ) { - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { + if ( [prefs boolForKey:@"UseMonospacedFonts"] ) { [[indexColumn dataCell] setFont:[NSFont fontWithName:@"Monaco" size:10]]; } else @@ -507,7 +512,7 @@ sets the connection (received from TableDocument) and makes things that have to } } while ( (fieldColumn = [fieldColumnsEnumerator nextObject]) ) { - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { + if ( [prefs boolForKey:@"UseMonospacedFonts"] ) { [[fieldColumn dataCell] setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; } else @@ -537,7 +542,7 @@ fetches the result as an array with a dictionary for each row in it for (int i = 0; i < [keys count] ; i++) { key = [keys objectAtIndex:i]; if ( [[tempRow objectForKey:key] isMemberOfClass:[NSNull class]] ) - [tempRow setObject:[prefs objectForKey:@"nullValue"] forKey:key]; + [tempRow setObject:[prefs objectForKey:@"NullValue"] forKey:key]; } // change some fields to be more human-readable or GUI compatible if ( [[tempRow objectForKey:@"Extra"] isEqualToString:@""] ) { @@ -606,22 +611,22 @@ returns YES if no row is beeing edited and nothing has to be written to db if ( isEditingNewRow ) { //ADD syntax if ( [[theRow objectForKey:@"Length"] isEqualToString:@""] || ![theRow objectForKey:@"Length"] ) { - queryString = [NSMutableString stringWithFormat:@"ALTER TABLE `%@` ADD `%@` %@", - selectedTable, [theRow objectForKey:@"Field"], [theRow objectForKey:@"Type"]]; + queryString = [NSMutableString stringWithFormat:@"ALTER TABLE %@ ADD %@ %@", + [selectedTable backtickQuotedString], [[theRow objectForKey:@"Field"] backtickQuotedString], [theRow objectForKey:@"Type"]]; } else { - queryString = [NSMutableString stringWithFormat:@"ALTER TABLE `%@` ADD `%@` %@(%@)", - selectedTable, [theRow objectForKey:@"Field"], [theRow objectForKey:@"Type"], + queryString = [NSMutableString stringWithFormat:@"ALTER TABLE %@ ADD %@ %@(%@)", + [selectedTable backtickQuotedString], [[theRow objectForKey:@"Field"] backtickQuotedString], [theRow objectForKey:@"Type"], [theRow objectForKey:@"Length"]]; } } else { //CHANGE syntax if (([[theRow objectForKey:@"Length"] isEqualToString:@""]) || (![theRow objectForKey:@"Length"]) || ([[theRow objectForKey:@"Type"] isEqualToString:@"datetime"])) { - queryString = [NSMutableString stringWithFormat:@"ALTER TABLE `%@` CHANGE `%@` `%@` %@", - selectedTable, [oldRow objectForKey:@"Field"], [theRow objectForKey:@"Field"], + queryString = [NSMutableString stringWithFormat:@"ALTER TABLE %@ CHANGE %@ %@ %@", + [selectedTable backtickQuotedString], [[oldRow objectForKey:@"Field"] backtickQuotedString], [[theRow objectForKey:@"Field"] backtickQuotedString], [theRow objectForKey:@"Type"]]; } else { - queryString = [NSMutableString stringWithFormat:@"ALTER TABLE `%@` CHANGE `%@` `%@` %@(%@)", - selectedTable, [oldRow objectForKey:@"Field"], [theRow objectForKey:@"Field"], + queryString = [NSMutableString stringWithFormat:@"ALTER TABLE %@ CHANGE %@ %@ %@(%@)", + [selectedTable backtickQuotedString], [[oldRow objectForKey:@"Field"] backtickQuotedString], [[theRow objectForKey:@"Field"] backtickQuotedString], [theRow objectForKey:@"Type"], [theRow objectForKey:@"Length"]]; } } @@ -641,7 +646,7 @@ returns YES if no row is beeing edited and nothing has to be written to db if ( [[theRow objectForKey:@"Null"] isEqualToString:@"NO"] ) [queryString appendString:@" NOT NULL"]; if ( ![[theRow objectForKey:@"Extra"] isEqualToString:@"auto_increment"] && !([[theRow objectForKey:@"Type"] isEqualToString:@"timestamp"] && [[theRow objectForKey:@"Default"] isEqualToString:@"NULL"]) ) { - if ( [[theRow objectForKey:@"Default"] isEqualToString:[prefs objectForKey:@"nullValue"]] ) { + if ( [[theRow objectForKey:@"Default"] isEqualToString:[prefs objectForKey:@"NullValue"]] ) { if ([[theRow objectForKey:@"Null"] isEqualToString:@"YES"] ) { [queryString appendString:@" DEFAULT NULL "]; } @@ -675,8 +680,8 @@ returns YES if no row is beeing edited and nothing has to be written to db if ( [chooseKeyButton indexOfSelectedItem] == 0 ) { [queryString appendString:@" PRIMARY KEY"]; } else { - [queryString appendString:[NSString stringWithFormat:@", ADD %@ (`%@`)", - [chooseKeyButton titleOfSelectedItem], [theRow objectForKey:@"Field"]]]; + [queryString appendString:[NSString stringWithFormat:@", ADD %@ (%@)", + [chooseKeyButton titleOfSelectedItem], [[theRow objectForKey:@"Field"] backtickQuotedString]]]; } } } @@ -743,8 +748,8 @@ returns YES if no row is beeing edited and nothing has to be written to db } else if ( [contextInfo isEqualToString:@"removefield"] ) { if ( returnCode == NSAlertDefaultReturn ) { //remove row - [mySQLConnection queryString:[NSString stringWithFormat:@"ALTER TABLE `%@` DROP `%@`", - selectedTable, [[tableFields objectAtIndex:[tableSourceView selectedRow]] objectForKey:@"Field"]]]; + [mySQLConnection queryString:[NSString stringWithFormat:@"ALTER TABLE %@ DROP %@", + [selectedTable backtickQuotedString], [[[tableFields objectAtIndex:[tableSourceView selectedRow]] objectForKey:@"Field"] backtickQuotedString]]]; if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [self loadTable:selectedTable]; @@ -763,10 +768,10 @@ returns YES if no row is beeing edited and nothing has to be written to db if ( returnCode == NSAlertDefaultReturn ) { //remove index if ( [[[indexes objectAtIndex:[indexView selectedRow]] objectForKey:@"Key_name"] isEqualToString:@"PRIMARY"] ) { - [mySQLConnection queryString:[NSString stringWithFormat:@"ALTER TABLE `%@` DROP PRIMARY KEY", selectedTable]]; + [mySQLConnection queryString:[NSString stringWithFormat:@"ALTER TABLE %@ DROP PRIMARY KEY", [selectedTable backtickQuotedString]]]; } else { - [mySQLConnection queryString:[NSString stringWithFormat:@"ALTER TABLE `%@` DROP INDEX `%@`", - selectedTable, [[indexes objectAtIndex:[indexView selectedRow]] objectForKey:@"Key_name"]]]; + [mySQLConnection queryString:[NSString stringWithFormat:@"ALTER TABLE %@ DROP INDEX %@", + [selectedTable backtickQuotedString], [[[indexes objectAtIndex:[indexView selectedRow]] objectForKey:@"Key_name"] backtickQuotedString]]]; } if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { @@ -788,9 +793,9 @@ get the default value for a specified field - (NSString *)defaultValueForField:(NSString *)field { if ( ![defaultValues objectForKey:field] ) { - return [prefs objectForKey:@"nullValue"]; + return [prefs objectForKey:@"NullValue"]; } else if ( [[defaultValues objectForKey:field] isMemberOfClass:[NSNull class]] ) { - return [prefs objectForKey:@"nullValue"]; + return [prefs objectForKey:@"NullValue"]; } else { return [defaultValues objectForKey:field]; } @@ -859,6 +864,9 @@ returns a dictionary containing enum/set field names as key and possible values forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { + //make sure that the drag operation is for the right table view + if (aTableView!=tableSourceView) return; + if ( !isEditingRow ) { [oldRow setDictionary:[tableFields objectAtIndex:rowIndex]]; isEditingRow = YES; @@ -876,6 +884,10 @@ Begin a drag and drop operation from the table - copy a single dragged row to th */ - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard { + //make sure that the drag operation is started from the right table view + if (tableView!=tableSourceView) return NO; + + int originalRow; NSArray *pboardTypes; @@ -903,6 +915,9 @@ would result in a position change. - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id )info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation { + //make sure that the drag operation is for the right table view + if (tableView!=tableSourceView) return NO; + NSArray *pboardTypes = [[info draggingPasteboard] types]; int originalRow; @@ -927,6 +942,9 @@ would result in a position change. */ - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id )info row:(int)destinationRowIndex dropOperation:(NSTableViewDropOperation)operation { + //make sure that the drag operation is for the right table view + if (tableView!=tableSourceView) return NO; + int originalRowIndex; NSMutableString *queryString; NSDictionary *originalRow; @@ -938,8 +956,8 @@ would result in a position change. [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; // Begin construction of the reordering query - queryString = [NSMutableString stringWithFormat:@"ALTER TABLE `%@` MODIFY COLUMN `%@` %@", selectedTable, - [originalRow objectForKey:@"Field"], + queryString = [NSMutableString stringWithFormat:@"ALTER TABLE %@ MODIFY COLUMN %@ %@", [selectedTable backtickQuotedString], + [[originalRow objectForKey:@"Field"] backtickQuotedString], [originalRow objectForKey:@"Type"]]; // Add the length parameter if necessary @@ -962,7 +980,7 @@ would result in a position change. } // Add the default value - if ([[originalRow objectForKey:@"Default"] isEqualToString:[prefs objectForKey:@"nullValue"]]) { + if ([[originalRow objectForKey:@"Default"] isEqualToString:[prefs objectForKey:@"NullValue"]]) { if ([[originalRow objectForKey:@"Null"] isEqualToString:@"YES"]) { [queryString appendString:@" DEFAULT NULL"]; } @@ -976,8 +994,8 @@ would result in a position change. if ( destinationRowIndex == 0 ){ [queryString appendString:@" FIRST"]; } else { - [queryString appendString:[NSString stringWithFormat:@" AFTER `%@`", - [[tableFields objectAtIndex:destinationRowIndex-1] objectForKey:@"Field"]]]; + [queryString appendString:[NSString stringWithFormat:@" AFTER %@", + [[[tableFields objectAtIndex:destinationRowIndex-1] objectForKey:@"Field"] backtickQuotedString]]]; } // Run the query; report any errors, or reload the table on success @@ -1008,13 +1026,32 @@ would result in a position change. - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { - - // Check our notification object is our table fields view - if ([aNotification object] != tableSourceView) - return; - - // If we are editing a row, attempt to save that row - if saving failed, reselect the edit row. - if ( isEditingRow && [tableSourceView selectedRow] != currentlyEditingRow && ![self saveRowOnDeselect] ) return; + //check for which table view the selection changed + if ([aNotification object] == tableSourceView) { + // If we are editing a row, attempt to save that row - if saving failed, reselect the edit row. + if ( isEditingRow && [tableSourceView selectedRow] != currentlyEditingRow ) { + [self saveRowOnDeselect]; + } + + // check if there is currently a field selected + // and change button state accordingly + if ([tableSourceView numberOfSelectedRows] > 0 && [tablesListInstance tableType] == SP_TABLETYPE_TABLE) { + [removeFieldButton setEnabled:YES]; + [copyFieldButton setEnabled:YES]; + } else { + [removeFieldButton setEnabled:NO]; + [copyFieldButton setEnabled:NO]; + } + } + else if ([aNotification object] == indexView) { + // check if there is currently an index selected + // and change button state accordingly + if ([indexView numberOfSelectedRows] > 0 && [tablesListInstance tableType] == SP_TABLETYPE_TABLE) { + [removeIndexButton setEnabled:YES]; + } else { + [removeIndexButton setEnabled:NO]; + } + } } /* @@ -1078,7 +1115,11 @@ traps enter and esc and make/cancel editing without entering next row * Modify cell display by disabling table cells when a view is selected, meaning structure/index * is uneditable. */ -- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { +- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { + + //make sure that the message is from the right table view + if (tableView!=tableSourceView) return; + [aCell setEnabled:([tablesListInstance tableType] == SP_TABLETYPE_TABLE)]; } diff --git a/Source/TableStatus.h b/Source/TableStatus.h index 73b68fd7..51c8b132 100644 --- a/Source/TableStatus.h +++ b/Source/TableStatus.h @@ -49,7 +49,7 @@ CMMCPResult *tableStatusResult; NSString *selectedTable; - NSDictionary* statusFields; + NSDictionary *statusFields; } // Table methods @@ -58,8 +58,5 @@ // Additional methods - (void)setConnection:(CMMCPConnection *)theConnection; -- (void)awakeFromNib; -// Initialization -- (id)init; @end diff --git a/Source/TableStatus.m b/Source/TableStatus.m index 4edaa4e0..9d4cf03f 100644 --- a/Source/TableStatus.m +++ b/Source/TableStatus.m @@ -4,11 +4,6 @@ @implementation TableStatus -- (void)awakeFromNib -{ - // TODO: implement awake code. -} - - (void)setConnection:(CMMCPConnection *)theConnection { mySQLConnection = theConnection; @@ -34,28 +29,20 @@ // Format date strings to the user's long date format else if ([aKey isEqualToString:@"Create_time"] || [aKey isEqualToString:@"Update_time"]) { - + // Create date formatter NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; - // Set the date format returned by MySQL - [dateFormatter setDateFormat:@"%Y-%m-%d %H:%M:%S"]; - - // Get the date instance - NSDate *date = [dateFormatter dateFromString:value]; - - // This behaviour should be set after the above date string is parsed to a date object so we can - // use the below style methods. [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; - + [dateFormatter setDateStyle:NSDateFormatterLongStyle]; [dateFormatter setTimeStyle:NSDateFormatterMediumStyle]; - - value = [dateFormatter stringFromDate:date]; + + value = [dateFormatter stringFromDate:[NSDate dateWithNaturalLanguageString:value]]; } } - NSString* labelVal = [NSString stringWithFormat:@"%@: %@", label, value]; + NSString *labelVal = [NSString stringWithFormat:@"%@: %@", label, value]; return labelVal; } @@ -64,11 +51,21 @@ { // Store the table name away for future use... selectedTable = aTable; + + // Retrieve the table status information via the table data cache + statusFields = [tableDataInstance statusValues]; + + // No table selected or view selected + if([aTable isEqualToString:@""] || !aTable || [[statusFields objectForKey:@"Engine"] isEqualToString:@"View"]) { - // No table selected - if([aTable isEqualToString:@""] || !aTable) { - [tableName setStringValue:@"Name: --"]; - [tableType setStringValue:@"Type: --"]; + if ([[statusFields objectForKey:@"Engine"] isEqualToString:@"View"]) { + [tableName setStringValue:[NSString stringWithFormat:@"Name: %@", selectedTable]]; + [tableType setStringValue:@"Type: View"]; + } else { + [tableName setStringValue:@"Name: --"]; + [tableType setStringValue:@"Type: --"]; + } + [tableCreatedAt setStringValue:@"Created At: --"]; [tableUpdatedAt setStringValue:@"Updated At: --"]; @@ -90,9 +87,6 @@ return; } - // Retrieve the table status information via the table data cache - statusFields = [tableDataInstance statusValues]; - // Assign the table values... [tableName setStringValue:[NSString stringWithFormat:@"Name: %@",selectedTable]]; [tableType setStringValue:[self formatValueWithKey:@"Engine" inDictionary:statusFields withLabel:@"Type"]]; @@ -129,4 +123,5 @@ return self; } + @end diff --git a/Source/TablesList.h b/Source/TablesList.h index b4875dd9..76b49609 100644 --- a/Source/TablesList.h +++ b/Source/TablesList.h @@ -31,8 +31,7 @@ enum sp_table_types SP_TABLETYPE_VIEW = 1 }; -@class CMMCResult; -@class CMMCPConnection; +@class CMMCResult, CMMCPConnection; @interface TablesList : NSObject { @@ -50,32 +49,35 @@ enum sp_table_types IBOutlet id copyTableNameField; IBOutlet id copyTableContentSwitch; IBOutlet id tabView; + IBOutlet id tableSheet; + IBOutlet id tableNameField; + IBOutlet id tableEncodingButton; + IBOutlet id addTableButton; CMMCPConnection *mySQLConnection; + NSMutableArray *tables; NSMutableArray *tableTypes; -// NSUserDefaults *prefs; + BOOL structureLoaded, contentLoaded, statusLoaded, alertSheetOpened; } -//IBAction methods +// IBAction methods - (IBAction)updateTables:(id)sender; - (IBAction)addTable:(id)sender; +- (IBAction)closeTableSheet:(id)sender; - (IBAction)removeTable:(id)sender; - (IBAction)copyTable:(id)sender; -//alert sheet methods -- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo; - -//copyTableSheet methods +// copyTableSheet methods - (IBAction)closeCopyTableSheet:(id)sender; -//additional methods +// Additional methods - (void)removeTable; - (void)setConnection:(CMMCPConnection *)theConnection; - (void)doPerformQueryService:(NSString *)query; -//getter methods +// Getters - (NSString *)tableName; - (int)tableType; - (NSArray *)tables; @@ -84,22 +86,7 @@ enum sp_table_types - (BOOL)contentLoaded; - (BOOL)statusLoaded; -// Setter methods +// Setters - (void)setContentRequiresReload:(BOOL)reload; -//tableView datasource methods -- (int)numberOfRowsInTableView:(NSTableView *)aTableView; -- (id)tableView:(NSTableView *)aTableView - objectValueForTableColumn:(NSTableColumn *)aTableColumn - row:(int)rowIndex; -- (void)tableView:(NSTableView *)aTableView - setObjectValue:(id)anObject - forTableColumn:(NSTableColumn *)aTableColumn - row:(int)rowIndex; - -//tableView delegate methods -- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command; -- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView; -- (void)tableViewSelectionDidChange:(NSNotification *)aNotification; - @end diff --git a/Source/TablesList.m b/Source/TablesList.m index ac1445a2..f6a5e441 100644 --- a/Source/TablesList.m +++ b/Source/TablesList.m @@ -31,21 +31,28 @@ #import "ImageAndTextCell.h" #import "CMMCPConnection.h" #import "CMMCPResult.h" +#import "SPStringAdditions.h" @implementation TablesList - #pragma mark IBAction methods -/* -loads all table names in array tables and reload the tableView -*/ +/** + * Loads all table names in array tables and reload the tableView + */ - (IBAction)updateTables:(id)sender { CMMCPResult *theResult; NSArray *resultRow; int i; BOOL containsViews = NO; + NSString *selectedTable = nil; + int selectedRowIndex; + + selectedRowIndex = [tablesListView selectedRow]; + if(selectedRowIndex > 0 && [tables count]){ + selectedTable = [NSString stringWithString:[tables objectAtIndex:selectedRowIndex]]; + } [tablesListView deselectAll:self]; [tables removeAllObjects]; @@ -89,58 +96,150 @@ loads all table names in array tables and reload the tableView // Notify listeners that the query has finished [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; - [tablesListView reloadData]; + [tablesListView reloadData]; + + //if the previous selected table still exists, select it + if( selectedTable != nil && [tables indexOfObject:selectedTable] < [tables count]) { + [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:[tables indexOfObject:selectedTable]] byExtendingSelection:NO]; + } } -/* -adds a new table to the tables-array (no changes in mysql-db) -*/ +/** + * Adds a new table to the tables-array (no changes in mysql-db) + */ - (IBAction)addTable:(id)sender { - if ( ![tableSourceInstance saveRowOnDeselect] || - ![tableContentInstance saveRowOnDeselect] || - ![tableDocumentInstance database] ) + if ((![tableSourceInstance saveRowOnDeselect]) || (![tableContentInstance saveRowOnDeselect]) || (![tableDocumentInstance database])) { return; + } + [tableWindow endEditingFor:nil]; + + [NSApp beginSheet:tableSheet + modalForWindow:tableWindow + modalDelegate:self + didEndSelector:nil + contextInfo:nil]; + + int returnCode = [NSApp runModalForWindow:tableSheet]; + + [NSApp endSheet:tableSheet]; + [tableSheet orderOut:nil]; + + if (!returnCode) { + // Clear table name + [tableNameField setStringValue:@""]; + + return; + } + + NSString *tableName = [tableNameField stringValue]; + NSString *createStatement = [NSString stringWithFormat:@"CREATE TABLE %@ (id INT)", [tableName backtickQuotedString]]; + + // If there is an encoding selected other than the default we must specify it in CREATE TABLE statement + if ([tableEncodingButton indexOfSelectedItem] > 0) { + createStatement = [NSString stringWithFormat:@"%@ DEFAULT CHARACTER SET %@", createStatement, [[tableDocumentInstance mysqlEncodingFromDisplayEncoding:[tableEncodingButton title]] backtickQuotedString]]; + } + + // Create the table + [mySQLConnection queryString:createStatement]; + + if ([[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { + + // Table creation was successful + [tables addObject:tableName]; + [tableTypes addObject:[NSNumber numberWithInt:SP_TABLETYPE_TABLE]]; + [tablesListView reloadData]; + [tablesListView selectRow:([tables count] - 1) byExtendingSelection:NO]; + + int selectedIndex = [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]; + + if (selectedIndex == 0) { + [tableSourceInstance loadTable:tableName]; + structureLoaded = YES; + contentLoaded = NO; + statusLoaded = NO; + } + else if (selectedIndex == 1) { + [tableContentInstance loadTable:tableName]; + structureLoaded = NO; + contentLoaded = YES; + statusLoaded = NO; + } + else if (selectedIndex == 3) { + [tableStatusInstance loadTable:tableName]; + structureLoaded = NO; + contentLoaded = NO; + statusLoaded = YES; + } + else { + statusLoaded = NO; + structureLoaded = NO; + contentLoaded = NO; + } + + // Set window title + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@/%@", [tableDocumentInstance mySQLVersion], + [tableDocumentInstance name], [tableDocumentInstance database], tableName]]; + } + else { + // Error while creating new table + alertSheetOpened = YES; + + NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, + @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addRow", + [NSString stringWithFormat:NSLocalizedString(@"Couldn't add table %@.\nMySQL said: %@", @"message of panel when table cannot be created with the given name"), + tableName, [mySQLConnection getLastErrorMessage]]); + + [tableTypes removeObjectAtIndex:([tableTypes count] - 1)]; + [tables removeObjectAtIndex:([tables count] - 1)]; + [tablesListView reloadData]; + } + + // Clear table name + [tableNameField setStringValue:@""]; +} - [tables addObject:@""]; - [tableTypes addObject:[NSNumber numberWithInt:SP_TABLETYPE_TABLE]]; - [tablesListView reloadData]; - [tablesListView selectRow:[tables count]-1 byExtendingSelection:NO]; - [tablesListView editColumn:0 row:[tables count]-1 withEvent:nil select:YES]; +/** + * Closes the add table sheet and stops the modal session + */ +- (IBAction)closeTableSheet:(id)sender +{ + [NSApp stopModalWithCode:[sender tag]]; } -/* -invoked when user hits the remove button -alert sheet to ask user if he really wants to delete the table -*/ +/** + * Invoked when user hits the remove button alert sheet to ask user if he really wants to delete the table. + */ - (IBAction)removeTable:(id)sender { - if ( ![tablesListView numberOfSelectedRows] ) + if (![tablesListView numberOfSelectedRows]) return; + [tableWindow endEditingFor:nil]; + + NSAlert *alert = [NSAlert alertWithMessageText:@"" defaultButton:NSLocalizedString(@"Delete", @"delete button") alternateButton:NSLocalizedString(@"Cancel", @"cancel button") otherButton:nil informativeTextWithFormat:@""]; - if ( [tablesListView numberOfSelectedRows] == 1 ) { - NSBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"Delete", @"delete button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, tableWindow, self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, - @"removeRow", [NSString stringWithFormat:NSLocalizedString(@"Do you really want to delete the table %@?", @"message of panel asking for confirmation for deleting table"), - [tables objectAtIndex:[tablesListView selectedRow]]]); - } else { - NSBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"Delete", @"delete button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, tableWindow, self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, - @"removeRow", [NSString stringWithFormat:NSLocalizedString(@"Do you really want to delete the selected tables?", @"message of panel asking for confirmation for deleting tables"), - [tables objectAtIndex:[tablesListView selectedRow]]]); + [alert setAlertStyle:NSCriticalAlertStyle]; + + if ([tablesListView numberOfSelectedRows] == 1) { + [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Delete table '%@'?", @"delete table message"), [tables objectAtIndex:[tablesListView selectedRow]]]]; + [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the table '%@'. This operation cannot be undone.", @"delete table informative message"), [tables objectAtIndex:[tablesListView selectedRow]]]]; + } + else { + [alert setMessageText:NSLocalizedString(@"Delete selected tables?", @"delete tables message")]; + [alert setInformativeText:NSLocalizedString(@"Are you sure you want to delete the selected tables. This operation cannot be undone.", @"delete tables informative message")]; } + + [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeRow"]; } -/* -copies a table, if desired with content -*/ +/** + * Copies a table, if desired with content + */ - (IBAction)copyTable:(id)sender { CMMCPResult *queryResult; - NSScanner *scanner = [NSScanner alloc]; - NSString *scanString; // NSArray *fieldNames; // NSArray *theRow; // NSMutableString *rowValue = [NSMutableString string]; @@ -151,8 +250,9 @@ copies a table, if desired with content if ( [tablesListView numberOfSelectedRows] != 1 ) return; - if ( ![tableSourceInstance saveRowOnDeselect] || ![tableContentInstance saveRowOnDeselect] ) + if ( ![tableSourceInstance saveRowOnDeselect] || ![tableContentInstance saveRowOnDeselect] ) { return; + } [tableWindow endEditingFor:nil]; //open copyTableSheet @@ -177,8 +277,9 @@ copies a table, if desired with content } //get table structure - queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", - [tables objectAtIndex:[tablesListView selectedRow]]]]; + queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE %@", + [[tables objectAtIndex:[tablesListView selectedRow]] backtickQuotedString] + ]]; if ( ![queryResult numOfRows] ) { //error while getting table structure @@ -187,10 +288,14 @@ copies a table, if desired with content } else { //insert new table name in create syntax and create new table + NSScanner *scanner = [NSScanner alloc]; + NSString *scanString; + [scanner initWithString:[[queryResult fetchRowAsDictionary] objectForKey:@"Create Table"]]; [scanner scanUpToString:@"(" intoString:nil]; [scanner scanUpToString:@"" intoString:&scanString]; - [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE TABLE `%@` %@", [copyTableNameField stringValue], scanString]]; + [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE TABLE %@ %@", [[copyTableNameField stringValue] backtickQuotedString], scanString]]; + [scanner release]; if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { //error while creating new table @@ -201,9 +306,9 @@ copies a table, if desired with content if ( [copyTableContentSwitch state] == NSOnState ) { //copy table content [mySQLConnection queryString:[NSString stringWithFormat: - @"INSERT INTO `%@` SELECT * FROM `%@`", - [copyTableNameField stringValue], - [tables objectAtIndex:[tablesListView selectedRow]] + @"INSERT INTO %@ SELECT * FROM %@", + [[copyTableNameField stringValue] backtickQuotedString], + [[tables objectAtIndex:[tablesListView selectedRow]] backtickQuotedString] ]]; if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { @@ -231,28 +336,25 @@ copies a table, if desired with content } } - #pragma mark Alert sheet methods -/* -method for alert sheets -invoked when user wants to delete a table -*/ +/** + * Method for alert sheets. Invoked when user wants to delete a table. + */ - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo { if ( [contextInfo isEqualToString:@"addRow"] ) { alertSheetOpened = NO; } else if ( [contextInfo isEqualToString:@"removeRow"] ) { if ( returnCode == NSAlertDefaultReturn ) { - [sheet orderOut:self]; [self removeTable]; } } } -/* -closes copyTableSheet and stops modal session -*/ +/** + * Closes copyTableSheet and stops modal session + */ - (IBAction)closeCopyTableSheet:(id)sender { [NSApp stopModalWithCode:[sender tag]]; @@ -260,10 +362,10 @@ closes copyTableSheet and stops modal session #pragma mark Additional methods -/* -removes selected table(s) from mysql-db and tableView -*/ -- (void)removeTable; +/** + * Removes selected table(s) from mysql-db and tableView + */ +- (void)removeTable { NSIndexSet *indexes = [tablesListView selectedRowIndexes]; NSString *errorText; @@ -273,7 +375,9 @@ removes selected table(s) from mysql-db and tableView unsigned currentIndex = [indexes lastIndex]; while (currentIndex != NSNotFound) { - [mySQLConnection queryString:[NSString stringWithFormat:@"DROP TABLE `%@`", [tables objectAtIndex:currentIndex]]]; + [mySQLConnection queryString: [NSString stringWithFormat: @"DROP TABLE %@", + [[tables objectAtIndex:currentIndex] backtickQuotedString] + ]]; if ( [[mySQLConnection getLastErrorMessage] isEqualTo:@""] ) { //dropped table with success @@ -296,37 +400,46 @@ removes selected table(s) from mysql-db and tableView [tablesListView reloadData]; // set window title - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@", [tableDocumentInstance mySQLVersion], [tableDocumentInstance user], - [tableDocumentInstance host], [tableDocumentInstance database]]]; + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", [tableDocumentInstance mySQLVersion], + [tableDocumentInstance name], [tableDocumentInstance database]]]; if ( error ) NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't remove table.\nMySQL said: %@", @"message of panel when table cannot be removed"), errorText]); } -/* -sets the connection (received from TableDocument) and makes things that have to be done only once -*/ +/** + * Sets the connection (received from TableDocument) and makes things that have to be done only once + */ - (void)setConnection:(CMMCPConnection *)theConnection { mySQLConnection = theConnection; [self updateTables:self]; } -/* -selects customQuery tab and passes query to customQueryInstance -*/ +/** + * Selects customQuery tab and passes query to customQueryInstance + */ - (void)doPerformQueryService:(NSString *)query { [tabView selectTabViewItemAtIndex:2]; [customQueryInstance doPerformQueryService:query]; } +/** + * Performs interface validation for various controls. + */ +- (void)controlTextDidChange:(NSNotification *)notification +{ + if ([notification object] == tableNameField) { + [addTableButton setEnabled:([[tableNameField stringValue] length] > 0)]; + } +} #pragma mark Getter methods -/* -returns the currently selected table or nil if no table or mulitple tables are selected -*/ +/** + * Returns the currently selected table or nil if no table or mulitple tables are selected + */ - (NSString *)tableName { if ( [tablesListView numberOfSelectedRows] == 1 ) { @@ -352,35 +465,41 @@ returns the currently selected table or nil if no table or mulitple tables are s } } +/** + * Database tables accessor + */ - (NSArray *)tables { return tables; } +/** + * Database table types accessor + */ - (NSArray *)tableTypes { return tableTypes; } -/* -returns YES if table source has already been loaded -*/ +/** + * Returns YES if table source has already been loaded + */ - (BOOL)structureLoaded { return structureLoaded; } -/* -returns YES if table content has already been loaded -*/ +/** + * Returns YES if table content has already been loaded + */ - (BOOL)contentLoaded { return contentLoaded; } -/* -returns YES if table status has already been loaded -*/ +/** + * Returns YES if table status has already been loaded + */ - (BOOL)statusLoaded { return statusLoaded; @@ -388,9 +507,9 @@ returns YES if table status has already been loaded #pragma mark Setter methods -/* -Mark the content table for refresh when it's next switched to -*/ +/** + * Mark the content table for refresh when it's next switched to + */ - (void)setContentRequiresReload:(BOOL)reload { contentLoaded = !reload; @@ -398,147 +517,90 @@ Mark the content table for refresh when it's next switched to #pragma mark Datasource methods +/** + * Returns the number of tables in the current database. + */ - (int)numberOfRowsInTableView:(NSTableView *)aTableView { return [tables count]; } -- (id)tableView:(NSTableView *)aTableView - objectValueForTableColumn:(NSTableColumn *)aTableColumn - row:(int)rowIndex +/** + * Returns the table names to be displayed in the tables list table view. + */ +- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { - return [tables objectAtIndex:rowIndex]; + return [tables objectAtIndex:rowIndex]; } /** - * adds or renames a table (in tables-array and mysql-db) - * removes new table from table-array if renaming had no success + * Renames a table (in tables-array and mysql-db). + * Removes new table from table-array if renaming had no success */ -- (void)tableView:(NSTableView *)aTableView - setObjectValue:(id)anObject - forTableColumn:(NSTableColumn *)aTableColumn - row:(int)rowIndex +- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { - - if ( [[tables objectAtIndex:rowIndex] isEqualToString:@""] ) { - //new table - if ( [anObject isEqualToString:@""] ) { - //table has no name - alertSheetOpened = YES; - NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addRow", NSLocalizedString(@"Table must have a name.", @"message of panel when no name is given for table")); - [tables removeObjectAtIndex:rowIndex]; - [tableTypes removeObjectAtIndex:rowIndex]; - [tablesListView reloadData]; - } else { - if ( [tableDocumentInstance supportsEncoding] ) { - [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE TABLE `%@` (id int) DEFAULT CHARACTER SET %@", anObject, [tableDocumentInstance connectionEncoding]]]; - } else { - [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE TABLE `%@` (id int)", anObject]]; - } + if ([[tables objectAtIndex:rowIndex] isEqualToString:anObject]) { + // No changes in table name + } + else if ([anObject isEqualToString:@""]) { + // Table has no name + alertSheetOpened = YES; + NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, + @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addRow", NSLocalizedString(@"Table must have a name.", @"message of panel when no name is given for table")); + } + else { + [mySQLConnection queryString:[NSString stringWithFormat:@"RENAME TABLE %@ TO %@", [[tables objectAtIndex:rowIndex] backtickQuotedString], [anObject backtickQuotedString]]]; + + if ([[mySQLConnection getLastErrorMessage] isEqualToString:@""]) { + // Renamed with success + [tables replaceObjectAtIndex:rowIndex withObject:anObject]; - if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { - //added table with success - [tables replaceObjectAtIndex:rowIndex withObject:anObject]; - - if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == 0 ) { - [tableSourceInstance loadTable:anObject]; - structureLoaded = YES; - contentLoaded = NO; - statusLoaded = NO; - } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == 1 ) { - [tableContentInstance loadTable:anObject]; - structureLoaded = NO; - contentLoaded = YES; - statusLoaded = NO; - } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == 3 ) { - [tableStatusInstance loadTable:anObject]; - statusLoaded = YES; - structureLoaded = NO; - contentLoaded = NO; - } else { - statusLoaded = NO; - structureLoaded = NO; - contentLoaded = NO; - } - - // set window title - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@/%@", [tableDocumentInstance mySQLVersion], [tableDocumentInstance user], - [tableDocumentInstance host], [tableDocumentInstance database], anObject]]; - } else { - - //error while adding new table - alertSheetOpened = YES; - NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addRow", - [NSString stringWithFormat:NSLocalizedString(@"Couldn't add table %@.\nMySQL said: %@", @"message of panel when table cannot be created with the given name"), - anObject, [mySQLConnection getLastErrorMessage]]); - [tableTypes removeObjectAtIndex:rowIndex]; - [tables removeObjectAtIndex:rowIndex]; - [tablesListView reloadData]; + int selectedIndex = [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]; + + if (selectedIndex == 0) { + [tableSourceInstance loadTable:anObject]; + structureLoaded = YES; + contentLoaded = NO; + statusLoaded = NO; + } + else if (selectedIndex == 1) { + [tableContentInstance loadTable:anObject]; + structureLoaded = NO; + contentLoaded = YES; + statusLoaded = NO; + } + else if (selectedIndex == 3) { + [tableStatusInstance loadTable:anObject]; + structureLoaded = NO; + contentLoaded = NO; + statusLoaded = YES; + } + else { + statusLoaded = NO; + structureLoaded = NO; + contentLoaded = NO; } - } - } else { - - //table modification - if ( [[tables objectAtIndex:rowIndex] isEqualToString:anObject] ) { - //no changes in table name -// NSLog(@"no changes in table name"); - } else if ( [anObject isEqualToString:@""] ) { - //table has no name -// NSLog(@"name is nil"); + + // Set window title + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@/%@", [tableDocumentInstance mySQLVersion], + [tableDocumentInstance name], [tableDocumentInstance database], anObject]]; + } + else { + // Error while renaming alertSheetOpened = YES; NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addRow", NSLocalizedString(@"Table must have a name.", @"message of panel when no name is given for table")); - } else { - [mySQLConnection queryString:[NSString stringWithFormat:@"RENAME TABLE `%@` TO `%@`", [tables objectAtIndex:rowIndex], anObject]]; - if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { -// NSLog(@"renamed table with success"); - //renamed with success - [tables replaceObjectAtIndex:rowIndex withObject:anObject]; - - if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == 0 ) { - [tableSourceInstance loadTable:anObject]; - structureLoaded = YES; - contentLoaded = NO; - statusLoaded = NO; - } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == 1 ) { - [tableContentInstance loadTable:anObject]; - structureLoaded = NO; - contentLoaded = YES; - statusLoaded = NO; - } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == 3 ) { - [tableStatusInstance loadTable:anObject]; - structureLoaded = NO; - contentLoaded = NO; - statusLoaded = YES; - } else { - statusLoaded = NO; - structureLoaded = NO; - contentLoaded = NO; - } - - // set window title - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@/%@", [tableDocumentInstance mySQLVersion], [tableDocumentInstance user], - [tableDocumentInstance host], [tableDocumentInstance database], anObject]]; - } else { - //error while renaming -// NSLog(@"couldn't rename table"); - alertSheetOpened = YES; - NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addRow", - [NSString stringWithFormat:NSLocalizedString(@"Couldn't rename table.\nMySQL said: %@", @"message of panel when table cannot be renamed"), - [mySQLConnection getLastErrorMessage]]); - } + @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addRow", + [NSString stringWithFormat:NSLocalizedString(@"Couldn't rename table.\nMySQL said: %@", @"message of panel when table cannot be renamed"), + [mySQLConnection getLastErrorMessage]]); } } } #pragma mark TableView delegate methods -/* -traps enter and esc and edit/cancel without entering next row -*/ +/** + * Traps enter and esc and edit/cancel without entering next row + */ - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command { if ( [textView methodForSelector:command] == [textView methodForSelector:@selector(insertNewline:)] ) { @@ -565,28 +627,19 @@ traps enter and esc and edit/cancel without entering next row } } +/** + * Table view delegate method + */ - (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView { -/* - int row = [tablesListView editedRow]; - int column = [tablesListView editedColumn]; - NSTableColumn *tableColumn; - NSCell *cell; - - if ( row != -1 ) { - tableColumn = [[tablesListView tableColumns] objectAtIndex:column]; - cell = [tableColumn dataCellForRow:row]; - [cell endEditing:[tablesListView currentEditor]]; - } -*/ - //end editing (otherwise problems when user hits reload button) + // End editing (otherwise problems when user hits reload button) [tableWindow endEditingFor:nil]; + if ( alertSheetOpened ) { return NO; } - //we have to be sure that TableSource and TableContent have finished editing -// if ( ![tableSourceInstance addRowToDB] || ![tableContentInstance addRowToDB] ) { + // We have to be sure that TableSource and TableContent have finished editing if ( ![tableSourceInstance saveRowOnDeselect] || ![tableContentInstance saveRowOnDeselect] ) { return NO; } else { @@ -606,7 +659,7 @@ traps enter and esc and edit/cancel without entering next row // If encoding is set to Autodetect, update the connection character set encoding // based on the newly selected table's encoding - but only if it differs from the current encoding. - if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"encoding"] isEqualToString:@"Autodetect"]) { + if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"DefaultEncoding"] isEqualToString:@"Autodetect"]) { if (![[tableDataInstance tableEncoding] isEqualToString:[tableDocumentInstance connectionEncoding]]) { [tableDocumentInstance setConnectionEncoding:[tableDataInstance tableEncoding] reloadingViews:NO]; [tableDataInstance resetAllData]; @@ -635,8 +688,8 @@ traps enter and esc and edit/cancel without entering next row } // set window title - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@/%@", [tableDocumentInstance mySQLVersion], [tableDocumentInstance user], - [tableDocumentInstance host], [tableDocumentInstance database], [tables objectAtIndex:[tablesListView selectedRow]]]]; + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@/%@", [tableDocumentInstance mySQLVersion], + [tableDocumentInstance name], [tableDocumentInstance database], [tables objectAtIndex:[tablesListView selectedRow]]]]; } else { [tableSourceInstance loadTable:nil]; [tableContentInstance loadTable:nil]; @@ -646,26 +699,31 @@ traps enter and esc and edit/cancel without entering next row statusLoaded = NO; // set window title - [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@", [tableDocumentInstance mySQLVersion], [tableDocumentInstance user], - [tableDocumentInstance host], [tableDocumentInstance database]]]; + [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", [tableDocumentInstance mySQLVersion], + [tableDocumentInstance name], [tableDocumentInstance database]]]; } } +/** + * Table view delegate method + */ - (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex { return (rowIndex != 0); } - +/** + * Table view delegate method + */ - (BOOL)tableView:(NSTableView *)aTableView isGroupRow:(int)row { return (row == 0); } -- (void)tableView:(NSTableView *)aTableView - willDisplayCell:(id)aCell - forTableColumn:(NSTableColumn *)aTableColumn - row:(int)rowIndex +/** + * Table view delegate method + */ +- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { if (rowIndex > 0 && [[aTableColumn identifier] isEqualToString:@"tables"]) { if ([[tableTypes objectAtIndex:rowIndex] intValue] == SP_TABLETYPE_VIEW) { @@ -673,36 +731,28 @@ traps enter and esc and edit/cancel without entering next row } else { [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"table-small"]]; } - [(ImageAndTextCell*)aCell setIndentationLevel:1]; - if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"useMonospacedFonts"] ) { - [(ImageAndTextCell*)aCell setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; - } - else - { - [(ImageAndTextCell*)aCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - } + [(ImageAndTextCell*)aCell setIndentationLevel:1]; + [(ImageAndTextCell*)aCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; } else { [(ImageAndTextCell*)aCell setImage:nil]; [(ImageAndTextCell*)aCell setIndentationLevel:0]; } } +/** + * Table view delegate method + */ - (float)tableView:(NSTableView *)tableView heightOfRow:(int)row { - if (row == 0) { - return 18; - } else { - return 17; - } + return (row == 0) ? 18 : 17; } -#pragma mark - #pragma mark TabView delegate methods -/* -loads structure or source if tab selected the first time -*/ +/** + * Loads structure or source if tab selected the first time + */ - (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem { if ( [tablesListView numberOfSelectedRows] == 1 ) { @@ -722,37 +772,51 @@ loads structure or source if tab selected the first time statusLoaded = YES; } } -/* - if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == 3 ) { - [tableDumpInstance reloadTables:self]; - } -*/ } -#pragma mark - -//last but not least +/** + * Menu item interface validation + */ +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + // popup button below table list + if ([menuItem action] == @selector(copyTable:) || + [menuItem action] == @selector(removeTable:)) + { + return [tablesListView numberOfSelectedRows] > 0; + } + + return [super validateMenuItem:menuItem]; +} + +#pragma mark Other + +/** + * Standard init method. Performs various ivar initialisations. + */ - (id)init { - self = [super init]; + if ((self = [super init])) { + tables = [[NSMutableArray alloc] init]; + tableTypes = [[NSMutableArray alloc] init]; + structureLoaded = NO; + contentLoaded = NO; + statusLoaded = NO; + [tables addObject:NSLocalizedString(@"TABLES",@"header for table list")]; + } - tables = [[NSMutableArray alloc] init]; - tableTypes = [[NSMutableArray alloc] init]; - structureLoaded = NO; - contentLoaded = NO; - statusLoaded = NO; - [tables addObject:NSLocalizedString(@"TABLES",@"header for table list")]; return self; } +/** + * Standard dealloc method. + */ - (void)dealloc -{ -// NSLog(@"TableList dealloc"); - - [tables release]; - [tableTypes release]; +{ + [tables release], tables = nil; + [tableTypes release], tableTypes = nil; [super dealloc]; } - @end diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index 7bf74a1b..0bc512bc 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -29,83 +29,89 @@ 17E641830EF01FA8001BC333 /* CMImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E6417F0EF01FA8001BC333 /* CMImageView.m */; }; 17E641840EF01FA8001BC333 /* CMTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E641810EF01FA8001BC333 /* CMTextView.m */; }; 17E641890EF01FB4001BC333 /* SSHTunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E641860EF01FB4001BC333 /* SSHTunnel.m */; }; - 17E641C00EF02036001BC333 /* appicon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 17E6418C0EF02036001BC333 /* appicon.icns */; }; - 17E641C10EF02036001BC333 /* clearconsole.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E6418D0EF02036001BC333 /* clearconsole.tiff */; }; - 17E641C30EF02036001BC333 /* columnadd.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E6418F0EF02036001BC333 /* columnadd.tiff */; }; - 17E641C40EF02036001BC333 /* columncopy.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641900EF02036001BC333 /* columncopy.tiff */; }; - 17E641C50EF02036001BC333 /* columndelete.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641910EF02036001BC333 /* columndelete.tiff */; }; - 17E641C60EF02036001BC333 /* columnrefresh.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641920EF02036001BC333 /* columnrefresh.tiff */; }; - 17E641C70EF02036001BC333 /* connectionadd.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641930EF02036001BC333 /* connectionadd.tiff */; }; - 17E641C80EF02036001BC333 /* connectioncopy.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641940EF02036001BC333 /* connectioncopy.tiff */; }; - 17E641C90EF02036001BC333 /* connectiondelete.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641950EF02036001BC333 /* connectiondelete.tiff */; }; - 17E641CA0EF02036001BC333 /* connectionrefresh.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641960EF02036001BC333 /* connectionrefresh.tiff */; }; - 17E641CB0EF02036001BC333 /* createtablesyntax.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641970EF02036001BC333 /* createtablesyntax.tiff */; }; - 17E641CC0EF02036001BC333 /* databases.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641980EF02036001BC333 /* databases.tiff */; }; - 17E641CD0EF02036001BC333 /* dbadd.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641990EF02036001BC333 /* dbadd.tiff */; }; - 17E641CE0EF02036001BC333 /* dbdelete.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E6419A0EF02036001BC333 /* dbdelete.tiff */; }; - 17E641CF0EF02036001BC333 /* dbrefresh.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E6419B0EF02036001BC333 /* dbrefresh.tiff */; }; - 17E641D00EF02036001BC333 /* flushprivileges.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E6419C0EF02036001BC333 /* flushprivileges.tiff */; }; - 17E641D10EF02036001BC333 /* grabber-horizontal.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E6419D0EF02036001BC333 /* grabber-horizontal.png */; }; - 17E641D20EF02036001BC333 /* grabber-vertical.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E6419E0EF02036001BC333 /* grabber-vertical.png */; }; - 17E641D30EF02036001BC333 /* hideconsole.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E6419F0EF02036001BC333 /* hideconsole.tiff */; }; - 17E641D40EF02036001BC333 /* indexadd.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A00EF02036001BC333 /* indexadd.tiff */; }; - 17E641D50EF02036001BC333 /* indexdelete.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A10EF02036001BC333 /* indexdelete.tiff */; }; - 17E641D60EF02036001BC333 /* logo-48.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A20EF02036001BC333 /* logo-48.png */; }; - 17E641D70EF02036001BC333 /* optimizetable.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A30EF02036001BC333 /* optimizetable.tiff */; }; - 17E641D80EF02036001BC333 /* query.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A40EF02036001BC333 /* query.tiff */; }; - 17E641D90EF02036001BC333 /* queryadd.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A50EF02036001BC333 /* queryadd.tiff */; }; - 17E641DA0EF02036001BC333 /* querycopy.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A60EF02036001BC333 /* querycopy.tiff */; }; - 17E641DB0EF02036001BC333 /* querydelete.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A70EF02036001BC333 /* querydelete.tiff */; }; - 17E641DC0EF02036001BC333 /* queryrefresh.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A80EF02036001BC333 /* queryrefresh.tiff */; }; - 17E641DD0EF02036001BC333 /* reload.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641A90EF02036001BC333 /* reload.tiff */; }; - 17E641DE0EF02036001BC333 /* rowadd.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641AA0EF02036001BC333 /* rowadd.tiff */; }; - 17E641DF0EF02036001BC333 /* rowcopy.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641AB0EF02036001BC333 /* rowcopy.tiff */; }; - 17E641E00EF02036001BC333 /* rowdelete.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641AC0EF02036001BC333 /* rowdelete.tiff */; }; - 17E641E10EF02036001BC333 /* rowrefresh.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641AD0EF02036001BC333 /* rowrefresh.tiff */; }; - 17E641E20EF02036001BC333 /* selectall.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641AE0EF02036001BC333 /* selectall.tiff */; }; - 17E641E30EF02036001BC333 /* selectnone.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641AF0EF02036001BC333 /* selectnone.tiff */; }; - 17E641E40EF02036001BC333 /* sheettotable.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B00EF02036001BC333 /* sheettotable.tiff */; }; - 17E641E50EF02036001BC333 /* showconsole.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B10EF02036001BC333 /* showconsole.tiff */; }; - 17E641E60EF02036001BC333 /* showvariables.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B20EF02036001BC333 /* showvariables.tiff */; }; - 17E641E70EF02036001BC333 /* spyglass.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B30EF02036001BC333 /* spyglass.tiff */; }; - 17E641E80EF02036001BC333 /* table-small.tif in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B40EF02036001BC333 /* table-small.tif */; }; - 17E641E90EF02036001BC333 /* tableadd.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B50EF02036001BC333 /* tableadd.tiff */; }; - 17E641EA0EF02036001BC333 /* tablecopy.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B60EF02036001BC333 /* tablecopy.tiff */; }; - 17E641EB0EF02036001BC333 /* tabledelete.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B70EF02036001BC333 /* tabledelete.tiff */; }; - 17E641EC0EF02036001BC333 /* tablerefresh.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B80EF02036001BC333 /* tablerefresh.tiff */; }; - 17E641ED0EF02036001BC333 /* tables.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641B90EF02036001BC333 /* tables.tiff */; }; - 17E641EE0EF02036001BC333 /* tabletosheet.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641BA0EF02036001BC333 /* tabletosheet.tiff */; }; - 17E641EF0EF02036001BC333 /* textencode.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E641BB0EF02036001BC333 /* textencode.tiff */; }; - 17E641F00EF02036001BC333 /* toolbar-switch-to-browse.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E641BC0EF02036001BC333 /* toolbar-switch-to-browse.png */; }; - 17E641F10EF02036001BC333 /* toolbar-switch-to-sql.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E641BD0EF02036001BC333 /* toolbar-switch-to-sql.png */; }; - 17E641F20EF02036001BC333 /* toolbar-switch-to-structure.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E641BE0EF02036001BC333 /* toolbar-switch-to-structure.png */; }; - 17E641F30EF02036001BC333 /* toolbar-switch-to-table-info.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E641BF0EF02036001BC333 /* toolbar-switch-to-table-info.png */; }; 17E641F90EF02088001BC333 /* askForPass.sh in Resources */ = {isa = PBXBuildFile; fileRef = 17E641F40EF02088001BC333 /* askForPass.sh */; }; 17E641FA0EF02088001BC333 /* Growl Registration Ticket.growlRegDict in Resources */ = {isa = PBXBuildFile; fileRef = 17E641F50EF02088001BC333 /* Growl Registration Ticket.growlRegDict */; }; 17E641FC0EF02088001BC333 /* sequel-pro.scriptSuite in Resources */ = {isa = PBXBuildFile; fileRef = 17E641F70EF02088001BC333 /* sequel-pro.scriptSuite */; }; 17E641FD0EF02088001BC333 /* sequel-pro.scriptTerminology in Resources */ = {isa = PBXBuildFile; fileRef = 17E641F80EF02088001BC333 /* sequel-pro.scriptTerminology */; }; 17E6423B0EF0216C001BC333 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 17E642390EF0216C001BC333 /* Credits.rtf */; }; 17E6423E0EF0218B001BC333 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 17E6423C0EF0218B001BC333 /* InfoPlist.strings */; }; - 4DECC3350EC2A170008D359E /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DECC3320EC2A170008D359E /* Sparkle.framework */; }; - 4DECC3370EC2A170008D359E /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DECC3340EC2A170008D359E /* Growl.framework */; }; - 4DECC48F0EC2B436008D359E /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4DECC3320EC2A170008D359E /* Sparkle.framework */; }; - 4DECC4910EC2B436008D359E /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4DECC3340EC2A170008D359E /* Growl.framework */; }; - 581242210F6C90210073361A /* MCPKit_bundled.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 581242200F6C90210073361A /* MCPKit_bundled.framework */; }; - 581242220F6C90310073361A /* MCPKit_bundled.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 581242200F6C90210073361A /* MCPKit_bundled.framework */; }; - 5838B7FF0F6C575D00F3863A /* button_add.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 5838B7FA0F6C575D00F3863A /* button_add.tiff */; }; - 5838B8000F6C575D00F3863A /* button_left.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 5838B7FB0F6C575D00F3863A /* button_left.tiff */; }; - 5838B8010F6C575D00F3863A /* button_progress_cancel.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 5838B7FC0F6C575D00F3863A /* button_progress_cancel.tiff */; }; - 5838B8020F6C575D00F3863A /* button_right.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 5838B7FD0F6C575D00F3863A /* button_right.tiff */; }; - 5838B8030F6C575D00F3863A /* button_subtract.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 5838B7FE0F6C575D00F3863A /* button_subtract.tiff */; }; - 584F5F8F0F50ACD800036517 /* table-view-small.tif in Resources */ = {isa = PBXBuildFile; fileRef = 584F5F8E0F50ACD800036517 /* table-view-small.tif */; }; + 584143E00F9A2E9B00A34B47 /* SPExportController.m in Sources */ = {isa = PBXBuildFile; fileRef = 584143DF0F9A2E9B00A34B47 /* SPExportController.m */; }; + 584143E40F9A2EF800A34B47 /* SPPreferenceController.m in Sources */ = {isa = PBXBuildFile; fileRef = 584143E30F9A2EF800A34B47 /* SPPreferenceController.m */; }; + 584143E70F9A2F5500A34B47 /* SPConsoleMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 584143E60F9A2F5500A34B47 /* SPConsoleMessage.m */; }; + 584143EA0F9A2F7900A34B47 /* SPFavoriteTextFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 584143E90F9A2F7900A34B47 /* SPFavoriteTextFieldCell.m */; }; + 5841442E0F9A326200A34B47 /* SPEditorTokens.l in Sources */ = {isa = PBXBuildFile; fileRef = 5841442D0F9A326200A34B47 /* SPEditorTokens.l */; }; + 584144310F9A32B800A34B47 /* NoodleLineNumberView.m in Sources */ = {isa = PBXBuildFile; fileRef = 584144300F9A32B800A34B47 /* NoodleLineNumberView.m */; }; + 584144590F9A34EC00A34B47 /* SPArrayAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 584144540F9A34EC00A34B47 /* SPArrayAdditions.m */; }; + 5841445A0F9A34EC00A34B47 /* SPTextViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 584144560F9A34EC00A34B47 /* SPTextViewAdditions.m */; }; + 5841445B0F9A34EC00A34B47 /* SPWindowAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 584144580F9A34EC00A34B47 /* SPWindowAdditions.m */; }; + 584144700F9A3C1200A34B47 /* PreferenceDefaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5841446F0F9A3C1200A34B47 /* PreferenceDefaults.plist */; }; + 584144720F9A3C6600A34B47 /* sparkle-public-key.pem in Resources */ = {isa = PBXBuildFile; fileRef = 584144710F9A3C6600A34B47 /* sparkle-public-key.pem */; }; + 584144740F9A3C8300A34B47 /* package-application.sh in Resources */ = {isa = PBXBuildFile; fileRef = 584144730F9A3C8300A34B47 /* package-application.sh */; }; + 584144E50F9A40BB00A34B47 /* appicon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 584144B30F9A40BB00A34B47 /* appicon.icns */; }; + 584144E60F9A40BB00A34B47 /* button_action.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144B40F9A40BB00A34B47 /* button_action.tiff */; }; + 584144E70F9A40BB00A34B47 /* button_add.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144B50F9A40BB00A34B47 /* button_add.tiff */; }; + 584144E80F9A40BB00A34B47 /* button_bar_handle.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144B60F9A40BB00A34B47 /* button_bar_handle.tiff */; }; + 584144E90F9A40BB00A34B47 /* button_bar_spacer.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144B70F9A40BB00A34B47 /* button_bar_spacer.tiff */; }; + 584144EA0F9A40BB00A34B47 /* button_duplicate.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144B80F9A40BB00A34B47 /* button_duplicate.tiff */; }; + 584144EB0F9A40BB00A34B47 /* button_edit_mode_selected.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144B90F9A40BB00A34B47 /* button_edit_mode_selected.tiff */; }; + 584144EC0F9A40BB00A34B47 /* button_edit_mode.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144BA0F9A40BB00A34B47 /* button_edit_mode.tiff */; }; + 584144ED0F9A40BB00A34B47 /* button_edit.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144BB0F9A40BB00A34B47 /* button_edit.tiff */; }; + 584144EE0F9A40BB00A34B47 /* button_info_pane_hide.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144BC0F9A40BB00A34B47 /* button_info_pane_hide.tiff */; }; + 584144EF0F9A40BB00A34B47 /* button_info_pane_show.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144BD0F9A40BB00A34B47 /* button_info_pane_show.tiff */; }; + 584144F00F9A40BB00A34B47 /* button_left.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144BE0F9A40BB00A34B47 /* button_left.tiff */; }; + 584144F10F9A40BB00A34B47 /* button_progress_cancel.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144BF0F9A40BB00A34B47 /* button_progress_cancel.tiff */; }; + 584144F20F9A40BB00A34B47 /* button_refresh.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144C00F9A40BB00A34B47 /* button_refresh.tiff */; }; + 584144F30F9A40BB00A34B47 /* button_remove.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144C10F9A40BB00A34B47 /* button_remove.tiff */; }; + 584144F40F9A40BB00A34B47 /* button_right.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144C20F9A40BB00A34B47 /* button_right.tiff */; }; + 584144F50F9A40BB00A34B47 /* clearconsole.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144C30F9A40BB00A34B47 /* clearconsole.tiff */; }; + 584144F60F9A40BB00A34B47 /* database.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144C40F9A40BB00A34B47 /* database.png */; }; + 584144F70F9A40BB00A34B47 /* dimple.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144C50F9A40BB00A34B47 /* dimple.tiff */; }; + 584144F80F9A40BB00A34B47 /* grabber-horizontal.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144C60F9A40BB00A34B47 /* grabber-horizontal.png */; }; + 584144F90F9A40BB00A34B47 /* grabber-vertical.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144C70F9A40BB00A34B47 /* grabber-vertical.png */; }; + 584144FA0F9A40BB00A34B47 /* hideconsole.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144C80F9A40BB00A34B47 /* hideconsole.tiff */; }; + 584144FB0F9A40BB00A34B47 /* logo-48.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144C90F9A40BB00A34B47 /* logo-48.png */; }; + 584144FC0F9A40BB00A34B47 /* selectall.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144CA0F9A40BB00A34B47 /* selectall.tiff */; }; + 584144FD0F9A40BB00A34B47 /* selectnone.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144CB0F9A40BB00A34B47 /* selectnone.tiff */; }; + 584144FE0F9A40BB00A34B47 /* showconsole.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144CC0F9A40BB00A34B47 /* showconsole.tiff */; }; + 584144FF0F9A40BB00A34B47 /* showvariables.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144CD0F9A40BB00A34B47 /* showvariables.tiff */; }; + 584145000F9A40BB00A34B47 /* spyglass.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144CE0F9A40BB00A34B47 /* spyglass.tiff */; }; + 584145010F9A40BB00A34B47 /* table-property.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144CF0F9A40BB00A34B47 /* table-property.png */; }; + 584145020F9A40BB00A34B47 /* table-small.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D00F9A40BB00A34B47 /* table-small.tiff */; }; + 584145030F9A40BB00A34B47 /* table-view-small.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D10F9A40BB00A34B47 /* table-view-small.tiff */; }; + 584145040F9A40BB00A34B47 /* toolbar-export-csv.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D20F9A40BB00A34B47 /* toolbar-export-csv.tiff */; }; + 584145050F9A40BB00A34B47 /* toolbar-export-html.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D30F9A40BB00A34B47 /* toolbar-export-html.tiff */; }; + 584145060F9A40BB00A34B47 /* toolbar-export-pdf.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D40F9A40BB00A34B47 /* toolbar-export-pdf.tiff */; }; + 584145070F9A40BB00A34B47 /* toolbar-export-sql.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D50F9A40BB00A34B47 /* toolbar-export-sql.tiff */; }; + 584145080F9A40BB00A34B47 /* toolbar-export-xls.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D60F9A40BB00A34B47 /* toolbar-export-xls.tiff */; }; + 584145090F9A40BB00A34B47 /* toolbar-export-xml.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D70F9A40BB00A34B47 /* toolbar-export-xml.tiff */; }; + 5841450A0F9A40BB00A34B47 /* toolbar-preferences-advanced.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D80F9A40BB00A34B47 /* toolbar-preferences-advanced.tiff */; }; + 5841450B0F9A40BB00A34B47 /* toolbar-preferences-autoupdate.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144D90F9A40BB00A34B47 /* toolbar-preferences-autoupdate.tiff */; }; + 5841450C0F9A40BB00A34B47 /* toolbar-preferences-favorites.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144DA0F9A40BB00A34B47 /* toolbar-preferences-favorites.png */; }; + 5841450D0F9A40BB00A34B47 /* toolbar-preferences-general.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144DB0F9A40BB00A34B47 /* toolbar-preferences-general.tiff */; }; + 5841450E0F9A40BB00A34B47 /* toolbar-preferences-network.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144DC0F9A40BB00A34B47 /* toolbar-preferences-network.png */; }; + 5841450F0F9A40BB00A34B47 /* toolbar-preferences-notifications.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144DD0F9A40BB00A34B47 /* toolbar-preferences-notifications.tiff */; }; + 584145100F9A40BB00A34B47 /* toolbar-preferences-security.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144DE0F9A40BB00A34B47 /* toolbar-preferences-security.tiff */; }; + 584145110F9A40BB00A34B47 /* toolbar-preferences-tables.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144DF0F9A40BB00A34B47 /* toolbar-preferences-tables.tiff */; }; + 584145120F9A40BB00A34B47 /* toolbar-switch-to-browse.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144E00F9A40BB00A34B47 /* toolbar-switch-to-browse.png */; }; + 584145130F9A40BB00A34B47 /* toolbar-switch-to-procedures.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584144E10F9A40BB00A34B47 /* toolbar-switch-to-procedures.tiff */; }; + 584145140F9A40BB00A34B47 /* toolbar-switch-to-sql.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144E20F9A40BB00A34B47 /* toolbar-switch-to-sql.png */; }; + 584145150F9A40BB00A34B47 /* toolbar-switch-to-structure.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144E30F9A40BB00A34B47 /* toolbar-switch-to-structure.png */; }; + 584145160F9A40BB00A34B47 /* toolbar-switch-to-table-info.png in Resources */ = {isa = PBXBuildFile; fileRef = 584144E40F9A40BB00A34B47 /* toolbar-switch-to-table-info.png */; }; + 584146810F9AAC6200A34B47 /* ConnectionErrorDialog.nib in Resources */ = {isa = PBXBuildFile; fileRef = 584146030F9A5BFA00A34B47 /* ConnectionErrorDialog.nib */; }; + 584146820F9AAC6200A34B47 /* Console.nib in Resources */ = {isa = PBXBuildFile; fileRef = 584146080F9A5C8300A34B47 /* Console.nib */; }; + 584146830F9AAC6200A34B47 /* DBView.nib in Resources */ = {isa = PBXBuildFile; fileRef = 5841460D0F9A651D00A34B47 /* DBView.nib */; }; + 584146840F9AAC6200A34B47 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 584146110F9A6BAF00A34B47 /* MainMenu.nib */; }; + 584146850F9AAC6200A34B47 /* Preferences.nib in Resources */ = {isa = PBXBuildFile; fileRef = 584146210F9A9BCB00A34B47 /* Preferences.nib */; }; + 584146D00F9AB00600A34B47 /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 584146BB0F9AAD6C00A34B47 /* Growl.framework */; }; + 584146D20F9AB01700A34B47 /* MCPKit_bundled.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 584146BC0F9AAD6C00A34B47 /* MCPKit_bundled.framework */; }; + 584146D40F9AB02B00A34B47 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 584146BA0F9AAD6C00A34B47 /* Sparkle.framework */; }; + 584146D60F9AB15B00A34B47 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 584146BA0F9AAD6C00A34B47 /* Sparkle.framework */; }; + 584146D70F9AB15B00A34B47 /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 584146BB0F9AAD6C00A34B47 /* Growl.framework */; }; + 584146D80F9AB15B00A34B47 /* MCPKit_bundled.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 584146BC0F9AAD6C00A34B47 /* MCPKit_bundled.framework */; }; 58C56EF50F438E120035701E /* SPDataCellFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C56EF40F438E120035701E /* SPDataCellFormatter.m */; }; - 58E2C6EA0F6C4DD300569E7C /* ConnectionErrorDialog.nib in Resources */ = {isa = PBXBuildFile; fileRef = 58E2C6E40F6C4DD300569E7C /* ConnectionErrorDialog.nib */; }; - 58E2C6EB0F6C4DD300569E7C /* DBView.nib in Resources */ = {isa = PBXBuildFile; fileRef = 58E2C6E60F6C4DD300569E7C /* DBView.nib */; }; - 58E2C6EC0F6C4DD300569E7C /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 58E2C6E80F6C4DD300569E7C /* MainMenu.nib */; }; 58FEF16D0F23D66600518E8E /* SPSQLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF16C0F23D66600518E8E /* SPSQLParser.m */; }; 58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF57D0F3B4E9700518E8E /* SPTableData.m */; }; 8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; }; - B5E2C5FA0F2353B5007446E0 /* TablePropertyIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = B5E2C5F90F2353B5007446E0 /* TablePropertyIcon.png */; }; B5EAC0FD0EC87FF900CC579C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5EAC0FC0EC87FF900CC579C /* Security.framework */; }; /* End PBXBuildFile section */ @@ -116,9 +122,9 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 4DECC48F0EC2B436008D359E /* Sparkle.framework in CopyFiles */, - 581242220F6C90310073361A /* MCPKit_bundled.framework in CopyFiles */, - 4DECC4910EC2B436008D359E /* Growl.framework in CopyFiles */, + 584146D60F9AB15B00A34B47 /* Sparkle.framework in CopyFiles */, + 584146D70F9AB15B00A34B47 /* Growl.framework in CopyFiles */, + 584146D80F9AB15B00A34B47 /* MCPKit_bundled.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -193,57 +199,6 @@ 17E641810EF01FA8001BC333 /* CMTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMTextView.m; sourceTree = ""; }; 17E641850EF01FB4001BC333 /* SSHTunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSHTunnel.h; sourceTree = ""; }; 17E641860EF01FB4001BC333 /* SSHTunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSHTunnel.m; sourceTree = ""; }; - 17E6418C0EF02036001BC333 /* appicon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = appicon.icns; sourceTree = ""; }; - 17E6418D0EF02036001BC333 /* clearconsole.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = clearconsole.tiff; sourceTree = ""; }; - 17E6418F0EF02036001BC333 /* columnadd.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = columnadd.tiff; sourceTree = ""; }; - 17E641900EF02036001BC333 /* columncopy.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = columncopy.tiff; sourceTree = ""; }; - 17E641910EF02036001BC333 /* columndelete.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = columndelete.tiff; sourceTree = ""; }; - 17E641920EF02036001BC333 /* columnrefresh.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = columnrefresh.tiff; sourceTree = ""; }; - 17E641930EF02036001BC333 /* connectionadd.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = connectionadd.tiff; sourceTree = ""; }; - 17E641940EF02036001BC333 /* connectioncopy.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = connectioncopy.tiff; sourceTree = ""; }; - 17E641950EF02036001BC333 /* connectiondelete.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = connectiondelete.tiff; sourceTree = ""; }; - 17E641960EF02036001BC333 /* connectionrefresh.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = connectionrefresh.tiff; sourceTree = ""; }; - 17E641970EF02036001BC333 /* createtablesyntax.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = createtablesyntax.tiff; sourceTree = ""; }; - 17E641980EF02036001BC333 /* databases.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = databases.tiff; sourceTree = ""; }; - 17E641990EF02036001BC333 /* dbadd.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = dbadd.tiff; sourceTree = ""; }; - 17E6419A0EF02036001BC333 /* dbdelete.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = dbdelete.tiff; sourceTree = ""; }; - 17E6419B0EF02036001BC333 /* dbrefresh.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = dbrefresh.tiff; sourceTree = ""; }; - 17E6419C0EF02036001BC333 /* flushprivileges.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = flushprivileges.tiff; sourceTree = ""; }; - 17E6419D0EF02036001BC333 /* grabber-horizontal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "grabber-horizontal.png"; sourceTree = ""; }; - 17E6419E0EF02036001BC333 /* grabber-vertical.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "grabber-vertical.png"; sourceTree = ""; }; - 17E6419F0EF02036001BC333 /* hideconsole.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = hideconsole.tiff; sourceTree = ""; }; - 17E641A00EF02036001BC333 /* indexadd.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = indexadd.tiff; sourceTree = ""; }; - 17E641A10EF02036001BC333 /* indexdelete.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = indexdelete.tiff; sourceTree = ""; }; - 17E641A20EF02036001BC333 /* logo-48.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-48.png"; sourceTree = ""; }; - 17E641A30EF02036001BC333 /* optimizetable.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = optimizetable.tiff; sourceTree = ""; }; - 17E641A40EF02036001BC333 /* query.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = query.tiff; sourceTree = ""; }; - 17E641A50EF02036001BC333 /* queryadd.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = queryadd.tiff; sourceTree = ""; }; - 17E641A60EF02036001BC333 /* querycopy.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = querycopy.tiff; sourceTree = ""; }; - 17E641A70EF02036001BC333 /* querydelete.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = querydelete.tiff; sourceTree = ""; }; - 17E641A80EF02036001BC333 /* queryrefresh.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = queryrefresh.tiff; sourceTree = ""; }; - 17E641A90EF02036001BC333 /* reload.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = reload.tiff; sourceTree = ""; }; - 17E641AA0EF02036001BC333 /* rowadd.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = rowadd.tiff; sourceTree = ""; }; - 17E641AB0EF02036001BC333 /* rowcopy.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = rowcopy.tiff; sourceTree = ""; }; - 17E641AC0EF02036001BC333 /* rowdelete.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = rowdelete.tiff; sourceTree = ""; }; - 17E641AD0EF02036001BC333 /* rowrefresh.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = rowrefresh.tiff; sourceTree = ""; }; - 17E641AE0EF02036001BC333 /* selectall.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = selectall.tiff; sourceTree = ""; }; - 17E641AF0EF02036001BC333 /* selectnone.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = selectnone.tiff; sourceTree = ""; }; - 17E641B00EF02036001BC333 /* sheettotable.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = sheettotable.tiff; sourceTree = ""; }; - 17E641B10EF02036001BC333 /* showconsole.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = showconsole.tiff; sourceTree = ""; }; - 17E641B20EF02036001BC333 /* showvariables.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = showvariables.tiff; sourceTree = ""; }; - 17E641B30EF02036001BC333 /* spyglass.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = spyglass.tiff; sourceTree = ""; }; - 17E641B40EF02036001BC333 /* table-small.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "table-small.tif"; sourceTree = ""; }; - 17E641B50EF02036001BC333 /* tableadd.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = tableadd.tiff; sourceTree = ""; }; - 17E641B60EF02036001BC333 /* tablecopy.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = tablecopy.tiff; sourceTree = ""; }; - 17E641B70EF02036001BC333 /* tabledelete.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = tabledelete.tiff; sourceTree = ""; }; - 17E641B80EF02036001BC333 /* tablerefresh.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = tablerefresh.tiff; sourceTree = ""; }; - 17E641B90EF02036001BC333 /* tables.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = tables.tiff; sourceTree = ""; }; - 17E641BA0EF02036001BC333 /* tabletosheet.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = tabletosheet.tiff; sourceTree = ""; }; - 17E641BB0EF02036001BC333 /* textencode.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = textencode.tiff; sourceTree = ""; }; - 17E641BC0EF02036001BC333 /* toolbar-switch-to-browse.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-browse.png"; sourceTree = ""; }; - 17E641BD0EF02036001BC333 /* toolbar-switch-to-sql.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-sql.png"; sourceTree = ""; }; - 17E641BE0EF02036001BC333 /* toolbar-switch-to-structure.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-structure.png"; sourceTree = ""; }; - 17E641BF0EF02036001BC333 /* toolbar-switch-to-table-info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-table-info.png"; sourceTree = ""; }; 17E641F40EF02088001BC333 /* askForPass.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = askForPass.sh; sourceTree = ""; }; 17E641F50EF02088001BC333 /* Growl Registration Ticket.growlRegDict */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "Growl Registration Ticket.growlRegDict"; sourceTree = ""; }; 17E641F60EF02088001BC333 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -253,26 +208,92 @@ 17E6423D0EF0218B001BC333 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = Interfaces/English.lproj/InfoPlist.strings; sourceTree = SOURCE_ROOT; }; 2A37F4C4FDCFA73011CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 2A37F4C5FDCFA73011CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 4DECC3320EC2A170008D359E /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Sparkle.framework; sourceTree = ""; }; - 4DECC3340EC2A170008D359E /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = Frameworks/Growl.framework; sourceTree = ""; }; - 581242200F6C90210073361A /* MCPKit_bundled.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MCPKit_bundled.framework; path = Frameworks/MCPKit_bundled.framework; sourceTree = ""; }; - 5838B7FA0F6C575D00F3863A /* button_add.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_add.tiff; sourceTree = ""; }; - 5838B7FB0F6C575D00F3863A /* button_left.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_left.tiff; sourceTree = ""; }; - 5838B7FC0F6C575D00F3863A /* button_progress_cancel.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_progress_cancel.tiff; sourceTree = ""; }; - 5838B7FD0F6C575D00F3863A /* button_right.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_right.tiff; sourceTree = ""; }; - 5838B7FE0F6C575D00F3863A /* button_subtract.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_subtract.tiff; sourceTree = ""; }; - 584F5F8E0F50ACD800036517 /* table-view-small.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "table-view-small.tif"; sourceTree = ""; }; + 584143DE0F9A2E9B00A34B47 /* SPExportController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPExportController.h; sourceTree = ""; }; + 584143DF0F9A2E9B00A34B47 /* SPExportController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPExportController.m; sourceTree = ""; }; + 584143E20F9A2EF800A34B47 /* SPPreferenceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPreferenceController.h; sourceTree = ""; }; + 584143E30F9A2EF800A34B47 /* SPPreferenceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPreferenceController.m; sourceTree = ""; }; + 584143E50F9A2F5500A34B47 /* SPConsoleMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPConsoleMessage.h; sourceTree = ""; }; + 584143E60F9A2F5500A34B47 /* SPConsoleMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPConsoleMessage.m; sourceTree = ""; }; + 584143E80F9A2F7900A34B47 /* SPFavoriteTextFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFavoriteTextFieldCell.h; sourceTree = ""; }; + 584143E90F9A2F7900A34B47 /* SPFavoriteTextFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFavoriteTextFieldCell.m; sourceTree = ""; }; + 5841442C0F9A326200A34B47 /* SPEditorTokens.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEditorTokens.h; sourceTree = ""; }; + 5841442D0F9A326200A34B47 /* SPEditorTokens.l */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.lex; path = SPEditorTokens.l; sourceTree = ""; }; + 5841442F0F9A32B800A34B47 /* NoodleLineNumberView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleLineNumberView.h; sourceTree = ""; }; + 584144300F9A32B800A34B47 /* NoodleLineNumberView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoodleLineNumberView.m; sourceTree = ""; }; + 584144530F9A34EC00A34B47 /* SPArrayAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPArrayAdditions.h; sourceTree = ""; }; + 584144540F9A34EC00A34B47 /* SPArrayAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPArrayAdditions.m; sourceTree = ""; }; + 584144550F9A34EC00A34B47 /* SPTextViewAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTextViewAdditions.h; sourceTree = ""; }; + 584144560F9A34EC00A34B47 /* SPTextViewAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTextViewAdditions.m; sourceTree = ""; }; + 584144570F9A34EC00A34B47 /* SPWindowAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPWindowAdditions.h; sourceTree = ""; }; + 584144580F9A34EC00A34B47 /* SPWindowAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPWindowAdditions.m; sourceTree = ""; }; + 5841446F0F9A3C1200A34B47 /* PreferenceDefaults.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = PreferenceDefaults.plist; sourceTree = ""; }; + 584144710F9A3C6600A34B47 /* sparkle-public-key.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "sparkle-public-key.pem"; sourceTree = ""; }; + 584144730F9A3C8300A34B47 /* package-application.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "package-application.sh"; sourceTree = ""; }; + 584144B30F9A40BB00A34B47 /* appicon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = appicon.icns; sourceTree = ""; }; + 584144B40F9A40BB00A34B47 /* button_action.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_action.tiff; sourceTree = ""; }; + 584144B50F9A40BB00A34B47 /* button_add.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_add.tiff; sourceTree = ""; }; + 584144B60F9A40BB00A34B47 /* button_bar_handle.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_bar_handle.tiff; sourceTree = ""; }; + 584144B70F9A40BB00A34B47 /* button_bar_spacer.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_bar_spacer.tiff; sourceTree = ""; }; + 584144B80F9A40BB00A34B47 /* button_duplicate.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_duplicate.tiff; sourceTree = ""; }; + 584144B90F9A40BB00A34B47 /* button_edit_mode_selected.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_edit_mode_selected.tiff; sourceTree = ""; }; + 584144BA0F9A40BB00A34B47 /* button_edit_mode.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_edit_mode.tiff; sourceTree = ""; }; + 584144BB0F9A40BB00A34B47 /* button_edit.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_edit.tiff; sourceTree = ""; }; + 584144BC0F9A40BB00A34B47 /* button_info_pane_hide.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_info_pane_hide.tiff; sourceTree = ""; }; + 584144BD0F9A40BB00A34B47 /* button_info_pane_show.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_info_pane_show.tiff; sourceTree = ""; }; + 584144BE0F9A40BB00A34B47 /* button_left.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_left.tiff; sourceTree = ""; }; + 584144BF0F9A40BB00A34B47 /* button_progress_cancel.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_progress_cancel.tiff; sourceTree = ""; }; + 584144C00F9A40BB00A34B47 /* button_refresh.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_refresh.tiff; sourceTree = ""; }; + 584144C10F9A40BB00A34B47 /* button_remove.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_remove.tiff; sourceTree = ""; }; + 584144C20F9A40BB00A34B47 /* button_right.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_right.tiff; sourceTree = ""; }; + 584144C30F9A40BB00A34B47 /* clearconsole.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = clearconsole.tiff; sourceTree = ""; }; + 584144C40F9A40BB00A34B47 /* database.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = database.png; sourceTree = ""; }; + 584144C50F9A40BB00A34B47 /* dimple.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = dimple.tiff; sourceTree = ""; }; + 584144C60F9A40BB00A34B47 /* grabber-horizontal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "grabber-horizontal.png"; sourceTree = ""; }; + 584144C70F9A40BB00A34B47 /* grabber-vertical.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "grabber-vertical.png"; sourceTree = ""; }; + 584144C80F9A40BB00A34B47 /* hideconsole.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = hideconsole.tiff; sourceTree = ""; }; + 584144C90F9A40BB00A34B47 /* logo-48.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-48.png"; sourceTree = ""; }; + 584144CA0F9A40BB00A34B47 /* selectall.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = selectall.tiff; sourceTree = ""; }; + 584144CB0F9A40BB00A34B47 /* selectnone.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = selectnone.tiff; sourceTree = ""; }; + 584144CC0F9A40BB00A34B47 /* showconsole.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = showconsole.tiff; sourceTree = ""; }; + 584144CD0F9A40BB00A34B47 /* showvariables.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = showvariables.tiff; sourceTree = ""; }; + 584144CE0F9A40BB00A34B47 /* spyglass.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = spyglass.tiff; sourceTree = ""; }; + 584144CF0F9A40BB00A34B47 /* table-property.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "table-property.png"; sourceTree = ""; }; + 584144D00F9A40BB00A34B47 /* table-small.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "table-small.tiff"; sourceTree = ""; }; + 584144D10F9A40BB00A34B47 /* table-view-small.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "table-view-small.tiff"; sourceTree = ""; }; + 584144D20F9A40BB00A34B47 /* toolbar-export-csv.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-export-csv.tiff"; sourceTree = ""; }; + 584144D30F9A40BB00A34B47 /* toolbar-export-html.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-export-html.tiff"; sourceTree = ""; }; + 584144D40F9A40BB00A34B47 /* toolbar-export-pdf.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-export-pdf.tiff"; sourceTree = ""; }; + 584144D50F9A40BB00A34B47 /* toolbar-export-sql.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-export-sql.tiff"; sourceTree = ""; }; + 584144D60F9A40BB00A34B47 /* toolbar-export-xls.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-export-xls.tiff"; sourceTree = ""; }; + 584144D70F9A40BB00A34B47 /* toolbar-export-xml.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-export-xml.tiff"; sourceTree = ""; }; + 584144D80F9A40BB00A34B47 /* toolbar-preferences-advanced.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-preferences-advanced.tiff"; sourceTree = ""; }; + 584144D90F9A40BB00A34B47 /* toolbar-preferences-autoupdate.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-preferences-autoupdate.tiff"; sourceTree = ""; }; + 584144DA0F9A40BB00A34B47 /* toolbar-preferences-favorites.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-preferences-favorites.png"; sourceTree = ""; }; + 584144DB0F9A40BB00A34B47 /* toolbar-preferences-general.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-preferences-general.tiff"; sourceTree = ""; }; + 584144DC0F9A40BB00A34B47 /* toolbar-preferences-network.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-preferences-network.png"; sourceTree = ""; }; + 584144DD0F9A40BB00A34B47 /* toolbar-preferences-notifications.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-preferences-notifications.tiff"; sourceTree = ""; }; + 584144DE0F9A40BB00A34B47 /* toolbar-preferences-security.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-preferences-security.tiff"; sourceTree = ""; }; + 584144DF0F9A40BB00A34B47 /* toolbar-preferences-tables.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-preferences-tables.tiff"; sourceTree = ""; }; + 584144E00F9A40BB00A34B47 /* toolbar-switch-to-browse.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-browse.png"; sourceTree = ""; }; + 584144E10F9A40BB00A34B47 /* toolbar-switch-to-procedures.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "toolbar-switch-to-procedures.tiff"; sourceTree = ""; }; + 584144E20F9A40BB00A34B47 /* toolbar-switch-to-sql.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-sql.png"; sourceTree = ""; }; + 584144E30F9A40BB00A34B47 /* toolbar-switch-to-structure.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-structure.png"; sourceTree = ""; }; + 584144E40F9A40BB00A34B47 /* toolbar-switch-to-table-info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-table-info.png"; sourceTree = ""; }; + 584146040F9A5BFA00A34B47 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/ConnectionErrorDialog.nib; sourceTree = ""; }; + 584146090F9A5C8300A34B47 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = Interfaces/English.lproj/Console.nib; sourceTree = ""; }; + 5841460E0F9A651D00A34B47 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/DBView.nib; sourceTree = ""; }; + 584146120F9A6BAF00A34B47 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; + 584146220F9A9BCB00A34B47 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/Preferences.nib; sourceTree = ""; }; + 584146BA0F9AAD6C00A34B47 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Sparkle.framework; sourceTree = ""; }; + 584146BB0F9AAD6C00A34B47 /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = Frameworks/Growl.framework; sourceTree = ""; }; + 584146BC0F9AAD6C00A34B47 /* MCPKit_bundled.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MCPKit_bundled.framework; path = Frameworks/MCPKit_bundled.framework; sourceTree = ""; }; 58C56EF30F438E120035701E /* SPDataCellFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDataCellFormatter.h; sourceTree = ""; }; 58C56EF40F438E120035701E /* SPDataCellFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDataCellFormatter.m; sourceTree = ""; }; - 58E2C6E50F6C4DD300569E7C /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/ConnectionErrorDialog.nib; sourceTree = ""; }; - 58E2C6E70F6C4DD300569E7C /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/DBView.nib; sourceTree = ""; }; - 58E2C6E90F6C4DD300569E7C /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; 58FEF16B0F23D66600518E8E /* SPSQLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLParser.h; sourceTree = ""; }; 58FEF16C0F23D66600518E8E /* SPSQLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSQLParser.m; sourceTree = ""; }; 58FEF57C0F3B4E9700518E8E /* SPTableData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTableData.h; sourceTree = ""; }; 58FEF57D0F3B4E9700518E8E /* SPTableData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableData.m; sourceTree = ""; }; 8D15AC370486D014006FF6A4 /* Sequel Pro.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Sequel Pro.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - B5E2C5F90F2353B5007446E0 /* TablePropertyIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TablePropertyIcon.png; sourceTree = ""; }; B5EAC0FC0EC87FF900CC579C /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -281,11 +302,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */, - 4DECC3350EC2A170008D359E /* Sparkle.framework in Frameworks */, - 4DECC3370EC2A170008D359E /* Growl.framework in Frameworks */, B5EAC0FD0EC87FF900CC579C /* Security.framework in Frameworks */, - 581242210F6C90210073361A /* MCPKit_bundled.framework in Frameworks */, + 8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */, + 584146D00F9AB00600A34B47 /* Growl.framework in Frameworks */, + 584146D20F9AB01700A34B47 /* MCPKit_bundled.framework in Frameworks */, + 584146D40F9AB02B00A34B47 /* Sparkle.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -295,9 +316,9 @@ 1058C7A6FEA54F5311CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( - 4DECC3320EC2A170008D359E /* Sparkle.framework */, - 581242200F6C90210073361A /* MCPKit_bundled.framework */, - 4DECC3340EC2A170008D359E /* Growl.framework */, + 584146BA0F9AAD6C00A34B47 /* Sparkle.framework */, + 584146BB0F9AAD6C00A34B47 /* Growl.framework */, + 584146BC0F9AAD6C00A34B47 /* MCPKit_bundled.framework */, 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; @@ -355,6 +376,7 @@ children = ( 1761FD9D0EF0488900331368 /* build-version.pl */, 178934980F30CDA10097539A /* trim-application.sh */, + 584144730F9A3C8300A34B47 /* package-application.sh */, ); path = Scripts; sourceTree = ""; @@ -377,6 +399,7 @@ children = ( 17E6418B0EF01FF7001BC333 /* Images */, 1703EF2A0F0B0742005BBE7E /* english_help */, + 5841446F0F9A3C1200A34B47 /* PreferenceDefaults.plist */, 17E641F60EF02088001BC333 /* Info.plist */, 17E6423C0EF0218B001BC333 /* InfoPlist.strings */, 17E642390EF0216C001BC333 /* Credits.rtf */, @@ -384,6 +407,7 @@ 17E641F70EF02088001BC333 /* sequel-pro.scriptSuite */, 17E641F80EF02088001BC333 /* sequel-pro.scriptTerminology */, 17E641F50EF02088001BC333 /* Growl Registration Ticket.growlRegDict */, + 584144710F9A3C6600A34B47 /* sparkle-public-key.pem */, ); path = Resources; sourceTree = ""; @@ -391,12 +415,8 @@ 17E641470EF01EB8001BC333 /* Controllers */ = { isa = PBXGroup; children = ( - 17E641480EF01EF6001BC333 /* CustomQuery.h */, - 17E641490EF01EF6001BC333 /* CustomQuery.m */, 17E6414A0EF01EF6001BC333 /* MainController.h */, 17E6414B0EF01EF6001BC333 /* MainController.m */, - 17E6414C0EF01EF6001BC333 /* SPGrowlController.h */, - 17E6414D0EF01EF6001BC333 /* SPGrowlController.m */, 17E6414E0EF01EF6001BC333 /* TableContent.h */, 17E6414F0EF01EF6001BC333 /* TableContent.m */, 17E641500EF01EF6001BC333 /* TableDocument.h */, @@ -405,10 +425,18 @@ 17E641530EF01EF6001BC333 /* TableDump.m */, 17E641540EF01EF6001BC333 /* TableSource.h */, 17E641550EF01EF6001BC333 /* TableSource.m */, + 17E641480EF01EF6001BC333 /* CustomQuery.h */, + 17E641490EF01EF6001BC333 /* CustomQuery.m */, + 584143DE0F9A2E9B00A34B47 /* SPExportController.h */, + 584143DF0F9A2E9B00A34B47 /* SPExportController.m */, + 17E6414C0EF01EF6001BC333 /* SPGrowlController.h */, + 17E6414D0EF01EF6001BC333 /* SPGrowlController.m */, 58FEF57C0F3B4E9700518E8E /* SPTableData.h */, 58FEF57D0F3B4E9700518E8E /* SPTableData.m */, 170088CC0F5870E200DD6B51 /* SPQueryConsole.h */, 170088CD0F5870E200DD6B51 /* SPQueryConsole.m */, + 584143E20F9A2EF800A34B47 /* SPPreferenceController.h */, + 584143E30F9A2EF800A34B47 /* SPPreferenceController.m */, ); name = Controllers; sourceTree = ""; @@ -422,6 +450,8 @@ 17E641610EF01F15001BC333 /* TablesList.m */, 17E641620EF01F15001BC333 /* TableStatus.h */, 17E641630EF01F15001BC333 /* TableStatus.m */, + 584143E50F9A2F5500A34B47 /* SPConsoleMessage.h */, + 584143E60F9A2F5500A34B47 /* SPConsoleMessage.m */, ); name = Model; sourceTree = ""; @@ -431,6 +461,8 @@ children = ( 17E641680EF01F37001BC333 /* ImageAndTextCell.h */, 17E641690EF01F37001BC333 /* ImageAndTextCell.m */, + 584143E80F9A2F7900A34B47 /* SPFavoriteTextFieldCell.h */, + 584143E90F9A2F7900A34B47 /* SPFavoriteTextFieldCell.m */, 17E6416A0EF01F37001BC333 /* TableDocumentSplitView.h */, 17E6416B0EF01F37001BC333 /* TableDocumentSplitView.m */, ); @@ -440,13 +472,12 @@ 17E6416E0EF01F3B001BC333 /* Other */ = { isa = PBXGroup; children = ( - 1789343A0F30C1DD0097539A /* SPStringAdditions.h */, - 1789343B0F30C1DD0097539A /* SPStringAdditions.m */, 17E6416F0EF01F4C001BC333 /* Keychain */, 17E641700EF01F52001BC333 /* MCPKit */, 58FEF15E0F23D60A00518E8E /* Parsing */, 17E641710EF01F5C001BC333 /* GUI */, 17E641720EF01F6B001BC333 /* SSHTunnel */, + 584144320F9A32D800A34B47 /* Category Additions */, ); name = Other; sourceTree = ""; @@ -482,6 +513,8 @@ 17E641810EF01FA8001BC333 /* CMTextView.m */, 58C56EF30F438E120035701E /* SPDataCellFormatter.h */, 58C56EF40F438E120035701E /* SPDataCellFormatter.m */, + 5841442F0F9A32B800A34B47 /* NoodleLineNumberView.h */, + 584144300F9A32B800A34B47 /* NoodleLineNumberView.m */, ); name = GUI; sourceTree = ""; @@ -498,64 +531,56 @@ 17E6418B0EF01FF7001BC333 /* Images */ = { isa = PBXGroup; children = ( - 17E6418C0EF02036001BC333 /* appicon.icns */, - B5E2C5F90F2353B5007446E0 /* TablePropertyIcon.png */, - 5838B7FA0F6C575D00F3863A /* button_add.tiff */, - 5838B7FB0F6C575D00F3863A /* button_left.tiff */, - 5838B7FC0F6C575D00F3863A /* button_progress_cancel.tiff */, - 5838B7FD0F6C575D00F3863A /* button_right.tiff */, - 5838B7FE0F6C575D00F3863A /* button_subtract.tiff */, - 17E6418D0EF02036001BC333 /* clearconsole.tiff */, - 17E6418F0EF02036001BC333 /* columnadd.tiff */, - 17E641900EF02036001BC333 /* columncopy.tiff */, - 17E641910EF02036001BC333 /* columndelete.tiff */, - 17E641920EF02036001BC333 /* columnrefresh.tiff */, - 17E641930EF02036001BC333 /* connectionadd.tiff */, - 17E641940EF02036001BC333 /* connectioncopy.tiff */, - 17E641950EF02036001BC333 /* connectiondelete.tiff */, - 17E641960EF02036001BC333 /* connectionrefresh.tiff */, - 17E641970EF02036001BC333 /* createtablesyntax.tiff */, - 17E641980EF02036001BC333 /* databases.tiff */, - 17E641990EF02036001BC333 /* dbadd.tiff */, - 17E6419A0EF02036001BC333 /* dbdelete.tiff */, - 17E6419B0EF02036001BC333 /* dbrefresh.tiff */, - 17E6419C0EF02036001BC333 /* flushprivileges.tiff */, - 17E6419D0EF02036001BC333 /* grabber-horizontal.png */, - 17E6419E0EF02036001BC333 /* grabber-vertical.png */, - 17E6419F0EF02036001BC333 /* hideconsole.tiff */, - 17E641A00EF02036001BC333 /* indexadd.tiff */, - 17E641A10EF02036001BC333 /* indexdelete.tiff */, - 17E641A20EF02036001BC333 /* logo-48.png */, - 17E641A30EF02036001BC333 /* optimizetable.tiff */, - 17E641A40EF02036001BC333 /* query.tiff */, - 17E641A50EF02036001BC333 /* queryadd.tiff */, - 17E641A60EF02036001BC333 /* querycopy.tiff */, - 17E641A70EF02036001BC333 /* querydelete.tiff */, - 17E641A80EF02036001BC333 /* queryrefresh.tiff */, - 17E641A90EF02036001BC333 /* reload.tiff */, - 17E641AA0EF02036001BC333 /* rowadd.tiff */, - 17E641AB0EF02036001BC333 /* rowcopy.tiff */, - 17E641AC0EF02036001BC333 /* rowdelete.tiff */, - 17E641AD0EF02036001BC333 /* rowrefresh.tiff */, - 17E641AE0EF02036001BC333 /* selectall.tiff */, - 17E641AF0EF02036001BC333 /* selectnone.tiff */, - 17E641B00EF02036001BC333 /* sheettotable.tiff */, - 17E641B10EF02036001BC333 /* showconsole.tiff */, - 17E641B20EF02036001BC333 /* showvariables.tiff */, - 17E641B30EF02036001BC333 /* spyglass.tiff */, - 17E641B40EF02036001BC333 /* table-small.tif */, - 584F5F8E0F50ACD800036517 /* table-view-small.tif */, - 17E641B50EF02036001BC333 /* tableadd.tiff */, - 17E641B60EF02036001BC333 /* tablecopy.tiff */, - 17E641B70EF02036001BC333 /* tabledelete.tiff */, - 17E641B80EF02036001BC333 /* tablerefresh.tiff */, - 17E641B90EF02036001BC333 /* tables.tiff */, - 17E641BA0EF02036001BC333 /* tabletosheet.tiff */, - 17E641BB0EF02036001BC333 /* textencode.tiff */, - 17E641BC0EF02036001BC333 /* toolbar-switch-to-browse.png */, - 17E641BD0EF02036001BC333 /* toolbar-switch-to-sql.png */, - 17E641BE0EF02036001BC333 /* toolbar-switch-to-structure.png */, - 17E641BF0EF02036001BC333 /* toolbar-switch-to-table-info.png */, + 584144B30F9A40BB00A34B47 /* appicon.icns */, + 584144B40F9A40BB00A34B47 /* button_action.tiff */, + 584144B50F9A40BB00A34B47 /* button_add.tiff */, + 584144B60F9A40BB00A34B47 /* button_bar_handle.tiff */, + 584144B70F9A40BB00A34B47 /* button_bar_spacer.tiff */, + 584144B80F9A40BB00A34B47 /* button_duplicate.tiff */, + 584144B90F9A40BB00A34B47 /* button_edit_mode_selected.tiff */, + 584144BA0F9A40BB00A34B47 /* button_edit_mode.tiff */, + 584144BB0F9A40BB00A34B47 /* button_edit.tiff */, + 584144BC0F9A40BB00A34B47 /* button_info_pane_hide.tiff */, + 584144BD0F9A40BB00A34B47 /* button_info_pane_show.tiff */, + 584144BE0F9A40BB00A34B47 /* button_left.tiff */, + 584144BF0F9A40BB00A34B47 /* button_progress_cancel.tiff */, + 584144C00F9A40BB00A34B47 /* button_refresh.tiff */, + 584144C10F9A40BB00A34B47 /* button_remove.tiff */, + 584144C20F9A40BB00A34B47 /* button_right.tiff */, + 584144C30F9A40BB00A34B47 /* clearconsole.tiff */, + 584144C40F9A40BB00A34B47 /* database.png */, + 584144C50F9A40BB00A34B47 /* dimple.tiff */, + 584144C60F9A40BB00A34B47 /* grabber-horizontal.png */, + 584144C70F9A40BB00A34B47 /* grabber-vertical.png */, + 584144C80F9A40BB00A34B47 /* hideconsole.tiff */, + 584144C90F9A40BB00A34B47 /* logo-48.png */, + 584144CA0F9A40BB00A34B47 /* selectall.tiff */, + 584144CB0F9A40BB00A34B47 /* selectnone.tiff */, + 584144CC0F9A40BB00A34B47 /* showconsole.tiff */, + 584144CD0F9A40BB00A34B47 /* showvariables.tiff */, + 584144CE0F9A40BB00A34B47 /* spyglass.tiff */, + 584144CF0F9A40BB00A34B47 /* table-property.png */, + 584144D00F9A40BB00A34B47 /* table-small.tiff */, + 584144D10F9A40BB00A34B47 /* table-view-small.tiff */, + 584144D20F9A40BB00A34B47 /* toolbar-export-csv.tiff */, + 584144D30F9A40BB00A34B47 /* toolbar-export-html.tiff */, + 584144D40F9A40BB00A34B47 /* toolbar-export-pdf.tiff */, + 584144D50F9A40BB00A34B47 /* toolbar-export-sql.tiff */, + 584144D60F9A40BB00A34B47 /* toolbar-export-xls.tiff */, + 584144D70F9A40BB00A34B47 /* toolbar-export-xml.tiff */, + 584144D80F9A40BB00A34B47 /* toolbar-preferences-advanced.tiff */, + 584144D90F9A40BB00A34B47 /* toolbar-preferences-autoupdate.tiff */, + 584144DA0F9A40BB00A34B47 /* toolbar-preferences-favorites.png */, + 584144DB0F9A40BB00A34B47 /* toolbar-preferences-general.tiff */, + 584144DC0F9A40BB00A34B47 /* toolbar-preferences-network.png */, + 584144DD0F9A40BB00A34B47 /* toolbar-preferences-notifications.tiff */, + 584144DE0F9A40BB00A34B47 /* toolbar-preferences-security.tiff */, + 584144DF0F9A40BB00A34B47 /* toolbar-preferences-tables.tiff */, + 584144E00F9A40BB00A34B47 /* toolbar-switch-to-browse.png */, + 584144E10F9A40BB00A34B47 /* toolbar-switch-to-procedures.tiff */, + 584144E20F9A40BB00A34B47 /* toolbar-switch-to-sql.png */, + 584144E30F9A40BB00A34B47 /* toolbar-switch-to-structure.png */, + 584144E40F9A40BB00A34B47 /* toolbar-switch-to-table-info.png */, ); path = Images; sourceTree = ""; @@ -563,9 +588,11 @@ 17E642050EF020A3001BC333 /* Interfaces */ = { isa = PBXGroup; children = ( - 58E2C6E40F6C4DD300569E7C /* ConnectionErrorDialog.nib */, - 58E2C6E60F6C4DD300569E7C /* DBView.nib */, - 58E2C6E80F6C4DD300569E7C /* MainMenu.nib */, + 584146030F9A5BFA00A34B47 /* ConnectionErrorDialog.nib */, + 584146080F9A5C8300A34B47 /* Console.nib */, + 5841460D0F9A651D00A34B47 /* DBView.nib */, + 584146110F9A6BAF00A34B47 /* MainMenu.nib */, + 584146210F9A9BCB00A34B47 /* Preferences.nib */, ); path = Interfaces; sourceTree = ""; @@ -600,11 +627,28 @@ name = Frameworks; sourceTree = ""; }; + 584144320F9A32D800A34B47 /* Category Additions */ = { + isa = PBXGroup; + children = ( + 584144530F9A34EC00A34B47 /* SPArrayAdditions.h */, + 584144540F9A34EC00A34B47 /* SPArrayAdditions.m */, + 1789343A0F30C1DD0097539A /* SPStringAdditions.h */, + 1789343B0F30C1DD0097539A /* SPStringAdditions.m */, + 584144550F9A34EC00A34B47 /* SPTextViewAdditions.h */, + 584144560F9A34EC00A34B47 /* SPTextViewAdditions.m */, + 584144570F9A34EC00A34B47 /* SPWindowAdditions.h */, + 584144580F9A34EC00A34B47 /* SPWindowAdditions.m */, + ); + name = "Category Additions"; + sourceTree = ""; + }; 58FEF15E0F23D60A00518E8E /* Parsing */ = { isa = PBXGroup; children = ( 58FEF16B0F23D66600518E8E /* SPSQLParser.h */, 58FEF16C0F23D66600518E8E /* SPSQLParser.m */, + 5841442C0F9A326200A34B47 /* SPEditorTokens.h */, + 5841442D0F9A326200A34B47 /* SPEditorTokens.l */, ); name = Parsing; sourceTree = ""; @@ -654,73 +698,70 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 17E641C00EF02036001BC333 /* appicon.icns in Resources */, - 5838B7FF0F6C575D00F3863A /* button_add.tiff in Resources */, - 5838B8000F6C575D00F3863A /* button_left.tiff in Resources */, - 5838B8010F6C575D00F3863A /* button_progress_cancel.tiff in Resources */, - 5838B8020F6C575D00F3863A /* button_right.tiff in Resources */, - 5838B8030F6C575D00F3863A /* button_subtract.tiff in Resources */, - 17E641C10EF02036001BC333 /* clearconsole.tiff in Resources */, - 17E641C30EF02036001BC333 /* columnadd.tiff in Resources */, - 17E641C40EF02036001BC333 /* columncopy.tiff in Resources */, - 17E641C50EF02036001BC333 /* columndelete.tiff in Resources */, - 17E641C60EF02036001BC333 /* columnrefresh.tiff in Resources */, - 17E641C70EF02036001BC333 /* connectionadd.tiff in Resources */, - 17E641C80EF02036001BC333 /* connectioncopy.tiff in Resources */, - 17E641C90EF02036001BC333 /* connectiondelete.tiff in Resources */, - 17E641CA0EF02036001BC333 /* connectionrefresh.tiff in Resources */, - 17E641CB0EF02036001BC333 /* createtablesyntax.tiff in Resources */, - 17E641CC0EF02036001BC333 /* databases.tiff in Resources */, - 17E641CD0EF02036001BC333 /* dbadd.tiff in Resources */, - 17E641CE0EF02036001BC333 /* dbdelete.tiff in Resources */, - 17E641CF0EF02036001BC333 /* dbrefresh.tiff in Resources */, - 17E641D00EF02036001BC333 /* flushprivileges.tiff in Resources */, - 17E641D10EF02036001BC333 /* grabber-horizontal.png in Resources */, - 17E641D20EF02036001BC333 /* grabber-vertical.png in Resources */, - 17E641D30EF02036001BC333 /* hideconsole.tiff in Resources */, - 17E641D40EF02036001BC333 /* indexadd.tiff in Resources */, - 17E641D50EF02036001BC333 /* indexdelete.tiff in Resources */, - 17E641D60EF02036001BC333 /* logo-48.png in Resources */, - 17E641D70EF02036001BC333 /* optimizetable.tiff in Resources */, - 17E641D80EF02036001BC333 /* query.tiff in Resources */, - 17E641D90EF02036001BC333 /* queryadd.tiff in Resources */, - 17E641DA0EF02036001BC333 /* querycopy.tiff in Resources */, - 17E641DB0EF02036001BC333 /* querydelete.tiff in Resources */, - 17E641DC0EF02036001BC333 /* queryrefresh.tiff in Resources */, - 17E641DD0EF02036001BC333 /* reload.tiff in Resources */, - 17E641DE0EF02036001BC333 /* rowadd.tiff in Resources */, - 17E641DF0EF02036001BC333 /* rowcopy.tiff in Resources */, - 17E641E00EF02036001BC333 /* rowdelete.tiff in Resources */, - 17E641E10EF02036001BC333 /* rowrefresh.tiff in Resources */, - 17E641E20EF02036001BC333 /* selectall.tiff in Resources */, - 17E641E30EF02036001BC333 /* selectnone.tiff in Resources */, - 17E641E40EF02036001BC333 /* sheettotable.tiff in Resources */, - 17E641E50EF02036001BC333 /* showconsole.tiff in Resources */, - 17E641E60EF02036001BC333 /* showvariables.tiff in Resources */, - 17E641E70EF02036001BC333 /* spyglass.tiff in Resources */, - 17E641E80EF02036001BC333 /* table-small.tif in Resources */, - 17E641E90EF02036001BC333 /* tableadd.tiff in Resources */, - 17E641EA0EF02036001BC333 /* tablecopy.tiff in Resources */, - 17E641EB0EF02036001BC333 /* tabledelete.tiff in Resources */, - 17E641EC0EF02036001BC333 /* tablerefresh.tiff in Resources */, - 17E641ED0EF02036001BC333 /* tables.tiff in Resources */, - 17E641EE0EF02036001BC333 /* tabletosheet.tiff in Resources */, - 17E641EF0EF02036001BC333 /* textencode.tiff in Resources */, - 17E641F00EF02036001BC333 /* toolbar-switch-to-browse.png in Resources */, - 17E641F10EF02036001BC333 /* toolbar-switch-to-sql.png in Resources */, - 17E641F20EF02036001BC333 /* toolbar-switch-to-structure.png in Resources */, - 17E641F30EF02036001BC333 /* toolbar-switch-to-table-info.png in Resources */, 17E641F90EF02088001BC333 /* askForPass.sh in Resources */, 17E641FA0EF02088001BC333 /* Growl Registration Ticket.growlRegDict in Resources */, 17E641FC0EF02088001BC333 /* sequel-pro.scriptSuite in Resources */, 17E641FD0EF02088001BC333 /* sequel-pro.scriptTerminology in Resources */, 17E6423B0EF0216C001BC333 /* Credits.rtf in Resources */, 17E6423E0EF0218B001BC333 /* InfoPlist.strings in Resources */, - B5E2C5FA0F2353B5007446E0 /* TablePropertyIcon.png in Resources */, - 584F5F8F0F50ACD800036517 /* table-view-small.tif in Resources */, - 58E2C6EA0F6C4DD300569E7C /* ConnectionErrorDialog.nib in Resources */, - 58E2C6EB0F6C4DD300569E7C /* DBView.nib in Resources */, - 58E2C6EC0F6C4DD300569E7C /* MainMenu.nib in Resources */, + 584144700F9A3C1200A34B47 /* PreferenceDefaults.plist in Resources */, + 584144720F9A3C6600A34B47 /* sparkle-public-key.pem in Resources */, + 584144740F9A3C8300A34B47 /* package-application.sh in Resources */, + 584144E50F9A40BB00A34B47 /* appicon.icns in Resources */, + 584144E60F9A40BB00A34B47 /* button_action.tiff in Resources */, + 584144E70F9A40BB00A34B47 /* button_add.tiff in Resources */, + 584144E80F9A40BB00A34B47 /* button_bar_handle.tiff in Resources */, + 584144E90F9A40BB00A34B47 /* button_bar_spacer.tiff in Resources */, + 584144EA0F9A40BB00A34B47 /* button_duplicate.tiff in Resources */, + 584144EB0F9A40BB00A34B47 /* button_edit_mode_selected.tiff in Resources */, + 584144EC0F9A40BB00A34B47 /* button_edit_mode.tiff in Resources */, + 584144ED0F9A40BB00A34B47 /* button_edit.tiff in Resources */, + 584144EE0F9A40BB00A34B47 /* button_info_pane_hide.tiff in Resources */, + 584144EF0F9A40BB00A34B47 /* button_info_pane_show.tiff in Resources */, + 584144F00F9A40BB00A34B47 /* button_left.tiff in Resources */, + 584144F10F9A40BB00A34B47 /* button_progress_cancel.tiff in Resources */, + 584144F20F9A40BB00A34B47 /* button_refresh.tiff in Resources */, + 584144F30F9A40BB00A34B47 /* button_remove.tiff in Resources */, + 584144F40F9A40BB00A34B47 /* button_right.tiff in Resources */, + 584144F50F9A40BB00A34B47 /* clearconsole.tiff in Resources */, + 584144F60F9A40BB00A34B47 /* database.png in Resources */, + 584144F70F9A40BB00A34B47 /* dimple.tiff in Resources */, + 584144F80F9A40BB00A34B47 /* grabber-horizontal.png in Resources */, + 584144F90F9A40BB00A34B47 /* grabber-vertical.png in Resources */, + 584144FA0F9A40BB00A34B47 /* hideconsole.tiff in Resources */, + 584144FB0F9A40BB00A34B47 /* logo-48.png in Resources */, + 584144FC0F9A40BB00A34B47 /* selectall.tiff in Resources */, + 584144FD0F9A40BB00A34B47 /* selectnone.tiff in Resources */, + 584144FE0F9A40BB00A34B47 /* showconsole.tiff in Resources */, + 584144FF0F9A40BB00A34B47 /* showvariables.tiff in Resources */, + 584145000F9A40BB00A34B47 /* spyglass.tiff in Resources */, + 584145010F9A40BB00A34B47 /* table-property.png in Resources */, + 584145020F9A40BB00A34B47 /* table-small.tiff in Resources */, + 584145030F9A40BB00A34B47 /* table-view-small.tiff in Resources */, + 584145040F9A40BB00A34B47 /* toolbar-export-csv.tiff in Resources */, + 584145050F9A40BB00A34B47 /* toolbar-export-html.tiff in Resources */, + 584145060F9A40BB00A34B47 /* toolbar-export-pdf.tiff in Resources */, + 584145070F9A40BB00A34B47 /* toolbar-export-sql.tiff in Resources */, + 584145080F9A40BB00A34B47 /* toolbar-export-xls.tiff in Resources */, + 584145090F9A40BB00A34B47 /* toolbar-export-xml.tiff in Resources */, + 5841450A0F9A40BB00A34B47 /* toolbar-preferences-advanced.tiff in Resources */, + 5841450B0F9A40BB00A34B47 /* toolbar-preferences-autoupdate.tiff in Resources */, + 5841450C0F9A40BB00A34B47 /* toolbar-preferences-favorites.png in Resources */, + 5841450D0F9A40BB00A34B47 /* toolbar-preferences-general.tiff in Resources */, + 5841450E0F9A40BB00A34B47 /* toolbar-preferences-network.png in Resources */, + 5841450F0F9A40BB00A34B47 /* toolbar-preferences-notifications.tiff in Resources */, + 584145100F9A40BB00A34B47 /* toolbar-preferences-security.tiff in Resources */, + 584145110F9A40BB00A34B47 /* toolbar-preferences-tables.tiff in Resources */, + 584145120F9A40BB00A34B47 /* toolbar-switch-to-browse.png in Resources */, + 584145130F9A40BB00A34B47 /* toolbar-switch-to-procedures.tiff in Resources */, + 584145140F9A40BB00A34B47 /* toolbar-switch-to-sql.png in Resources */, + 584145150F9A40BB00A34B47 /* toolbar-switch-to-structure.png in Resources */, + 584145160F9A40BB00A34B47 /* toolbar-switch-to-table-info.png in Resources */, + 584146810F9AAC6200A34B47 /* ConnectionErrorDialog.nib in Resources */, + 584146820F9AAC6200A34B47 /* Console.nib in Resources */, + 584146830F9AAC6200A34B47 /* DBView.nib in Resources */, + 584146840F9AAC6200A34B47 /* MainMenu.nib in Resources */, + 584146850F9AAC6200A34B47 /* Preferences.nib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -739,7 +780,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Add build/bundle version\n${SRCROOT}/Scripts/build-version.pl\n\n# Trim application if release build\nif [ \"$CONFIGURATION\" == 'Release' ]\nthen\n\t\"${SRCROOT}/Scripts/trim-application.sh\" -p \"${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}\" -a\nfi"; + shellScript = "# Add build/bundle version\n\"${SRCROOT}/Scripts/build-version.pl\"\n\n# Trim application if release or dist build\nif test \"$CONFIGURATION\" == 'Release' || test \"$CONFIGURATION\" == 'Distribution'\nthen\n\t\"${SRCROOT}/Scripts/trim-application.sh\" -p \"${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}\" -a\nfi\n\n# Perform distribution tasks on a dist build\nif [ \"$CONFIGURATION\" == 'Distribution' ]\nthen\n\t\"${SRCROOT}/Scripts/package-application.sh\" -p \"${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}\"\nfi"; }; /* End PBXShellScriptBuildPhase section */ @@ -773,6 +814,15 @@ 58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */, 58C56EF50F438E120035701E /* SPDataCellFormatter.m in Sources */, 170088CE0F5870E200DD6B51 /* SPQueryConsole.m in Sources */, + 584143E00F9A2E9B00A34B47 /* SPExportController.m in Sources */, + 584143E40F9A2EF800A34B47 /* SPPreferenceController.m in Sources */, + 584143E70F9A2F5500A34B47 /* SPConsoleMessage.m in Sources */, + 584143EA0F9A2F7900A34B47 /* SPFavoriteTextFieldCell.m in Sources */, + 5841442E0F9A326200A34B47 /* SPEditorTokens.l in Sources */, + 584144310F9A32B800A34B47 /* NoodleLineNumberView.m in Sources */, + 584144590F9A34EC00A34B47 /* SPArrayAdditions.m in Sources */, + 5841445A0F9A34EC00A34B47 /* SPTextViewAdditions.m in Sources */, + 5841445B0F9A34EC00A34B47 /* SPWindowAdditions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -795,33 +845,93 @@ name = InfoPlist.strings; sourceTree = ""; }; - 58E2C6E40F6C4DD300569E7C /* ConnectionErrorDialog.nib */ = { + 584146030F9A5BFA00A34B47 /* ConnectionErrorDialog.nib */ = { isa = PBXVariantGroup; children = ( - 58E2C6E50F6C4DD300569E7C /* English */, + 584146040F9A5BFA00A34B47 /* English */, ); name = ConnectionErrorDialog.nib; sourceTree = ""; }; - 58E2C6E60F6C4DD300569E7C /* DBView.nib */ = { + 584146080F9A5C8300A34B47 /* Console.nib */ = { + isa = PBXVariantGroup; + children = ( + 584146090F9A5C8300A34B47 /* English */, + ); + name = Console.nib; + path = ..; + sourceTree = ""; + }; + 5841460D0F9A651D00A34B47 /* DBView.nib */ = { isa = PBXVariantGroup; children = ( - 58E2C6E70F6C4DD300569E7C /* English */, + 5841460E0F9A651D00A34B47 /* English */, ); name = DBView.nib; sourceTree = ""; }; - 58E2C6E80F6C4DD300569E7C /* MainMenu.nib */ = { + 584146110F9A6BAF00A34B47 /* MainMenu.nib */ = { isa = PBXVariantGroup; children = ( - 58E2C6E90F6C4DD300569E7C /* English */, + 584146120F9A6BAF00A34B47 /* English */, ); name = MainMenu.nib; sourceTree = ""; }; + 584146210F9A9BCB00A34B47 /* Preferences.nib */ = { + isa = PBXVariantGroup; + children = ( + 584146220F9A9BCB00A34B47 /* English */, + ); + name = Preferences.nib; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 584144750F9A3CE100A34B47 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = macosx10.4; + }; + name = Distribution; + }; + 584144760F9A3CE100A34B47 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", + "$(inherited)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_2)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_3)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_4)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_5)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_6)", + "\"$(SRCROOT)\"", + "\"$(SRCROOT)/Frameworks\"", + ); + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\\\"$(SRCROOT)/Frameworks\\\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_2 = "\\\"$(SRCROOT)\\\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_3 = "\\\"$(SRCROOT)/Frameworks\\\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_4 = "\\\"$(SRCROOT)\\\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_5 = "\\\"$(SRCROOT)/Frameworks\\\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_6 = "\\\"$(SRCROOT)/Frameworks\\\""; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Source/sequel-pro_Prefix.pch"; + INFOPLIST_FILE = Resources/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = "Sequel Pro"; + }; + name = Distribution; + }; C05733C808A9546B00998B17 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -834,6 +944,8 @@ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_3)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_4)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_5)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_6)", + "\"$(SRCROOT)\"", "\"$(SRCROOT)/Frameworks\"", ); FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\\\"$(SRCROOT)/Frameworks\\\""; @@ -841,6 +953,7 @@ FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_3 = "\\\"$(SRCROOT)/Frameworks\\\""; FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_4 = "\\\"$(SRCROOT)\\\""; FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_5 = "\\\"$(SRCROOT)/Frameworks\\\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_6 = "\\\"$(SRCROOT)/Frameworks\\\""; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; @@ -865,6 +978,8 @@ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_3)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_4)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_5)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_6)", + "\"$(SRCROOT)\"", "\"$(SRCROOT)/Frameworks\"", ); FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\\\"$(SRCROOT)/Frameworks\\\""; @@ -872,6 +987,7 @@ FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_3 = "\\\"$(SRCROOT)/Frameworks\\\""; FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_4 = "\\\"$(SRCROOT)\\\""; FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_5 = "\\\"$(SRCROOT)/Frameworks\\\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_6 = "\\\"$(SRCROOT)/Frameworks\\\""; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Source/sequel-pro_Prefix.pch"; @@ -916,6 +1032,7 @@ buildConfigurations = ( C05733C808A9546B00998B17 /* Debug */, C05733C908A9546B00998B17 /* Release */, + 584144760F9A3CE100A34B47 /* Distribution */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -925,6 +1042,7 @@ buildConfigurations = ( C05733CC08A9546B00998B17 /* Debug */, C05733CD08A9546B00998B17 /* Release */, + 584144750F9A3CE100A34B47 /* Distribution */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; -- cgit v1.2.3