1/****************************************************************************
2**
3** Copyright (C) 2007 - 2013 Urs Wolfer <uwolfer @ kde.org>
4** Copyright (C) 2009 - 2010 Tony Murray <murraytony @ gmail.com>
5**
6** This file is part of KDE.
7**
8** This program is free software; you can redistribute it and/or modify
9** it under the terms of the GNU General Public License as published by
10** the Free Software Foundation; either version 2 of the License, or
11** (at your option) any later version.
12**
13** This program is distributed in the hope that it will be useful,
14** but WITHOUT ANY WARRANTY; without even the implied warranty of
15** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16** GNU General Public License for more details.
17**
18** You should have received a copy of the GNU General Public License
19** along with this program; see the file COPYING. If not, write to
20** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21** Boston, MA 02110-1301, USA.
22**
23****************************************************************************/
24
25#include "mainwindow.h"
26
27#include "remoteview.h"
28#include "settings.h"
29#include "config/preferencesdialog.h"
30#include "floatingtoolbar.h"
31#include "bookmarkmanager.h"
32#include "connectiondelegate.h"
33#include "remotedesktopsmodel.h"
34#include "systemtrayicon.h"
35#include "tabbedviewwidget.h"
36#include "hostpreferences.h"
37
38#ifdef TELEPATHY_SUPPORT
39#include "tubesmanager.h"
40#endif
41
42#include <KAction>
43#include <KActionCollection>
44#include <KActionMenu>
45#include <KComboBox>
46#include <KIcon>
47#include <KInputDialog>
48#include <KLineEdit>
49#include <KLocale>
50#include <KMenu>
51#include <KMenuBar>
52#include <KMessageBox>
53#include <KNotifyConfigWidget>
54#include <KPluginInfo>
55#include <KPushButton>
56#include <KStatusBar>
57#include <KToggleAction>
58#include <KToggleFullScreenAction>
59#include <KServiceTypeTrader>
60
61#include <QClipboard>
62#include <QDockWidget>
63#include <QFontMetrics>
64#include <QGroupBox>
65#include <QHBoxLayout>
66#include <QHeaderView>
67#include <QLabel>
68#include <QLayout>
69#include <QScrollArea>
70#include <QSortFilterProxyModel>
71#include <QTableView>
72#include <QTimer>
73#include <QToolBar>
74#include <QVBoxLayout>
75
76MainWindow::MainWindow(QWidget *parent)
77 : KXmlGuiWindow(parent),
78 m_fullscreenWindow(0),
79 m_protocolInput(0),
80 m_addressInput(0),
81 m_toolBar(0),
82 m_currentRemoteView(-1),
83 m_systemTrayIcon(0),
84 m_dockWidgetTableView(0),
85 m_newConnectionTableView(0),
86#ifdef TELEPATHY_SUPPORT
87 m_tubesManager(0),
88#endif
89 m_newConnectionWidget(0)
90{
91 loadAllPlugins();
92
93 setupActions();
94
95 setStandardToolBarMenuEnabled(true);
96
97 m_tabWidget = new TabbedViewWidget(this);
98 m_tabWidget->setMovable(true);
99 m_tabWidget->setTabPosition((KTabWidget::TabPosition) Settings::tabPosition());
100
101#if QT_VERSION >= 0x040500
102 m_tabWidget->setTabsClosable(Settings::tabCloseButton());
103#else
104 m_tabWidget->setCloseButtonEnabled(Settings::tabCloseButton());
105#endif
106
107 connect(m_tabWidget, SIGNAL(closeRequest(QWidget*)), SLOT(closeTab(QWidget*)));
108
109 if (Settings::tabMiddleClick())
110 connect(m_tabWidget, SIGNAL(mouseMiddleClick(QWidget*)), SLOT(closeTab(QWidget*)));
111
112 connect(m_tabWidget, SIGNAL(mouseDoubleClick(QWidget*)), SLOT(openTabSettings(QWidget*)));
113 connect(m_tabWidget, SIGNAL(mouseDoubleClick()), SLOT(newConnectionPage()));
114 connect(m_tabWidget, SIGNAL(contextMenu(QWidget*,QPoint)), SLOT(tabContextMenu(QWidget*,QPoint)));
115
116 m_tabWidget->setMinimumSize(600, 400);
117 setCentralWidget(m_tabWidget);
118
119 createDockWidget();
120
121 setupGUI(ToolBar | Keys | Save | Create);
122
123 if (Settings::systemTrayIcon()) {
124 m_systemTrayIcon = new SystemTrayIcon(this);
125 if(m_fullscreenWindow) m_systemTrayIcon->setAssociatedWidget(m_fullscreenWindow);
126 }
127
128 connect(m_tabWidget, SIGNAL(currentChanged(int)), SLOT(tabChanged(int)));
129
130 if (Settings::showStatusBar())
131 statusBar()->showMessage(i18n("KDE Remote Desktop Client started"));
132
133 updateActionStatus(); // disable remote view actions
134
135 if (Settings::openSessions().count() == 0) // just create a new connection tab if there are no open sessions
136 m_tabWidget->addTab(newConnectionWidget(), i18n("New Connection"));
137
138 if (Settings::rememberSessions()) // give some time to create and show the window first
139 QTimer::singleShot(100, this, SLOT(restoreOpenSessions()));
140}
141
142MainWindow::~MainWindow()
143{
144}
145
146void MainWindow::setupActions()
147{
148 QAction *connectionAction = actionCollection()->addAction("new_connection");
149 connectionAction->setText(i18n("New Connection"));
150 connectionAction->setIcon(KIcon("network-connect"));
151 connect(connectionAction, SIGNAL(triggered()), SLOT(newConnectionPage()));
152
153 QAction *screenshotAction = actionCollection()->addAction("take_screenshot");
154 screenshotAction->setText(i18n("Copy Screenshot to Clipboard"));
155 screenshotAction->setIconText(i18n("Screenshot"));
156 screenshotAction->setIcon(KIcon("ksnapshot"));
157 connect(screenshotAction, SIGNAL(triggered()), SLOT(takeScreenshot()));
158
159 KAction *fullscreenAction = actionCollection()->addAction("switch_fullscreen"); // note: please do not switch to KStandardShortcut unless you know what you are doing (see history of this file)
160 fullscreenAction->setText(i18n("Switch to Full Screen Mode"));
161 fullscreenAction->setIconText(i18n("Full Screen"));
162 fullscreenAction->setIcon(KIcon("view-fullscreen"));
163 fullscreenAction->setShortcut(KStandardShortcut::fullScreen());
164 connect(fullscreenAction, SIGNAL(triggered()), SLOT(switchFullscreen()));
165
166 QAction *viewOnlyAction = actionCollection()->addAction("view_only");
167 viewOnlyAction->setCheckable(true);
168 viewOnlyAction->setText(i18n("View Only"));
169 viewOnlyAction->setIcon(KIcon("document-preview"));
170 connect(viewOnlyAction, SIGNAL(triggered(bool)), SLOT(viewOnly(bool)));
171
172 KAction *disconnectAction = actionCollection()->addAction("disconnect");
173 disconnectAction->setText(i18n("Disconnect"));
174 disconnectAction->setIcon(KIcon("network-disconnect"));
175 disconnectAction->setShortcut(QKeySequence::Close);
176 connect(disconnectAction, SIGNAL(triggered()), SLOT(disconnectHost()));
177
178 QAction *showLocalCursorAction = actionCollection()->addAction("show_local_cursor");
179 showLocalCursorAction->setCheckable(true);
180 showLocalCursorAction->setIcon(KIcon("input-mouse"));
181 showLocalCursorAction->setText(i18n("Show Local Cursor"));
182 showLocalCursorAction->setIconText(i18n("Local Cursor"));
183 connect(showLocalCursorAction, SIGNAL(triggered(bool)), SLOT(showLocalCursor(bool)));
184
185 QAction *grabAllKeysAction = actionCollection()->addAction("grab_all_keys");
186 grabAllKeysAction->setCheckable(true);
187 grabAllKeysAction->setIcon(KIcon("configure-shortcuts"));
188 grabAllKeysAction->setText(i18n("Grab All Possible Keys"));
189 grabAllKeysAction->setIconText(i18n("Grab Keys"));
190 connect(grabAllKeysAction, SIGNAL(triggered(bool)), SLOT(grabAllKeys(bool)));
191
192 QAction *scaleAction = actionCollection()->addAction("scale");
193 scaleAction->setCheckable(true);
194 scaleAction->setIcon(KIcon("zoom-fit-best"));
195 scaleAction->setText(i18n("Scale Remote Screen to Fit Window Size"));
196 scaleAction->setIconText(i18n("Scale"));
197 connect(scaleAction, SIGNAL(triggered(bool)), SLOT(scale(bool)));
198
199 KStandardAction::quit(this, SLOT(quit()), actionCollection());
200 KStandardAction::preferences(this, SLOT(preferences()), actionCollection());
201 QAction *configNotifyAction = KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection());
202 configNotifyAction->setVisible(false);
203 m_menubarAction = KStandardAction::showMenubar(this, SLOT(showMenubar()), actionCollection());
204 m_menubarAction->setChecked(!menuBar()->isHidden());
205
206 KActionMenu *bookmarkMenu = new KActionMenu(i18n("Bookmarks"), actionCollection());
207 m_bookmarkManager = new BookmarkManager(actionCollection(), bookmarkMenu->menu(), this);
208 actionCollection()->addAction("bookmark" , bookmarkMenu);
209 connect(m_bookmarkManager, SIGNAL(openUrl(KUrl)), SLOT(newConnection(KUrl)));
210}
211
212void MainWindow::loadAllPlugins()
213{
214 const KService::List offers = KServiceTypeTrader::self()->query("KRDC/Plugin");
215
216 KConfigGroup conf(KGlobal::config(), "Plugins");
217
218 for (int i = 0; i < offers.size(); i++) {
219 KService::Ptr offer = offers[i];
220
221 RemoteViewFactory *remoteView;
222
223 KPluginInfo description(offer);
224 description.load(conf);
225
226 const bool selected = description.isPluginEnabled();
227
228 if (selected) {
229 if ((remoteView = createPluginFromService(offer)) != 0) {
230 kDebug(5010) << "### Plugin " + description.name() + " found ###";
231 kDebug(5010) << "# Version:" << description.version();
232 kDebug(5010) << "# Description:" << description.comment();
233 kDebug(5010) << "# Author:" << description.author();
234 const int sorting = offer->property("X-KDE-KRDC-Sorting").toInt();
235 kDebug(5010) << "# Sorting:" << sorting;
236
237 m_remoteViewFactories.insert(sorting, remoteView);
238 } else {
239 kDebug(5010) << "Error loading KRDC plugin (" << (offers[i])->library() << ')';
240 }
241 } else {
242 kDebug(5010) << "# Plugin " + description.name() + " found, however it's not activated, skipping...";
243 continue;
244 }
245 }
246
247#ifdef TELEPATHY_SUPPORT
248 /* Start tube handler */
249 m_tubesManager = new TubesManager(this);
250 connect(m_tubesManager, SIGNAL(newConnection(KUrl)), SLOT(newConnection(KUrl)));
251#endif
252}
253
254RemoteViewFactory *MainWindow::createPluginFromService(const KService::Ptr &service)
255{
256 //try to load the specified library
257 KPluginFactory *factory = KPluginLoader(service->library()).factory();
258
259 if (!factory) {
260 kError(5010) << "KPluginFactory could not load the plugin:" << service->library();
261 return 0;
262 }
263
264 RemoteViewFactory *plugin = factory->create<RemoteViewFactory>();
265
266 return plugin;
267}
268
269void MainWindow::restoreOpenSessions()
270{
271 const QStringList list = Settings::openSessions();
272 QStringList::ConstIterator it = list.begin();
273 QStringList::ConstIterator end = list.end();
274 while (it != end) {
275 newConnection(*it);
276 ++it;
277 }
278}
279
280KUrl MainWindow::getInputUrl()
281{
282 return KUrl(m_protocolInput->currentText() + "://" + m_addressInput->text());
283}
284
285void MainWindow::newConnection(const KUrl &newUrl, bool switchFullscreenWhenConnected, const QString &tabName)
286{
287 m_switchFullscreenWhenConnected = switchFullscreenWhenConnected;
288
289 const KUrl url = newUrl.isEmpty() ? getInputUrl() : newUrl;
290
291 if (!url.isValid() || (url.host().isEmpty() && url.port() < 0)
292 || (!url.path().isEmpty() && url.path() != QLatin1String("/"))) {
293 KMessageBox::error(this,
294 i18n("The entered address does not have the required form.\n Syntax: [username@]host[:port]"),
295 i18n("Malformed URL"));
296 return;
297 }
298
299 if (m_protocolInput && m_addressInput) {
300 int index = m_protocolInput->findText(url.protocol());
301 if (index>=0) m_protocolInput->setCurrentIndex(index);
302 m_addressInput->setText(url.authority());
303 }
304
305 RemoteView *view = 0;
306 KConfigGroup configGroup = Settings::self()->config()->group("hostpreferences").group(url.prettyUrl(KUrl::RemoveTrailingSlash));
307
308 foreach(RemoteViewFactory *factory, m_remoteViewFactories) {
309 if (factory->supportsUrl(url)) {
310 view = factory->createView(this, url, configGroup);
311 kDebug(5010) << "Found plugin to handle url (" + url.url() + "): " + view->metaObject()->className();
312 break;
313 }
314 }
315
316 if (!view) {
317 KMessageBox::error(this,
318 i18n("The entered address cannot be handled."),
319 i18n("Unusable URL"));
320 return;
321 }
322
323 // Configure the view
324 HostPreferences* prefs = view->hostPreferences();
325 if (! prefs->showDialogIfNeeded(this)) {
326 delete view;
327 return;
328 }
329
330 view->showDotCursor(prefs->showLocalCursor() ? RemoteView::CursorOn : RemoteView::CursorOff);
331 view->setViewOnly(prefs->viewOnly());
332 if (! switchFullscreenWhenConnected) view->enableScaling(prefs->windowedScale());
333
334 connect(view, SIGNAL(framebufferSizeChanged(int,int)), this, SLOT(resizeTabWidget(int,int)));
335 connect(view, SIGNAL(statusChanged(RemoteView::RemoteStatus)), this, SLOT(statusChanged(RemoteView::RemoteStatus)));
336 connect(view, SIGNAL(disconnected()), this, SLOT(disconnectHost()));
337
338 view->winId(); // native widget workaround for bug 253365
339 QScrollArea *scrollArea = createScrollArea(m_tabWidget, view);
340
341 const int indexOfNewConnectionWidget = m_tabWidget->indexOf(m_newConnectionWidget);
342 if (indexOfNewConnectionWidget >= 0)
343 m_tabWidget->removeTab(indexOfNewConnectionWidget);
344
345 const int newIndex = m_tabWidget->addTab(scrollArea, KIcon("krdc"), tabName.isEmpty() ? url.prettyUrl(KUrl::RemoveTrailingSlash) : tabName);
346 m_tabWidget->setCurrentIndex(newIndex);
347 m_remoteViewMap.insert(m_tabWidget->widget(newIndex), view);
348 tabChanged(newIndex); // force to update m_currentRemoteView (tabChanged is not emitted when start page has been disabled)
349
350 view->start();
351}
352
353void MainWindow::openFromRemoteDesktopsModel(const QModelIndex &index)
354{
355 const QString urlString = index.data(10001).toString();
356 const QString nameString = index.data(10003).toString();
357 if (!urlString.isEmpty()) {
358 const KUrl url(urlString);
359 // first check if url has already been opened; in case show the tab
360 foreach (QWidget *widget, m_remoteViewMap.keys()) {
361 if (m_remoteViewMap.value(widget)->url() == url) {
362 m_tabWidget->setCurrentWidget(widget);
363 return;
364 }
365 }
366
367 newConnection(url, false, nameString);
368 }
369}
370
371void MainWindow::resizeTabWidget(int w, int h)
372{
373 kDebug(5010) << "tabwidget resize, view size: w: " << w << ", h: " << h;
374 if (m_fullscreenWindow) {
375 kDebug(5010) << "in fullscreen mode, refusing to resize";
376 return;
377 }
378
379 const QSize viewSize = QSize(w,h);
380 QDesktopWidget *desktop = QApplication::desktop();
381
382 if (Settings::fullscreenOnConnect()) {
383 int currentScreen = desktop->screenNumber(this);
384 const QSize screenSize = desktop->screenGeometry(currentScreen).size();
385
386 if (screenSize == viewSize) {
387 kDebug(5010) << "screen size equal to target view size -> switch to fullscreen mode";
388 switchFullscreen();
389 return;
390 }
391 }
392
393 if (Settings::resizeOnConnect()) {
394 QWidget* currentWidget = m_tabWidget->currentWidget();
395 const QSize newWindowSize = size() - currentWidget->frameSize() + viewSize;
396
397 const QSize desktopSize = desktop->availableGeometry().size();
398 kDebug(5010) << "new window size: " << newWindowSize << " available space:" << desktopSize;
399
400 if ((newWindowSize.width() >= desktopSize.width()) || (newWindowSize.height() >= desktopSize.height())) {
401 kDebug(5010) << "remote desktop needs more space than available -> show window maximized";
402 setWindowState(windowState() | Qt::WindowMaximized);
403 return;
404 }
405
406 resize(newWindowSize);
407 }
408}
409
410void MainWindow::statusChanged(RemoteView::RemoteStatus status)
411{
412 kDebug(5010) << status;
413
414 // the remoteview is already deleted, so don't show it; otherwise it would crash
415 if (status == RemoteView::Disconnecting || status == RemoteView::Disconnected)
416 return;
417
418 RemoteView *view = qobject_cast<RemoteView*>(QObject::sender());
419 const QString host = view->host();
420
421 QString iconName = "krdc";
422 QString message;
423
424 switch (status) {
425 case RemoteView::Connecting:
426 iconName = "network-connect";
427 message = i18n("Connecting to %1", host);
428 break;
429 case RemoteView::Authenticating:
430 iconName = "dialog-password";
431 message = i18n("Authenticating at %1", host);
432 break;
433 case RemoteView::Preparing:
434 iconName = "view-history";
435 message = i18n("Preparing connection to %1", host);
436 break;
437 case RemoteView::Connected:
438 iconName = "krdc";
439 message = i18n("Connected to %1", host);
440
441 if (view->grabAllKeys() != view->hostPreferences()->grabAllKeys()) {
442 view->setGrabAllKeys(view->hostPreferences()->grabAllKeys());
443 updateActionStatus();
444 }
445
446 // when started with command line fullscreen argument
447 if (m_switchFullscreenWhenConnected) {
448 m_switchFullscreenWhenConnected = false;
449 switchFullscreen();
450 }
451
452 if (Settings::rememberHistory()) {
453 m_bookmarkManager->addHistoryBookmark(view);
454 }
455
456 break;
457 default:
458 break;
459 }
460
461 m_tabWidget->setTabIcon(m_tabWidget->indexOf(view), KIcon(iconName));
462 if (Settings::showStatusBar())
463 statusBar()->showMessage(message);
464}
465
466void MainWindow::takeScreenshot()
467{
468 const QPixmap snapshot = currentRemoteView()->takeScreenshot();
469
470 QApplication::clipboard()->setPixmap(snapshot);
471}
472
473void MainWindow::switchFullscreen()
474{
475 kDebug(5010);
476
477 if (m_fullscreenWindow) {
478 // Leaving full screen mode
479 m_fullscreenWindow->setWindowState(0);
480 m_fullscreenWindow->hide();
481
482 m_tabWidget->setTabBarHidden(m_tabWidget->count() <= 1 && !Settings::showTabBar());
483 m_tabWidget->setDocumentMode(false);
484 setCentralWidget(m_tabWidget);
485
486 show();
487 restoreGeometry(m_mainWindowGeometry);
488 if (m_systemTrayIcon) m_systemTrayIcon->setAssociatedWidget(this);
489
490 foreach (RemoteView * view, m_remoteViewMap.values()) {
491 view->enableScaling(view->hostPreferences()->windowedScale());
492 }
493
494 if (m_toolBar) {
495 m_toolBar->hideAndDestroy();
496 m_toolBar->deleteLater();
497 m_toolBar = 0;
498 }
499
500 actionCollection()->action("switch_fullscreen")->setIcon(KIcon("view-fullscreen"));
501 actionCollection()->action("switch_fullscreen")->setText(i18n("Switch to Full Screen Mode"));
502 actionCollection()->action("switch_fullscreen")->setIconText(i18n("Full Screen"));
503
504 m_fullscreenWindow->deleteLater();
505 m_fullscreenWindow = 0;
506 } else {
507 // Entering full screen mode
508 m_fullscreenWindow = new QWidget(this, Qt::Window);
509 m_fullscreenWindow->setWindowTitle(i18nc("window title when in full screen mode (for example displayed in tasklist)",
510 "KDE Remote Desktop Client (Full Screen)"));
511
512 m_mainWindowGeometry = saveGeometry();
513
514 m_tabWidget->setTabBarHidden(true);
515 m_tabWidget->setDocumentMode(true);
516
517 foreach(RemoteView *currentView, m_remoteViewMap) {
518 currentView->enableScaling(currentView->hostPreferences()->fullscreenScale());
519 }
520
521 QVBoxLayout *fullscreenLayout = new QVBoxLayout(m_fullscreenWindow);
522 fullscreenLayout->setContentsMargins(QMargins(0, 0, 0, 0));
523 fullscreenLayout->addWidget(m_tabWidget);
524
525 KToggleFullScreenAction::setFullScreen(m_fullscreenWindow, true);
526
527 MinimizePixel *minimizePixel = new MinimizePixel(m_fullscreenWindow);
528 minimizePixel->winId(); // force it to be a native widget (prevents problem with QX11EmbedContainer)
529 connect(minimizePixel, SIGNAL(rightClicked()), m_fullscreenWindow, SLOT(showMinimized()));
530 m_fullscreenWindow->installEventFilter(this);
531
532 m_fullscreenWindow->show();
533 hide(); // hide after showing the new window so it stays on the same screen
534
535 if (m_systemTrayIcon) m_systemTrayIcon->setAssociatedWidget(m_fullscreenWindow);
536
537 actionCollection()->action("switch_fullscreen")->setIcon(KIcon("view-restore"));
538 actionCollection()->action("switch_fullscreen")->setText(i18n("Switch to Window Mode"));
539 actionCollection()->action("switch_fullscreen")->setIconText(i18n("Window Mode"));
540 showRemoteViewToolbar();
541 }
542 if (m_tabWidget->currentWidget() == m_newConnectionWidget) {
543 m_addressInput->setFocus();
544 }
545}
546
547QScrollArea *MainWindow::createScrollArea(QWidget *parent, RemoteView *remoteView)
548{
549 RemoteViewScrollArea *scrollArea = new RemoteViewScrollArea(parent);
550 scrollArea->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
551
552 connect(scrollArea, SIGNAL(resized(int,int)), remoteView, SLOT(scaleResize(int,int)));
553
554 QPalette palette = scrollArea->palette();
555 palette.setColor(QPalette::Dark, Settings::backgroundColor());
556 scrollArea->setPalette(palette);
557
558 scrollArea->setBackgroundRole(QPalette::Dark);
559 scrollArea->setFrameStyle(QFrame::NoFrame);
560
561 scrollArea->setWidget(remoteView);
562
563 return scrollArea;
564}
565
566void MainWindow::disconnectHost()
567{
568 kDebug(5010);
569
570 RemoteView *view = qobject_cast<RemoteView*>(QObject::sender());
571
572 QWidget *widgetToDelete;
573 if (view) {
574 widgetToDelete = (QWidget*) view->parent()->parent();
575 m_remoteViewMap.remove(m_remoteViewMap.key(view));
576 } else {
577 widgetToDelete = m_tabWidget->currentWidget();
578 view = currentRemoteView();
579 m_remoteViewMap.remove(m_remoteViewMap.key(view));
580 }
581
582 saveHostPrefs(view);
583 view->startQuitting(); // some deconstructors can't properly quit, so quit early
584 m_tabWidget->removePage(widgetToDelete);
585 widgetToDelete->deleteLater();
586#ifdef TELEPATHY_SUPPORT
587 m_tubesManager->closeTube(view->url());
588#endif
589
590 // if closing the last connection, create new connection tab
591 if (m_tabWidget->count() == 0) {
592 newConnectionPage(false);
593 }
594
595 // if the newConnectionWidget is the only tab and we are fullscreen, switch to window mode
596 if (m_fullscreenWindow && m_tabWidget->count() == 1 && m_tabWidget->currentWidget() == m_newConnectionWidget) {
597 switchFullscreen();
598 }
599}
600
601void MainWindow::closeTab(QWidget *widget)
602{
603 bool isNewConnectionPage = widget == m_newConnectionWidget;
604
605 if (!isNewConnectionPage) {
606 RemoteView *view = m_remoteViewMap.value(widget);
607 m_remoteViewMap.remove(m_remoteViewMap.key(view));
608 view->startQuitting();
609#ifdef TELEPATHY_SUPPORT
610 m_tubesManager->closeTube(view->url());
611#endif
612 widget->deleteLater();
613 }
614
615 m_tabWidget->removePage(widget);
616
617 // if closing the last connection, create new connection tab
618 if (m_tabWidget->count() == 0) {
619 newConnectionPage(false);
620 }
621
622 // if the newConnectionWidget is the only tab and we are fullscreen, switch to window mode
623 if (m_fullscreenWindow && m_tabWidget->count() == 1 && m_tabWidget->currentWidget() == m_newConnectionWidget) {
624 switchFullscreen();
625 }
626}
627
628void MainWindow::openTabSettings(QWidget *widget)
629{
630 RemoteViewScrollArea *scrollArea = qobject_cast<RemoteViewScrollArea*>(widget);
631 if (!scrollArea) return;
632 RemoteView *view = qobject_cast<RemoteView*>(scrollArea->widget());
633 if (!view) return;
634
635 const QString url = view->url().url();
636 kDebug(5010) << url;
637
638 showSettingsDialog(url);
639}
640
641void MainWindow::showSettingsDialog(const QString &url)
642{
643 HostPreferences *prefs = 0;
644
645 foreach(RemoteViewFactory *factory, remoteViewFactoriesList()) {
646 if (factory->supportsUrl(url)) {
647 prefs = factory->createHostPreferences(Settings::self()->config()->group("hostpreferences").group(url), this);
648 if (prefs) {
649 kDebug(5010) << "Found plugin to handle url (" + url + "): " + prefs->metaObject()->className();
650 } else {
651 kDebug(5010) << "Found plugin to handle url (" + url + "), but plugin does not provide preferences";
652 }
653 }
654 }
655
656 if (prefs) {
657 prefs->setShownWhileConnected(true);
658 prefs->showDialog(this);
659 } else {
660 KMessageBox::error(this,
661 i18n("The selected host cannot be handled."),
662 i18n("Unusable URL"));
663 }
664}
665
666void MainWindow::showConnectionContextMenu(const QPoint &pos)
667{
668 // QTableView does not take headers into account when it does mapToGlobal(), so calculate the offset
669 QPoint offset = QPoint(m_newConnectionTableView->verticalHeader()->size().width(),
670 m_newConnectionTableView->horizontalHeader()->size().height());
671 QModelIndex index = m_newConnectionTableView->indexAt(pos);
672
673 if (!index.isValid())
674 return;
675
676 const QString url = index.data(10001).toString();
677 const QString title = index.model()->index(index.row(), RemoteDesktopsModel::Title).data(Qt::DisplayRole).toString();
678 const QString source = index.model()->index(index.row(), RemoteDesktopsModel::Source).data(Qt::DisplayRole).toString();
679
680 KMenu *menu = new KMenu(m_newConnectionTableView);
681 menu->addTitle(url);
682
683 QAction *connectAction = menu->addAction(KIcon("network-connect"), i18n("Connect"));
684 QAction *renameAction = menu->addAction(KIcon("edit-rename"), i18n("Rename"));
685 QAction *settingsAction = menu->addAction(KIcon("configure"), i18n("Settings"));
686 QAction *deleteAction = menu->addAction(KIcon("edit-delete"), i18n("Delete"));
687
688 // not very clean, but it works,
689 if (!(source == i18nc("Where each displayed link comes from", "Bookmarks") ||
690 source == i18nc("Where each displayed link comes from", "History"))) {
691 renameAction->setEnabled(false);
692 deleteAction->setEnabled(false);
693 }
694
695 QAction *selectedAction = menu->exec(m_newConnectionTableView->mapToGlobal(pos + offset));
696
697 if (selectedAction == connectAction) {
698 openFromRemoteDesktopsModel(index);
699 } else if (selectedAction == renameAction) {
700 //TODO: use inline editor if possible
701 bool ok = false;
702 const QString newTitle = KInputDialog::getText(i18n("Rename %1", title), i18n("Rename %1 to", title), title, &ok, this);
703 if (ok && !newTitle.isEmpty()) {
704 BookmarkManager::updateTitle(m_bookmarkManager->getManager(), url, newTitle);
705 }
706 } else if (selectedAction == settingsAction) {
707 showSettingsDialog(url);
708 } else if (selectedAction == deleteAction) {
709
710 if (KMessageBox::warningContinueCancel(this, i18n("Are you sure you want to delete %1?", url), i18n("Delete %1", title),
711 KStandardGuiItem::del()) == KMessageBox::Continue) {
712 BookmarkManager::removeByUrl(m_bookmarkManager->getManager(), url);
713 }
714 }
715
716 menu->deleteLater();
717}
718
719void MainWindow::tabContextMenu(QWidget *widget, const QPoint &point)
720{
721 RemoteViewScrollArea *scrollArea = qobject_cast<RemoteViewScrollArea*>(widget);
722 if (!scrollArea) return;
723 RemoteView *view = qobject_cast<RemoteView*>(scrollArea->widget());
724 if (!view) return;
725
726 const QString url = view->url().prettyUrl(KUrl::RemoveTrailingSlash);
727 kDebug(5010) << url;
728
729 KMenu *menu = new KMenu(this);
730 menu->addTitle(url);
731 QAction *bookmarkAction = menu->addAction(KIcon("bookmark-new"), i18n("Add Bookmark"));
732 QAction *closeAction = menu->addAction(KIcon("tab-close"), i18n("Close Tab"));
733 QAction *selectedAction = menu->exec(point);
734 if (selectedAction) {
735 if (selectedAction == closeAction) {
736 closeTab(widget);
737 } else if (selectedAction == bookmarkAction) {
738 m_bookmarkManager->addManualBookmark(url, url);
739 }
740 }
741 menu->deleteLater();
742}
743
744void MainWindow::showLocalCursor(bool showLocalCursor)
745{
746 kDebug(5010) << showLocalCursor;
747
748 RemoteView* view = currentRemoteView();
749 view->showDotCursor(showLocalCursor ? RemoteView::CursorOn : RemoteView::CursorOff);
750 view->hostPreferences()->setShowLocalCursor(showLocalCursor);
751 saveHostPrefs(view);
752}
753
754void MainWindow::viewOnly(bool viewOnly)
755{
756 kDebug(5010) << viewOnly;
757
758 RemoteView* view = currentRemoteView();
759 view->setViewOnly(viewOnly);
760 view->hostPreferences()->setViewOnly(viewOnly);
761 saveHostPrefs(view);
762}
763
764void MainWindow::grabAllKeys(bool grabAllKeys)
765{
766 kDebug(5010);
767
768 RemoteView* view = currentRemoteView();
769 view->setGrabAllKeys(grabAllKeys);
770 view->hostPreferences()->setGrabAllKeys(grabAllKeys);
771 saveHostPrefs(view);
772}
773
774void MainWindow::scale(bool scale)
775{
776 kDebug(5010);
777
778 RemoteView* view = currentRemoteView();
779 view->enableScaling(scale);
780 if (m_fullscreenWindow)
781 view->hostPreferences()->setFullscreenScale(scale);
782 else
783 view->hostPreferences()->setWindowedScale(scale);
784
785 saveHostPrefs(view);
786}
787
788void MainWindow::showRemoteViewToolbar()
789{
790 kDebug(5010);
791
792 if (!m_toolBar) {
793 m_toolBar = new FloatingToolBar(m_fullscreenWindow, m_fullscreenWindow);
794 m_toolBar->winId(); // force it to be a native widget (prevents problem with QX11EmbedContainer)
795 m_toolBar->setSide(FloatingToolBar::Top);
796
797 KComboBox *sessionComboBox = new KComboBox(m_toolBar);
798 sessionComboBox->setStyleSheet("QComboBox:!editable{background:transparent;}");
799 sessionComboBox->setModel(m_tabWidget->getModel());
800 sessionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
801 sessionComboBox->setCurrentIndex(m_tabWidget->currentIndex());
802 connect(sessionComboBox, SIGNAL(activated(int)), m_tabWidget, SLOT(setCurrentIndex(int)));
803 connect(m_tabWidget, SIGNAL(currentChanged(int)), sessionComboBox, SLOT(setCurrentIndex(int)));
804 m_toolBar->addWidget(sessionComboBox);
805
806 QToolBar *buttonBox = new QToolBar(m_toolBar);
807
808 buttonBox->addAction(actionCollection()->action("new_connection"));
809 buttonBox->addAction(actionCollection()->action("switch_fullscreen"));
810
811 QAction *minimizeAction = new QAction(m_toolBar);
812 minimizeAction->setIcon(KIcon("go-down"));
813 minimizeAction->setText(i18n("Minimize Full Screen Window"));
814 connect(minimizeAction, SIGNAL(triggered()), m_fullscreenWindow, SLOT(showMinimized()));
815 buttonBox->addAction(minimizeAction);
816
817 buttonBox->addAction(actionCollection()->action("take_screenshot"));
818 buttonBox->addAction(actionCollection()->action("view_only"));
819 buttonBox->addAction(actionCollection()->action("show_local_cursor"));
820 buttonBox->addAction(actionCollection()->action("grab_all_keys"));
821 buttonBox->addAction(actionCollection()->action("scale"));
822 buttonBox->addAction(actionCollection()->action("disconnect"));
823 buttonBox->addAction(actionCollection()->action("file_quit"));
824
825 QAction *stickToolBarAction = new QAction(m_toolBar);
826 stickToolBarAction->setCheckable(true);
827 stickToolBarAction->setIcon(KIcon("object-locked"));
828 stickToolBarAction->setText(i18n("Stick Toolbar"));
829 connect(stickToolBarAction, SIGNAL(triggered(bool)), m_toolBar, SLOT(setSticky(bool)));
830 buttonBox->addAction(stickToolBarAction);
831
832 m_toolBar->addWidget(buttonBox);
833 }
834}
835
836void setActionStatus(QAction* action, bool enabled, bool visible, bool checked)
837{
838 action->setEnabled(enabled);
839 action->setVisible(visible);
840 action->setChecked(checked);
841}
842
843void MainWindow::updateActionStatus()
844{
845 kDebug(5010) << m_tabWidget->currentIndex();
846
847 bool enabled = true;
848
849 if (m_tabWidget->currentWidget() == m_newConnectionWidget)
850 enabled = false;
851
852 RemoteView* view = (m_currentRemoteView >= 0 && enabled) ? currentRemoteView() : 0;
853
854 actionCollection()->action("take_screenshot")->setEnabled(enabled);
855 actionCollection()->action("disconnect")->setEnabled(enabled);
856
857 setActionStatus(actionCollection()->action("view_only"),
858 enabled,
859 true,
860 view ? view->viewOnly() : false);
861
862 setActionStatus(actionCollection()->action("show_local_cursor"),
863 enabled,
864 view ? view->supportsLocalCursor() : false,
865 view ? view->dotCursorState() == RemoteView::CursorOn : false);
866
867 setActionStatus(actionCollection()->action("scale"),
868 enabled,
869 view ? view->supportsScaling() : false,
870 view ? view->scaling() : false);
871
872 setActionStatus(actionCollection()->action("grab_all_keys"),
873 enabled,
874 enabled,
875 view ? view->grabAllKeys() : false);
876}
877
878void MainWindow::preferences()
879{
880 // An instance of your dialog could be already created and could be
881 // cached, in which case you want to display the cached dialog
882 // instead of creating another one
883 if (PreferencesDialog::showDialog("preferences"))
884 return;
885
886 // KConfigDialog didn't find an instance of this dialog, so lets
887 // create it:
888 PreferencesDialog *dialog = new PreferencesDialog(this, Settings::self());
889
890 // User edited the configuration - update your local copies of the
891 // configuration data
892 connect(dialog, SIGNAL(settingsChanged(QString)),
893 this, SLOT(updateConfiguration()));
894
895 dialog->show();
896}
897
898void MainWindow::updateConfiguration()
899{
900 if (!Settings::showStatusBar())
901 statusBar()->deleteLater();
902 else
903 statusBar()->showMessage(""); // force creation of statusbar
904
905 m_tabWidget->setTabBarHidden((m_tabWidget->count() <= 1 && !Settings::showTabBar()) || m_fullscreenWindow);
906 m_tabWidget->setTabPosition((KTabWidget::TabPosition) Settings::tabPosition());
907#if QT_VERSION >= 0x040500
908 m_tabWidget->setTabsClosable(Settings::tabCloseButton());
909#else
910 m_tabWidget->setCloseButtonEnabled(Settings::tabCloseButton());
911#endif
912 disconnect(m_tabWidget, SIGNAL(mouseMiddleClick(QWidget*)), this, SLOT(closeTab(QWidget*))); // just be sure it is not connected twice
913 if (Settings::tabMiddleClick())
914 connect(m_tabWidget, SIGNAL(mouseMiddleClick(QWidget*)), SLOT(closeTab(QWidget*)));
915
916 if (Settings::systemTrayIcon() && !m_systemTrayIcon) {
917 m_systemTrayIcon = new SystemTrayIcon(this);
918 if(m_fullscreenWindow) m_systemTrayIcon->setAssociatedWidget(m_fullscreenWindow);
919 } else if (m_systemTrayIcon) {
920 delete m_systemTrayIcon;
921 m_systemTrayIcon = 0;
922 }
923
924 // update the scroll areas background color
925 for (int i = 0; i < m_tabWidget->count(); ++i) {
926 QPalette palette = m_tabWidget->widget(i)->palette();
927 palette.setColor(QPalette::Dark, Settings::backgroundColor());
928 m_tabWidget->widget(i)->setPalette(palette);
929 }
930
931 // Send update configuration message to all views
932 foreach (RemoteView *view, m_remoteViewMap.values()) {
933 view->updateConfiguration();
934 }
935
936}
937
938void MainWindow::quit(bool systemEvent)
939{
940 const bool haveRemoteConnections = !m_remoteViewMap.isEmpty();
941 if (systemEvent || !haveRemoteConnections || KMessageBox::warningContinueCancel(this,
942 i18n("Are you sure you want to quit the KDE Remote Desktop Client?"),
943 i18n("Confirm Quit"),
944 KStandardGuiItem::quit(), KStandardGuiItem::cancel(),
945 "DoNotAskBeforeExit") == KMessageBox::Continue) {
946
947 if (Settings::rememberSessions()) { // remember open remote views for next startup
948 QStringList list;
949 foreach (RemoteView *view, m_remoteViewMap.values()) {
950 kDebug(5010) << view->url();
951 list.append(view->url().prettyUrl(KUrl::RemoveTrailingSlash));
952 }
953 Settings::setOpenSessions(list);
954 }
955
956 saveHostPrefs();
957
958 foreach (RemoteView *view, m_remoteViewMap.values()) {
959 view->startQuitting();
960 }
961
962 Settings::self()->writeConfig();
963
964 qApp->quit();
965 }
966}
967
968void MainWindow::configureNotifications()
969{
970 KNotifyConfigWidget::configure(this);
971}
972
973void MainWindow::showMenubar()
974{
975 if (m_menubarAction->isChecked())
976 menuBar()->show();
977 else
978 menuBar()->hide();
979}
980
981bool MainWindow::eventFilter(QObject *obj, QEvent *event)
982{
983 // check for close events from the fullscreen window.
984 if (obj == m_fullscreenWindow && event->type() == QEvent::Close) {
985 quit(true);
986 }
987 // allow other events to pass through.
988 return QObject::eventFilter(obj, event);
989}
990
991void MainWindow::closeEvent(QCloseEvent *event)
992{
993 if (event->spontaneous()) { // Returns true if the event originated outside the application (a system event); otherwise returns false.
994 event->ignore();
995 if (Settings::systemTrayIcon()) {
996 hide(); // just hide the mainwindow, keep it in systemtray
997 } else {
998 quit();
999 }
1000 } else {
1001 quit(true);
1002 }
1003}
1004
1005void MainWindow::saveProperties(KConfigGroup &group)
1006{
1007 kDebug(5010);
1008 KMainWindow::saveProperties(group);
1009 saveHostPrefs();
1010}
1011
1012
1013void MainWindow::saveHostPrefs()
1014{
1015 foreach (RemoteView *view, m_remoteViewMap.values()) {
1016 saveHostPrefs(view);
1017 view->startQuitting();
1018 }
1019}
1020
1021void MainWindow::saveHostPrefs(RemoteView* view)
1022{
1023 // should saving this be a user option?
1024 if (view && view->scaling()) {
1025 QSize viewSize = m_tabWidget->currentWidget()->size();
1026 kDebug(5010) << "saving window size:" << viewSize;
1027 view->hostPreferences()->setWidth(viewSize.width());
1028 view->hostPreferences()->setHeight(viewSize.height());
1029 }
1030
1031 Settings::self()->config()->sync();
1032}
1033
1034void MainWindow::tabChanged(int index)
1035{
1036 kDebug(5010) << index;
1037
1038 m_tabWidget->setTabBarHidden((m_tabWidget->count() <= 1 && !Settings::showTabBar()) || m_fullscreenWindow);
1039
1040 m_currentRemoteView = index;
1041
1042 if (m_tabWidget->currentWidget() == m_newConnectionWidget) {
1043 m_currentRemoteView = -1;
1044 if(m_addressInput) m_addressInput->setFocus();
1045 }
1046
1047 const QString tabTitle = m_tabWidget->tabText(index).remove('&');
1048 setCaption(tabTitle == i18n("New Connection") ? QString() : tabTitle);
1049
1050 updateActionStatus();
1051}
1052
1053QWidget* MainWindow::newConnectionWidget()
1054{
1055 if (m_newConnectionWidget)
1056 return m_newConnectionWidget;
1057
1058 m_newConnectionWidget = new QWidget(this);
1059
1060 QVBoxLayout *startLayout = new QVBoxLayout(m_newConnectionWidget);
1061 startLayout->setContentsMargins(QMargins(8, 12, 8, 4));
1062
1063 QLabel *headerLabel = new QLabel(m_newConnectionWidget);
1064 headerLabel->setText(i18n("<h1>KDE Remote Desktop Client</h1><br />Enter or select the address of the desktop you would like to connect to."));
1065
1066 QLabel *headerIconLabel = new QLabel(m_newConnectionWidget);
1067 headerIconLabel->setPixmap(KIcon("krdc").pixmap(80));
1068
1069 QHBoxLayout *headerLayout = new QHBoxLayout;
1070 headerLayout->addWidget(headerLabel, 1, Qt::AlignTop);
1071 headerLayout->addWidget(headerIconLabel);
1072 startLayout->addLayout(headerLayout);
1073
1074 QSortFilterProxyModel *remoteDesktopsModelProxy = new QSortFilterProxyModel(this);
1075 remoteDesktopsModelProxy->setSourceModel(m_remoteDesktopsModel);
1076 remoteDesktopsModelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
1077 remoteDesktopsModelProxy->setFilterRole(10002);
1078
1079 {
1080 QHBoxLayout *connectLayout = new QHBoxLayout;
1081
1082 QLabel *addressLabel = new QLabel(i18n("Connect to:"), m_newConnectionWidget);
1083 m_protocolInput = new KComboBox(m_newConnectionWidget);
1084 m_addressInput = new KLineEdit(m_newConnectionWidget);
1085 m_addressInput->setClearButtonShown(true);
1086 m_addressInput->setClickMessage(i18n("Type here to connect to an address and filter the list."));
1087 connect(m_addressInput, SIGNAL(textChanged(QString)), remoteDesktopsModelProxy, SLOT(setFilterFixedString(QString)));
1088
1089 foreach(RemoteViewFactory *factory, m_remoteViewFactories) {
1090 m_protocolInput->addItem(factory->scheme());
1091 }
1092
1093 connect(m_addressInput, SIGNAL(returnPressed()), SLOT(newConnection()));
1094 m_addressInput->setToolTip(i18n("Type an IP or DNS Name here. Clear the line to get a list of connection methods."));
1095
1096 KPushButton *connectButton = new KPushButton(m_newConnectionWidget);
1097 connectButton->setToolTip(i18n("Goto Address"));
1098 connectButton->setIcon(KIcon("go-jump-locationbar"));
1099 connect(connectButton, SIGNAL(clicked()), SLOT(newConnection()));
1100
1101 connectLayout->addWidget(addressLabel);
1102 connectLayout->addWidget(m_protocolInput);
1103 connectLayout->addWidget(m_addressInput, 1);
1104 connectLayout->addWidget(connectButton);
1105 connectLayout->setContentsMargins(QMargins(0, 6, 0, 10));
1106 startLayout->addLayout(connectLayout);
1107 }
1108
1109 {
1110 m_newConnectionTableView = new QTableView(m_newConnectionWidget);
1111 m_newConnectionTableView->setModel(remoteDesktopsModelProxy);
1112
1113 // set up the view so it looks nice
1114 m_newConnectionTableView->setItemDelegate(new ConnectionDelegate(m_newConnectionTableView));
1115 m_newConnectionTableView->setShowGrid(false);
1116 m_newConnectionTableView->setSelectionMode(QAbstractItemView::NoSelection);
1117 m_newConnectionTableView->verticalHeader()->hide();
1118 m_newConnectionTableView->verticalHeader()->setDefaultSectionSize(
1119 m_newConnectionTableView->fontMetrics().height() + 3);
1120 m_newConnectionTableView->horizontalHeader()->setStretchLastSection(true);
1121 m_newConnectionTableView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
1122 m_newConnectionTableView->setAlternatingRowColors(true);
1123 // set up sorting and actions (double click open, right click custom menu)
1124 m_newConnectionTableView->setSortingEnabled(true);
1125 m_newConnectionTableView->sortByColumn(Settings::connectionListSortColumn(), Qt::SortOrder(Settings::connectionListSortOrder()));
1126 connect(m_newConnectionTableView->horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
1127 SLOT(saveConnectionListSort(int,Qt::SortOrder)));
1128 connect(m_newConnectionTableView, SIGNAL(doubleClicked(QModelIndex)),
1129 SLOT(openFromRemoteDesktopsModel(QModelIndex)));
1130 m_newConnectionTableView->setContextMenuPolicy(Qt::CustomContextMenu);
1131 connect(m_newConnectionTableView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showConnectionContextMenu(QPoint)));
1132
1133 startLayout->addWidget(m_newConnectionTableView);
1134 }
1135
1136 return m_newConnectionWidget;
1137}
1138
1139void MainWindow::saveConnectionListSort(const int logicalindex, const Qt::SortOrder order)
1140{
1141 Settings::setConnectionListSortColumn(logicalindex);
1142 Settings::setConnectionListSortOrder(order);
1143 Settings::self()->writeConfig();
1144}
1145
1146void MainWindow::newConnectionPage(bool clearInput)
1147{
1148 const int indexOfNewConnectionWidget = m_tabWidget->indexOf(m_newConnectionWidget);
1149 if (indexOfNewConnectionWidget >= 0)
1150 m_tabWidget->setCurrentIndex(indexOfNewConnectionWidget);
1151 else {
1152 const int index = m_tabWidget->addTab(newConnectionWidget(), i18n("New Connection"));
1153 m_tabWidget->setCurrentIndex(index);
1154 }
1155 if(clearInput) {
1156 m_addressInput->clear();
1157 } else {
1158 m_addressInput->selectAll();
1159 }
1160 m_addressInput->setFocus();
1161}
1162
1163QMap<QWidget *, RemoteView *> MainWindow::remoteViewList() const
1164{
1165 return m_remoteViewMap;
1166}
1167
1168QList<RemoteViewFactory *> MainWindow::remoteViewFactoriesList() const
1169{
1170 return m_remoteViewFactories.values();
1171}
1172
1173RemoteView* MainWindow::currentRemoteView() const
1174{
1175 if (m_currentRemoteView >= 0) {
1176 return m_remoteViewMap.value(m_tabWidget->widget(m_currentRemoteView));
1177 } else {
1178 return 0;
1179 }
1180}
1181
1182void MainWindow::createDockWidget()
1183{
1184 QDockWidget *remoteDesktopsDockWidget = new QDockWidget(this);
1185 QWidget *remoteDesktopsDockLayoutWidget = new QWidget(remoteDesktopsDockWidget);
1186 QVBoxLayout *remoteDesktopsDockLayout = new QVBoxLayout(remoteDesktopsDockLayoutWidget);
1187 remoteDesktopsDockWidget->setObjectName("remoteDesktopsDockWidget"); // required for saving position / state
1188 remoteDesktopsDockWidget->setWindowTitle(i18n("Remote Desktops"));
1189 QFontMetrics fontMetrics(remoteDesktopsDockWidget->font());
1190 remoteDesktopsDockWidget->setMinimumWidth(fontMetrics.width("vnc://192.168.100.100:6000"));
1191 actionCollection()->addAction("remote_desktop_dockwidget",
1192 remoteDesktopsDockWidget->toggleViewAction());
1193
1194 m_dockWidgetTableView = new QTableView(remoteDesktopsDockLayoutWidget);
1195 m_remoteDesktopsModel = new RemoteDesktopsModel(this);
1196 QSortFilterProxyModel *remoteDesktopsModelProxy = new QSortFilterProxyModel(this);
1197 remoteDesktopsModelProxy->setSourceModel(m_remoteDesktopsModel);
1198 remoteDesktopsModelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
1199 remoteDesktopsModelProxy->setFilterRole(10002);
1200 m_dockWidgetTableView->setModel(remoteDesktopsModelProxy);
1201
1202 m_dockWidgetTableView->setShowGrid(false);
1203 m_dockWidgetTableView->verticalHeader()->hide();
1204 m_dockWidgetTableView->verticalHeader()->setDefaultSectionSize(
1205 m_dockWidgetTableView->fontMetrics().height() + 2);
1206 m_dockWidgetTableView->horizontalHeader()->hide();
1207 m_dockWidgetTableView->horizontalHeader()->setStretchLastSection(true);
1208 // hide all columns, then show the one we want
1209 for (int i=0; i < remoteDesktopsModelProxy->columnCount(); i++) {
1210 m_dockWidgetTableView->hideColumn(i);
1211 }
1212 m_dockWidgetTableView->showColumn(RemoteDesktopsModel::Title);
1213 m_dockWidgetTableView->sortByColumn(RemoteDesktopsModel::Title, Qt::AscendingOrder);
1214
1215 connect(m_dockWidgetTableView, SIGNAL(doubleClicked(QModelIndex)),
1216 SLOT(openFromRemoteDesktopsModel(QModelIndex)));
1217
1218 KLineEdit *filterLineEdit = new KLineEdit(remoteDesktopsDockLayoutWidget);
1219 filterLineEdit->setClickMessage(i18n("Filter"));
1220 filterLineEdit->setClearButtonShown(true);
1221 connect(filterLineEdit, SIGNAL(textChanged(QString)), remoteDesktopsModelProxy, SLOT(setFilterFixedString(QString)));
1222 remoteDesktopsDockLayout->addWidget(filterLineEdit);
1223 remoteDesktopsDockLayout->addWidget(m_dockWidgetTableView);
1224 remoteDesktopsDockWidget->setWidget(remoteDesktopsDockLayoutWidget);
1225 addDockWidget(Qt::LeftDockWidgetArea, remoteDesktopsDockWidget);
1226}
1227
1228#include "mainwindow.moc"
1229