File indexing completed on 2024-04-28 08:51:04
0001 /* 0002 SPDX-FileCopyrightText: 2007-2013 Urs Wolfer <uwolfer@kde.org> 0003 SPDX-FileCopyrightText: 2009-2010 Tony Murray <murraytony@gmail.com> 0004 SPDX-FileCopyrightText: 2021 RafaĆ Lalik <rafallalik@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "config-kactivities.h" 0010 0011 #include "mainwindow.h" 0012 #include "krdc_debug.h" 0013 #include "settings.h" 0014 #include "config/preferencesdialog.h" 0015 #include "floatingtoolbar.h" 0016 #include "bookmarkmanager.h" 0017 #include "connectiondelegate.h" 0018 #include "remotedesktopsmodel.h" 0019 #include "systemtrayicon.h" 0020 #include "tabbedviewwidget.h" 0021 #include "hostpreferences.h" 0022 #include "factorwidget.h" 0023 0024 #include <KActionCollection> 0025 #include <KActionMenu> 0026 #include <KComboBox> 0027 #include <KLineEdit> 0028 #include <KLocalizedString> 0029 #include <KMessageBox> 0030 #include <KNotifyConfigWidget> 0031 #include <KPluginMetaData> 0032 #include <KToggleAction> 0033 #include <KToggleFullScreenAction> 0034 #include <KToolBar> 0035 0036 #if HAVE_KACTIVITIES 0037 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0038 #include <KActivities/ResourceInstance> 0039 #else 0040 #include <PlasmaActivities/ResourceInstance> 0041 #endif 0042 #endif 0043 0044 #include <QClipboard> 0045 #include <QDockWidget> 0046 #include <QFontMetrics> 0047 #include <QGroupBox> 0048 #include <QGuiApplication> 0049 #include <QHBoxLayout> 0050 #include <QHeaderView> 0051 #include <QIcon> 0052 #include <QInputDialog> 0053 #include <QLabel> 0054 #include <QLayout> 0055 #include <QMenu> 0056 #include <QMenuBar> 0057 #include <QPushButton> 0058 #include <QSortFilterProxyModel> 0059 #include <QStatusBar> 0060 #include <QScrollBar> 0061 #include <QTabWidget> 0062 #include <QTableView> 0063 #include <QTimer> 0064 #include <QToolBar> 0065 #include <QVBoxLayout> 0066 #include <QRegularExpression> 0067 0068 MainWindow::MainWindow(QWidget *parent) 0069 : KXmlGuiWindow(parent), 0070 m_fullscreenWindow(nullptr), 0071 m_protocolInput(nullptr), 0072 m_addressInput(nullptr), 0073 m_toolBar(nullptr), 0074 m_currentRemoteView(-1), 0075 m_systemTrayIcon(nullptr), 0076 m_dockWidgetTableView(nullptr), 0077 m_newConnectionTableView(nullptr), 0078 m_newConnectionWidget(nullptr) 0079 { 0080 loadAllPlugins(); 0081 0082 setupActions(); 0083 0084 setStandardToolBarMenuEnabled(true); 0085 0086 m_tabWidget = new TabbedViewWidget(this); 0087 m_tabWidget->setAutoFillBackground(true); 0088 m_tabWidget->setMovable(true); 0089 m_tabWidget->setTabPosition((QTabWidget::TabPosition) Settings::tabPosition()); 0090 0091 m_tabWidget->setTabsClosable(Settings::tabCloseButton()); 0092 0093 connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), SLOT(closeTab(int))); 0094 0095 if (Settings::tabMiddleClick()) 0096 connect(m_tabWidget, SIGNAL(mouseMiddleClick(int)), SLOT(closeTab(int))); 0097 0098 connect(m_tabWidget, SIGNAL(tabBarDoubleClicked(int)), SLOT(openTabSettings(int))); 0099 0100 m_tabWidget->tabBar()->setContextMenuPolicy(Qt::CustomContextMenu); 0101 connect(m_tabWidget->tabBar(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(tabContextMenu(QPoint))); 0102 0103 m_tabWidget->setMinimumSize(600, 400); 0104 setCentralWidget(m_tabWidget); 0105 0106 createDockWidget(); 0107 0108 setupGUI(ToolBar | Keys | Save | Create); 0109 0110 if (Settings::systemTrayIcon()) { 0111 m_systemTrayIcon = new SystemTrayIcon(this); 0112 if(m_fullscreenWindow) { 0113 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0114 m_systemTrayIcon->setAssociatedWidget(m_fullscreenWindow); 0115 #else 0116 m_systemTrayIcon->setAssociatedWindow(m_fullscreenWindow->windowHandle()); 0117 #endif 0118 } 0119 0120 } 0121 0122 connect(m_tabWidget, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); 0123 0124 if (Settings::showStatusBar()) 0125 statusBar()->showMessage(i18n("KDE Remote Desktop Client started")); 0126 0127 updateActionStatus(); // disable remote view actions 0128 0129 if (Settings::openSessions().count() == 0) // just create a new connection tab if there are no open sessions 0130 m_tabWidget->addTab(newConnectionWidget(), i18n("New Connection")); 0131 0132 if (Settings::rememberSessions()) // give some time to create and show the window first 0133 QTimer::singleShot(100, this, SLOT(restoreOpenSessions())); 0134 } 0135 0136 MainWindow::~MainWindow() 0137 { 0138 } 0139 0140 void MainWindow::setupActions() 0141 { 0142 QAction *connectionAction = actionCollection()->addAction(QStringLiteral("new_connection")); 0143 connectionAction->setText(i18n("New Connection")); 0144 connectionAction->setIcon(QIcon::fromTheme(QStringLiteral("network-connect"))); 0145 actionCollection()->setDefaultShortcuts(connectionAction, KStandardShortcut::openNew()); 0146 connect(connectionAction, SIGNAL(triggered()), SLOT(newConnectionPage())); 0147 0148 QAction *screenshotAction = actionCollection()->addAction(QStringLiteral("take_screenshot")); 0149 screenshotAction->setText(i18n("Copy Screenshot to Clipboard")); 0150 screenshotAction->setIconText(i18n("Screenshot")); 0151 screenshotAction->setIcon(QIcon::fromTheme(QStringLiteral("ksnapshot"))); 0152 connect(screenshotAction, SIGNAL(triggered()), SLOT(takeScreenshot())); 0153 0154 QAction *fullscreenAction = actionCollection()->addAction(QStringLiteral("switch_fullscreen")); // note: please do not switch to KStandardShortcut unless you know what you are doing (see history of this file) 0155 fullscreenAction->setText(i18n("Switch to Full Screen Mode")); 0156 fullscreenAction->setIconText(i18n("Full Screen")); 0157 fullscreenAction->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen"))); 0158 actionCollection()->setDefaultShortcuts(fullscreenAction, KStandardShortcut::fullScreen()); 0159 connect(fullscreenAction, SIGNAL(triggered()), SLOT(switchFullscreen())); 0160 0161 QAction *viewOnlyAction = actionCollection()->addAction(QStringLiteral("view_only")); 0162 viewOnlyAction->setCheckable(true); 0163 viewOnlyAction->setText(i18n("View Only")); 0164 viewOnlyAction->setIcon(QIcon::fromTheme(QStringLiteral("document-preview"))); 0165 connect(viewOnlyAction, SIGNAL(triggered(bool)), SLOT(viewOnly(bool))); 0166 0167 QAction *disconnectAction = actionCollection()->addAction(QStringLiteral("disconnect")); 0168 disconnectAction->setText(i18n("Disconnect")); 0169 disconnectAction->setIcon(QIcon::fromTheme(QStringLiteral("network-disconnect"))); 0170 actionCollection()->setDefaultShortcuts(disconnectAction, KStandardShortcut::close()); 0171 connect(disconnectAction, SIGNAL(triggered()), SLOT(disconnectHost())); 0172 0173 QAction *showLocalCursorAction = actionCollection()->addAction(QStringLiteral("show_local_cursor")); 0174 showLocalCursorAction->setCheckable(true); 0175 showLocalCursorAction->setIcon(QIcon::fromTheme(QStringLiteral("input-mouse"))); 0176 showLocalCursorAction->setText(i18n("Show Local Cursor")); 0177 showLocalCursorAction->setIconText(i18n("Local Cursor")); 0178 connect(showLocalCursorAction, SIGNAL(triggered(bool)), SLOT(showLocalCursor(bool))); 0179 0180 QAction *grabAllKeysAction = actionCollection()->addAction(QStringLiteral("grab_all_keys")); 0181 grabAllKeysAction->setCheckable(true); 0182 grabAllKeysAction->setIcon(QIcon::fromTheme(QStringLiteral("configure-shortcuts"))); 0183 grabAllKeysAction->setText(i18n("Grab All Possible Keys")); 0184 grabAllKeysAction->setIconText(i18n("Grab Keys")); 0185 connect(grabAllKeysAction, SIGNAL(triggered(bool)), SLOT(grabAllKeys(bool))); 0186 0187 QAction *scaleAction = actionCollection()->addAction(QStringLiteral("scale")); 0188 scaleAction->setCheckable(true); 0189 scaleAction->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-best"))); 0190 scaleAction->setText(i18n("Scale Remote Screen to Fit Window Size")); 0191 scaleAction->setIconText(i18n("Scale")); 0192 connect(scaleAction, SIGNAL(triggered(bool)), SLOT(scale(bool))); 0193 0194 FactorWidget * m_scaleSlider = new FactorWidget(i18n("Scaling Factor"), this, actionCollection()); 0195 QAction * scaleFactorAction = actionCollection()->addAction(QStringLiteral("scale_factor"), m_scaleSlider); 0196 scaleFactorAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); 0197 0198 KStandardAction::quit(this, SLOT(quit()), actionCollection()); 0199 KStandardAction::preferences(this, SLOT(preferences()), actionCollection()); 0200 QAction *configNotifyAction = KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection()); 0201 configNotifyAction->setVisible(false); 0202 m_menubarAction = KStandardAction::showMenubar(this, SLOT(showMenubar()), actionCollection()); 0203 m_menubarAction->setChecked(!menuBar()->isHidden()); 0204 0205 KActionMenu *bookmarkMenu = new KActionMenu(i18n("Bookmarks"), actionCollection()); 0206 m_bookmarkManager = new BookmarkManager(actionCollection(), bookmarkMenu->menu(), this); 0207 actionCollection()->addAction(QStringLiteral("bookmark") , bookmarkMenu); 0208 connect(m_bookmarkManager, SIGNAL(openUrl(QUrl)), SLOT(newConnection(QUrl))); 0209 } 0210 0211 void MainWindow::loadAllPlugins() 0212 { 0213 const QVector<KPluginMetaData> offers = KPluginMetaData::findPlugins(QStringLiteral("krdc")); 0214 const KConfigGroup conf = KSharedConfig::openConfig()->group(QStringLiteral("Plugins")); 0215 0216 for (const KPluginMetaData &plugin : offers) { 0217 const bool enabled = plugin.isEnabled(conf); 0218 0219 if (enabled) { 0220 const auto result = KPluginFactory::instantiatePlugin<RemoteViewFactory>(plugin); 0221 0222 if (result) { 0223 RemoteViewFactory *component = result.plugin; 0224 const int sorting = plugin.value(QStringLiteral("X-KDE-KRDC-Sorting"), 0); 0225 m_remoteViewFactories.insert(sorting, component); 0226 } 0227 } 0228 } 0229 } 0230 0231 void MainWindow::restoreOpenSessions() 0232 { 0233 const QStringList list = Settings::openSessions(); 0234 QListIterator<QString> it(list); 0235 while (it.hasNext()) { 0236 newConnection(QUrl(it.next())); 0237 } 0238 } 0239 0240 QUrl MainWindow::getInputUrl() 0241 { 0242 QString userInput = m_addressInput->text(); 0243 qCDebug(KRDC) << "input url " << userInput; 0244 // percent encode usernames so QUrl can parse it 0245 static QRegularExpression reg(QStringLiteral("@[^@]+$")); 0246 int lastAtIndex = userInput.indexOf(reg); 0247 if (lastAtIndex >0) { 0248 userInput = QString::fromLatin1(QUrl::toPercentEncoding(userInput.left(lastAtIndex))) + userInput.mid(lastAtIndex); 0249 qCDebug(KRDC) << "input url " << userInput; 0250 } 0251 0252 return QUrl(m_protocolInput->currentText() + QStringLiteral("://") + userInput); 0253 } 0254 0255 void MainWindow::newConnection(const QUrl &newUrl, bool switchFullscreenWhenConnected, const QString &tabName) 0256 { 0257 m_switchFullscreenWhenConnected = switchFullscreenWhenConnected; 0258 0259 const QUrl url = newUrl.isEmpty() ? getInputUrl() : newUrl; 0260 0261 if (!url.isValid() || (url.host().isEmpty() && url.port() < 0) 0262 || (!url.path().isEmpty() && url.path() != QStringLiteral("/"))) { 0263 KMessageBox::error(this, 0264 i18n("The entered address does not have the required form.\n Syntax: [username@]host[:port]"), 0265 i18n("Malformed URL")); 0266 return; 0267 } 0268 0269 if (m_protocolInput && m_addressInput) { 0270 m_protocolInput->setCurrentText(url.scheme()); 0271 m_addressInput->setText(url.authority()); 0272 } 0273 0274 RemoteView *view = nullptr; 0275 KConfigGroup configGroup = Settings::self()->config()->group(QStringLiteral("hostpreferences")).group(url.toDisplayString(QUrl::StripTrailingSlash)); 0276 0277 for (RemoteViewFactory *factory : qAsConst(m_remoteViewFactories)) { 0278 if (factory->supportsUrl(url)) { 0279 view = factory->createView(this, url, configGroup); 0280 qCDebug(KRDC) << "Found plugin to handle url (" << url.url() << "): " << view->metaObject()->className(); 0281 break; 0282 } 0283 } 0284 0285 if (!view) { 0286 KMessageBox::error(this, 0287 i18n("The entered address cannot be handled."), 0288 i18n("Unusable URL")); 0289 return; 0290 } 0291 0292 // Configure the view 0293 HostPreferences* prefs = view->hostPreferences(); 0294 // if the user press cancel 0295 if (! prefs->showDialogIfNeeded(this)) return; 0296 0297 view->showLocalCursor(prefs->showLocalCursor() ? RemoteView::CursorOn : RemoteView::CursorOff); 0298 view->setViewOnly(prefs->viewOnly()); 0299 bool scale_state = false; 0300 if (switchFullscreenWhenConnected) scale_state = prefs->fullscreenScale(); 0301 else scale_state = prefs->windowedScale(); 0302 0303 view->enableScaling(scale_state); 0304 0305 connect(view, SIGNAL(framebufferSizeChanged(int,int)), this, SLOT(resizeTabWidget(int,int))); 0306 connect(view, SIGNAL(statusChanged(RemoteView::RemoteStatus)), this, SLOT(statusChanged(RemoteView::RemoteStatus))); 0307 connect(view, SIGNAL(disconnected()), this, SLOT(disconnectHost())); 0308 0309 QScrollArea *scrollArea = createScrollArea(m_tabWidget, view); 0310 0311 const int indexOfNewConnectionWidget = m_tabWidget->indexOf(m_newConnectionWidget); 0312 if (indexOfNewConnectionWidget >= 0) 0313 m_tabWidget->removeTab(indexOfNewConnectionWidget); 0314 0315 const int newIndex = m_tabWidget->addTab(scrollArea, QIcon::fromTheme(QStringLiteral("krdc")), tabName.isEmpty() ? url.toDisplayString(QUrl::StripTrailingSlash) : tabName); 0316 m_tabWidget->setCurrentIndex(newIndex); 0317 m_remoteViewMap.insert(m_tabWidget->widget(newIndex), view); 0318 tabChanged(newIndex); // force to update m_currentRemoteView (tabChanged is not emitted when start page has been disabled) 0319 0320 view->start(); 0321 setFactor(view->hostPreferences()->scaleFactor()); 0322 0323 #if HAVE_KACTIVITIES 0324 KActivities::ResourceInstance::notifyAccessed(url, QGuiApplication::desktopFileName()); 0325 #endif 0326 0327 Q_EMIT factorUpdated(view->hostPreferences()->scaleFactor()); 0328 Q_EMIT scaleUpdated(scale_state); 0329 } 0330 0331 void MainWindow::openFromRemoteDesktopsModel(const QModelIndex &index) 0332 { 0333 const QString urlString = index.data(10001).toString(); 0334 const QString nameString = index.data(10003).toString(); 0335 if (!urlString.isEmpty()) { 0336 const QUrl url(urlString); 0337 // first check if url has already been opened; in case show the tab 0338 for (auto it = m_remoteViewMap.constBegin(), end = m_remoteViewMap.constEnd(); it != end; ++it) { 0339 RemoteView *view = it.value(); 0340 if (view->url() == url) { 0341 QWidget *widget = it.key(); 0342 m_tabWidget->setCurrentWidget(widget); 0343 return; 0344 } 0345 } 0346 0347 newConnection(url, false, nameString); 0348 } 0349 } 0350 0351 void MainWindow::selectFromRemoteDesktopsModel(const QModelIndex &index) 0352 { 0353 const QString urlString = index.data(10001).toString(); 0354 0355 if (!urlString.isEmpty() && m_protocolInput && m_addressInput) { 0356 const QUrl url(urlString); 0357 m_addressInput->blockSignals(true); // block signals so we don't filter the address list on click 0358 m_addressInput->setText(url.authority()); 0359 m_addressInput->blockSignals(false); 0360 m_protocolInput->setCurrentText(url.scheme()); 0361 } 0362 } 0363 0364 void MainWindow::resizeTabWidget(int w, int h) 0365 { 0366 qCDebug(KRDC) << "tabwidget resize, view size: w: " << w << ", h: " << h; 0367 if (m_fullscreenWindow) { 0368 qCDebug(KRDC) << "in fullscreen mode, refusing to resize"; 0369 return; 0370 } 0371 0372 const QSize viewSize = QSize(w,h); 0373 QScreen *currentScreen = QGuiApplication::screenAt(geometry().center()); 0374 0375 if (Settings::fullscreenOnConnect()) { 0376 const QSize screenSize = currentScreen->availableGeometry().size(); 0377 0378 if (screenSize == viewSize) { 0379 qCDebug(KRDC) << "screen size equal to target view size -> switch to fullscreen mode"; 0380 switchFullscreen(); 0381 return; 0382 } 0383 } 0384 0385 if (Settings::resizeOnConnect()) { 0386 QWidget* currentWidget = m_tabWidget->currentWidget(); 0387 const QSize newWindowSize = size() - currentWidget->frameSize() + viewSize; 0388 0389 const QSize desktopSize = currentScreen->availableGeometry().size(); 0390 qCDebug(KRDC) << "new window size: " << newWindowSize << " available space:" << desktopSize; 0391 0392 if ((newWindowSize.width() >= desktopSize.width()) || (newWindowSize.height() >= desktopSize.height())) { 0393 qCDebug(KRDC) << "remote desktop needs more space than available -> show window maximized"; 0394 setWindowState(windowState() | Qt::WindowMaximized); 0395 return; 0396 } 0397 setWindowState(windowState() & ~ Qt::WindowMaximized); 0398 resize(newWindowSize); 0399 } 0400 } 0401 0402 void MainWindow::statusChanged(RemoteView::RemoteStatus status) 0403 { 0404 qCDebug(KRDC) << status; 0405 0406 // the remoteview is already deleted, so don't show it; otherwise it would crash 0407 if (status == RemoteView::Disconnecting || status == RemoteView::Disconnected) 0408 return; 0409 0410 RemoteView *view = qobject_cast<RemoteView*>(QObject::sender()); 0411 const QString host = view->host(); 0412 0413 QString iconName = QStringLiteral("krdc"); 0414 QString message; 0415 0416 switch (status) { 0417 case RemoteView::Connecting: 0418 iconName = QStringLiteral("network-connect"); 0419 message = i18n("Connecting to %1", host); 0420 break; 0421 case RemoteView::Authenticating: 0422 iconName = QStringLiteral("dialog-password"); 0423 message = i18n("Authenticating at %1", host); 0424 break; 0425 case RemoteView::Preparing: 0426 iconName = QStringLiteral("view-history"); 0427 message = i18n("Preparing connection to %1", host); 0428 break; 0429 case RemoteView::Connected: 0430 iconName = QStringLiteral("krdc"); 0431 message = i18n("Connected to %1", host); 0432 0433 if (view->grabAllKeys() != view->hostPreferences()->grabAllKeys()) { 0434 view->setGrabAllKeys(view->hostPreferences()->grabAllKeys()); 0435 updateActionStatus(); 0436 } 0437 0438 // when started with command line fullscreen argument 0439 if (m_switchFullscreenWhenConnected) { 0440 m_switchFullscreenWhenConnected = false; 0441 switchFullscreen(); 0442 } 0443 0444 if (Settings::rememberHistory()) { 0445 m_bookmarkManager->addHistoryBookmark(view); 0446 } 0447 0448 break; 0449 default: 0450 break; 0451 } 0452 0453 m_tabWidget->setTabIcon(m_tabWidget->indexOf(view), QIcon::fromTheme(iconName)); 0454 if (Settings::showStatusBar()) 0455 statusBar()->showMessage(message); 0456 } 0457 0458 void MainWindow::takeScreenshot() 0459 { 0460 const QPixmap snapshot = currentRemoteView()->takeScreenshot(); 0461 0462 QApplication::clipboard()->setPixmap(snapshot); 0463 } 0464 0465 void MainWindow::switchFullscreen() 0466 { 0467 qCDebug(KRDC); 0468 0469 RemoteView* view = currentRemoteView(); 0470 bool scale_state = false; 0471 0472 if (m_fullscreenWindow) { 0473 // Leaving full screen mode 0474 m_fullscreenWindow->setWindowState(Qt::WindowNoState); 0475 m_fullscreenWindow->hide(); 0476 0477 m_tabWidget->tabBar()->setHidden(m_tabWidget->count() <= 1 && !Settings::showTabBar()); 0478 m_tabWidget->setDocumentMode(false); 0479 setCentralWidget(m_tabWidget); 0480 0481 show(); 0482 restoreGeometry(m_mainWindowGeometry); 0483 if (m_systemTrayIcon) { 0484 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0485 m_systemTrayIcon->setAssociatedWidget(this); 0486 #else 0487 m_systemTrayIcon->setAssociatedWindow(windowHandle()); 0488 #endif 0489 } 0490 0491 for (RemoteView * view : qAsConst(m_remoteViewMap)) { 0492 view->enableScaling(view->hostPreferences()->windowedScale()); 0493 } 0494 0495 if (m_toolBar) { 0496 m_toolBar->hideAndDestroy(); 0497 m_toolBar->deleteLater(); 0498 m_toolBar = nullptr; 0499 } 0500 0501 actionCollection()->action(QStringLiteral("switch_fullscreen"))->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen"))); 0502 actionCollection()->action(QStringLiteral("switch_fullscreen"))->setText(i18n("Switch to Full Screen Mode")); 0503 actionCollection()->action(QStringLiteral("switch_fullscreen"))->setIconText(i18n("Full Screen")); 0504 if (view) 0505 scale_state = view->hostPreferences()->windowedScale(); 0506 0507 m_fullscreenWindow->deleteLater(); 0508 m_fullscreenWindow = nullptr; 0509 } else { 0510 // Entering full screen mode 0511 m_fullscreenWindow = new QWidget(this, Qt::Window); 0512 m_fullscreenWindow->setWindowTitle(i18nc("window title when in full screen mode (for example displayed in tasklist)", 0513 "KDE Remote Desktop Client (Full Screen)")); 0514 0515 m_mainWindowGeometry = saveGeometry(); 0516 0517 m_tabWidget->tabBar()->hide(); 0518 m_tabWidget->setDocumentMode(true); 0519 0520 for (RemoteView *currentView : qAsConst(m_remoteViewMap)) { 0521 currentView->enableScaling(currentView->hostPreferences()->fullscreenScale()); 0522 } 0523 0524 QVBoxLayout *fullscreenLayout = new QVBoxLayout(m_fullscreenWindow); 0525 fullscreenLayout->setContentsMargins(QMargins(0, 0, 0, 0)); 0526 fullscreenLayout->addWidget(m_tabWidget); 0527 0528 KToggleFullScreenAction::setFullScreen(m_fullscreenWindow, true); 0529 0530 MinimizePixel *minimizePixel = new MinimizePixel(m_fullscreenWindow); 0531 connect(minimizePixel, SIGNAL(rightClicked()), m_fullscreenWindow, SLOT(showMinimized())); 0532 m_fullscreenWindow->installEventFilter(this); 0533 0534 m_fullscreenWindow->show(); 0535 hide(); // hide after showing the new window so it stays on the same screen 0536 0537 if (m_systemTrayIcon) { 0538 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0539 m_systemTrayIcon->setAssociatedWidget(m_fullscreenWindow); 0540 #else 0541 m_systemTrayIcon->setAssociatedWindow(m_fullscreenWindow->windowHandle()); 0542 #endif 0543 } 0544 0545 actionCollection()->action(QStringLiteral("switch_fullscreen"))->setIcon(QIcon::fromTheme(QStringLiteral("view-restore"))); 0546 actionCollection()->action(QStringLiteral("switch_fullscreen"))->setText(i18n("Switch to Window Mode")); 0547 actionCollection()->action(QStringLiteral("switch_fullscreen"))->setIconText(i18n("Window Mode")); 0548 showRemoteViewToolbar(); 0549 if (view) 0550 scale_state = view->hostPreferences()->fullscreenScale(); 0551 } 0552 if (m_tabWidget->currentWidget() == m_newConnectionWidget && m_addressInput) { 0553 m_addressInput->setFocus(); 0554 } 0555 0556 if (view) { 0557 Q_EMIT factorUpdated(view->hostPreferences()->scaleFactor()); 0558 Q_EMIT scaleUpdated(scale_state); 0559 } 0560 actionCollection()->action(QStringLiteral("scale"))->setChecked(scale_state); 0561 } 0562 0563 QScrollArea *MainWindow::createScrollArea(QWidget *parent, RemoteView *remoteView) 0564 { 0565 RemoteViewScrollArea *scrollArea = new RemoteViewScrollArea(parent); 0566 scrollArea->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); 0567 0568 connect(scrollArea, SIGNAL(resized(int,int)), remoteView, SLOT(scaleResize(int,int))); 0569 0570 QPalette palette = scrollArea->palette(); 0571 palette.setColor(QPalette::Window, Settings::backgroundColor()); 0572 scrollArea->setPalette(palette); 0573 0574 scrollArea->setFrameStyle(QFrame::NoFrame); 0575 scrollArea->setAutoFillBackground(true); 0576 scrollArea->setWidget(remoteView); 0577 0578 return scrollArea; 0579 } 0580 0581 void MainWindow::disconnectHost() 0582 { 0583 qCDebug(KRDC); 0584 0585 RemoteView *view = qobject_cast<RemoteView*>(QObject::sender()); 0586 0587 QWidget *widgetToDelete; 0588 if (view) { 0589 widgetToDelete = (QWidget*) view->parent()->parent(); 0590 m_remoteViewMap.remove(m_remoteViewMap.key(view)); 0591 } else { 0592 widgetToDelete = m_tabWidget->currentWidget(); 0593 view = currentRemoteView(); 0594 m_remoteViewMap.remove(m_remoteViewMap.key(view)); 0595 } 0596 0597 saveHostPrefs(view); 0598 view->startQuitting(); // some deconstructors can't properly quit, so quit early 0599 m_tabWidget->removePage(widgetToDelete); 0600 widgetToDelete->deleteLater(); 0601 0602 // if closing the last connection, create new connection tab 0603 if (m_tabWidget->count() == 0) { 0604 newConnectionPage(false); 0605 } 0606 0607 // if the newConnectionWidget is the only tab and we are fullscreen, switch to window mode 0608 if (m_fullscreenWindow && m_tabWidget->count() == 1 && m_tabWidget->currentWidget() == m_newConnectionWidget) { 0609 switchFullscreen(); 0610 } 0611 } 0612 0613 void MainWindow::closeTab(int index) 0614 { 0615 if (index == -1) { 0616 return; 0617 } 0618 QWidget *widget = m_tabWidget->widget(index); 0619 bool isNewConnectionPage = widget == m_newConnectionWidget; 0620 0621 if (!isNewConnectionPage) { 0622 RemoteView *view = m_remoteViewMap.take(widget); 0623 view->startQuitting(); 0624 widget->deleteLater(); 0625 } 0626 0627 m_tabWidget->removePage(widget); 0628 0629 // if closing the last connection, create new connection tab 0630 if (m_tabWidget->count() == 0) { 0631 newConnectionPage(false); 0632 } 0633 0634 // if the newConnectionWidget is the only tab and we are fullscreen, switch to window mode 0635 if (m_fullscreenWindow && m_tabWidget->count() == 1 && m_tabWidget->currentWidget() == m_newConnectionWidget) { 0636 switchFullscreen(); 0637 } 0638 } 0639 0640 void MainWindow::openTabSettings(int index) 0641 { 0642 if (index == -1) { 0643 newConnectionPage(); 0644 return; 0645 } 0646 QWidget *widget = m_tabWidget->widget(index); 0647 RemoteViewScrollArea *scrollArea = qobject_cast<RemoteViewScrollArea*>(widget); 0648 if (!scrollArea) return; 0649 RemoteView *view = qobject_cast<RemoteView*>(scrollArea->widget()); 0650 if (!view) return; 0651 0652 const QString url = view->url().url(); 0653 qCDebug(KRDC) << url; 0654 0655 showSettingsDialog(url); 0656 } 0657 0658 void MainWindow::showSettingsDialog(const QString &url) 0659 { 0660 HostPreferences *prefs = nullptr; 0661 0662 for (RemoteViewFactory *factory : qAsConst(m_remoteViewFactories)) { 0663 if (factory->supportsUrl(QUrl(url))) { 0664 prefs = factory->createHostPreferences(Settings::self()->config()->group(QStringLiteral("hostpreferences")).group(url), this); 0665 if (prefs) { 0666 qCDebug(KRDC) << "Found plugin to handle url (" << url << "): " << prefs->metaObject()->className(); 0667 } else { 0668 qCDebug(KRDC) << "Found plugin to handle url (" << url << "), but plugin does not provide preferences"; 0669 } 0670 } 0671 } 0672 0673 if (prefs) { 0674 prefs->setShownWhileConnected(true); 0675 prefs->showDialog(this); 0676 } else { 0677 KMessageBox::error(this, 0678 i18n("The selected host cannot be handled."), 0679 i18n("Unusable URL")); 0680 } 0681 } 0682 0683 void MainWindow::showConnectionContextMenu(const QPoint &pos) 0684 { 0685 // QTableView does not take headers into account when it does mapToGlobal(), so calculate the offset 0686 QPoint offset = QPoint(m_newConnectionTableView->verticalHeader()->size().width(), 0687 m_newConnectionTableView->horizontalHeader()->size().height()); 0688 QModelIndex index = m_newConnectionTableView->indexAt(pos); 0689 0690 if (!index.isValid()) 0691 return; 0692 0693 const QString url = index.data(10001).toString(); 0694 const QString title = index.model()->index(index.row(), RemoteDesktopsModel::Title).data(Qt::DisplayRole).toString(); 0695 const QString source = index.model()->index(index.row(), RemoteDesktopsModel::Source).data(Qt::DisplayRole).toString(); 0696 0697 QMenu *menu = new QMenu(url, m_newConnectionTableView); 0698 0699 QAction *connectAction = menu->addAction(QIcon::fromTheme(QStringLiteral("network-connect")), i18n("Connect")); 0700 QAction *renameAction = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("Rename")); 0701 QAction *settingsAction = menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Settings")); 0702 QAction *deleteAction = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete")); 0703 0704 // not very clean, but it works, 0705 if (!(source == i18nc("Where each displayed link comes from", "Bookmarks") || 0706 source == i18nc("Where each displayed link comes from", "History"))) { 0707 renameAction->setEnabled(false); 0708 deleteAction->setEnabled(false); 0709 } 0710 0711 QAction *selectedAction = menu->exec(m_newConnectionTableView->mapToGlobal(pos + offset)); 0712 0713 if (selectedAction == connectAction) { 0714 openFromRemoteDesktopsModel(index); 0715 } else if (selectedAction == renameAction) { 0716 //TODO: use inline editor if possible 0717 bool ok = false; 0718 const QString newTitle = QInputDialog::getText(this, i18n("Rename %1", title), i18n("Rename %1 to", title), QLineEdit::EchoMode::Normal, title, &ok); 0719 if (ok && !newTitle.isEmpty()) { 0720 BookmarkManager::updateTitle(m_bookmarkManager->getManager(), url, newTitle); 0721 } 0722 } else if (selectedAction == settingsAction) { 0723 showSettingsDialog(url); 0724 } else if (selectedAction == deleteAction) { 0725 0726 if (KMessageBox::warningContinueCancel(this, i18n("Are you sure you want to delete %1?", url), i18n("Delete %1", title), 0727 KStandardGuiItem::del()) == KMessageBox::Continue) { 0728 BookmarkManager::removeByUrl(m_bookmarkManager->getManager(), url); 0729 } 0730 } 0731 0732 menu->deleteLater(); 0733 } 0734 0735 void MainWindow::tabContextMenu(const QPoint &point) 0736 { 0737 int index = m_tabWidget->tabBar()->tabAt(point); 0738 QWidget *widget = m_tabWidget->widget(index); 0739 RemoteViewScrollArea *scrollArea = qobject_cast<RemoteViewScrollArea*>(widget); 0740 if (!scrollArea) return; 0741 RemoteView *view = qobject_cast<RemoteView*>(scrollArea->widget()); 0742 if (!view) return; 0743 0744 const QString url = view->url().toDisplayString(QUrl::StripTrailingSlash); 0745 qCDebug(KRDC) << url; 0746 0747 QMenu *menu = new QMenu(url, this); 0748 QAction *bookmarkAction = menu->addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Add Bookmark")); 0749 QAction *closeAction = menu->addAction(QIcon::fromTheme(QStringLiteral("tab-close")), i18n("Close Tab")); 0750 QAction *selectedAction = menu->exec(QCursor::pos()); 0751 if (selectedAction) { 0752 if (selectedAction == closeAction) { 0753 closeTab(m_tabWidget->indexOf(widget)); 0754 } else if (selectedAction == bookmarkAction) { 0755 m_bookmarkManager->addManualBookmark(view->url(), url); 0756 } 0757 } 0758 menu->deleteLater(); 0759 } 0760 0761 void MainWindow::showLocalCursor(bool showLocalCursor) 0762 { 0763 qCDebug(KRDC) << showLocalCursor; 0764 0765 RemoteView* view = currentRemoteView(); 0766 view->showLocalCursor(showLocalCursor ? RemoteView::CursorOn : RemoteView::CursorOff); 0767 view->hostPreferences()->setShowLocalCursor(showLocalCursor); 0768 saveHostPrefs(view); 0769 } 0770 0771 void MainWindow::viewOnly(bool viewOnly) 0772 { 0773 qCDebug(KRDC) << viewOnly; 0774 0775 RemoteView* view = currentRemoteView(); 0776 view->setViewOnly(viewOnly); 0777 view->hostPreferences()->setViewOnly(viewOnly); 0778 saveHostPrefs(view); 0779 } 0780 0781 void MainWindow::grabAllKeys(bool grabAllKeys) 0782 { 0783 qCDebug(KRDC); 0784 0785 RemoteView* view = currentRemoteView(); 0786 view->setGrabAllKeys(grabAllKeys); 0787 view->hostPreferences()->setGrabAllKeys(grabAllKeys); 0788 saveHostPrefs(view); 0789 } 0790 0791 void setActionStatus(QAction* action, bool enabled, bool visible, bool checked) 0792 { 0793 action->setEnabled(enabled); 0794 action->setVisible(visible); 0795 action->setChecked(checked); 0796 } 0797 0798 void MainWindow::scale(bool scale) 0799 { 0800 qCDebug(KRDC); 0801 0802 RemoteView* view = currentRemoteView(); 0803 view->enableScaling(scale); 0804 if (m_fullscreenWindow) 0805 view->hostPreferences()->setFullscreenScale(scale); 0806 else 0807 view->hostPreferences()->setWindowedScale(scale); 0808 0809 saveHostPrefs(view); 0810 0811 Q_EMIT scaleUpdated(scale); 0812 } 0813 0814 void MainWindow::setFactor(int scale) 0815 { 0816 float s = float(scale)/100.; 0817 0818 RemoteView* view = currentRemoteView(); 0819 if (view) { 0820 view->setScaleFactor(s); 0821 0822 view->enableScaling(view->scaling()); 0823 view->hostPreferences()->setScaleFactor(scale); 0824 0825 saveHostPrefs(view); 0826 } 0827 } 0828 0829 void MainWindow::showRemoteViewToolbar() 0830 { 0831 qCDebug(KRDC); 0832 0833 if (!m_toolBar) { 0834 m_toolBar = new FloatingToolBar(m_fullscreenWindow, m_fullscreenWindow); 0835 m_toolBar->setSide(FloatingToolBar::Top); 0836 0837 KComboBox *sessionComboBox = new KComboBox(m_toolBar); 0838 sessionComboBox->setStyleSheet(QStringLiteral("QComboBox:!editable{background:transparent;}")); 0839 sessionComboBox->setModel(m_tabWidget->getModel()); 0840 sessionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); 0841 sessionComboBox->setCurrentIndex(m_tabWidget->currentIndex()); 0842 connect(sessionComboBox, SIGNAL(activated(int)), m_tabWidget, SLOT(setCurrentIndex(int))); 0843 connect(m_tabWidget, SIGNAL(currentChanged(int)), sessionComboBox, SLOT(setCurrentIndex(int))); 0844 m_toolBar->addWidget(sessionComboBox); 0845 0846 QToolBar *buttonBox = new QToolBar(m_toolBar); 0847 0848 buttonBox->addAction(actionCollection()->action(QStringLiteral("new_connection"))); 0849 buttonBox->addAction(actionCollection()->action(QStringLiteral("switch_fullscreen"))); 0850 0851 QAction *minimizeAction = new QAction(m_toolBar); 0852 minimizeAction->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); 0853 minimizeAction->setText(i18n("Minimize Full Screen Window")); 0854 connect(minimizeAction, SIGNAL(triggered()), m_fullscreenWindow, SLOT(showMinimized())); 0855 buttonBox->addAction(minimizeAction); 0856 0857 buttonBox->addAction(actionCollection()->action(QStringLiteral("take_screenshot"))); 0858 buttonBox->addAction(actionCollection()->action(QStringLiteral("view_only"))); 0859 buttonBox->addAction(actionCollection()->action(QStringLiteral("show_local_cursor"))); 0860 buttonBox->addAction(actionCollection()->action(QStringLiteral("grab_all_keys"))); 0861 buttonBox->addAction(actionCollection()->action(QStringLiteral("scale"))); 0862 buttonBox->addAction(actionCollection()->action(QStringLiteral("scale_factor"))); 0863 buttonBox->addAction(actionCollection()->action(QStringLiteral("disconnect"))); 0864 buttonBox->addAction(actionCollection()->action(QStringLiteral("file_quit"))); 0865 0866 QAction *stickToolBarAction = new QAction(m_toolBar); 0867 stickToolBarAction->setCheckable(true); 0868 stickToolBarAction->setIcon(QIcon::fromTheme(QStringLiteral("object-locked"))); 0869 stickToolBarAction->setText(i18n("Stick Toolbar")); 0870 connect(stickToolBarAction, SIGNAL(triggered(bool)), m_toolBar, SLOT(setSticky(bool))); 0871 buttonBox->addAction(stickToolBarAction); 0872 0873 m_toolBar->addWidget(buttonBox); 0874 } 0875 } 0876 0877 void MainWindow::updateActionStatus() 0878 { 0879 qCDebug(KRDC) << m_tabWidget->currentIndex(); 0880 0881 bool enabled = true; 0882 0883 if (m_tabWidget->currentWidget() == m_newConnectionWidget) 0884 enabled = false; 0885 0886 RemoteView* view = (m_currentRemoteView >= 0 && enabled) ? currentRemoteView() : nullptr; 0887 0888 actionCollection()->action(QStringLiteral("take_screenshot"))->setEnabled(enabled); 0889 actionCollection()->action(QStringLiteral("disconnect"))->setEnabled(enabled); 0890 0891 setActionStatus(actionCollection()->action(QStringLiteral("view_only")), 0892 enabled, 0893 view ? view->supportsViewOnly() : false, 0894 view ? view->viewOnly() : false); 0895 0896 setActionStatus(actionCollection()->action(QStringLiteral("show_local_cursor")), 0897 enabled, 0898 view ? view->supportsLocalCursor() : false, 0899 view ? view->localCursorState() == RemoteView::CursorOn : false); 0900 0901 setActionStatus(actionCollection()->action(QStringLiteral("scale")), 0902 enabled, 0903 view ? view->supportsScaling() : false, 0904 view ? view->scaling() : false); 0905 0906 actionCollection()->action(QStringLiteral("scale_factor"))->setVisible(view ? view->supportsScaling() : false); 0907 setFactor(view ? view->hostPreferences()->scaleFactor() : 0); 0908 Q_EMIT factorUpdated(view ? view->hostPreferences()->scaleFactor() : 0); 0909 Q_EMIT scaleUpdated(view ? view->scaling() : false); 0910 0911 setActionStatus(actionCollection()->action(QStringLiteral("grab_all_keys")), 0912 enabled, 0913 enabled, 0914 view ? view->grabAllKeys() : false); 0915 } 0916 0917 void MainWindow::preferences() 0918 { 0919 // An instance of your dialog could be already created and could be 0920 // cached, in which case you want to display the cached dialog 0921 // instead of creating another one 0922 if (PreferencesDialog::showDialog(QStringLiteral("preferences"))) 0923 return; 0924 0925 // KConfigDialog didn't find an instance of this dialog, so lets 0926 // create it: 0927 PreferencesDialog *dialog = new PreferencesDialog(this, Settings::self()); 0928 0929 // User edited the configuration - update your local copies of the 0930 // configuration data 0931 connect(dialog, SIGNAL(settingsChanged(QString)), 0932 this, SLOT(updateConfiguration())); 0933 0934 dialog->show(); 0935 } 0936 0937 void MainWindow::updateConfiguration() 0938 { 0939 if (!Settings::showStatusBar()) 0940 statusBar()->deleteLater(); 0941 else 0942 statusBar()->showMessage({}); // force creation of statusbar 0943 0944 m_tabWidget->tabBar()->setHidden((m_tabWidget->count() <= 1 && !Settings::showTabBar()) || m_fullscreenWindow); 0945 m_tabWidget->setTabPosition((QTabWidget::TabPosition) Settings::tabPosition()); 0946 m_tabWidget->setTabsClosable(Settings::tabCloseButton()); 0947 0948 disconnect(m_tabWidget, SIGNAL(mouseMiddleClick(int)), this, SLOT(closeTab(int))); // just be sure it is not connected twice 0949 if (Settings::tabMiddleClick()) 0950 connect(m_tabWidget, SIGNAL(mouseMiddleClick(int)), SLOT(closeTab(int))); 0951 0952 if (Settings::systemTrayIcon() && !m_systemTrayIcon) { 0953 m_systemTrayIcon = new SystemTrayIcon(this); 0954 if (m_systemTrayIcon) { 0955 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0956 m_systemTrayIcon->setAssociatedWidget(m_fullscreenWindow); 0957 #else 0958 m_systemTrayIcon->setAssociatedWindow(m_fullscreenWindow->windowHandle()); 0959 #endif 0960 } 0961 } else if (m_systemTrayIcon) { 0962 delete m_systemTrayIcon; 0963 m_systemTrayIcon = nullptr; 0964 } 0965 0966 // update the scroll areas background color 0967 for (int i = 0; i < m_tabWidget->count(); ++i) { 0968 QPalette palette = m_tabWidget->widget(i)->palette(); 0969 palette.setColor(QPalette::Dark, Settings::backgroundColor()); 0970 m_tabWidget->widget(i)->setPalette(palette); 0971 } 0972 0973 if (m_protocolInput) { 0974 m_protocolInput->setCurrentText(Settings::defaultProtocol()); 0975 } 0976 0977 // Send update configuration message to all views 0978 for (RemoteView *view : qAsConst(m_remoteViewMap)) { 0979 view->updateConfiguration(); 0980 } 0981 } 0982 0983 void MainWindow::quit(bool systemEvent) 0984 { 0985 const bool haveRemoteConnections = !m_remoteViewMap.isEmpty(); 0986 if (systemEvent || !haveRemoteConnections || KMessageBox::warningContinueCancel(this, 0987 i18n("Are you sure you want to quit the KDE Remote Desktop Client?"), 0988 i18n("Confirm Quit"), 0989 KStandardGuiItem::quit(), KStandardGuiItem::cancel(), 0990 QStringLiteral("DoNotAskBeforeExit")) == KMessageBox::Continue) { 0991 0992 if (Settings::rememberSessions()) { // remember open remote views for next startup 0993 QStringList list; 0994 for (RemoteView *view : qAsConst(m_remoteViewMap)) { 0995 qCDebug(KRDC) << view->url(); 0996 list.append(view->url().toDisplayString(QUrl::StripTrailingSlash)); 0997 } 0998 Settings::setOpenSessions(list); 0999 } 1000 1001 saveHostPrefs(); 1002 1003 const QMap<QWidget *, RemoteView *> currentViews = m_remoteViewMap; 1004 for (RemoteView *view : currentViews) { 1005 view->startQuitting(); 1006 } 1007 1008 Settings::self()->save(); 1009 1010 qApp->quit(); 1011 } 1012 } 1013 1014 void MainWindow::configureNotifications() 1015 { 1016 KNotifyConfigWidget::configure(this); 1017 } 1018 1019 void MainWindow::showMenubar() 1020 { 1021 if (m_menubarAction->isChecked()) 1022 menuBar()->show(); 1023 else 1024 menuBar()->hide(); 1025 } 1026 1027 bool MainWindow::eventFilter(QObject *obj, QEvent *event) 1028 { 1029 // check for close events from the fullscreen window. 1030 if (obj == m_fullscreenWindow && event->type() == QEvent::Close) { 1031 quit(true); 1032 } 1033 // allow other events to pass through. 1034 return QObject::eventFilter(obj, event); 1035 } 1036 1037 void MainWindow::closeEvent(QCloseEvent *event) 1038 { 1039 if (event->spontaneous()) { // Returns true if the event originated outside the application (a system event); otherwise returns false. 1040 event->ignore(); 1041 if (Settings::systemTrayIcon()) { 1042 hide(); // just hide the mainwindow, keep it in systemtray 1043 } else { 1044 quit(); 1045 } 1046 } else { 1047 quit(true); 1048 } 1049 } 1050 1051 void MainWindow::saveProperties(KConfigGroup &group) 1052 { 1053 qCDebug(KRDC); 1054 KMainWindow::saveProperties(group); 1055 saveHostPrefs(); 1056 } 1057 1058 1059 void MainWindow::saveHostPrefs() 1060 { 1061 for (RemoteView *view : qAsConst(m_remoteViewMap)) { 1062 saveHostPrefs(view); 1063 } 1064 } 1065 1066 void MainWindow::saveHostPrefs(RemoteView* view) 1067 { 1068 // should saving this be a user option? 1069 if (view && view->scaling()) { 1070 QSize viewSize = m_tabWidget->currentWidget()->size(); 1071 qCDebug(KRDC) << "saving window size:" << viewSize; 1072 view->hostPreferences()->setWidth(viewSize.width()); 1073 view->hostPreferences()->setHeight(viewSize.height()); 1074 } 1075 1076 Settings::self()->config()->sync(); 1077 } 1078 1079 void MainWindow::tabChanged(int index) 1080 { 1081 qCDebug(KRDC) << index; 1082 1083 m_tabWidget->tabBar()->setHidden((m_tabWidget->count() <= 1 && !Settings::showTabBar()) || m_fullscreenWindow); 1084 1085 m_currentRemoteView = index; 1086 1087 if (m_tabWidget->currentWidget() == m_newConnectionWidget) { 1088 m_currentRemoteView = -1; 1089 if(m_addressInput) m_addressInput->setFocus(); 1090 } 1091 1092 const QString tabTitle = m_tabWidget->tabText(index).remove(QLatin1Char('&')); 1093 setCaption(tabTitle == i18n("New Connection") ? QString() : tabTitle); 1094 1095 updateActionStatus(); 1096 } 1097 1098 QWidget* MainWindow::newConnectionWidget() 1099 { 1100 if (m_newConnectionWidget) 1101 return m_newConnectionWidget; 1102 1103 m_newConnectionWidget = new QWidget(this); 1104 1105 QVBoxLayout *startLayout = new QVBoxLayout(m_newConnectionWidget); 1106 startLayout->setContentsMargins(QMargins(8, 4, 8, 4)); 1107 1108 QSortFilterProxyModel *remoteDesktopsModelProxy = new QSortFilterProxyModel(this); 1109 remoteDesktopsModelProxy->setSourceModel(m_remoteDesktopsModel); 1110 remoteDesktopsModelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); 1111 remoteDesktopsModelProxy->setFilterRole(10002); 1112 1113 { 1114 QHBoxLayout *connectLayout = new QHBoxLayout; 1115 1116 QLabel *addressLabel = new QLabel(i18n("Connect to:"), m_newConnectionWidget); 1117 m_protocolInput = new KComboBox(m_newConnectionWidget); 1118 m_addressInput = new KLineEdit(m_newConnectionWidget); 1119 m_addressInput->setClearButtonEnabled(true); 1120 m_addressInput->setPlaceholderText(i18n("Type here to connect to an address and filter the list.")); 1121 connect(m_addressInput, SIGNAL(textChanged(QString)), remoteDesktopsModelProxy, SLOT(setFilterFixedString(QString))); 1122 1123 for (RemoteViewFactory *factory : qAsConst(m_remoteViewFactories)) { 1124 m_protocolInput->addItem(factory->scheme()); 1125 } 1126 m_protocolInput->setCurrentText(Settings::defaultProtocol()); 1127 1128 connect(m_addressInput, SIGNAL(returnPressed()), SLOT(newConnection())); 1129 m_addressInput->setToolTip(i18n("Type an IP or DNS Name here. Clear the line to get a list of connection methods.")); 1130 1131 QPushButton *connectButton = new QPushButton(m_newConnectionWidget); 1132 connectButton->setToolTip(i18n("Goto Address")); 1133 connectButton->setIcon(QIcon::fromTheme(QStringLiteral("go-jump-locationbar"))); 1134 connect(connectButton, SIGNAL(clicked()), SLOT(newConnection())); 1135 1136 connectLayout->addWidget(addressLabel); 1137 connectLayout->addWidget(m_protocolInput); 1138 connectLayout->addWidget(m_addressInput, 1); 1139 connectLayout->addWidget(connectButton); 1140 connectLayout->setContentsMargins(QMargins(0, 6, 0, 10)); 1141 startLayout->addLayout(connectLayout); 1142 } 1143 1144 { 1145 m_newConnectionTableView = new QTableView(m_newConnectionWidget); 1146 m_newConnectionTableView->setModel(remoteDesktopsModelProxy); 1147 1148 // set up the view so it looks nice 1149 m_newConnectionTableView->setItemDelegate(new ConnectionDelegate(m_newConnectionTableView)); 1150 m_newConnectionTableView->setShowGrid(false); 1151 m_newConnectionTableView->setSelectionMode(QAbstractItemView::NoSelection); 1152 m_newConnectionTableView->verticalHeader()->hide(); 1153 m_newConnectionTableView->verticalHeader()->setDefaultSectionSize( 1154 m_newConnectionTableView->fontMetrics().height() + 3); 1155 m_newConnectionTableView->horizontalHeader()->setStretchLastSection(true); 1156 m_newConnectionTableView->setAlternatingRowColors(true); 1157 // set up sorting and actions (double click open, right click custom menu) 1158 m_newConnectionTableView->setSortingEnabled(true); 1159 m_newConnectionTableView->sortByColumn(Settings::connectionListSortColumn(), Qt::SortOrder(Settings::connectionListSortOrder())); 1160 m_newConnectionTableView->resizeColumnsToContents(); 1161 connect(m_newConnectionTableView->horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), 1162 SLOT(saveConnectionListSort(int,Qt::SortOrder))); 1163 connect(m_newConnectionTableView, SIGNAL(doubleClicked(QModelIndex)), 1164 SLOT(openFromRemoteDesktopsModel(QModelIndex))); 1165 // useful to edit similar address 1166 connect(m_newConnectionTableView, SIGNAL(clicked(QModelIndex)), 1167 SLOT(selectFromRemoteDesktopsModel(QModelIndex))); 1168 m_newConnectionTableView->setContextMenuPolicy(Qt::CustomContextMenu); 1169 connect(m_newConnectionTableView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showConnectionContextMenu(QPoint))); 1170 1171 startLayout->addWidget(m_newConnectionTableView); 1172 } 1173 1174 return m_newConnectionWidget; 1175 } 1176 1177 void MainWindow::saveConnectionListSort(const int logicalindex, const Qt::SortOrder order) 1178 { 1179 Settings::setConnectionListSortColumn(logicalindex); 1180 Settings::setConnectionListSortOrder(order); 1181 Settings::self()->save(); 1182 } 1183 1184 void MainWindow::newConnectionPage(bool clearInput) 1185 { 1186 const int indexOfNewConnectionWidget = m_tabWidget->indexOf(m_newConnectionWidget); 1187 if (indexOfNewConnectionWidget >= 0) 1188 m_tabWidget->setCurrentIndex(indexOfNewConnectionWidget); 1189 else { 1190 const int index = m_tabWidget->addTab(newConnectionWidget(), i18n("New Connection")); 1191 m_tabWidget->setCurrentIndex(index); 1192 } 1193 if(clearInput) { 1194 m_addressInput->clear(); 1195 } else { 1196 m_addressInput->selectAll(); 1197 } 1198 m_addressInput->setFocus(); 1199 } 1200 1201 QMap<QWidget *, RemoteView *> MainWindow::remoteViewList() const 1202 { 1203 return m_remoteViewMap; 1204 } 1205 1206 QList<RemoteViewFactory *> MainWindow::remoteViewFactoriesList() const 1207 { 1208 return m_remoteViewFactories.values(); 1209 } 1210 1211 RemoteView* MainWindow::currentRemoteView() const 1212 { 1213 if (m_currentRemoteView >= 0) { 1214 return m_remoteViewMap.value(m_tabWidget->widget(m_currentRemoteView)); 1215 } else { 1216 return nullptr; 1217 } 1218 } 1219 1220 void MainWindow::createDockWidget() 1221 { 1222 QDockWidget *remoteDesktopsDockWidget = new QDockWidget(this); 1223 QWidget *remoteDesktopsDockLayoutWidget = new QWidget(remoteDesktopsDockWidget); 1224 QVBoxLayout *remoteDesktopsDockLayout = new QVBoxLayout(remoteDesktopsDockLayoutWidget); 1225 remoteDesktopsDockWidget->setObjectName(QStringLiteral("remoteDesktopsDockWidget")); // required for saving position / state 1226 remoteDesktopsDockWidget->setWindowTitle(i18n("Remote Desktops")); 1227 QFontMetrics fontMetrics(remoteDesktopsDockWidget->font()); 1228 remoteDesktopsDockWidget->setMinimumWidth(fontMetrics.horizontalAdvance(QStringLiteral("vnc://192.168.100.100:6000"))); 1229 actionCollection()->addAction(QStringLiteral("remote_desktop_dockwidget"), 1230 remoteDesktopsDockWidget->toggleViewAction()); 1231 1232 m_dockWidgetTableView = new QTableView(remoteDesktopsDockLayoutWidget); 1233 m_remoteDesktopsModel = new RemoteDesktopsModel(this, m_bookmarkManager->getManager()); 1234 QSortFilterProxyModel *remoteDesktopsModelProxy = new QSortFilterProxyModel(this); 1235 remoteDesktopsModelProxy->setSourceModel(m_remoteDesktopsModel); 1236 remoteDesktopsModelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); 1237 remoteDesktopsModelProxy->setFilterRole(10002); 1238 m_dockWidgetTableView->setModel(remoteDesktopsModelProxy); 1239 1240 m_dockWidgetTableView->setShowGrid(false); 1241 m_dockWidgetTableView->verticalHeader()->hide(); 1242 m_dockWidgetTableView->verticalHeader()->setDefaultSectionSize( 1243 m_dockWidgetTableView->fontMetrics().height() + 2); 1244 m_dockWidgetTableView->horizontalHeader()->hide(); 1245 m_dockWidgetTableView->horizontalHeader()->setStretchLastSection(true); 1246 // hide all columns, then show the one we want 1247 for (int i=0; i < remoteDesktopsModelProxy->columnCount(); i++) { 1248 m_dockWidgetTableView->hideColumn(i); 1249 } 1250 m_dockWidgetTableView->showColumn(RemoteDesktopsModel::Title); 1251 m_dockWidgetTableView->sortByColumn(RemoteDesktopsModel::Title, Qt::AscendingOrder); 1252 1253 connect(m_dockWidgetTableView, SIGNAL(doubleClicked(QModelIndex)), 1254 SLOT(openFromRemoteDesktopsModel(QModelIndex))); 1255 1256 KLineEdit *filterLineEdit = new KLineEdit(remoteDesktopsDockLayoutWidget); 1257 filterLineEdit->setPlaceholderText(i18n("Filter")); 1258 filterLineEdit->setClearButtonEnabled(true); 1259 connect(filterLineEdit, SIGNAL(textChanged(QString)), remoteDesktopsModelProxy, SLOT(setFilterFixedString(QString))); 1260 remoteDesktopsDockLayout->addWidget(filterLineEdit); 1261 remoteDesktopsDockLayout->addWidget(m_dockWidgetTableView); 1262 remoteDesktopsDockWidget->setWidget(remoteDesktopsDockLayoutWidget); 1263 addDockWidget(Qt::LeftDockWidgetArea, remoteDesktopsDockWidget); 1264 } 1265 1266