1 /****************************************************************************
3 ** Copyright (C) 2007 - 2009 Urs Wolfer <uwolfer @ kde.org>
5 ** This file is part of KDE.
7 ** This program is free software; you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License as published by
9 ** the Free Software Foundation; either version 2 of the License, or
10 ** (at your option) any later version.
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; see the file COPYING. If not, write to
19 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 ** Boston, MA 02110-1301, USA.
22 ****************************************************************************/
24 #include "mainwindow.h"
26 #include "remoteview.h"
28 #include "config/preferencesdialog.h"
29 #include "floatingtoolbar.h"
30 #include "bookmarkmanager.h"
31 #include "remotedesktopsmodel.h"
32 #include "systemtrayicon.h"
33 #include "hostpreferences.h"
36 #include "zeroconf/zeroconfpage.h"
40 #include <KActionCollection>
41 #include <KActionMenu>
42 #include <KApplication>
44 #include <KEditToolBar>
49 #include <KMessageBox>
50 #include <KNotifyConfigWidget>
51 #include <KPluginInfo>
52 #include <KPushButton>
54 #include <KShortcutsDialog>
57 #include <KToggleAction>
58 #include <KToggleFullScreenAction>
59 #include <KUrlNavigator>
60 #include <KServiceTypeTrader>
61 #include <KStandardDirs>
64 #include <QCloseEvent>
65 #include <QDesktopWidget>
66 #include <QDockWidget>
67 #include <QFontMetrics>
68 #include <QHeaderView>
71 #include <QScrollArea>
73 #include <QToolButton>
77 MainWindow::MainWindow(QWidget
*parent
)
78 : KXmlGuiWindow(parent
),
79 m_fullscreenWindow(0),
83 m_currentRemoteView(-1),
84 m_showStartPage(false),
92 setStandardToolBarMenuEnabled(true);
94 m_tabWidget
= new KTabWidget(this);
95 m_tabWidget
->setTabBarHidden(!Settings::showTabBar());
96 m_tabWidget
->setTabPosition((KTabWidget::TabPosition
) Settings::tabPosition());
98 #if QT_VERSION <= 0x040500
99 m_tabWidget
->setTabsClosable(Settings::tabCloseButton());
101 m_tabWidget
->setCloseButtonEnabled(Settings::tabCloseButton());
104 connect(m_tabWidget
, SIGNAL(closeRequest(QWidget
*)), SLOT(closeTab(QWidget
*)));
106 if (Settings::tabMiddleClick())
107 connect(m_tabWidget
, SIGNAL(mouseMiddleClick(QWidget
*)), SLOT(closeTab(QWidget
*)));
109 connect(m_tabWidget
, SIGNAL(mouseDoubleClick(QWidget
*)), SLOT(openTabSettings(QWidget
*)));
110 connect(m_tabWidget
, SIGNAL(contextMenu(QWidget
*, const QPoint
&)), SLOT(tabContextMenu(QWidget
*, const QPoint
&)));
112 m_tabWidget
->setMinimumSize(600, 400);
113 setCentralWidget(m_tabWidget
);
115 QDockWidget
*remoteDesktopsDockWidget
= new QDockWidget(this);
116 remoteDesktopsDockWidget
->setObjectName("remoteDesktopsDockWidget"); // required for saving position / state
117 remoteDesktopsDockWidget
->setWindowTitle(i18n("Remote desktops"));
118 QFontMetrics
fontMetrics(remoteDesktopsDockWidget
->font());
119 remoteDesktopsDockWidget
->setMinimumWidth(fontMetrics
.width("vnc://192.168.100.100:6000"));
120 actionCollection()->addAction("remote_desktop_dockwidget",
121 remoteDesktopsDockWidget
->toggleViewAction());
122 m_remoteDesktopsTreeView
= new QTreeView(remoteDesktopsDockWidget
);
123 RemoteDesktopsModel
*remoteDesktopsModel
= new RemoteDesktopsModel(this);
124 connect(remoteDesktopsModel
, SIGNAL(modelReset()), this, SLOT(expandTreeViewItems()));
125 m_remoteDesktopsTreeView
->setModel(remoteDesktopsModel
);
126 m_remoteDesktopsTreeView
->header()->hide();
127 m_remoteDesktopsTreeView
->expandAll();
128 m_remoteDesktopsTreeView
->setItemsExpandable(false);
129 m_remoteDesktopsTreeView
->setRootIsDecorated(false);
130 connect(m_remoteDesktopsTreeView
, SIGNAL(doubleClicked(const QModelIndex
&)),
131 SLOT(openFromDockWidget(const QModelIndex
&)));
133 remoteDesktopsDockWidget
->setWidget(m_remoteDesktopsTreeView
);
134 addDockWidget(Qt::LeftDockWidgetArea
, remoteDesktopsDockWidget
);
136 createGUI("krdcui.rc");
138 if (Settings::systemTrayIcon()) {
139 m_systemTrayIcon
= new SystemTrayIcon(this);
140 m_systemTrayIcon
->setVisible(true);
143 connect(m_tabWidget
, SIGNAL(currentChanged(int)), SLOT(tabChanged(int)));
145 statusBar()->showMessage(i18n("KDE Remote Desktop Client started"));
147 updateActionStatus(); // disable remote view actions
149 if (Settings::showStartPage())
152 setAutoSaveSettings(); // e.g toolbar position, mainwindow size, ...
154 m_addressNavigator
->setFocus();
156 if (Settings::rememberSessions()) // give some time to create and show the window first
157 QTimer::singleShot(100, this, SLOT(restoreOpenSessions()));
160 MainWindow::~MainWindow()
164 void MainWindow::setupActions()
166 foreach(RemoteViewFactory
*factory
, m_remoteViewFactories
) {
167 QAction
*connectionAction
= actionCollection()->addAction("new_" + factory
->scheme() + "_connection");
168 connectionAction
->setProperty("schemeString", factory
->scheme());
169 connectionAction
->setProperty("toolTipString", factory
->connectToolTipText());
170 connectionAction
->setText(factory
->connectActionText());
171 connectionAction
->setIcon(KIcon("network-connect"));
172 connect(connectionAction
, SIGNAL(triggered()), SLOT(newConnectionToolTip()));
175 QAction
*zeroconfAction
= actionCollection()->addAction("zeroconf_page");
176 zeroconfAction
->setText(i18n("Browse Remote Desktop Services on Local Network..."));
177 zeroconfAction
->setIcon(KIcon("network-connect"));
178 connect(zeroconfAction
, SIGNAL(triggered()), SLOT(createZeroconfPage()));
179 #ifndef BUILD_ZEROCONF
180 zeroconfAction
->deleteLater();
183 QAction
*screenshotAction
= actionCollection()->addAction("take_screenshot");
184 screenshotAction
->setText(i18n("Copy Screenshot to Clipboard"));
185 screenshotAction
->setIcon(KIcon("ksnapshot"));
186 connect(screenshotAction
, SIGNAL(triggered()), SLOT(takeScreenshot()));
188 QAction
*fullscreenAction
= actionCollection()->addAction("switch_fullscreen");
189 fullscreenAction
->setText(i18n("Switch to Fullscreen Mode"));
190 fullscreenAction
->setIcon(KIcon("view-fullscreen"));
191 connect(fullscreenAction
, SIGNAL(triggered()), SLOT(switchFullscreen()));
193 QAction
*viewOnlyAction
= actionCollection()->addAction("view_only");
194 viewOnlyAction
->setCheckable(true);
195 viewOnlyAction
->setText(i18n("View Only"));
196 viewOnlyAction
->setIcon(KIcon("document-preview"));
197 connect(viewOnlyAction
, SIGNAL(triggered(bool)), SLOT(viewOnly(bool)));
199 QAction
*disconnectAction
= actionCollection()->addAction("disconnect");
200 disconnectAction
->setText(i18n("Disconnect"));
201 disconnectAction
->setIcon(KIcon("system-log-out"));
202 disconnectAction
->setShortcut(QKeySequence::Close
);
203 connect(disconnectAction
, SIGNAL(triggered()), SLOT(disconnectHost()));
205 QAction
*showLocalCursorAction
= actionCollection()->addAction("show_local_cursor");
206 showLocalCursorAction
->setCheckable(true);
207 showLocalCursorAction
->setIcon(KIcon("input-mouse"));
208 showLocalCursorAction
->setText(i18n("Show Local Cursor"));
209 connect(showLocalCursorAction
, SIGNAL(triggered(bool)), SLOT(showLocalCursor(bool)));
211 QAction
*grabAllKeysAction
= actionCollection()->addAction("grab_all_keys");
212 grabAllKeysAction
->setCheckable(true);
213 grabAllKeysAction
->setIcon(KIcon("configure-shortcuts"));
214 grabAllKeysAction
->setText(i18n("Grab all possible keys"));
215 connect(grabAllKeysAction
, SIGNAL(triggered(bool)), SLOT(grabAllKeys(bool)));
217 QAction
*scaleAction
= actionCollection()->addAction("scale");
218 scaleAction
->setCheckable(true);
219 scaleAction
->setIcon(KIcon("zoom-fit-best"));
220 scaleAction
->setText(i18n("Scale remote screen to fit window size"));
221 connect(scaleAction
, SIGNAL(triggered(bool)), SLOT(scale(bool)));
223 KStandardAction::quit(this, SLOT(quit()), actionCollection());
224 KStandardAction::preferences(this, SLOT(preferences()), actionCollection());
225 KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection());
226 KStandardAction::keyBindings(this, SLOT(configureKeys()), actionCollection());
227 QAction
*configNotifyAction
= KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection());
228 configNotifyAction
->setVisible(false);
229 m_menubarAction
= KStandardAction::showMenubar(this, SLOT(showMenubar()), actionCollection());
230 m_menubarAction
->setChecked(!menuBar()->isHidden());
232 const QString
initialProtocol(!m_remoteViewFactories
.isEmpty() ? (*m_remoteViewFactories
.begin())->scheme() : QString());
233 m_addressNavigator
= new KUrlNavigator(0, KUrl(initialProtocol
+ "://"), this);
236 foreach(RemoteViewFactory
*factory
, m_remoteViewFactories
) {
237 schemes
<< factory
->scheme();
239 m_addressNavigator
->setCustomProtocols(schemes
);
241 m_addressNavigator
->setUrlEditable(Settings::normalUrlInputLine());
242 connect(m_addressNavigator
, SIGNAL(returnPressed()), SLOT(newConnection()));
244 QLabel
*addressLabel
= new QLabel(i18n("Remote desktop:"), this);
246 QWidget
*addressWidget
= new QWidget(this);
247 QHBoxLayout
*addressLayout
= new QHBoxLayout(addressWidget
);
248 addressLayout
->setMargin(0);
249 addressLayout
->addWidget(addressLabel
);
250 addressLayout
->addWidget(m_addressNavigator
, 1);
252 KAction
*addressLineAction
= new KAction(i18nc("Title for remote address input action", "Address"), this);
253 actionCollection()->addAction("address_line", addressLineAction
);
254 addressLineAction
->setDefaultWidget(addressWidget
);
256 QAction
*gotoAction
= actionCollection()->addAction("goto_address");
257 gotoAction
->setText(i18n("Goto Address"));
258 gotoAction
->setIcon(KIcon("go-jump-locationbar"));
259 connect(gotoAction
, SIGNAL(triggered()), SLOT(newConnection()));
261 KActionMenu
*bookmarkMenu
= new KActionMenu(i18n("Bookmarks"), actionCollection());
262 m_bookmarkManager
= new BookmarkManager(actionCollection(), bookmarkMenu
->menu(), this);
263 actionCollection()->addAction("bookmark" , bookmarkMenu
);
264 connect(m_bookmarkManager
, SIGNAL(openUrl(KUrl
)), SLOT(newConnection(KUrl
)));
267 void MainWindow::loadAllPlugins()
269 const KService::List offers
= KServiceTypeTrader::self()->query("KRDC/Plugin");
271 KConfigGroup
conf(KGlobal::config(), "Plugins");
273 for (int i
= 0; i
< offers
.size(); i
++) {
274 KService::Ptr offer
= offers
[i
];
276 RemoteViewFactory
*remoteView
;
278 KPluginInfo
description(offer
);
279 description
.load(conf
);
281 const bool selected
= description
.isPluginEnabled();
284 if ((remoteView
= createPluginFromService(offer
)) != 0) {
285 kDebug(5010) << "### Plugin " + description
.name() + " found ###";
286 kDebug(5010) << "# Version:" << description
.version();
287 kDebug(5010) << "# Description:" << description
.comment();
288 kDebug(5010) << "# Author:" << description
.author();
289 const int sorting
= offer
->property("X-KDE-KRDC-Sorting").toInt();
290 kDebug(5010) << "# Sorting:" << sorting
;
292 m_remoteViewFactories
.insert(sorting
, remoteView
);
294 kDebug(5010) << "Error loading KRDC plugin (" << (offers
[i
])->library() << ')';
297 kDebug(5010) << "# Plugin " + description
.name() + " found, however it's not activated, skipping...";
303 RemoteViewFactory
*MainWindow::createPluginFromService(const KService::Ptr
&service
)
305 //try to load the specified library
306 KPluginFactory
*factory
= KPluginLoader(service
->library()).factory();
309 kError(5010) << "KPluginFactory could not load the plugin:" << service
->library();
313 RemoteViewFactory
*plugin
= factory
->create
<RemoteViewFactory
>();
318 void MainWindow::restoreOpenSessions()
320 const QStringList list
= Settings::openSessions();
321 QStringList::ConstIterator it
= list
.begin();
322 QStringList::ConstIterator end
= list
.end();
329 void MainWindow::newConnection(const KUrl
&newUrl
, bool switchFullscreenWhenConnected
)
331 m_switchFullscreenWhenConnected
= switchFullscreenWhenConnected
;
333 const KUrl url
= newUrl
.isEmpty() ? m_addressNavigator
->uncommittedUrl() : newUrl
;
335 if (!url
.isValid() || (url
.host().isEmpty() && url
.port() < 0)
336 || !url
.path().isEmpty()) {
337 KMessageBox::error(this,
338 i18n("The entered address does not have the required form."),
339 i18n("Malformed URL"));
343 m_addressNavigator
->setUrl(KUrl(url
.scheme().toLower() + "://"));
345 RemoteView
*view
= 0;
346 KConfigGroup configGroup
= Settings::self()->config()->group("hostpreferences").group(url
.prettyUrl(KUrl::RemoveTrailingSlash
));
348 foreach(RemoteViewFactory
*factory
, m_remoteViewFactories
) {
349 if (factory
->supportsUrl(url
)) {
350 view
= factory
->createView(this, url
, configGroup
);
351 kDebug(5010) << "Found plugin to handle url (" + url
.url() + "): " + view
->metaObject()->className();
357 KMessageBox::error(this,
358 i18n("The entered address cannot be handled."),
359 i18n("Unusable URL"));
363 // Configure the view
364 HostPreferences
* prefs
= view
->hostPreferences();
365 if (! prefs
->showDialogIfNeeded()) {
372 view
->setGrabAllKeys(prefs
->grabAllKeys());
373 view
->showDotCursor(prefs
->showLocalCursor() ? RemoteView::CursorOn
: RemoteView::CursorOff
);
374 view
->setViewOnly(prefs
->viewOnly());
375 if (! switchFullscreenWhenConnected
) view
->enableScaling(prefs
->windowedScale());
377 connect(view
, SIGNAL(framebufferSizeChanged(int, int)), this, SLOT(resizeTabWidget(int, int)));
378 connect(view
, SIGNAL(statusChanged(RemoteView::RemoteStatus
)), this, SLOT(statusChanged(RemoteView::RemoteStatus
)));
380 m_remoteViewList
.append(view
);
382 // view->resize(0, 0);
384 int numNonRemoteView
= 0;
390 if (m_remoteViewList
.size() == 1) {
391 kDebug(5010) << "First connection, restoring window size)";
392 restoreWindowSize(view
->hostPreferences()->configGroup());
395 QScrollArea
*scrollArea
= createScrollArea(m_tabWidget
, view
);
397 const int newIndex
= m_tabWidget
->addTab(scrollArea
, KIcon("krdc"), url
.prettyUrl(KUrl::RemoveTrailingSlash
));
398 m_tabWidget
->setCurrentIndex(newIndex
);
399 tabChanged(newIndex
); // force to update m_currentRemoteView (tabChanged is not emitted when start page has been disabled)
404 void MainWindow::openFromDockWidget(const QModelIndex
&index
)
406 const QString data
= index
.data(Qt::UserRole
).toString();
407 if (!data
.isEmpty()) {
408 const KUrl
url(data
);
409 // first check if url has already been opened; in case show the tab
410 for (int i
= 0; i
< m_remoteViewList
.count(); ++i
) {
411 if (m_remoteViewList
.at(i
)->url() == url
) {
412 int numNonRemoteView
= 0;
417 m_tabWidget
->setCurrentIndex(i
+ numNonRemoteView
);
425 void MainWindow::resizeTabWidget(int w
, int h
)
427 kDebug(5010) << "tabwidget resize: w: " << w
<< ", h: " << h
;
429 RemoteView
* view
= m_currentRemoteView
>= 0 ? m_remoteViewList
.at(m_currentRemoteView
) : NULL
;
430 if (view
&& view
->scaling()) return;
432 if (m_topBottomBorder
== 0) { // the values are not cached yet
433 QScrollArea
*tmp
= qobject_cast
<QScrollArea
*>(m_tabWidget
->currentWidget());
435 m_leftRightBorder
= m_tabWidget
->width() - m_tabWidget
->currentWidget()->width() + (2 * tmp
->frameWidth());
436 m_topBottomBorder
= m_tabWidget
->height() - m_tabWidget
->currentWidget()->height() + (2 * tmp
->frameWidth());
438 kDebug(5010) << "tabwidget border: w: " << m_leftRightBorder
<< ", h: " << m_topBottomBorder
;
441 const int newTabWidth
= w
+ m_leftRightBorder
;
442 const int newTabHeight
= h
+ m_topBottomBorder
;
444 const QSize newWindowSize
= size() - m_tabWidget
->size() + QSize(newTabWidth
, newTabHeight
);
446 const QSize desktopSize
= QSize(QApplication::desktop()->availableGeometry().width(),
447 QApplication::desktop()->availableGeometry().height());
449 if ((newWindowSize
.height() >= desktopSize
.height()) || (newWindowSize
.width() >= desktopSize
.width())) {
450 kDebug(5010) << "remote desktop needs more place than available -> show window maximized";
451 setWindowState(windowState() | Qt::WindowMaximized
);
455 //WORKAROUND: QTabWidget resize problem. Let's see if there is a clean solution for this issue.
456 m_tabWidget
->setMinimumSize(newTabWidth
, newTabHeight
);
457 m_tabWidget
->adjustSize();
458 QCoreApplication::processEvents();
459 m_tabWidget
->setMinimumSize(500, 400);
462 void MainWindow::statusChanged(RemoteView::RemoteStatus status
)
464 kDebug(5010) << status
;
466 // the remoteview is already deleted, so don't show it; otherwise it would crash
467 if (status
== RemoteView::Disconnecting
|| status
== RemoteView::Disconnected
)
470 const QString host
= m_remoteViewList
.at(m_currentRemoteView
)->host();
472 QString iconName
= "krdc";
476 case RemoteView::Connecting
:
477 iconName
= "network-connect";
478 message
= i18n("Connecting to %1", host
);
480 case RemoteView::Authenticating
:
481 iconName
= "dialog-password";
482 message
= i18n("Authenticating at %1", host
);
484 case RemoteView::Preparing
:
485 iconName
= "view-history";
486 message
= i18n("Preparing connection to %1", host
);
488 case RemoteView::Connected
:
490 message
= i18n("Connected to %1", host
);
492 // when started with command line fullscreen argument
493 if (m_switchFullscreenWhenConnected
) {
494 m_switchFullscreenWhenConnected
= false;
498 m_bookmarkManager
->addHistoryBookmark();
505 m_tabWidget
->setTabIcon(m_tabWidget
->currentIndex(), KIcon(iconName
));
506 statusBar()->showMessage(message
);
509 void MainWindow::takeScreenshot()
511 const QPixmap snapshot
= QPixmap::grabWidget(m_remoteViewList
.at(m_currentRemoteView
));
513 QApplication::clipboard()->setPixmap(snapshot
);
516 void MainWindow::switchFullscreen()
520 RemoteView
* view
= m_remoteViewList
.at(m_currentRemoteView
);
522 if (m_fullscreenWindow
) {
523 // Leaving fullscreen mode
524 view
->enableScaling(view
->hostPreferences()->windowedScale());
526 restoreGeometry(m_mainWindowGeometry
);
528 m_fullscreenWindow
->setWindowState(0);
529 m_fullscreenWindow
->hide();
531 QScrollArea
*scrollArea
= createScrollArea(m_tabWidget
, view
);
533 const int currentTab
= m_tabWidget
->currentIndex();
534 m_tabWidget
->insertTab(currentTab
, scrollArea
, m_tabWidget
->tabIcon(currentTab
), m_tabWidget
->tabText(currentTab
));
535 m_tabWidget
->removeTab(m_tabWidget
->currentIndex());
536 m_tabWidget
->setCurrentIndex(currentTab
);
538 resizeTabWidget(view
->sizeHint().width(), view
->sizeHint().height());
541 m_toolBar
->hideAndDestroy();
542 m_toolBar
->deleteLater();
546 actionCollection()->action("switch_fullscreen")->setIcon(KIcon("view-fullscreen"));
547 actionCollection()->action("switch_fullscreen")->setText(i18n("Switch to Fullscreen Mode"));
549 m_fullscreenWindow
->deleteLater();
551 m_fullscreenWindow
= 0;
553 // Entering fullscreen mode
554 m_fullscreenWindow
= new QWidget(this, Qt::Window
);
555 m_fullscreenWindow
->setWindowTitle(i18nc("window title when in fullscreen mode (for example displayed in tasklist)",
556 "KDE Remote Desktop Client (Fullscreen)"));
558 QScrollArea
*scrollArea
= createScrollArea(m_fullscreenWindow
, view
);
559 scrollArea
->setFrameShape(QFrame::NoFrame
);
561 QVBoxLayout
*fullscreenLayout
= new QVBoxLayout(m_fullscreenWindow
);
562 fullscreenLayout
->setMargin(0);
563 fullscreenLayout
->addWidget(scrollArea
);
565 MinimizePixel
*minimizePixel
= new MinimizePixel(m_fullscreenWindow
);
566 minimizePixel
->winId(); // force it to be a native widget (prevents problem with QX11EmbedContainer)
567 connect(minimizePixel
, SIGNAL(rightClicked()), m_fullscreenWindow
, SLOT(showMinimized()));
569 view
->enableScaling(view
->hostPreferences()->fullscreenScale());
571 m_fullscreenWindow
->show();
573 KToggleFullScreenAction::setFullScreen(m_fullscreenWindow
, true);
575 m_mainWindowGeometry
= saveGeometry();
578 showRemoteViewToolbar();
582 QScrollArea
*MainWindow::createScrollArea(QWidget
*parent
, RemoteView
*remoteView
)
584 RemoteViewScrollArea
*scrollArea
= new RemoteViewScrollArea(parent
);
585 scrollArea
->setAlignment(Qt::AlignHCenter
| Qt::AlignVCenter
);
587 connect(scrollArea
, SIGNAL(resized(int, int)), remoteView
, SLOT(scaleResize(int, int)));
589 QPalette palette
= scrollArea
->palette();
590 palette
.setColor(QPalette::Dark
, Settings::backgroundColor());
591 scrollArea
->setPalette(palette
);
593 scrollArea
->setBackgroundRole(QPalette::Dark
);
594 scrollArea
->setWidget(remoteView
);
599 void MainWindow::disconnectHost()
603 if (m_fullscreenWindow
) { // first close fullscreen view
607 QWidget
*tmp
= m_tabWidget
->currentWidget();
608 m_remoteViewList
.removeAt(m_currentRemoteView
);
609 m_tabWidget
->removeTab(m_tabWidget
->currentIndex());
615 void MainWindow::closeTab(QWidget
*widget
)
617 const int index
= m_tabWidget
->indexOf(widget
);
619 kDebug(5010) << index
;
621 if (m_showStartPage
&& index
== 0) {
622 KMessageBox::information(this, i18n("You can enable the start page in the settings again."));
624 Settings::setShowStartPage(false);
625 m_tabWidget
->removeTab(0);
626 m_showStartPage
= false;
630 #ifdef BUILD_ZEROCONF
631 if (widget
== m_zeroconfPage
) {
637 int numNonRemoteView
= 0;
643 if (index
- numNonRemoteView
>= 0)
644 m_remoteViewList
.removeAt(index
- numNonRemoteView
);
646 m_tabWidget
->removeTab(index
);
648 widget
->deleteLater();
651 void MainWindow::openTabSettings(QWidget
*widget
)
653 RemoteViewScrollArea
*scrollArea
= qobject_cast
<RemoteViewScrollArea
*>(widget
);
654 if (!scrollArea
) return;
655 RemoteView
*view
= qobject_cast
<RemoteView
*>(scrollArea
->widget());
658 const QString url
= view
->url().url();
661 HostPreferences
*prefs
= 0;
663 foreach(RemoteViewFactory
*factory
, remoteViewFactoriesList()) {
664 if (factory
->supportsUrl(url
)) {
665 prefs
= factory
->createHostPreferences(Settings::self()->config()->group("hostpreferences").group(url
), this);
667 kDebug(5010) << "Found plugin to handle url (" + url
+ "): " + prefs
->metaObject()->className();
669 kDebug(5010) << "Found plugin to handle url (" + url
+ "), but plugin does not provide preferences";
675 prefs
->setShownWhileConnected(true);
679 KMessageBox::error(this,
680 i18n("The selected host cannot be handled."),
681 i18n("Unusable URL"));
685 void MainWindow::tabContextMenu(QWidget
*widget
, const QPoint
&point
)
687 RemoteViewScrollArea
*scrollArea
= qobject_cast
<RemoteViewScrollArea
*>(widget
);
688 if (!scrollArea
) return;
689 RemoteView
*view
= qobject_cast
<RemoteView
*>(scrollArea
->widget());
692 const QString url
= view
->url().url();
695 KMenu
*menu
= new KMenu(this);
697 QAction
*bookmarkAction
= menu
->addAction(KIcon("bookmark-new"), i18n("Add Bookmark"));
698 QAction
*closeAction
= menu
->addAction(KIcon("tab-close"), i18n("Close Tab"));
699 QAction
*selectedAction
= menu
->exec(point
);
700 if (selectedAction
) {
701 if (selectedAction
== closeAction
) {
703 } else if (selectedAction
== bookmarkAction
) {
704 m_bookmarkManager
->addManualBookmark(url
, url
);
710 void MainWindow::showLocalCursor(bool showLocalCursor
)
712 kDebug(5010) << showLocalCursor
;
714 RemoteView
* view
= m_remoteViewList
.at(m_currentRemoteView
);
715 view
->showDotCursor(showLocalCursor
? RemoteView::CursorOn
: RemoteView::CursorOff
);
716 view
->hostPreferences()->setViewOnly(showLocalCursor
);
720 void MainWindow::viewOnly(bool viewOnly
)
722 kDebug(5010) << viewOnly
;
724 RemoteView
* view
= m_remoteViewList
.at(m_currentRemoteView
);
725 view
->setViewOnly(viewOnly
);
726 view
->hostPreferences()->setViewOnly(viewOnly
);
730 void MainWindow::grabAllKeys(bool grabAllKeys
)
734 RemoteView
* view
= m_remoteViewList
.at(m_currentRemoteView
);
735 view
->setGrabAllKeys(grabAllKeys
);
736 view
->hostPreferences()->setGrabAllKeys(grabAllKeys
);
740 void MainWindow::scale(bool scale
)
744 RemoteView
* view
= m_remoteViewList
.at(m_currentRemoteView
);
745 view
->enableScaling(scale
);
746 if (m_fullscreenWindow
)
747 view
->hostPreferences()->setFullscreenScale(scale
);
749 view
->hostPreferences()->setWindowedScale(scale
);
754 void MainWindow::showRemoteViewToolbar()
759 actionCollection()->action("switch_fullscreen")->setIcon(KIcon("view-restore"));
760 actionCollection()->action("switch_fullscreen")->setText(i18n("Switch to Window Mode"));
762 m_toolBar
= new FloatingToolBar(m_fullscreenWindow
, m_fullscreenWindow
);
763 m_toolBar
->winId(); // force it to be a native widget (prevents problem with QX11EmbedContainer)
764 m_toolBar
->setSide(FloatingToolBar::Top
);
766 QLabel
*hostLabel
= new QLabel(m_remoteViewList
.at(m_currentRemoteView
)->url().prettyUrl(KUrl::RemoveTrailingSlash
), m_toolBar
);
767 hostLabel
->setMargin(4);
768 QFont
font(hostLabel
->font());
770 hostLabel
->setFont(font
);
771 m_toolBar
->addWidget(hostLabel
);
773 #if 0 //TODO: implement functionality
774 KComboBox
*sessionComboBox
= new KComboBox(m_toolBar
);
775 sessionComboBox
->setEditable(false);
776 sessionComboBox
->addItem(i18n("Switch to..."));
777 for (int i
= 0; i
< m_remoteViewList
.count(); i
++) {
778 sessionComboBox
->addItem(m_remoteViewList
.at(i
)->url().prettyUrl(KUrl::RemoveTrailingSlash
));
780 sessionComboBox
->setVisible(m_remoteViewList
.count() > 1); // just show it if there are sessions to switch
781 m_toolBar
->addWidget(sessionComboBox
);
784 m_toolBar
->addAction(actionCollection()->action("switch_fullscreen"));
786 QAction
*minimizeAction
= new QAction(m_toolBar
);
787 minimizeAction
->setIcon(KIcon("window-suppressed"));
788 minimizeAction
->setText(i18n("Minimize Fullscreen Window"));
789 connect(minimizeAction
, SIGNAL(triggered()), m_fullscreenWindow
, SLOT(showMinimized()));
790 m_toolBar
->addAction(minimizeAction
);
792 m_toolBar
->addAction(actionCollection()->action("take_screenshot"));
793 m_toolBar
->addAction(actionCollection()->action("view_only"));
794 m_toolBar
->addAction(actionCollection()->action("show_local_cursor"));
795 m_toolBar
->addAction(actionCollection()->action("grab_all_keys"));
796 m_toolBar
->addAction(actionCollection()->action("scale"));
797 m_toolBar
->addAction(actionCollection()->action("disconnect"));
799 QAction
*stickToolBarAction
= new QAction(m_toolBar
);
800 stickToolBarAction
->setCheckable(true);
801 stickToolBarAction
->setIcon(KIcon("object-locked"));
802 stickToolBarAction
->setText(i18n("Stick Toolbar"));
803 connect(stickToolBarAction
, SIGNAL(triggered(bool)), m_toolBar
, SLOT(setSticky(bool)));
804 m_toolBar
->addAction(stickToolBarAction
);
807 m_toolBar
->showAndAnimate();
810 void setActionStatus(QAction
* action
, bool enabled
, bool visible
, bool checked
)
812 action
->setEnabled(enabled
);
813 action
->setVisible(visible
);
814 action
->setChecked(checked
);
817 void MainWindow::updateActionStatus()
819 kDebug(5010) << m_tabWidget
->currentIndex();
823 if ((m_showStartPage
&& (m_tabWidget
->currentIndex() == 0)) ||
824 #ifdef BUILD_ZEROCONF
825 (m_zeroconfPage
&& (m_tabWidget
->currentIndex() == m_tabWidget
->indexOf(m_zeroconfPage
))) ||
827 (!m_showStartPage
&& (m_tabWidget
->currentIndex() < 0)))
832 RemoteView
* view
= m_currentRemoteView
>= 0 ? m_remoteViewList
.at(m_currentRemoteView
) : NULL
;
834 actionCollection()->action("switch_fullscreen")->setEnabled(enabled
);
835 actionCollection()->action("take_screenshot")->setEnabled(enabled
);
836 actionCollection()->action("disconnect")->setEnabled(enabled
);
838 setActionStatus(actionCollection()->action("view_only"),
841 view
? view
->viewOnly() : false);
843 setActionStatus(actionCollection()->action("show_local_cursor"),
845 view
? view
->supportsLocalCursor() : false,
846 view
? view
->dotCursorState() == RemoteView::CursorOn
: false);
848 setActionStatus(actionCollection()->action("scale"),
850 view
? view
->supportsScaling() : false,
851 view
? view
->scaling() : false);
853 setActionStatus(actionCollection()->action("grab_all_keys"),
856 view
? view
->grabAllKeys() : false);
859 void MainWindow::preferences()
861 // An instance of your dialog could be already created and could be
862 // cached, in which case you want to display the cached dialog
863 // instead of creating another one
864 if (PreferencesDialog::showDialog("preferences"))
867 // KConfigDialog didn't find an instance of this dialog, so lets
869 PreferencesDialog
*dialog
= new PreferencesDialog(this, Settings::self());
871 // User edited the configuration - update your local copies of the
872 // configuration data
873 connect(dialog
, SIGNAL(settingsChanged(const QString
&)),
874 this, SLOT(updateConfiguration()));
879 void MainWindow::updateConfiguration()
881 m_addressNavigator
->setUrlEditable(Settings::normalUrlInputLine());
883 m_tabWidget
->setTabBarHidden(!Settings::showTabBar());
884 m_tabWidget
->setTabPosition((KTabWidget::TabPosition
) Settings::tabPosition());
885 #if QT_VERSION <= 0x040500
886 m_tabWidget
->setTabsClosable(Settings::tabCloseButton());
888 m_tabWidget
->setCloseButtonEnabled(Settings::tabCloseButton());
890 disconnect(m_tabWidget
, SIGNAL(mouseMiddleClick(QWidget
*)), this, SLOT(closeTab(QWidget
*))); // just be sure it is not connected twice
891 if (Settings::tabMiddleClick())
892 connect(m_tabWidget
, SIGNAL(mouseMiddleClick(QWidget
*)), SLOT(closeTab(QWidget
*)));
894 if (Settings::systemTrayIcon() && !m_systemTrayIcon
) {
895 m_systemTrayIcon
= new SystemTrayIcon(this);
896 m_systemTrayIcon
->setVisible(true);
897 } else if (m_systemTrayIcon
) {
898 delete m_systemTrayIcon
;
899 m_systemTrayIcon
= 0;
902 if (Settings::showStartPage() && !m_showStartPage
)
904 else if (!Settings::showStartPage() && m_showStartPage
) {
905 m_tabWidget
->removeTab(0);
906 m_showStartPage
= false;
908 updateActionStatus();
910 // update the scroll areas background color
911 int numNonRemoteView
= 0;
916 for (int i
= numNonRemoteView
; i
< m_tabWidget
->count(); ++i
) {
917 QPalette palette
= m_tabWidget
->widget(i
)->palette();
918 palette
.setColor(QPalette::Dark
, Settings::backgroundColor());
919 m_tabWidget
->widget(i
)->setPalette(palette
);
922 // Send update configuration message to all views
923 for (int i
= 0; i
< m_remoteViewList
.count(); ++i
) {
924 m_remoteViewList
.at(i
)->updateConfiguration();
929 void MainWindow::quit(bool systemEvent
)
931 const bool haveRemoteConnections
= !m_remoteViewList
.isEmpty();
932 if (systemEvent
|| !haveRemoteConnections
|| KMessageBox::warningContinueCancel(this,
933 i18n("Are you sure you want to quit the KDE Remote Desktop Client?"),
934 i18n("Confirm Quit"),
935 KStandardGuiItem::quit(), KStandardGuiItem::cancel(),
936 "DoNotAskBeforeExit") == KMessageBox::Continue
) {
938 if (Settings::rememberSessions()) { // remember open remote views for next startup
940 for (int i
= 0; i
< m_remoteViewList
.count(); ++i
) {
941 kDebug(5010) << m_remoteViewList
.at(i
)->url();
942 list
.append(m_remoteViewList
.at(i
)->url().prettyUrl(KUrl::RemoveTrailingSlash
));
944 Settings::setOpenSessions(list
);
949 Settings::self()->writeConfig();
955 void MainWindow::configureNotifications()
957 KNotifyConfigWidget::configure(this);
960 void MainWindow::configureKeys()
962 KShortcutsDialog::configure(actionCollection());
965 void MainWindow::configureToolbars()
967 KEditToolBar
edit(actionCollection());
968 connect(&edit
, SIGNAL(newToolbarConfig()), this, SLOT(newToolbarConfig()));
972 void MainWindow::showMenubar()
974 if (m_menubarAction
->isChecked())
980 void MainWindow::closeEvent(QCloseEvent
*event
)
982 if (event
->spontaneous()) { // Returns true if the event originated outside the application (a system event); otherwise returns false.
984 if (Settings::systemTrayIcon()) {
985 hide(); // just hide the mainwindow, keep it in systemtray
994 void MainWindow::saveProperties(KConfigGroup
&group
)
997 KMainWindow::saveProperties(group
);
1001 void MainWindow::saveHostPrefs()
1003 // Save default window size for currently active view
1004 RemoteView
* view
= m_currentRemoteView
>= 0 ? m_remoteViewList
.at(m_currentRemoteView
) : NULL
;
1006 saveWindowSize(view
->hostPreferences()->configGroup());
1008 Settings::self()->config()->sync();
1011 void MainWindow::tabChanged(int index
)
1013 kDebug(5010) << index
;
1015 int numNonRemoteView
= 0;
1016 if (m_showStartPage
)
1021 m_currentRemoteView
= index
- numNonRemoteView
;
1023 const QString tabTitle
= m_tabWidget
->tabText(index
).remove('&');
1024 setCaption(tabTitle
== i18n("Start Page") ? QString() : tabTitle
);
1026 updateActionStatus();
1029 void MainWindow::createStartPage()
1031 m_showStartPage
= true;
1033 QWidget
*startWidget
= new QWidget(this);
1034 startWidget
->setStyleSheet("QWidget { background-color: palette(base) }");
1036 QVBoxLayout
*startLayout
= new QVBoxLayout(startWidget
);
1038 QLabel
*headerLabel
= new QLabel(this);
1039 headerLabel
->setText(i18n("<h1>KDE Remote Desktop Client</h1><br /><br />What would you like to do?<br />"));
1041 QLabel
*headerIconLabel
= new QLabel(this);
1042 headerIconLabel
->setPixmap(KIcon("krdc").pixmap(128));
1044 QHBoxLayout
*headerLayout
= new QHBoxLayout
;
1045 headerLayout
->setMargin(20);
1046 headerLayout
->addWidget(headerLabel
, 1, Qt::AlignTop
);
1047 headerLayout
->addWidget(headerIconLabel
);
1048 startLayout
->addLayout(headerLayout
);
1050 foreach(RemoteViewFactory
*factory
, m_remoteViewFactories
) {
1051 KPushButton
*connectButton
= new KPushButton(this);
1052 connectButton
->setProperty("schemeString", factory
->scheme());
1053 connectButton
->setProperty("toolTipString", factory
->connectToolTipText());
1054 connectButton
->setStyleSheet("KPushButton { padding: 12px; margin: 10px; }");
1055 connectButton
->setIcon(KIcon(actionCollection()->action("new_" + factory
->scheme() + "_connection")->icon()));
1056 connectButton
->setText(factory
->connectButtonText());
1057 connect(connectButton
, SIGNAL(clicked()), SLOT(newConnectionToolTip()));
1058 startLayout
->addWidget(connectButton
);
1061 KPushButton
*zeroconfButton
= new KPushButton(this);
1062 zeroconfButton
->setStyleSheet("KPushButton { padding: 12px; margin: 10px; }");
1063 zeroconfButton
->setIcon(KIcon(actionCollection()->action("zeroconf_page")->icon()));
1064 zeroconfButton
->setText(i18n("Browse Remote Desktop Services on Local Network"));
1065 connect(zeroconfButton
, SIGNAL(clicked()), SLOT(createZeroconfPage()));
1066 #ifndef BUILD_ZEROCONF
1067 zeroconfButton
->setVisible(false);
1070 startLayout
->addWidget(zeroconfButton
);
1071 startLayout
->addStretch();
1073 m_tabWidget
->insertTab(0, startWidget
, KIcon("krdc"), i18n("Start Page"));
1076 void MainWindow::newConnectionToolTip()
1078 QObject
*senderObject
= qobject_cast
<QObject
*>(sender());
1079 const QString
toolTip(senderObject
->property("toolTipString").toString());
1080 const QString
scheme(senderObject
->property("schemeString").toString());
1082 m_addressNavigator
->setUrl(KUrl(scheme
+ "://"));
1083 m_addressNavigator
->setFocus();
1085 QToolTip::showText(m_addressNavigator
->pos() + pos() + QPoint(m_addressNavigator
->width(),
1086 m_addressNavigator
->height() / 2),
1090 void MainWindow::createZeroconfPage()
1092 #ifdef BUILD_ZEROCONF
1096 m_zeroconfPage
= new ZeroconfPage(this);
1097 connect(m_zeroconfPage
, SIGNAL(newConnection(const KUrl
, bool)), this, SLOT(newConnection(const KUrl
, bool)));
1098 connect(m_zeroconfPage
, SIGNAL(closeZeroconfPage()), this, SLOT(closeZeroconfPage()));
1099 const int zeroconfTabIndex
= m_tabWidget
->insertTab(m_showStartPage
? 1 : 0, m_zeroconfPage
, KIcon("krdc"), i18n("Browse Local Network"));
1100 m_tabWidget
->setCurrentIndex(zeroconfTabIndex
);
1104 void MainWindow::closeZeroconfPage()
1106 #ifdef BUILD_ZEROCONF
1107 const int index
= m_tabWidget
->indexOf(m_zeroconfPage
);
1108 m_tabWidget
->removeTab(index
);
1109 m_zeroconfPage
->deleteLater();
1114 QList
<RemoteView
*> MainWindow::remoteViewList() const
1116 return m_remoteViewList
;
1119 QList
<RemoteViewFactory
*> MainWindow::remoteViewFactoriesList() const
1121 return m_remoteViewFactories
.values();
1124 int MainWindow::currentRemoteView() const
1126 return m_currentRemoteView
;
1129 void MainWindow::expandTreeViewItems()
1131 metaObject()->invokeMethod(m_remoteDesktopsTreeView
, "expandAll", Qt::QueuedConnection
);
1134 #include "mainwindow.moc"