mainwindow.cpp 45.6 KB
Newer Older
1 2
/****************************************************************************
**
3
** Copyright (C) 2007 - 2010 Urs Wolfer <uwolfer @ kde.org>
4
** Copyright (C) 2009 - 2010 Tony Murray <murraytony @ gmail.com>
5 6 7 8 9 10 11 12 13 14 15 16 17
**
** This file is part of KDE.
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
18 19
** You should have received a copy of the GNU General Public License
** along with this program; see the file COPYING. If not, write to
20 21 22 23 24 25 26
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
** Boston, MA 02110-1301, USA.
**
****************************************************************************/

#include "mainwindow.h"

Christian Ehrlicher's avatar
Christian Ehrlicher committed
27
#include "remoteview.h"
28 29
#include "settings.h"
#include "config/preferencesdialog.h"
Urs Wolfer's avatar
Urs Wolfer committed
30
#include "floatingtoolbar.h"
31
#include "bookmarkmanager.h"
Tony Murray's avatar
Tony Murray committed
32
#include "connectiondelegate.h"
33
#include "remotedesktopsmodel.h"
34
#include "systemtrayicon.h"
35
#include "tabbedviewwidget.h"
Urs Wolfer's avatar
Urs Wolfer committed
36
#include "hostpreferences.h"
37

Abner Silva's avatar
Abner Silva committed
38 39 40 41
#ifdef TELEPATHY_SUPPORT
#include "tubesmanager.h"
#endif

42 43
#include <KAction>
#include <KActionCollection>
44
#include <KActionMenu>
45
#include <KComboBox>
46 47
#include <KEditToolBar>
#include <KIcon>
48
#include <KInputDialog>
49
#include <KLineEdit>
50
#include <KLocale>
Urs Wolfer's avatar
Urs Wolfer committed
51
#include <KMenu>
52
#include <KMenuBar>
53 54
#include <KMessageBox>
#include <KNotifyConfigWidget>
Urs Wolfer's avatar
Urs Wolfer committed
55
#include <KPluginInfo>
56
#include <KPushButton>
57
#include <KShortcutsDialog>
58
#include <KStatusBar>
59
#include <KToggleAction>
Urs Wolfer's avatar
Urs Wolfer committed
60
#include <KToggleFullScreenAction>
Urs Wolfer's avatar
Urs Wolfer committed
61
#include <KServiceTypeTrader>
62

Urs Wolfer's avatar
Urs Wolfer committed
63
#include <QClipboard>
64
#include <QDockWidget>
65
#include <QFontMetrics>
Tony Murray's avatar
Tony Murray committed
66
#include <QGroupBox>
67
#include <QHBoxLayout>
68
#include <QHeaderView>
69
#include <QLabel>
70
#include <QLayout>
71
#include <QScrollArea>
72
#include <QSortFilterProxyModel>
Tony Murray's avatar
Tony Murray committed
73
#include <QTableView>
Urs Wolfer's avatar
Urs Wolfer committed
74
#include <QTimer>
75
#include <QToolBar>
76
#include <QVBoxLayout>
77 78

MainWindow::MainWindow(QWidget *parent)
79 80
        : KXmlGuiWindow(parent),
        m_fullscreenWindow(0),
81 82
        m_protocolInput(0),
        m_addressInput(0),
83 84
        m_toolBar(0),
        m_currentRemoteView(-1),
Urs Wolfer's avatar
Urs Wolfer committed
85
        m_systemTrayIcon(0),
Tony Murray's avatar
Tony Murray committed
86 87
        m_dockWidgetTableView(0),
        m_newConnectionTableView(0),
Abner Silva's avatar
Abner Silva committed
88 89 90
#ifdef TELEPATHY_SUPPORT
        m_tubesManager(0),
#endif
91
        m_newConnectionWidget(0)
92
{
Urs Wolfer's avatar
Urs Wolfer committed
93 94
    loadAllPlugins();

95 96 97 98
    setupActions();

    setStandardToolBarMenuEnabled(true);

99
    m_tabWidget = new TabbedViewWidget(this);
Urs Wolfer's avatar
Urs Wolfer committed
100 101
    m_tabWidget->setTabPosition((KTabWidget::TabPosition) Settings::tabPosition());

Urs Wolfer's avatar
Urs Wolfer committed
102 103 104
#if QT_VERSION <= 0x040500
    m_tabWidget->setTabsClosable(Settings::tabCloseButton());
#else
Urs Wolfer's avatar
Urs Wolfer committed
105
    m_tabWidget->setCloseButtonEnabled(Settings::tabCloseButton());
Urs Wolfer's avatar
Urs Wolfer committed
106
#endif
Urs Wolfer's avatar
Urs Wolfer committed
107

Urs Wolfer's avatar
Urs Wolfer committed
108 109
    connect(m_tabWidget, SIGNAL(closeRequest(QWidget *)), SLOT(closeTab(QWidget *)));

Urs Wolfer's avatar
Urs Wolfer committed
110 111 112 113 114 115
    if (Settings::tabMiddleClick())
        connect(m_tabWidget, SIGNAL(mouseMiddleClick(QWidget *)), SLOT(closeTab(QWidget *)));

    connect(m_tabWidget, SIGNAL(mouseDoubleClick(QWidget *)), SLOT(openTabSettings(QWidget *)));
    connect(m_tabWidget, SIGNAL(contextMenu(QWidget *, const QPoint &)), SLOT(tabContextMenu(QWidget *, const QPoint &)));

Urs Wolfer's avatar
Urs Wolfer committed
116
    m_tabWidget->setMinimumSize(600, 400);
117
    setCentralWidget(m_tabWidget);
118

119
    createDockWidget();
120 121 122

    createGUI("krdcui.rc");

123 124
    if (Settings::systemTrayIcon()) {
        m_systemTrayIcon = new SystemTrayIcon(this);
125
        if(m_fullscreenWindow) m_systemTrayIcon->setAssociatedWidget(m_fullscreenWindow);
126 127
    }

128 129
    connect(m_tabWidget, SIGNAL(currentChanged(int)), SLOT(tabChanged(int)));

130 131
    if (Settings::showStatusBar())
        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

    setAutoSaveSettings(); // e.g toolbar position, mainwindow size, ...
139 140 141

    if (Settings::rememberSessions()) // give some time to create and show the window first
        QTimer::singleShot(100, this, SLOT(restoreOpenSessions()));
142 143 144 145 146 147 148 149
}

MainWindow::~MainWindow()
{
}

void MainWindow::setupActions()
{
150 151 152 153
    QAction *connectionAction = actionCollection()->addAction("new_connection");
    connectionAction->setText(i18n("New Connection"));
    connectionAction->setIcon(KIcon("network-connect"));
    connect(connectionAction, SIGNAL(triggered()), SLOT(newConnectionPage()));
Urs Wolfer's avatar
Urs Wolfer committed
154

Urs Wolfer's avatar
Urs Wolfer committed
155
    QAction *screenshotAction = actionCollection()->addAction("take_screenshot");
Urs Wolfer's avatar
Urs Wolfer committed
156
    screenshotAction->setText(i18n("Copy Screenshot to Clipboard"));
157
    screenshotAction->setIconText(i18n("Screenshot"));
Urs Wolfer's avatar
Urs Wolfer committed
158
    screenshotAction->setIcon(KIcon("ksnapshot"));
Urs Wolfer's avatar
Urs Wolfer committed
159
    connect(screenshotAction, SIGNAL(triggered()), SLOT(takeScreenshot()));
Urs Wolfer's avatar
Urs Wolfer committed
160

161 162
    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)
    fullscreenAction->setText(i18n("Switch to Full Screen Mode"));
163
    fullscreenAction->setIconText(i18n("Full Screen"));
164 165 166 167
    fullscreenAction->setIcon(KIcon("view-fullscreen"));
    fullscreenAction->setShortcut(KStandardShortcut::fullScreen());
    connect(fullscreenAction, SIGNAL(triggered()), SLOT(switchFullscreen()));

Urs Wolfer's avatar
Urs Wolfer committed
168 169
    QAction *viewOnlyAction = actionCollection()->addAction("view_only");
    viewOnlyAction->setCheckable(true);
Urs Wolfer's avatar
Urs Wolfer committed
170
    viewOnlyAction->setText(i18n("View Only"));
171
    viewOnlyAction->setIcon(KIcon("document-preview"));
Urs Wolfer's avatar
Urs Wolfer committed
172
    connect(viewOnlyAction, SIGNAL(triggered(bool)), SLOT(viewOnly(bool)));
Urs Wolfer's avatar
Urs Wolfer committed
173

174
    KAction *disconnectAction = actionCollection()->addAction("disconnect");
175
    disconnectAction->setText(i18n("Disconnect"));
176
    disconnectAction->setIcon(KIcon("network-disconnect"));
177
    disconnectAction->setShortcut(QKeySequence::Close);
Urs Wolfer's avatar
Urs Wolfer committed
178
    connect(disconnectAction, SIGNAL(triggered()), SLOT(disconnectHost()));
Urs Wolfer's avatar
Urs Wolfer committed
179

180 181
    QAction *showLocalCursorAction = actionCollection()->addAction("show_local_cursor");
    showLocalCursorAction->setCheckable(true);
182
    showLocalCursorAction->setIcon(KIcon("input-mouse"));
Urs Wolfer's avatar
Urs Wolfer committed
183
    showLocalCursorAction->setText(i18n("Show Local Cursor"));
184
    showLocalCursorAction->setIconText(i18n("Local Cursor"));
Urs Wolfer's avatar
Urs Wolfer committed
185
    connect(showLocalCursorAction, SIGNAL(triggered(bool)), SLOT(showLocalCursor(bool)));
186

187 188 189
    QAction *grabAllKeysAction = actionCollection()->addAction("grab_all_keys");
    grabAllKeysAction->setCheckable(true);
    grabAllKeysAction->setIcon(KIcon("configure-shortcuts"));
190
    grabAllKeysAction->setText(i18n("Grab All Possible Keys"));
191
    grabAllKeysAction->setIconText(i18n("Grab Keys"));
192
    connect(grabAllKeysAction, SIGNAL(triggered(bool)), SLOT(grabAllKeys(bool)));
193

194 195 196
    QAction *scaleAction = actionCollection()->addAction("scale");
    scaleAction->setCheckable(true);
    scaleAction->setIcon(KIcon("zoom-fit-best"));
197
    scaleAction->setText(i18n("Scale Remote Screen to Fit Window Size"));
198
    scaleAction->setIconText(i18n("Scale"));
199 200
    connect(scaleAction, SIGNAL(triggered(bool)), SLOT(scale(bool)));

201
    KStandardAction::quit(this, SLOT(quit()), actionCollection());
202 203 204
    KStandardAction::preferences(this, SLOT(preferences()), actionCollection());
    KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection());
    KStandardAction::keyBindings(this, SLOT(configureKeys()), actionCollection());
205 206
    QAction *configNotifyAction = KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection());
    configNotifyAction->setVisible(false);
Urs Wolfer's avatar
Urs Wolfer committed
207
    m_menubarAction = KStandardAction::showMenubar(this, SLOT(showMenubar()), actionCollection());
208 209
    m_menubarAction->setChecked(!menuBar()->isHidden());

Urs Wolfer's avatar
Urs Wolfer committed
210
    KActionMenu *bookmarkMenu = new KActionMenu(i18n("Bookmarks"), actionCollection());
211 212
    m_bookmarkManager = new BookmarkManager(actionCollection(), bookmarkMenu->menu(), this);
    actionCollection()->addAction("bookmark" , bookmarkMenu);
Urs Wolfer's avatar
Urs Wolfer committed
213
    connect(m_bookmarkManager, SIGNAL(openUrl(KUrl)), SLOT(newConnection(KUrl)));
214 215
}

Urs Wolfer's avatar
Urs Wolfer committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
void MainWindow::loadAllPlugins()
{
    const KService::List offers = KServiceTypeTrader::self()->query("KRDC/Plugin");

    KConfigGroup conf(KGlobal::config(), "Plugins");

    for (int i = 0; i < offers.size(); i++) {
        KService::Ptr offer = offers[i];

        RemoteViewFactory *remoteView;

        KPluginInfo description(offer);
        description.load(conf);

        const bool selected = description.isPluginEnabled();

        if (selected) {
            if ((remoteView = createPluginFromService(offer)) != 0) {
                kDebug(5010) << "### Plugin " + description.name() + " found ###";
                kDebug(5010) << "# Version:" << description.version();
                kDebug(5010) << "# Description:" << description.comment();
                kDebug(5010) << "# Author:" << description.author();
                const int sorting = offer->property("X-KDE-KRDC-Sorting").toInt();
                kDebug(5010) << "# Sorting:" << sorting;

                m_remoteViewFactories.insert(sorting, remoteView);
            } else {
                kDebug(5010) << "Error loading KRDC plugin (" << (offers[i])->library() << ')';
            }
        } else {
            kDebug(5010) << "# Plugin " + description.name() + " found, however it's not activated, skipping...";
            continue;
        }
    }
Abner Silva's avatar
Abner Silva committed
250 251 252 253 254 255 256 257 258 259 260

#ifdef TELEPATHY_SUPPORT
    /* Start tube handler */
    m_tubesManager = Tp::SharedPtr<TubesManager>(new TubesManager(this));
    connect(m_tubesManager.data(),
            SIGNAL(newConnection(KUrl)),
            SLOT(newConnection(KUrl)));

    m_registrar = Tp::ClientRegistrar::create();
    m_registrar->registerClient(Tp::AbstractClientPtr::dynamicCast(m_tubesManager), "krdc_rfb_handler");
#endif
Urs Wolfer's avatar
Urs Wolfer committed
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
}

RemoteViewFactory *MainWindow::createPluginFromService(const KService::Ptr &service)
{
    //try to load the specified library
    KPluginFactory *factory = KPluginLoader(service->library()).factory();

    if (!factory) {
        kError(5010) << "KPluginFactory could not load the plugin:" << service->library();
        return 0;
    }

    RemoteViewFactory *plugin = factory->create<RemoteViewFactory>();

    return plugin;
}

278 279
void MainWindow::restoreOpenSessions()
{
Urs Wolfer's avatar
Urs Wolfer committed
280 281 282
    const QStringList list = Settings::openSessions();
    QStringList::ConstIterator it = list.begin();
    QStringList::ConstIterator end = list.end();
283
    while (it != end) {
Urs Wolfer's avatar
Urs Wolfer committed
284
        newConnection(*it);
285 286 287 288
        ++it;
    }
}

289 290 291 292 293 294 295 296
KUrl MainWindow::getInputUrl()
{
    KUrl inputUrl;
    inputUrl.setAuthority(m_addressInput->text());
    inputUrl.setProtocol(m_protocolInput->currentText());
    return inputUrl;
}

297
void MainWindow::newConnection(const KUrl &newUrl, bool switchFullscreenWhenConnected, const QString &tabName)
298
{
299
    m_switchFullscreenWhenConnected = switchFullscreenWhenConnected;
Urs Wolfer's avatar
Urs Wolfer committed
300

301
    const KUrl url = newUrl.isEmpty() ? getInputUrl() : newUrl;
302

303
    if (!url.isValid() || (url.host().isEmpty() && url.port() < 0)
304
        || (!url.path().isEmpty() && url.path() != QLatin1String("/"))) {
305
        KMessageBox::error(this,
306
                           i18n("The entered address does not have the required form.\n Syntax: [username@]host[:port]"),
307
                           i18n("Malformed URL"));
308
        return;
309
    }
310

311 312 313 314
    if (m_protocolInput && m_addressInput) {
        int index = m_protocolInput->findText(url.protocol());
        if (index>=0) m_protocolInput->setCurrentIndex(index);
        m_addressInput->setText(url.authority());
315
    }
316

317
    RemoteView *view = 0;
318
    KConfigGroup configGroup = Settings::self()->config()->group("hostpreferences").group(url.prettyUrl(KUrl::RemoveTrailingSlash));
319

Urs Wolfer's avatar
Urs Wolfer committed
320 321 322 323 324 325
    foreach(RemoteViewFactory *factory, m_remoteViewFactories) {
        if (factory->supportsUrl(url)) {
            view = factory->createView(this, url, configGroup);
            kDebug(5010) << "Found plugin to handle url (" + url.url() + "): " + view->metaObject()->className();
            break;
        }
326
    }
327

328
    if (!view) {
Urs Wolfer's avatar
Urs Wolfer committed
329 330 331
        KMessageBox::error(this,
                           i18n("The entered address cannot be handled."),
                           i18n("Unusable URL"));
332 333
        return;
    }
334

335 336
    // Configure the view
    HostPreferences* prefs = view->hostPreferences();
337
    if (! prefs->showDialogIfNeeded(this)) {
338 339 340
        delete view;
        return;
    }
341

342
    saveHostPrefs();
343

344 345 346
    view->setGrabAllKeys(prefs->grabAllKeys());
    view->showDotCursor(prefs->showLocalCursor() ? RemoteView::CursorOn : RemoteView::CursorOff);
    view->setViewOnly(prefs->viewOnly());
Tony Murray's avatar
Tony Murray committed
347
    if (! switchFullscreenWhenConnected) view->enableScaling(prefs->windowedScale());
348

349
    connect(view, SIGNAL(framebufferSizeChanged(int, int)), this, SLOT(resizeTabWidget(int, int)));
350
    connect(view, SIGNAL(statusChanged(RemoteView::RemoteStatus)), this, SLOT(statusChanged(RemoteView::RemoteStatus)));
351
    connect(view, SIGNAL(disconnected()), this, SLOT(disconnectHost()));
352

353 354
    m_remoteViewList.append(view);

355
    QScrollArea *scrollArea = createScrollArea(m_tabWidget, view);
356

357 358 359 360
    const int indexOfNewConnectionWidget = m_tabWidget->indexOf(m_newConnectionWidget);
    if (indexOfNewConnectionWidget >= 0)
        m_tabWidget->removeTab(indexOfNewConnectionWidget);

361
    const int newIndex = m_tabWidget->addTab(scrollArea, KIcon("krdc"), tabName.isEmpty() ? url.prettyUrl(KUrl::RemoveTrailingSlash) : tabName);
362
    m_tabWidget->setCurrentIndex(newIndex);
363
    tabChanged(newIndex); // force to update m_currentRemoteView (tabChanged is not emitted when start page has been disabled)
364

365
    view->start();
366 367
}

Tony Murray's avatar
Tony Murray committed
368
void MainWindow::openFromRemoteDesktopsModel(const QModelIndex &index)
369
{
370 371 372 373
    const QString urlString = index.data(10001).toString();
    const QString nameString = index.data(10003).toString();
    if (!urlString.isEmpty()) {
        const KUrl url(urlString);
374
        // first check if url has already been opened; in case show the tab
Urs Wolfer's avatar
Urs Wolfer committed
375
        for (int i = 0; i < m_remoteViewList.count(); ++i) {
376
            if (m_remoteViewList.at(i)->url() == url) {
377
                m_tabWidget->setCurrentIndex(i);
378 379 380
                return;
            }
        }
381
        newConnection(url, false, nameString);
382
    }
383 384
}

385 386
void MainWindow::resizeTabWidget(int w, int h)
{
387
    kDebug(5010) << "tabwidget resize, view size: w: " << w << ", h: " << h;
388 389 390 391
    if (m_fullscreenWindow) {
        kDebug(5010) << "in fullscreen mode, refusing to resize";
        return;
    }
392

393
    const QSize viewSize = QSize(w,h);
394
    QDesktopWidget *desktop = QApplication::desktop();
395

396 397 398 399 400 401 402 403 404
    if (Settings::fullscreenOnConnect()) {
        int currentScreen = desktop->screenNumber(this);
        const QSize screenSize = desktop->screenGeometry(currentScreen).size();

        if (screenSize == viewSize) {
            kDebug(5010) << "screen size equal to target view size -> switch to fullscreen mode";
            switchFullscreen();
            return;
        }
405 406
    }

407 408 409
    if (Settings::resizeOnConnect()) {
        QWidget* currentWidget = m_tabWidget->currentWidget();
        const QSize newWindowSize = size() - currentWidget->frameSize() + viewSize;
410

411 412
        const QSize desktopSize = desktop->availableGeometry().size();
        kDebug(5010) << "new window size: " << newWindowSize << " available space:" << desktopSize;
413

414 415 416 417 418
        if ((newWindowSize.width() >= desktopSize.width()) || (newWindowSize.height() >= desktopSize.height())) {
            kDebug(5010) << "remote desktop needs more space than available -> show window maximized";
            setWindowState(windowState() | Qt::WindowMaximized);
            return;
        }
419

420 421
        resize(newWindowSize);
    }
Urs Wolfer's avatar
Urs Wolfer committed
422 423
}

424 425
void MainWindow::statusChanged(RemoteView::RemoteStatus status)
{
Urs Wolfer's avatar
Urs Wolfer committed
426
    kDebug(5010) << status;
427

428 429 430 431
    // the remoteview is already deleted, so don't show it; otherwise it would crash
    if (status == RemoteView::Disconnecting || status == RemoteView::Disconnected)
        return;

Urs Wolfer's avatar
Urs Wolfer committed
432
    const QString host = m_remoteViewList.at(m_currentRemoteView)->host();
433 434 435 436 437 438

    QString iconName = "krdc";
    QString message;

    switch (status) {
    case RemoteView::Connecting:
439
        iconName = "network-connect";
440 441 442
        message = i18n("Connecting to %1", host);
        break;
    case RemoteView::Authenticating:
443
        iconName = "dialog-password";
444 445 446
        message = i18n("Authenticating at %1", host);
        break;
    case RemoteView::Preparing:
447
        iconName = "view-history";
448 449 450 451 452
        message = i18n("Preparing connection to %1", host);
        break;
    case RemoteView::Connected:
        iconName = "krdc";
        message = i18n("Connected to %1", host);
Urs Wolfer's avatar
Urs Wolfer committed
453

454 455 456
        // when started with command line fullscreen argument
        if (m_switchFullscreenWhenConnected) {
            m_switchFullscreenWhenConnected = false;
Urs Wolfer's avatar
Urs Wolfer committed
457
            switchFullscreen();
Urs Wolfer's avatar
Urs Wolfer committed
458 459
        }

460 461 462
        if (Settings::rememberHistory()) {
            m_bookmarkManager->addHistoryBookmark();
        }
463

464 465
        break;
    default:
Urs Wolfer's avatar
Urs Wolfer committed
466
        break;
467 468 469
    }

    m_tabWidget->setTabIcon(m_tabWidget->currentIndex(), KIcon(iconName));
470 471
    if (Settings::showStatusBar())
        statusBar()->showMessage(message);
472 473
}

Urs Wolfer's avatar
Urs Wolfer committed
474
void MainWindow::takeScreenshot()
Urs Wolfer's avatar
Urs Wolfer committed
475
{
476
    const QPixmap snapshot = m_remoteViewList.at(m_currentRemoteView)->takeScreenshot();
Urs Wolfer's avatar
Urs Wolfer committed
477 478 479 480

    QApplication::clipboard()->setPixmap(snapshot);
}

Urs Wolfer's avatar
Urs Wolfer committed
481
void MainWindow::switchFullscreen()
Urs Wolfer's avatar
Urs Wolfer committed
482
{
Urs Wolfer's avatar
Urs Wolfer committed
483
    kDebug(5010);
Urs Wolfer's avatar
Urs Wolfer committed
484 485

    if (m_fullscreenWindow) {
486
        // Leaving full screen mode
Urs Wolfer's avatar
Urs Wolfer committed
487 488 489
        m_fullscreenWindow->setWindowState(0);
        m_fullscreenWindow->hide();

490
        m_tabWidget->setTabBarHidden(m_tabWidget->count() <= 1 && !Settings::showTabBar());
491 492
        m_tabWidget->setDocumentMode(false);
        setCentralWidget(m_tabWidget);
Urs Wolfer's avatar
Urs Wolfer committed
493

494 495
        show();
        restoreGeometry(m_mainWindowGeometry);
496
        if (m_systemTrayIcon) m_systemTrayIcon->setAssociatedWidget(this);
497

Tony Murray's avatar
Tony Murray committed
498
        foreach(RemoteView *currentView, m_remoteViewList) {
499 500
            currentView->enableScaling(currentView->hostPreferences()->windowedScale());
        }
Urs Wolfer's avatar
Urs Wolfer committed
501 502 503

        if (m_toolBar) {
            m_toolBar->hideAndDestroy();
504
            m_toolBar->deleteLater();
Urs Wolfer's avatar
Urs Wolfer committed
505 506 507
            m_toolBar = 0;
        }

508 509
        actionCollection()->action("switch_fullscreen")->setIcon(KIcon("view-fullscreen"));
        actionCollection()->action("switch_fullscreen")->setText(i18n("Switch to Full Screen Mode"));
510
        actionCollection()->action("switch_fullscreen")->setIconText(i18n("Full Screen"));
511

Urs Wolfer's avatar
Urs Wolfer committed
512 513 514
        m_fullscreenWindow->deleteLater();
        m_fullscreenWindow = 0;
    } else {
515
        // Entering full screen mode
Urs Wolfer's avatar
Urs Wolfer committed
516
        m_fullscreenWindow = new QWidget(this, Qt::Window);
517
        m_fullscreenWindow->setWindowTitle(i18nc("window title when in full screen mode (for example displayed in tasklist)",
Tony Murray's avatar
Tony Murray committed
518
                                           "KDE Remote Desktop Client (Full Screen)"));
Urs Wolfer's avatar
Urs Wolfer committed
519

520 521 522 523 524
        m_mainWindowGeometry = saveGeometry();

        m_tabWidget->setTabBarHidden(true);
        m_tabWidget->setDocumentMode(true);

Tony Murray's avatar
Tony Murray committed
525
        foreach(RemoteView *currentView, m_remoteViewList) {
526 527
            currentView->enableScaling(currentView->hostPreferences()->fullscreenScale());
        }
Urs Wolfer's avatar
Urs Wolfer committed
528

Urs Wolfer's avatar
Urs Wolfer committed
529
        QVBoxLayout *fullscreenLayout = new QVBoxLayout(m_fullscreenWindow);
Tony Murray's avatar
Tony Murray committed
530
        fullscreenLayout->setContentsMargins(QMargins(0, 0, 0, 0));
531 532 533
        fullscreenLayout->addWidget(m_tabWidget);

        KToggleFullScreenAction::setFullScreen(m_fullscreenWindow, true);
Urs Wolfer's avatar
Urs Wolfer committed
534

535
        MinimizePixel *minimizePixel = new MinimizePixel(m_fullscreenWindow);
536
        minimizePixel->winId(); // force it to be a native widget (prevents problem with QX11EmbedContainer)
537
        connect(minimizePixel, SIGNAL(rightClicked()), m_fullscreenWindow, SLOT(showMinimized()));
538
        m_fullscreenWindow->installEventFilter(this);
539

Urs Wolfer's avatar
Urs Wolfer committed
540
        m_fullscreenWindow->show();
541
        hide();  // hide after showing the new window so it stays on the same screen
Urs Wolfer's avatar
Urs Wolfer committed
542

543
        if (m_systemTrayIcon) m_systemTrayIcon->setAssociatedWidget(m_fullscreenWindow);
544
        showRemoteViewToolbar();
Urs Wolfer's avatar
Urs Wolfer committed
545
    }
546 547 548
    if (m_tabWidget->currentWidget() == m_newConnectionWidget) {
        m_addressInput->setFocus();
    }
Urs Wolfer's avatar
Urs Wolfer committed
549 550
}

551 552
QScrollArea *MainWindow::createScrollArea(QWidget *parent, RemoteView *remoteView)
{
553
    RemoteViewScrollArea *scrollArea = new RemoteViewScrollArea(parent);
554 555
    scrollArea->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);

556 557
    connect(scrollArea, SIGNAL(resized(int, int)), remoteView, SLOT(scaleResize(int, int)));

558 559 560 561 562
    QPalette palette = scrollArea->palette();
    palette.setColor(QPalette::Dark, Settings::backgroundColor());
    scrollArea->setPalette(palette);

    scrollArea->setBackgroundRole(QPalette::Dark);
563 564
    scrollArea->setFrameStyle(QFrame::NoFrame);

565 566 567 568 569
    scrollArea->setWidget(remoteView);

    return scrollArea;
}

Urs Wolfer's avatar
Urs Wolfer committed
570
void MainWindow::disconnectHost()
Urs Wolfer's avatar
Urs Wolfer committed
571
{
Urs Wolfer's avatar
Urs Wolfer committed
572
    kDebug(5010);
Urs Wolfer's avatar
Urs Wolfer committed
573

574
    RemoteView *view = qobject_cast<RemoteView*>(QObject::sender());
575

576
    QWidget *widgetToDelete;
577
    if (view) {
578
        widgetToDelete = (QWidget*) view->parent()->parent();
579
        m_remoteViewList.removeOne(view);
580 581 582 583
    } else {
        widgetToDelete = m_tabWidget->currentWidget();
        view = m_remoteViewList.takeAt(m_currentRemoteView);
    }
584

585 586 587 588
    saveHostPrefs(view);
    view->startQuitting();  // some deconstructors can't properly quit, so quit early
    m_tabWidget->removePage(widgetToDelete);
    widgetToDelete->deleteLater();
Abner Silva's avatar
Abner Silva committed
589 590 591
#ifdef TELEPATHY_SUPPORT
        m_tubesManager->closeTube(view->url());
#endif
592

593
    // if closing the last connection, create new connection tab
594 595 596
    if (m_tabWidget->count() == 0) {
        newConnectionPage();
    }
597 598 599 600 601

    // if the newConnectionWidget is the only tab and we are fullscreen, switch to window mode
    if (m_fullscreenWindow && m_tabWidget->count() == 1  && m_tabWidget->currentWidget() == m_newConnectionWidget) {
        switchFullscreen();
    }
Urs Wolfer's avatar
Urs Wolfer committed
602 603
}

Urs Wolfer's avatar
Urs Wolfer committed
604 605
void MainWindow::closeTab(QWidget *widget)
{
606
    bool isNewConnectionPage = m_tabWidget->currentWidget() == m_newConnectionWidget;
Urs Wolfer's avatar
Urs Wolfer committed
607
    const int index = m_tabWidget->indexOf(widget);
Urs Wolfer's avatar
Urs Wolfer committed
608

609 610
    kDebug(5010) << index;

611
    if (!isNewConnectionPage) {
612
        RemoteView *view = m_remoteViewList.takeAt(index);
613
        view->startQuitting();
Abner Silva's avatar
Abner Silva committed
614
#ifdef TELEPATHY_SUPPORT
615
        m_tubesManager->closeTube(view->url());
Abner Silva's avatar
Abner Silva committed
616
#endif
617
        widget->deleteLater();
Urs Wolfer's avatar
Urs Wolfer committed
618 619
    }

620
    m_tabWidget->removePage(widget);
621

622
    // if closing the last connection, create new connection tab
623 624
    if (m_tabWidget->count() == 0) {
        newConnectionPage();
Urs Wolfer's avatar
Urs Wolfer committed
625
    }
626 627 628 629 630

    // if the newConnectionWidget is the only tab and we are fullscreen, switch to window mode
    if (m_fullscreenWindow && m_tabWidget->count() == 1  && m_tabWidget->currentWidget() == m_newConnectionWidget) {
        switchFullscreen();
    }
Urs Wolfer's avatar
Urs Wolfer committed
631 632
}

Urs Wolfer's avatar
Urs Wolfer committed
633 634 635 636 637 638 639 640 641 642
void MainWindow::openTabSettings(QWidget *widget)
{
    RemoteViewScrollArea *scrollArea = qobject_cast<RemoteViewScrollArea*>(widget);
    if (!scrollArea) return;
    RemoteView *view = qobject_cast<RemoteView*>(scrollArea->widget());
    if (!view) return;

    const QString url = view->url().url();
    kDebug(5010) << url;

Tony Murray's avatar
Tony Murray committed
643 644 645 646 647
    showSettingsDialog(url);
}

void MainWindow::showSettingsDialog(const QString &url)
{
Urs Wolfer's avatar
Urs Wolfer committed
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
    HostPreferences *prefs = 0;

    foreach(RemoteViewFactory *factory, remoteViewFactoriesList()) {
        if (factory->supportsUrl(url)) {
            prefs = factory->createHostPreferences(Settings::self()->config()->group("hostpreferences").group(url), this);
            if (prefs) {
                kDebug(5010) << "Found plugin to handle url (" + url + "): " + prefs->metaObject()->className();
            } else {
                kDebug(5010) << "Found plugin to handle url (" + url + "), but plugin does not provide preferences";
            }
        }
    }

    if (prefs) {
        prefs->setShownWhileConnected(true);
663
        prefs->showDialog(this);
Urs Wolfer's avatar
Urs Wolfer committed
664 665 666 667 668 669 670
    } else {
        KMessageBox::error(this,
                           i18n("The selected host cannot be handled."),
                           i18n("Unusable URL"));
    }
}

Tony Murray's avatar
Tony Murray committed
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
void MainWindow::showConnectionContextMenu(const QPoint &pos)
{
    // QTableView does not take headers into account when it does mapToGlobal(), so calculate the offset
    QPoint offset = QPoint(m_newConnectionTableView->verticalHeader()->size().width(),
                           m_newConnectionTableView->horizontalHeader()->size().height());
    QModelIndex index = m_newConnectionTableView->indexAt(pos);

    if (!index.isValid())
        return;

    const QString url = index.data(10001).toString();
    const QString title = m_remoteDesktopsModelProxy->index(index.row(), RemoteDesktopsModel::Title).data(Qt::DisplayRole).toString();
    const QString source = m_remoteDesktopsModelProxy->index(index.row(), RemoteDesktopsModel::Source).data(Qt::DisplayRole).toString();

    KMenu *menu = new KMenu(m_newConnectionTableView);
    menu->addTitle(url);

    QAction *connectAction = menu->addAction(KIcon("network-connect"), i18n("Connect"));
    QAction *renameAction = menu->addAction(KIcon("edit-rename"), i18n("Rename"));
    QAction *settingsAction = menu->addAction(KIcon("configure"), i18n("Settings"));
    QAction *deleteAction = menu->addAction(KIcon("edit-delete"), i18n("Delete"));

    // not very clean, but it works,
    if (!(source == i18nc("Where each displayed link comes from", "Bookmarks") ||
Tony Murray's avatar
Tony Murray committed
695
            source == i18nc("Where each displayed link comes from", "History"))) {
Tony Murray's avatar
Tony Murray committed
696 697 698 699 700 701 702 703 704 705
        renameAction->setEnabled(false);
        deleteAction->setEnabled(false);
    }

    QAction *selectedAction = menu->exec(m_newConnectionTableView->mapToGlobal(pos + offset));

    if (selectedAction == connectAction) {
        openFromRemoteDesktopsModel(index);
    } else if (selectedAction == renameAction) {
        //TODO: use inline editor if possible
706 707 708
        bool ok = false;
        const QString newTitle = KInputDialog::getText(i18n("Rename %1", title), i18n("Rename %1 to", title), "", &ok, this);
        if (ok && !newTitle.isEmpty()) {
Tony Murray's avatar
Tony Murray committed
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
            BookmarkManager::updateTitle(m_bookmarkManager->getManager(), url, newTitle);
        }
    } else if (selectedAction == settingsAction) {
        showSettingsDialog(url);
    } else if (selectedAction == deleteAction) {

        if (KMessageBox::warningContinueCancel(this, i18n("Are you sure you want to delete %1?", url), i18n("Delete %1", title),
                                               KStandardGuiItem::del()) == KMessageBox::Continue) {
            BookmarkManager::removeByUrl(m_bookmarkManager->getManager(), url);
        }
    }

    menu->deleteLater();
}

Urs Wolfer's avatar
Urs Wolfer committed
724 725 726 727 728 729 730
void MainWindow::tabContextMenu(QWidget *widget, const QPoint &point)
{
    RemoteViewScrollArea *scrollArea = qobject_cast<RemoteViewScrollArea*>(widget);
    if (!scrollArea) return;
    RemoteView *view = qobject_cast<RemoteView*>(scrollArea->widget());
    if (!view) return;

731
    const QString url = view->url().prettyUrl(KUrl::RemoveTrailingSlash);
Urs Wolfer's avatar
Urs Wolfer committed
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
    kDebug(5010) << url;

    KMenu *menu = new KMenu(this);
    menu->addTitle(url);
    QAction *bookmarkAction = menu->addAction(KIcon("bookmark-new"), i18n("Add Bookmark"));
    QAction *closeAction = menu->addAction(KIcon("tab-close"), i18n("Close Tab"));
    QAction *selectedAction = menu->exec(point);
    if (selectedAction) {
        if (selectedAction == closeAction) {
            closeTab(widget);
        } else if (selectedAction == bookmarkAction) {
            m_bookmarkManager->addManualBookmark(url, url);
        }
    }
    menu->deleteLater();
}

Urs Wolfer's avatar
Urs Wolfer committed
749
void MainWindow::showLocalCursor(bool showLocalCursor)
750
{
Urs Wolfer's avatar
Urs Wolfer committed
751
    kDebug(5010) << showLocalCursor;
752

753 754
    RemoteView* view = m_remoteViewList.at(m_currentRemoteView);
    view->showDotCursor(showLocalCursor ? RemoteView::CursorOn : RemoteView::CursorOff);
755
    view->hostPreferences()->setShowLocalCursor(showLocalCursor);
756
    saveHostPrefs();
757 758
}

Urs Wolfer's avatar
Urs Wolfer committed
759
void MainWindow::viewOnly(bool viewOnly)
Urs Wolfer's avatar
Urs Wolfer committed
760
{
Urs Wolfer's avatar
Urs Wolfer committed
761
    kDebug(5010) << viewOnly;
Urs Wolfer's avatar
Urs Wolfer committed
762

763 764 765 766
    RemoteView* view = m_remoteViewList.at(m_currentRemoteView);
    view->setViewOnly(viewOnly);
    view->hostPreferences()->setViewOnly(viewOnly);
    saveHostPrefs();
Urs Wolfer's avatar
Urs Wolfer committed
767 768
}

769
void MainWindow::grabAllKeys(bool grabAllKeys)
770
{
Urs Wolfer's avatar
Urs Wolfer committed
771
    kDebug(5010);
772

773 774 775 776
    RemoteView* view = m_remoteViewList.at(m_currentRemoteView);
    view->setGrabAllKeys(grabAllKeys);
    view->hostPreferences()->setGrabAllKeys(grabAllKeys);
    saveHostPrefs();
777 778
}

779 780 781 782
void MainWindow::scale(bool scale)
{
    kDebug(5010);

783 784
    RemoteView* view = m_remoteViewList.at(m_currentRemoteView);
    view->enableScaling(scale);
Tony Murray's avatar
Tony Murray committed
785
    if (m_fullscreenWindow)
786 787 788
        view->hostPreferences()->setFullscreenScale(scale);
    else
        view->hostPreferences()->setWindowedScale(scale);
Tony Murray's avatar
Tony Murray committed
789

790
    saveHostPrefs();
791 792
}

Urs Wolfer's avatar
Urs Wolfer committed
793 794
void MainWindow::showRemoteViewToolbar()
{
Urs Wolfer's avatar
Urs Wolfer committed
795
    kDebug(5010);
Urs Wolfer's avatar
Urs Wolfer committed
796 797

    if (!m_toolBar) {
798 799
        actionCollection()->action("switch_fullscreen")->setIcon(KIcon("view-restore"));
        actionCollection()->action("switch_fullscreen")->setText(i18n("Switch to Window Mode"));
800
        actionCollection()->action("switch_fullscreen")->setIconText(i18n("Window Mode"));
801

Urs Wolfer's avatar
Urs Wolfer committed
802
        m_toolBar = new FloatingToolBar(m_fullscreenWindow, m_fullscreenWindow);
803
        m_toolBar->winId(); // force it to be a native widget (prevents problem with QX11EmbedContainer)
Urs Wolfer's avatar
Urs Wolfer committed
804
        m_toolBar->setSide(FloatingToolBar::Top);
805 806

        KComboBox *sessionComboBox = new KComboBox(m_toolBar);
807 808
        sessionComboBox->setStyleSheet("QComboBox:!editable{background:transparent;}");
        sessionComboBox->setModel(m_tabWidget->getModel());
809
        sessionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
810 811 812
        sessionComboBox->setCurrentIndex(m_tabWidget->currentIndex());
        connect(sessionComboBox, SIGNAL(activated(int)), m_tabWidget, SLOT(setCurrentIndex(int)));
        connect(m_tabWidget, SIGNAL(currentChanged(int)), sessionComboBox, SLOT(setCurrentIndex(int)));
813 814
        m_toolBar->addWidget(sessionComboBox);

815 816 817 818
        QToolBar *buttonBox = new QToolBar(m_toolBar);

        buttonBox->addAction(actionCollection()->action("new_connection"));
        buttonBox->addAction(actionCollection()->action("switch_fullscreen"));