owncloudgui.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. /*
  2. * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. * for more details.
  13. */
  14. #include "owncloudgui.h"
  15. #include "account.h"
  16. #include "accountmanager.h"
  17. #include "accountstate.h"
  18. #include "application.h"
  19. #include "callstatechecker.h"
  20. #include "emojimodel.h"
  21. #include "fileactivitylistmodel.h"
  22. #include "folderman.h"
  23. #include "guiutility.h"
  24. #include "logbrowser.h"
  25. #include "logger.h"
  26. #include "openfilemanager.h"
  27. #include "owncloudsetupwizard.h"
  28. #include "progressdispatcher.h"
  29. #include "settingsdialog.h"
  30. #include "theme.h"
  31. #include "wheelhandler.h"
  32. #include "filedetails/filedetails.h"
  33. #include "filedetails/shareemodel.h"
  34. #include "filedetails/sharemodel.h"
  35. #include "filedetails/sortedsharemodel.h"
  36. #include "tray/sortedactivitylistmodel.h"
  37. #include "tray/syncstatussummary.h"
  38. #include "tray/unifiedsearchresultslistmodel.h"
  39. #ifdef WITH_LIBCLOUDPROVIDERS
  40. #include "cloudproviders/cloudprovidermanager.h"
  41. #endif
  42. #include <QQmlApplicationEngine>
  43. #include <QDesktopServices>
  44. #include <QDir>
  45. #include <QMessageBox>
  46. #include <QSignalMapper>
  47. #ifdef WITH_LIBCLOUDPROVIDERS
  48. #include <QtDBus/QDBusConnection>
  49. #include <QtDBus/QDBusInterface>
  50. #endif
  51. #include <QQmlEngine>
  52. #include <QQmlComponent>
  53. #include <QQmlApplicationEngine>
  54. #include <QQuickItem>
  55. #include <QQmlContext>
  56. namespace OCC {
  57. Q_LOGGING_CATEGORY(lcOwnCloudGui, "com.nextcloud.owncloudgui")
  58. const char propertyAccountC[] = "oc_account";
  59. ownCloudGui::ownCloudGui(Application *parent)
  60. : QObject(parent)
  61. , _tray(nullptr)
  62. , _settingsDialog(nullptr)
  63. , _logBrowser(nullptr)
  64. #ifdef WITH_LIBCLOUDPROVIDERS
  65. , _bus(QDBusConnection::sessionBus())
  66. #endif
  67. , _app(parent)
  68. {
  69. _tray = Systray::instance();
  70. _tray->setTrayEngine(new QQmlApplicationEngine(this));
  71. // for the beginning, set the offline icon until the account was verified
  72. _tray->setIcon(Theme::instance()->folderOfflineIcon(/*systray?*/ true));
  73. _tray->show();
  74. connect(_tray.data(), &QSystemTrayIcon::activated,
  75. this, &ownCloudGui::slotTrayClicked);
  76. connect(_tray.data(), &Systray::openHelp,
  77. this, &ownCloudGui::slotHelp);
  78. connect(_tray.data(), &Systray::openAccountWizard,
  79. this, &ownCloudGui::slotNewAccountWizard);
  80. connect(_tray.data(), &Systray::openSettings,
  81. this, &ownCloudGui::slotShowSettings);
  82. connect(_tray.data(), &Systray::shutdown,
  83. this, &ownCloudGui::slotShutdown);
  84. ProgressDispatcher *pd = ProgressDispatcher::instance();
  85. connect(pd, &ProgressDispatcher::progressInfo, this,
  86. &ownCloudGui::slotUpdateProgress);
  87. FolderMan *folderMan = FolderMan::instance();
  88. connect(folderMan, &FolderMan::folderSyncStateChange,
  89. this, &ownCloudGui::slotSyncStateChange);
  90. connect(Logger::instance(), &Logger::guiLog,
  91. this, &ownCloudGui::slotShowTrayMessage);
  92. connect(Logger::instance(), &Logger::optionalGuiLog,
  93. this, &ownCloudGui::slotShowOptionalTrayMessage);
  94. connect(Logger::instance(), &Logger::guiMessage,
  95. this, &ownCloudGui::slotShowGuiMessage);
  96. qmlRegisterType<SyncStatusSummary>("com.nextcloud.desktopclient", 1, 0, "SyncStatusSummary");
  97. qmlRegisterType<EmojiModel>("com.nextcloud.desktopclient", 1, 0, "EmojiModel");
  98. qmlRegisterType<UserStatusSelectorModel>("com.nextcloud.desktopclient", 1, 0, "UserStatusSelectorModel");
  99. qmlRegisterType<ActivityListModel>("com.nextcloud.desktopclient", 1, 0, "ActivityListModel");
  100. qmlRegisterType<FileActivityListModel>("com.nextcloud.desktopclient", 1, 0, "FileActivityListModel");
  101. qmlRegisterType<SortedActivityListModel>("com.nextcloud.desktopclient", 1, 0, "SortedActivityListModel");
  102. qmlRegisterType<WheelHandler>("com.nextcloud.desktopclient", 1, 0, "WheelHandler");
  103. qmlRegisterType<CallStateChecker>("com.nextcloud.desktopclient", 1, 0, "CallStateChecker");
  104. qmlRegisterType<FileDetails>("com.nextcloud.desktopclient", 1, 0, "FileDetails");
  105. qmlRegisterType<ShareModel>("com.nextcloud.desktopclient", 1, 0, "ShareModel");
  106. qmlRegisterType<ShareeModel>("com.nextcloud.desktopclient", 1, 0, "ShareeModel");
  107. qmlRegisterType<SortedShareModel>("com.nextcloud.desktopclient", 1, 0, "SortedShareModel");
  108. qmlRegisterUncreatableType<UnifiedSearchResultsListModel>("com.nextcloud.desktopclient", 1, 0, "UnifiedSearchResultsListModel", "UnifiedSearchResultsListModel");
  109. qmlRegisterUncreatableType<UserStatus>("com.nextcloud.desktopclient", 1, 0, "UserStatus", "Access to Status enum");
  110. qmlRegisterUncreatableType<Sharee>("com.nextcloud.desktopclient", 1, 0, "Sharee", "Access to Type enum");
  111. qRegisterMetaTypeStreamOperators<Emoji>();
  112. qRegisterMetaType<ActivityListModel *>("ActivityListModel*");
  113. qRegisterMetaType<UnifiedSearchResultsListModel *>("UnifiedSearchResultsListModel*");
  114. qRegisterMetaType<UserStatus>("UserStatus");
  115. qRegisterMetaType<SharePtr>("SharePtr");
  116. qRegisterMetaType<ShareePtr>("ShareePtr");
  117. qRegisterMetaType<Sharee>("Sharee");
  118. qmlRegisterSingletonInstance("com.nextcloud.desktopclient", 1, 0, "UserModel", UserModel::instance());
  119. qmlRegisterSingletonInstance("com.nextcloud.desktopclient", 1, 0, "UserAppsModel", UserAppsModel::instance());
  120. qmlRegisterSingletonInstance("com.nextcloud.desktopclient", 1, 0, "Theme", Theme::instance());
  121. qmlRegisterSingletonInstance("com.nextcloud.desktopclient", 1, 0, "Systray", Systray::instance());
  122. }
  123. void ownCloudGui::createTray()
  124. {
  125. _tray->create();
  126. }
  127. #ifdef WITH_LIBCLOUDPROVIDERS
  128. void ownCloudGui::setupCloudProviders()
  129. {
  130. new CloudProviderManager(this);
  131. }
  132. bool ownCloudGui::cloudProviderApiAvailable()
  133. {
  134. if (!_bus.isConnected()) {
  135. return false;
  136. }
  137. QDBusInterface dbus_iface("org.freedesktop.CloudProviderManager", "/org/freedesktop/CloudProviderManager",
  138. "org.freedesktop.CloudProvider.Manager1", _bus);
  139. if (!dbus_iface.isValid()) {
  140. qCInfo(lcApplication) << "DBus interface unavailable";
  141. return false;
  142. }
  143. return true;
  144. }
  145. #endif
  146. // This should rather be in application.... or rather in ConfigFile?
  147. void ownCloudGui::slotOpenSettingsDialog()
  148. {
  149. // if account is set up, start the configuration wizard.
  150. if (!AccountManager::instance()->accounts().isEmpty()) {
  151. if (_settingsDialog.isNull() || QApplication::activeWindow() != _settingsDialog) {
  152. slotShowSettings();
  153. } else {
  154. _settingsDialog->close();
  155. }
  156. } else {
  157. qCInfo(lcApplication) << "No configured folders yet, starting setup wizard";
  158. slotNewAccountWizard();
  159. }
  160. }
  161. void ownCloudGui::slotOpenMainDialog()
  162. {
  163. _tray->showWindow();
  164. }
  165. void ownCloudGui::slotTrayClicked(QSystemTrayIcon::ActivationReason reason)
  166. {
  167. if (reason == QSystemTrayIcon::DoubleClick && UserModel::instance()->currentUser()->hasLocalFolder()) {
  168. UserModel::instance()->openCurrentAccountLocalFolder();
  169. } else if (reason == QSystemTrayIcon::Trigger) {
  170. if (OwncloudSetupWizard::bringWizardToFrontIfVisible()) {
  171. // brought wizard to front
  172. } else if (_tray->raiseDialogs()) {
  173. // Brings dialogs hidden by other apps to front, returns true if any raised
  174. } else if (_tray->isOpen()) {
  175. _tray->hideWindow();
  176. } else {
  177. if (AccountManager::instance()->accounts().isEmpty()) {
  178. this->slotOpenSettingsDialog();
  179. } else {
  180. _tray->showWindow();
  181. }
  182. }
  183. }
  184. // FIXME: Also make sure that any auto updater dialogue https://github.com/owncloud/client/issues/5613
  185. // or SSL error dialog also comes to front.
  186. }
  187. void ownCloudGui::slotSyncStateChange(Folder *folder)
  188. {
  189. slotComputeOverallSyncStatus();
  190. if (!folder) {
  191. return; // Valid, just a general GUI redraw was needed.
  192. }
  193. auto result = folder->syncResult();
  194. qCInfo(lcApplication) << "Sync state changed for folder " << folder->remoteUrl().toString() << ": " << result.statusString();
  195. if (result.status() == SyncResult::Success
  196. || result.status() == SyncResult::Problem
  197. || result.status() == SyncResult::SyncAbortRequested
  198. || result.status() == SyncResult::Error) {
  199. Logger::instance()->enterNextLogFile();
  200. }
  201. }
  202. void ownCloudGui::slotFoldersChanged()
  203. {
  204. slotComputeOverallSyncStatus();
  205. }
  206. void ownCloudGui::slotOpenPath(const QString &path)
  207. {
  208. showInFileManager(path);
  209. }
  210. void ownCloudGui::slotAccountStateChanged()
  211. {
  212. slotComputeOverallSyncStatus();
  213. }
  214. void ownCloudGui::slotTrayMessageIfServerUnsupported(Account *account)
  215. {
  216. if (account->serverVersionUnsupported()) {
  217. slotShowTrayMessage(
  218. tr("Unsupported Server Version"),
  219. tr("The server on account %1 runs an unsupported version %2. "
  220. "Using this client with unsupported server versions is untested and "
  221. "potentially dangerous. Proceed at your own risk.")
  222. .arg(account->displayName(), account->serverVersion()));
  223. }
  224. }
  225. void ownCloudGui::slotComputeOverallSyncStatus()
  226. {
  227. bool allSignedOut = true;
  228. bool allPaused = true;
  229. bool allDisconnected = true;
  230. QVector<AccountStatePtr> problemAccounts;
  231. auto setStatusText = [&](const QString &text) {
  232. // FIXME: So this doesn't do anything? Needs to be revisited
  233. Q_UNUSED(text)
  234. // Don't overwrite the status if we're currently syncing
  235. if (FolderMan::instance()->isAnySyncRunning())
  236. return;
  237. //_actionStatus->setText(text);
  238. };
  239. foreach (auto a, AccountManager::instance()->accounts()) {
  240. if (!a->isSignedOut()) {
  241. allSignedOut = false;
  242. }
  243. if (!a->isConnected()) {
  244. problemAccounts.append(a);
  245. } else {
  246. allDisconnected = false;
  247. }
  248. }
  249. foreach (Folder *f, FolderMan::instance()->map()) {
  250. if (!f->syncPaused()) {
  251. allPaused = false;
  252. }
  253. }
  254. if (!problemAccounts.empty()) {
  255. _tray->setIcon(Theme::instance()->folderOfflineIcon(true));
  256. if (allDisconnected) {
  257. setStatusText(tr("Disconnected"));
  258. } else {
  259. setStatusText(tr("Disconnected from some accounts"));
  260. }
  261. #ifdef Q_OS_WIN
  262. // Windows has a 128-char tray tooltip length limit.
  263. QStringList accountNames;
  264. foreach (AccountStatePtr a, problemAccounts) {
  265. accountNames.append(a->account()->displayName());
  266. }
  267. _tray->setToolTip(tr("Disconnected from %1").arg(accountNames.join(QLatin1String(", "))));
  268. #else
  269. QStringList messages;
  270. messages.append(tr("Disconnected from accounts:"));
  271. foreach (AccountStatePtr a, problemAccounts) {
  272. QString message = tr("Account %1: %2").arg(a->account()->displayName(), a->stateString(a->state()));
  273. if (!a->connectionErrors().empty()) {
  274. message += QLatin1String("\n");
  275. message += a->connectionErrors().join(QLatin1String("\n"));
  276. }
  277. messages.append(message);
  278. }
  279. _tray->setToolTip(messages.join(QLatin1String("\n\n")));
  280. #endif
  281. return;
  282. }
  283. if (allSignedOut) {
  284. _tray->setIcon(Theme::instance()->folderOfflineIcon(true));
  285. _tray->setToolTip(tr("Please sign in"));
  286. setStatusText(tr("Signed out"));
  287. return;
  288. } else if (allPaused) {
  289. _tray->setIcon(Theme::instance()->syncStateIcon(SyncResult::Paused, true));
  290. _tray->setToolTip(tr("Account synchronization is disabled"));
  291. setStatusText(tr("Synchronization is paused"));
  292. return;
  293. }
  294. // display the info of the least successful sync (eg. do not just display the result of the latest sync)
  295. QString trayMessage;
  296. FolderMan *folderMan = FolderMan::instance();
  297. Folder::Map map = folderMan->map();
  298. SyncResult::Status overallStatus = SyncResult::Undefined;
  299. bool hasUnresolvedConflicts = false;
  300. FolderMan::trayOverallStatus(map.values(), &overallStatus, &hasUnresolvedConflicts);
  301. // If the sync succeeded but there are unresolved conflicts,
  302. // show the problem icon!
  303. auto iconStatus = overallStatus;
  304. if (iconStatus == SyncResult::Success && hasUnresolvedConflicts) {
  305. iconStatus = SyncResult::Problem;
  306. }
  307. // If we don't get a status for whatever reason, that's a Problem
  308. if (iconStatus == SyncResult::Undefined) {
  309. iconStatus = SyncResult::Problem;
  310. }
  311. QIcon statusIcon = Theme::instance()->syncStateIcon(iconStatus, true);
  312. _tray->setIcon(statusIcon);
  313. // create the tray blob message, check if we have an defined state
  314. if (map.count() > 0) {
  315. #ifdef Q_OS_WIN
  316. // Windows has a 128-char tray tooltip length limit.
  317. trayMessage = folderMan->trayTooltipStatusString(overallStatus, hasUnresolvedConflicts, false);
  318. #else
  319. QStringList allStatusStrings;
  320. foreach (Folder *folder, map.values()) {
  321. QString folderMessage = FolderMan::trayTooltipStatusString(
  322. folder->syncResult().status(),
  323. folder->syncResult().hasUnresolvedConflicts(),
  324. folder->syncPaused());
  325. allStatusStrings += tr("Folder %1: %2").arg(folder->shortGuiLocalPath(), folderMessage);
  326. }
  327. trayMessage = allStatusStrings.join(QLatin1String("\n"));
  328. #endif
  329. _tray->setToolTip(trayMessage);
  330. if (overallStatus == SyncResult::Success || overallStatus == SyncResult::Problem) {
  331. if (hasUnresolvedConflicts) {
  332. setStatusText(tr("Unresolved conflicts"));
  333. } else {
  334. setStatusText(tr("Up to date"));
  335. }
  336. } else if (overallStatus == SyncResult::Paused) {
  337. setStatusText(tr("Synchronization is paused"));
  338. } else {
  339. setStatusText(tr("Error during synchronization"));
  340. }
  341. } else {
  342. _tray->setToolTip(tr("There are no sync folders configured."));
  343. setStatusText(tr("No sync folders configured"));
  344. }
  345. }
  346. void ownCloudGui::hideAndShowTray()
  347. {
  348. _tray->hide();
  349. _tray->show();
  350. }
  351. void ownCloudGui::slotShowTrayMessage(const QString &title, const QString &msg)
  352. {
  353. qCDebug(lcOwnCloudGui) << "Going to show notification with title: '" << title << "' and message: '" << msg << "'";
  354. if (_tray) {
  355. _tray->showMessage(title, msg);
  356. } else {
  357. qCWarning(lcApplication) << "Tray not ready: " << msg;
  358. }
  359. }
  360. void ownCloudGui::slotShowTrayUpdateMessage(const QString &title, const QString &msg, const QUrl &webUrl)
  361. {
  362. if(_tray) {
  363. _tray->showUpdateMessage(title, msg, webUrl);
  364. } else {
  365. qCWarning(lcApplication) << "Tray not ready: " << msg;
  366. }
  367. }
  368. void ownCloudGui::slotShowOptionalTrayMessage(const QString &title, const QString &msg)
  369. {
  370. slotShowTrayMessage(title, msg);
  371. }
  372. /*
  373. * open the folder with the given Alias
  374. */
  375. void ownCloudGui::slotFolderOpenAction(const QString &alias)
  376. {
  377. Folder *f = FolderMan::instance()->folder(alias);
  378. if (f) {
  379. qCInfo(lcApplication) << "opening local url " << f->path();
  380. QUrl url = QUrl::fromLocalFile(f->path());
  381. #ifdef Q_OS_WIN
  382. // work around a bug in QDesktopServices on Win32, see i-net
  383. QString filePath = f->path();
  384. if (filePath.startsWith(QLatin1String("\\\\")) || filePath.startsWith(QLatin1String("//")))
  385. url = QUrl::fromLocalFile(QDir::toNativeSeparators(filePath));
  386. else
  387. url = QUrl::fromLocalFile(filePath);
  388. #endif
  389. QDesktopServices::openUrl(url);
  390. }
  391. }
  392. void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo &progress)
  393. {
  394. Q_UNUSED(folder);
  395. // FIXME: Lots of messages computed for nothing in this method, needs revisiting
  396. if (progress.status() == ProgressInfo::Discovery) {
  397. #if 0
  398. if (!progress._currentDiscoveredRemoteFolder.isEmpty()) {
  399. _actionStatus->setText(tr("Checking for changes in remote \"%1\"")
  400. .arg(progress._currentDiscoveredRemoteFolder));
  401. } else if (!progress._currentDiscoveredLocalFolder.isEmpty()) {
  402. _actionStatus->setText(tr("Checking for changes in local \"%1\"")
  403. .arg(progress._currentDiscoveredLocalFolder));
  404. }
  405. #endif
  406. } else if (progress.status() == ProgressInfo::Done) {
  407. QTimer::singleShot(2000, this, &ownCloudGui::slotComputeOverallSyncStatus);
  408. }
  409. if (progress.status() != ProgressInfo::Propagation) {
  410. return;
  411. }
  412. if (progress.totalSize() == 0) {
  413. qint64 currentFile = progress.currentFile();
  414. qint64 totalFileCount = qMax(progress.totalFiles(), currentFile);
  415. QString msg;
  416. if (progress.trustEta()) {
  417. msg = tr("Syncing %1 of %2 (%3 left)")
  418. .arg(currentFile)
  419. .arg(totalFileCount)
  420. .arg(Utility::durationToDescriptiveString2(progress.totalProgress().estimatedEta));
  421. } else {
  422. msg = tr("Syncing %1 of %2")
  423. .arg(currentFile)
  424. .arg(totalFileCount);
  425. }
  426. //_actionStatus->setText(msg);
  427. } else {
  428. QString totalSizeStr = Utility::octetsToString(progress.totalSize());
  429. QString msg;
  430. if (progress.trustEta()) {
  431. msg = tr("Syncing %1 (%2 left)")
  432. .arg(totalSizeStr, Utility::durationToDescriptiveString2(progress.totalProgress().estimatedEta));
  433. } else {
  434. msg = tr("Syncing %1")
  435. .arg(totalSizeStr);
  436. }
  437. //_actionStatus->setText(msg);
  438. }
  439. if (!progress._lastCompletedItem.isEmpty()) {
  440. QString kindStr = Progress::asResultString(progress._lastCompletedItem);
  441. QString timeStr = QTime::currentTime().toString("hh:mm");
  442. QString actionText = tr("%1 (%2, %3)").arg(progress._lastCompletedItem._file, kindStr, timeStr);
  443. auto *action = new QAction(actionText, this);
  444. Folder *f = FolderMan::instance()->folder(folder);
  445. if (f) {
  446. QString fullPath = f->path() + '/' + progress._lastCompletedItem._file;
  447. if (QFile(fullPath).exists()) {
  448. connect(action, &QAction::triggered, this, [this, fullPath] { this->slotOpenPath(fullPath); });
  449. } else {
  450. action->setEnabled(false);
  451. }
  452. }
  453. if (_recentItemsActions.length() > 5) {
  454. _recentItemsActions.takeFirst()->deleteLater();
  455. }
  456. _recentItemsActions.append(action);
  457. }
  458. }
  459. void ownCloudGui::slotLogin()
  460. {
  461. if (auto account = qvariant_cast<AccountStatePtr>(sender()->property(propertyAccountC))) {
  462. account->account()->resetRejectedCertificates();
  463. account->signIn();
  464. } else {
  465. auto list = AccountManager::instance()->accounts();
  466. foreach (const auto &a, list) {
  467. a->signIn();
  468. }
  469. }
  470. }
  471. void ownCloudGui::slotLogout()
  472. {
  473. auto list = AccountManager::instance()->accounts();
  474. if (auto account = qvariant_cast<AccountStatePtr>(sender()->property(propertyAccountC))) {
  475. list.clear();
  476. list.append(account);
  477. }
  478. foreach (const auto &ai, list) {
  479. ai->signOutByUi();
  480. }
  481. }
  482. void ownCloudGui::slotNewAccountWizard()
  483. {
  484. OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)));
  485. }
  486. void ownCloudGui::slotShowGuiMessage(const QString &title, const QString &message)
  487. {
  488. auto *msgBox = new QMessageBox;
  489. msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint);
  490. msgBox->setAttribute(Qt::WA_DeleteOnClose);
  491. msgBox->setText(message);
  492. msgBox->setWindowTitle(title);
  493. msgBox->setIcon(QMessageBox::Information);
  494. msgBox->open();
  495. }
  496. void ownCloudGui::slotShowSettings()
  497. {
  498. if (_settingsDialog.isNull()) {
  499. _settingsDialog = new SettingsDialog(this);
  500. _settingsDialog->setAttribute(Qt::WA_DeleteOnClose, true);
  501. _settingsDialog->show();
  502. }
  503. raiseDialog(_settingsDialog.data());
  504. }
  505. void ownCloudGui::slotSettingsDialogActivated()
  506. {
  507. emit isShowingSettingsDialog();
  508. }
  509. void ownCloudGui::slotShowSyncProtocol()
  510. {
  511. slotShowSettings();
  512. //_settingsDialog->showActivityPage();
  513. }
  514. void ownCloudGui::slotShutdown()
  515. {
  516. // explicitly close windows. This is somewhat of a hack to ensure
  517. // that saving the geometries happens ASAP during a OS shutdown
  518. // those do delete on close
  519. if (!_settingsDialog.isNull())
  520. _settingsDialog->close();
  521. if (!_logBrowser.isNull())
  522. _logBrowser->deleteLater();
  523. _app->quit();
  524. }
  525. void ownCloudGui::slotToggleLogBrowser()
  526. {
  527. if (_logBrowser.isNull()) {
  528. // init the log browser.
  529. _logBrowser = new LogBrowser;
  530. // ## TODO: allow new log name maybe?
  531. }
  532. if (_logBrowser->isVisible()) {
  533. _logBrowser->hide();
  534. } else {
  535. raiseDialog(_logBrowser);
  536. }
  537. }
  538. void ownCloudGui::slotOpenOwnCloud()
  539. {
  540. if (auto account = qvariant_cast<AccountPtr>(sender()->property(propertyAccountC))) {
  541. Utility::openBrowser(account->url());
  542. }
  543. }
  544. void ownCloudGui::slotHelp()
  545. {
  546. QDesktopServices::openUrl(QUrl(Theme::instance()->helpUrl()));
  547. }
  548. void ownCloudGui::raiseDialog(QWidget *raiseWidget)
  549. {
  550. if (raiseWidget && !raiseWidget->parentWidget()) {
  551. // Qt has a bug which causes parent-less dialogs to pop-under.
  552. raiseWidget->showNormal();
  553. raiseWidget->raise();
  554. raiseWidget->activateWindow();
  555. #ifdef Q_OS_WIN
  556. // Windows disallows raising a Window when you're not the active application.
  557. // Use a common hack to attach to the active application
  558. const auto activeProcessId = GetWindowThreadProcessId(GetForegroundWindow(), nullptr);
  559. if (activeProcessId != qApp->applicationPid()) {
  560. const auto threadId = GetCurrentThreadId();
  561. // don't step here with a debugger...
  562. if (AttachThreadInput(threadId, activeProcessId, true))
  563. {
  564. const auto hwnd = reinterpret_cast<HWND>(raiseWidget->winId());
  565. SetForegroundWindow(hwnd);
  566. SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  567. AttachThreadInput(threadId, activeProcessId, false);
  568. }
  569. }
  570. #endif
  571. }
  572. }
  573. void ownCloudGui::slotShowShareDialog(const QString &localPath) const
  574. {
  575. _tray->createShareDialog(localPath);
  576. }
  577. void ownCloudGui::slotShowFileActivityDialog(const QString &localPath) const
  578. {
  579. _tray->createFileActivityDialog(localPath);
  580. }
  581. } // end namespace