PageRenderTime 64ms CodeModel.GetById 20ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

/guitone-1.0rc5/src/GuitoneStandalone.cpp

#
C++ | 360 lines | 252 code | 52 blank | 56 comment | 26 complexity | 4065a372bbe0842b1a7fb6410f629cbe MD5 | raw file
Possible License(s): GPL-3.0
  1/***************************************************************************
  2 *   Copyright (C) 2008 by Thomas Keller                                   *
  3 *   me@thomaskeller.biz                                                   *
  4 *                                                                         *
  5 *   This program is free software; you can redistribute it and/or modify  *
  6 *   it under the terms of the GNU General Public License as published by  *
  7 *   the Free Software Foundation, either version 3 of the License, or     *
  8 *   (at your option) any later version.                                   *
  9 *                                                                         *
 10 *   This program is distributed in the hope that it will be useful,       *
 11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 13 *   GNU General Public License for more details.                          *
 14 *                                                                         *
 15 *   You should have received a copy of the GNU General Public License     *
 16 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 17 ***************************************************************************/
 18
 19#include "GuitoneStandalone.h"
 20#include "WorkspaceWindow.h"
 21#include "DatabaseWindow.h"
 22#include "ServerWindow.h"
 23#include "Settings.h"
 24
 25#include <QMutexLocker>
 26#include <QMessageBox>
 27#include <QFileInfo>
 28#include <QFileDialog>
 29#include <QDesktopWidget>
 30#include <QFileOpenEvent>
 31
 32#ifdef Q_WS_MACX
 33#include <Carbon/Carbon.h>
 34#endif
 35
 36GuitoneStandalone::GuitoneStandalone(int & argc, char** argv)
 37  : GuitoneCore(argc, argv), openPrompt(0), somethingLoaded(false)
 38{
 39#ifdef Q_WS_MACX
 40     // this is kind of backwards, but there is no other way to do it:
 41     // to hide an application from the doc on Mac OS X, you have to
 42     // set LSUIElement to 1 in its Info.plist - this is done globally
 43     // for guitone so we don't get an application menu if we're running
 44     // in driver mode; however this also hides the app in standalone mode!
 45     // so, as a little trick we're doing what Qt does inside
 46     // qapplication_mac.cpp: we're explicitely foregrounding the
 47     // standalone version, and therefor ignoring the plist setting
 48     ProcessSerialNumber psn;
 49     if (GetCurrentProcess(&psn) == noErr)
 50     {
 51         OSStatus returnCode =
 52            TransformProcessType(&psn, kProcessTransformToForegroundApplication);
 53         if (returnCode != 0)
 54         {
 55             C(QString("Could not bring the application to front (error %1)")
 56               .arg(returnCode));
 57         }
 58     }
 59#endif
 60
 61    setQuitOnLastWindowClosed(false);
 62
 63    programArgs = arguments();
 64
 65    connect(
 66        this, SIGNAL(monotoneBinaryPathSet()),
 67        this, SLOT(tryLoadSomething()),
 68        Qt::QueuedConnection
 69    );
 70}
 71
 72GuitoneStandalone::~GuitoneStandalone()
 73{
 74    // wait until all windows have been closed
 75    while (openWindows.size() > 0)
 76        QCoreApplication::processEvents();
 77}
 78
 79void GuitoneStandalone::tryLoadSomething()
 80{
 81    I(manager()->monotoneBinaryPathSet());
 82
 83    D("checking command line arguments and file open requests");
 84    for (int i=1, j=programArgs.size(); i<j; i++)
 85    {
 86        // we're ok if any of these arguments could be loaded
 87        somethingLoaded = somethingLoaded || loadFromString(programArgs.at(i));
 88    }
 89
 90    if (somethingLoaded) return;
 91
 92    // if nothing was requested, prompt the user to load a workspace or database
 93    openPrompt = new OpenPrompt(NULL);
 94
 95    connect(
 96        openPrompt, SIGNAL(loadFromHandle(const MonotoneHandlePtr &)),
 97        this, SLOT(loadFromHandle(const MonotoneHandlePtr &))
 98    );
 99
100    connect(
101        openPrompt, SIGNAL(loadFromString(const QString &)),
102        this, SLOT(loadFromString(const QString &))
103    );
104
105    connect(
106        openPrompt, SIGNAL(quitApp()),
107        this, SLOT(quit())
108    );
109
110    connect(
111        openPrompt, SIGNAL(quitApp()),
112        openPrompt, SLOT(deleteLater())
113    );
114
115    connect(
116        this, SIGNAL(updateRecentLists()),
117        openPrompt, SLOT(updateRecentLists())
118    );
119
120    connect(
121        this, SIGNAL(loadFromStringFailed(const QString &, const QString &)),
122        openPrompt, SLOT(loadFromStringFailed(const QString &, const QString &))
123    );
124
125    openPrompt->show();
126}
127
128// this code is borrowed and adapted from QDesigner - thanks to the Trolls!
129bool GuitoneStandalone::event(QEvent * ev)
130{
131    bool eaten = false;
132    switch (ev->type())
133    {
134        case QEvent::FileOpen:
135        {
136            QFileOpenEvent * fopev = reinterpret_cast<QFileOpenEvent *>(ev);
137
138            QString fileOrUri = fopev->file();
139            if (fileOrUri.isEmpty())
140            {
141                fileOrUri = QString(fopev->url().toEncoded());
142            }
143
144            if (!manager()->monotoneBinaryPathSet())
145            {
146                D("deferring FileOpen event since binary path is not yet set");
147                programArgs.push_back(fileOrUri);
148            }
149            else
150            {
151                loadFromString(fileOrUri);
152            }
153            eaten = true;
154            break;
155        }
156#ifdef Q_WS_MAC
157        // I'm not sure if this is a bug in Qt or a bug in my code, but
158        // if you click on "Quit" in the application menu in the dock,
159        // Qt receives a Close event on the application, not on the widget
160        // and completly ignores that. (Close events are only documented on
161        // widgets, _not_ the application!) Calling quit() here directly will
162        // at first close all windows (see our quit() implementation above)
163        // and then call quit() again, so we just close all windos here
164        // and, oh magic, even though quitOnLastWindowClose is set to false
165        // Qt magically ends the event loop as well!
166        case QEvent::Close:
167        {
168            eaten = closeAllWindows();
169        }
170#endif
171        default:
172            eaten = QCoreApplication::event(ev);
173            break;
174    }
175    return eaten;
176}
177
178void GuitoneStandalone::loadFromHandle(const MonotoneHandlePtr & handle)
179{
180    QMutexLocker locker(&lock);
181    MainWindow * wnd = 0;
182
183    I(!handle.isNull());
184
185    switch (handle->getType())
186    {
187        case MonotoneHandle::database_handle:
188            wnd = new DatabaseWindow();
189            Settings::addItemToList("RecentDatabaseList", handle->getData(), 5);
190            break;
191        case MonotoneHandle::workspace_handle:
192            wnd = new WorkspaceWindow();
193            Settings::addItemToList("RecentWorkspaceList", handle->getData(), 5);
194            break;
195        case MonotoneHandle::server_handle:
196            wnd = new ServerWindow();
197            Settings::addItemToList("RecentServerList", handle->getData(), 5);
198            break;
199        default:
200            I(false);
201    }
202
203    wnd->init();
204    wnd->load(handle);
205
206    openWindows.append(wnd);
207    ensureCascadedWindowPlacement(wnd);
208    wnd->show();
209
210    triggerUpdateWindowList();
211    emit updateRecentLists();
212
213    if (openPrompt)
214    {
215        openPrompt->hide();
216        // we delete the cruft and invalidate all the set-up connections
217        // once we have something loaded to avoid unwanted side effects
218        // calling delete here directly will fail because there are still
219        // some signals which might have to be delivered to the dialog
220        // before
221        openPrompt->deleteLater();
222        openPrompt = 0;
223    }
224}
225
226bool GuitoneStandalone::loadFromString(const QString & pathOrURI)
227{
228    MonotoneHandlePtr handle;
229    try
230    {
231        handle = MonotoneHandle::create(pathOrURI);
232
233        if (handle->getType() == MonotoneHandle::empty_handle)
234        {
235            throw new GuitoneException(tr("Source was empty"));
236        }
237    }
238    catch (GuitoneException & e)
239    {
240        Settings::removeItemFromList("RecentWorkspaceList", pathOrURI);
241        Settings::removeItemFromList("RecentDatabaseList", pathOrURI);
242        Settings::removeItemFromList("RecentServerList", pathOrURI);
243
244        emit updateRecentLists();
245
246        W(QString("loading of '%1' failed: %2").arg(pathOrURI).arg(e.what()));
247
248        emit loadFromStringFailed(pathOrURI, e.what());
249
250        return false;
251    }
252
253    loadFromHandle(handle);
254
255    return true;
256}
257
258void GuitoneStandalone::windowClosed(MainWindow * wnd)
259{
260    QMutexLocker locker(&lock);
261    int i = openWindows.indexOf(wnd);
262    I(i != -1);
263    openWindows.removeAt(i);
264    delete wnd;
265
266    if (openWindows.size() == 0)
267    {
268        quit();
269    }
270
271    triggerUpdateWindowList();
272}
273
274void GuitoneStandalone::triggerUpdateWindowList()
275{
276    QStringList list;
277    foreach (MainWindow * win, openWindows)
278    {
279        list.append(win->windowTitle());
280    }
281    emit updateWindowList(list);
282}
283
284bool GuitoneStandalone::closeAllWindows()
285{
286    // do we need to close any windows?
287    if (openWindows.size() > 0)
288    {
289        // let all open windows close themselves to allow them cleaning up
290        // stuff and saving settings
291        // note that if the last window is closed, quit() is called again,
292        // so we return here after closing all windows to avoid recursion
293        // problems
294        foreach (MainWindow * wnd, openWindows)
295        {
296            // QWidget::close() returns true if the widget could be closed,
297            // i.e. hasn't ignored the close event
298            if (!wnd->close()) return false;
299        }
300    }
301    return true;
302}
303
304void GuitoneStandalone::quit()
305{
306    if (!closeAllWindows()) return;
307    GuitoneCore::quit();
308}
309
310/*!
311    Ensures somewhat that new windows do not overdraw current ones
312    by adding a little x/y offset the original position of the window
313    opened before this window
314*/
315void GuitoneStandalone::ensureCascadedWindowPlacement(MainWindow * window)
316{
317    int curIdx = openWindows.indexOf(window);
318
319    if (curIdx > 0)
320    {
321        MainWindow * prevWnd = openWindows.at(curIdx - 1);
322        I(prevWnd);
323
324        QDesktopWidget * desk = QApplication::desktop();
325
326        int cascade = 20;
327        QRect geom = desk->availableGeometry(prevWnd);
328        int newX = prevWnd->x() + cascade;
329        int newY = prevWnd->y() + cascade;
330
331        if (newX + window->width() > geom.right() ||
332            newY + window->height() > geom.bottom())
333        {
334            newX = geom.x();
335            newY = geom.y();
336        }
337        window->move(newX, newY);
338    }
339}
340
341void GuitoneStandalone::activateWindow(int idx)
342{
343    if (idx < 0 || idx > openWindows.size() - 1)
344        return;
345
346    MainWindow * wnd = openWindows.at(idx);
347    wnd->activateWindow();
348    wnd->raise();
349}
350
351void GuitoneStandalone::raiseAllWindows()
352{
353    for (int i=0, j=openWindows.size(); i<j; i++)
354    {
355        MainWindow * wnd = openWindows.at(i);
356        wnd->activateWindow();
357        wnd->raise();
358    }
359}
360