# # # patch "guitone.pro" # from [ed7cd2d7b369b4fe918668e2d94b57090c962c8b] # to [c255b8e3fe24ff5499440754f8e8acab3f651a8a] # # patch "res/forms/main_window.ui" # from [47bc6a1094496073f1befd61a20d5b2fed5f5f39] # to [6c5b7e9f7df0fc48722a97cf9e48cbc51ea46e2d] # # patch "src/Guitone.cpp" # from [c9a9565124a9a03a2d6d61b51f0a59650bcaaaf7] # to [53f0f6999f12e2ee3ffb89b6f74acc0dbdcfc753] # # patch "src/Guitone.h" # from [f56c7a540e0185fa8ef7008a7b35d2af4376bbb7] # to [881dffddd5bdd9999f2ed36ecdcaa019de153cb4] # # patch "src/util/CocoaUtil.h" # from [023ccba6801d260542c9311a01e3ca39e20b69c5] # to [8424a0c7f877f0167271bb2386fd00472a32f551] # # patch "src/util/CocoaUtil.mm" # from [122f1726e6d7f1289c48a51074063ebb753bf26e] # to [af7058410a39e0fa1f7fdb9d1ff5019261fd0960] # # patch "src/view/MainWindow.cpp" # from [1e0d6b1c8dc5305f78a6e72f8dae9cf118ab5866] # to [d90f84228175d6b63f614248e511611bcf167cf3] # # patch "src/view/MainWindow.h" # from [b662cc90c17734984e6a62fa323d2168def713ba] # to [627db714e8b3f00acd00cd164015dffc4c3f3075] # ============================================================ --- guitone.pro ed7cd2d7b369b4fe918668e2d94b57090c962c8b +++ guitone.pro c255b8e3fe24ff5499440754f8e8acab3f651a8a @@ -183,10 +183,10 @@ macx { macx { # add sources for Sparkle HEADERS += src/util/CocoaUtil.h - OBJECTIVE_SOURCES += src/util/CocoaUtil.mm + SOURCES += src/util/CocoaUtil.mm - # add the Sparkle framework - QMAKE_LFLAGS += -framework Sparkle + # add the Sparkle and the Carbon framework + QMAKE_LFLAGS += -framework Sparkle -framework Carbon # copy i18n resources into the final app bundle and # put the current version number into Info.plist ============================================================ --- res/forms/main_window.ui 47bc6a1094496073f1befd61a20d5b2fed5f5f39 +++ res/forms/main_window.ui 6c5b7e9f7df0fc48722a97cf9e48cbc51ea46e2d @@ -26,7 +26,7 @@ - 0 + 1 @@ -90,29 +90,6 @@ - - - - 0 - - - 0 - - - - - Currently there is no workspace or database loaded. - -To open a workspace, go to File > Open workspace -or File > Open Database for a database respectively. - - - Qt::AlignCenter - - - - - @@ -476,9 +453,9 @@ or File > Open Database for a database r - AttributesView - QTreeView -
../AttributesView.h
+ Splitter + QSplitter +
../Splitter.h
InventoryView @@ -486,9 +463,9 @@ or File > Open Database for a database r
../InventoryView.h
- Splitter - QSplitter -
../Splitter.h
+ AttributesView + QTreeView +
../AttributesView.h
============================================================ --- src/Guitone.cpp c9a9565124a9a03a2d6d61b51f0a59650bcaaaf7 +++ src/Guitone.cpp 53f0f6999f12e2ee3ffb89b6f74acc0dbdcfc753 @@ -24,14 +24,92 @@ #include "Preferences.h" #include "Settings.h" -#ifdef Q_WS_MAC +#ifdef Q_WS_MACX #include "CocoaUtil.h" +#include #endif #include #include #include +#include +#include +#ifdef Q_WS_MACX +static OSErr OpenDocumentAE(const AppleEvent * evt, AppleEvent *, long) +{ + AEDescList docList; + DescType retType; + AEKeyword keywd; + FSRef fsref; + long count, size; + + AEGetParamDesc(evt, keyDirectObject, typeAEList, &docList); + AECountItems(&docList, &count); + + OSErr err; + + for (int i = 1; i <= count; i++) + { + err = AEGetNthPtr(&docList, i, typeFSRef, &keywd, + &retType, (Ptr)&fsref, sizeof(fsref), &size); + + if (err != noErr) + { + W("Couldn't retrieve FSRef."); + continue; + } + + QString path = CocoaUtil::FSRefToPath(fsref); + if (path.isEmpty()) + { + W("Couldn't convert FSRef to path."); + continue; + } + + D(QString("Acquired new path: %1").arg(path)); + APP->loadFromPath(path); + } + AEDisposeDesc(&docList); + + return 0; +} + +bool Guitone::macEventFilter(EventHandlerCallRef caller, EventRef event) +{ + // + // taken from + // http://developer.apple.com/documentation/AppleScript/Conceptual/ + // AppleEvents/dispatch_aes_aepg/chapter_4_section_3.html + // + Boolean release = false; + EventRecord eventRecord; + OSErr ignoredErr; + + // Events of type kEventAppleEvent must be removed from the queue + // before being passed to AEProcessAppleEvent. + if (IsEventInQueue(GetMainEventQueue(), event)) + { + // RemoveEventFromQueue will release the event, which will + // destroy it if we don't retain it first. + RetainEvent(event); + release = true; + RemoveEventFromQueue(GetMainEventQueue(), event); + } + + // Convert the event ref to the type AEProcessAppleEvent expects. + ConvertEventRefToEventRecord(event, &eventRecord); + ignoredErr = AEProcessAppleEvent(&eventRecord); + + if (release) + ReleaseEvent(event); + + // This Carbon event has been handled, even if no AppleEvent handlers + // were installed for the Apple event. + return noErr; +} +#endif + Guitone::Guitone(int argc, char** argv) : QApplication(argc, argv), updateDialog(0) { @@ -40,14 +118,27 @@ Guitone::Guitone(int argc, char** argv) setOrganizationName("Thomas Keller"); setOrganizationDomain("thomaskeller.biz"); setApplicationName("guitone"); + +#ifdef Q_WS_MACX + // install apple event handler to open documents + OSErr err = AEInstallEventHandler( + kCoreEventClass, kAEOpenDocuments, + NewAEEventHandlerUPP(OpenDocumentAE), 0, false + ); + require_noerr(err, error_installing_event_handler); + return; + +error_installing_event_handler: + C("Could not install OpenDocumentAE event handler"); +#endif } bool Guitone::init() { if (Settings::getBool("CheckForUpdates", true)) { -#ifdef Q_WS_MAC - //CocoaUtil::initialize(); +#ifdef Q_WS_MACX + //CocoaUtil::initialize(); #else updateDialog = new ApplicationUpdate(NULL); if (updateDialog->updateAvailable()) @@ -58,21 +149,40 @@ bool Guitone::init() #endif } - MainWindow * mainWnd = addWindow(); - if (!addMonotoneInstance(mainWnd)) + QStringList args = arguments(); + bool somethingLoaded = false; + + for (int i=1, j=args.size(); iloadRecent(); + // if no command line arguments have been given or + // they have been invalid or anything else went wrong, + // try to load the recently used workspace/database + if (!somethingLoaded) + { + QStringList workspaces = Settings::getItemList("RecentWorkspaceList"); + somethingLoaded = workspaces.size() > 0 && loadWorkspace(workspaces.at(0)); + } - // trigger the setting of the correct window title in the window menu - emit windowListChanged(); + if (!somethingLoaded) + { + QStringList databases = Settings::getItemList("RecentDatabaseList"); + somethingLoaded = databases.size() > 0 && loadDatabase(databases.at(0)); + } - mainWnd->show(); - return true; + // if still nothing is loaded, prompt the user to load a workspace + if (!somethingLoaded) + { + QString workspaceDir = + QFileDialog::getExistingDirectory(0, tr("Select your workspace...")); + somethingLoaded = !workspaceDir.isEmpty() && loadWorkspace(workspaceDir); + } + + // if nothing could be loaded (= no window could be created), + // let the app close itself + return somethingLoaded; } Guitone::~Guitone() @@ -82,13 +192,32 @@ Guitone::~Guitone() if (updateDialog) delete updateDialog; } -void Guitone::loadWorkspace(const QString & path) +bool Guitone::loadFromPath(const QString & path) { + QFileInfo fileInfo(path); + if (!fileInfo.exists()) + { + W(QString("Non-existant file/folder %1").arg(path)); + return false; + } + + if (fileInfo.isDir()) + { + return loadWorkspace(path); + } + + return loadDatabase(path); +} + +bool Guitone::loadWorkspace(const QString & path) +{ + QMutexLocker locker(&mutex); + MainWindow * wnd = addWindow(); if (!addMonotoneInstance(wnd) || !wnd->doLoadWorkspace(path)) { removeWindow(wnd); - return; + return false; } // now that the workspace is loaded, the window should have gotten a @@ -96,25 +225,33 @@ void Guitone::loadWorkspace(const QStrin emit windowListChanged(); wnd->show(); + + return true; } -void Guitone::loadDatabase(const QString & path) +bool Guitone::loadDatabase(const QString & path) { + QMutexLocker locker(&mutex); + MainWindow * wnd = addWindow(); if (!addMonotoneInstance(wnd) || !wnd->doLoadDatabase(path)) { removeWindow(wnd); - return; + return false; } // now that the database is loaded, the window should have gotten a // reasonable name emit windowListChanged(); wnd->show(); + + return true; } void Guitone::windowClosed(MainWindow * wnd) { + QMutexLocker locker(&mutex); + removeMonotoneInstance(wnd); removeWindow(wnd); if (openWindows.size() == 0) quit(); @@ -122,8 +259,6 @@ MainWindow * Guitone::addWindow() MainWindow * Guitone::addWindow() { - QMutexLocker locker(&lock); - MainWindow * wnd = new MainWindow(); connect( @@ -177,13 +312,17 @@ MainWindow * Guitone::addWindow() openWindows.append(wnd); + D(QString("Added window %1").arg((int)wnd)); + return wnd; } void Guitone::removeWindow(MainWindow * wnd) { - QMutexLocker locker(&lock); + Q_ASSERT(wnd); + D(QString("Removing window %1").arg((int)wnd)); + int i = openWindows.indexOf(wnd); if (i == -1) return; openWindows.removeAt(i); @@ -269,6 +408,8 @@ bool Guitone::addMonotoneInstance(MainWi bool Guitone::addMonotoneInstance(MainWindow * wnd) { + Q_ASSERT(wnd); + Monotone * mtn = new Monotone(wnd); // check the current monotone version and prompt the user @@ -300,15 +441,21 @@ bool Guitone::addMonotoneInstance(MainWi monotoneInstances.insert(wnd, mtn); + D(QString("Added monotone instance %1 for %2").arg((int)mtn).arg((int)wnd)); + return true; } bool Guitone::removeMonotoneInstance(MainWindow * wnd) { + Q_ASSERT(wnd); + if (!monotoneInstances.contains(wnd)) return false; Monotone * mtn = monotoneInstances.value(wnd); + D(QString("Removing monotone instance %1 for %2").arg((int)mtn).arg((int)wnd)); + delete mtn; monotoneInstances.remove(wnd); @@ -336,3 +483,15 @@ Monotone * Guitone::getMonotoneInstance( return monotoneInstances.value(wnd); } +void Guitone::lock() +{ + D("lock called"); + mutex.lock(); +} + +void Guitone::unlock() +{ + D("unlock called"); + mutex.unlock(); +} + ============================================================ --- src/Guitone.h f56c7a540e0185fa8ef7008a7b35d2af4376bbb7 +++ src/Guitone.h 881dffddd5bdd9999f2ed36ecdcaa019de153cb4 @@ -42,13 +42,17 @@ public: MainWindow * findMainWindow(QObject *); Monotone * getMonotoneInstance(QObject *); - + bool loadFromPath(const QString &); + + void lock(); + void unlock(); + signals: void windowListChanged(); private slots: - void loadWorkspace(const QString &); - void loadDatabase(const QString &); + bool loadWorkspace(const QString &); + bool loadDatabase(const QString &); void windowClosed(MainWindow *); void quit(); @@ -58,10 +62,14 @@ private: bool addMonotoneInstance(MainWindow *); bool removeMonotoneInstance(MainWindow *); + +#ifdef Q_WS_MACX + bool macEventFilter(EventHandlerCallRef, EventRef); +#endif QList openWindows; QMap monotoneInstances; - QMutex lock; + QMutex mutex; ApplicationUpdate * updateDialog; }; ============================================================ --- src/util/CocoaUtil.h 023ccba6801d260542c9311a01e3ca39e20b69c5 +++ src/util/CocoaUtil.h 8424a0c7f877f0167271bb2386fd00472a32f551 @@ -5,7 +5,8 @@ #ifndef COCOAUTIL_H #define COCOAUTIL_H -#include +#include +#include class SUUpdater; @@ -13,6 +14,7 @@ namespace CocoaUtil { void initialize(); void checkForUpdates(); + QString FSRefToPath(FSRef ref); }; #endif ============================================================ --- src/util/CocoaUtil.mm 122f1726e6d7f1289c48a51074063ebb753bf26e +++ src/util/CocoaUtil.mm af7058410a39e0fa1f7fdb9d1ff5019261fd0960 @@ -1,9 +1,10 @@ /* * taken from the Axel project (http://excalibur.inria.fr/), * licensed under GPL */ #include #include + #include "CocoaUtil.h" void CocoaUtil::initialize() @@ -18,3 +19,16 @@ void CocoaUtil::checkForUpdates() SUUpdater * updater = [SUUpdater alloc]; [updater checkForUpdates:nil]; } + +QString CocoaUtil::FSRefToPath(FSRef fsref) +{ + CFURLRef url = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsref); + if (!url) + { + return QString(); + } + NSString * pathName = (NSString*)CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + [pathName autorelease]; + CFRelease(url); + return QString::fromUtf8([pathName UTF8String]); +} ============================================================ --- src/view/MainWindow.cpp 1e0d6b1c8dc5305f78a6e72f8dae9cf118ab5866 +++ src/view/MainWindow.cpp d90f84228175d6b63f614248e511611bcf167cf3 @@ -55,7 +55,6 @@ MainWindow::MainWindow() MainWindow::MainWindow() : QMainWindow() { - mode = None; setAttribute(Qt::WA_MacMetalStyle); // ensure that the shortcut keys are properly recognized by linguist @@ -125,17 +124,6 @@ MainWindow::MainWindow() this, SLOT(doUpdatePreviousDatabasesMenu()) ); - connect( - this, SIGNAL(loadWorkspace(const QString &)), - this, SLOT(checkIfWindowClose()) - ); - - connect( - this, SIGNAL(loadDatabase(const QString &)), - this, SLOT(checkIfWindowClose()) - ); - - doUpdatePreviousWorkspacesMenu(); doUpdatePreviousDatabasesMenu(); @@ -165,26 +153,6 @@ MainWindow::~MainWindow() delete proxyModelFileList; } -// try to load the most recent workspace or database, if there are any -// if everything fails, load nothing and hide the appropriate menus -void MainWindow::loadRecent() -{ - QStringList workspaces = Settings::getItemList("RecentWorkspaceList"); - bool something_loaded = workspaces.size() > 0 && doLoadWorkspace(workspaces[0]); - - if (!something_loaded) - { - QStringList databases = Settings::getItemList("RecentDatabaseList"); - something_loaded = databases.size() > 0 && doLoadDatabase(databases[0]); - } - - if (!something_loaded) - { - switchMode(None); - } - -} - void MainWindow::on_actionOpen_Workspace_triggered() { QString fn = QFileDialog::getExistingDirectory(0, tr("Select your workspace...")); @@ -299,9 +267,6 @@ void MainWindow::switchMode(Mode m) void MainWindow::switchMode(Mode m) { - // FIXME: by switching the current mode we're also changing the window - // title - we should find an easy way to tell all other windows about - // that so a user can still recognize the entry properly mode = m; windowMode->setCurrentIndex(mode); restoreGeometry(Settings::getWindowGeometry("MainWindow_mode" + mode)); @@ -311,16 +276,18 @@ void MainWindow::switchMode(Mode m) // ensure somewhat that new windows do not overdraw current ones // by adding a little x/y offset the original position of the window // opened before this window - if (openWindows.size() > 1) + int curIdx = openWindows.indexOf(this); + if (curIdx > 0) { - MainWindow * prevWnd = openWindows.at(openWindows.indexOf(this) - 1); + MainWindow * prevWnd = openWindows.at(curIdx - 1); Q_ASSERT(prevWnd); + QDesktopWidget * desk = APP->desktop(); if (desk->numScreens() > 1) { - qWarning("Guitone::addWindow: No support for window cascading " - "on systems with more than one monitor yet."); + W("No support for window cascading on systems with " + "more than one monitor yet."); } else { @@ -370,11 +337,7 @@ void MainWindow::switchMode(Mode m) } else { - menuView->menuAction()->setVisible(false); - menuWorkspace->menuAction()->setVisible(false); - menuDatabase->menuAction()->setVisible(false); - - setWindowTitle(tr("No workspace or database loaded - guitone")); + Q_ASSERT(false); } emit modeChanged(mode); @@ -663,14 +626,6 @@ void MainWindow::invalidWorkspaceFormat( emit updatePreviousWorkspacesMenu(); - switchMode(None); + close(); } -// this slot is called whenever a new workspace or database is loaded -// if the current window is in "None" mode, we can safely close it because -// it cannot be switched to something else anymore anyways -void MainWindow::checkIfWindowClose() -{ - if (mode == None) QTimer::singleShot(0, this, SLOT(close())); -} - ============================================================ --- src/view/MainWindow.h b662cc90c17734984e6a62fa323d2168def713ba +++ src/view/MainWindow.h 627db714e8b3f00acd00cd164015dffc4c3f3075 @@ -36,12 +36,10 @@ public: Q_OBJECT public: - enum Mode { Workspace = 0, Database, None }; + enum Mode { Workspace = 0, Database}; MainWindow(); ~MainWindow(); - void loadRecent(); - void switchMode(Mode); - + bool doLoadWorkspace(QString); bool doLoadDatabase(QString); @@ -80,11 +78,11 @@ private slots: void updateWindowList(); void activateOtherWindow(); void invalidWorkspaceFormat(const QString &); - void checkIfWindowClose(); private: void closeEvent(QCloseEvent *); - + void switchMode(Mode); + Inventory *invModel; Attributes *attrModel; InventoryProxyModel *proxyModelFolderTree;