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