konqmainwindow.cpp 207 KB
Newer Older
1
/* This file is part of the KDE project
2
   Copyright (C) 1998, 1999 Simon Hausmann <hausmann@kde.org>
3
   Copyright (C) 2000 Carsten Pfeiffer <pfeiffer@kde.org>
4
   Copyright (C) 2000-2005 David Faure <faure@kde.org>
5 6
   Copyright (C) 2007 Eduardo Robles Elvira <edulix@gmail.com>
   Copyright (C) 2007 Daniel García Moreno <danigm@gmail.com>
7

8 9
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
10 11
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
12

13
   This program is distributed in the hope that it will be useful,
14 15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
    General Public License for more details.
17

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
Dirk Mueller's avatar
Dirk Mueller committed
20
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Dirk Mueller's avatar
Dirk Mueller committed
21
   Boston, MA 02110-1301, USA.
22
*/
23

24
#include "konqmainwindow.h"
25
#include "konqmouseeventfilter.h"
26
#include "konqclosedwindowsmanager.h"
27 28
#include "konqsessionmanager.h"
#include "konqsessiondlg.h"
29
#include "konqdraggablelabel.h"
30
#include "konqcloseditem.h"
31 32
#include "konqapplication.h"
#include "konqguiclients.h"
33
#include "konqmainwindowfactory.h"
34
#include "KonqMainWindowAdaptor.h"
35
#include "KonquerorAdaptor.h"
36 37 38 39 40 41 42
#include "konqview.h"
#include "konqrun.h"
#include "konqmisc.h"
#include "konqviewmanager.h"
#include "konqframestatusbar.h"
#include "konqtabs.h"
#include "konqactions.h"
Rafael Fernández López's avatar
Rafael Fernández López committed
43
#include "konqsettingsxt.h"
44
#include "konqextensionmanager.h"
45
#include "konqueror_interface.h"
46
#include "delayedinitializer.h"
47 48
#include "konqextendedbookmarkowner.h"
#include "konqframevisitor.h"
49
#include "konqbookmarkbar.h"
50
#include "konqundomanager.h"
51
#include "konqhistorydialog.h"
52
#include <config-konqueror.h>
53
#include <kstringhandler.h>
54 55

#include <konq_events.h>
56
#include <konqpixmapprovider.h>
57
#include <kbookmarkmanager.h>
Nadeem Hasan's avatar
Nadeem Hasan committed
58
#include <kinputdialog.h>
David Faure's avatar
David Faure committed
59
#include <kcomponentdata.h>
60
#include <klineedit.h>
61
#include <kzip.h>
62
#include <pwd.h>
63 64 65 66 67
// we define STRICT_ANSI to get rid of some warnings in glibc
#ifndef __STRICT_ANSI__
#define __STRICT_ANSI__
#define _WE_DEFINED_IT_
#endif
68
#include <netdb.h>
69 70 71 72
#ifdef _WE_DEFINED_IT_
#undef __STRICT_ANSI__
#undef _WE_DEFINED_IT_
#endif
73 74
#include <assert.h>
#include <stdlib.h>
Peter Kelly's avatar
compile  
Peter Kelly committed
75
#include <time.h>
Laurent Montel's avatar
Laurent Montel committed
76
#include <kde_file.h>
77 78
#include <sys/types.h>
#include <sys/stat.h>
79
#include <unistd.h>
80

81 82
#include <QDesktopServices>
#include <QFile>
83
#include <QClipboard>
84
#include <QStackedWidget>
85
#include <QFileInfo>
86
#if KONQ_HAVE_X11
87
#include <QX11Info>
88
#endif
Dirk Mueller's avatar
Dirk Mueller committed
89
#include <QtCore/QEvent>
90
#include <QKeyEvent>
Dirk Mueller's avatar
Dirk Mueller committed
91 92
#include <QtCore/QByteRef>
#include <QtCore/QList>
93 94
#include <QPixmap>
#include <QLineEdit>
95

96
#include <kaboutdata.h>
97
#include <ktoolbar.h>
Daniel Teske's avatar
Daniel Teske committed
98
#include <konqbookmarkmenu.h>
99
#include <kcmultidialog.h>
David Faure's avatar
David Faure committed
100
#include <QDebug>
101
#include <kdesktopfile.h>
102
#include <kedittoolbar.h>
103
#include <klocalizedstring.h>
104
#include <kmessagebox.h>
105
#include <kmessagebox_queued.h>
106
#include <knewfilemenu.h>
107
#include <konq_popupmenu.h>
108
#include "konqsettings.h"
109
#include "konqanimatedlogo_p.h"
110
#include <kprotocolinfo.h>
Aaron J. Seigo's avatar
Aaron J. Seigo committed
111
#include <kprotocolmanager.h>
Aaron J. Seigo's avatar
build  
Aaron J. Seigo committed
112
#include <kstandardshortcut.h>
Aaron J. Seigo's avatar
Aaron J. Seigo committed
113
#include <kstandardaction.h>
David Faure's avatar
David Faure committed
114
#include <kstandarddirs.h>
115
#include <ksycoca.h>
116
#include <QTemporaryFile>
117
#include <ktogglefullscreenaction.h>
Aaron J. Seigo's avatar
Aaron J. Seigo committed
118
#include <ktoolbarpopupaction.h>
119
#include <kurlcompletion.h>
Holger Freyther's avatar
Holger Freyther committed
120
#include <kurlrequesterdialog.h>
Alexander Neundorf's avatar
 
Alexander Neundorf committed
121
#include <kurlrequester.h>
Aaron J. Seigo's avatar
Aaron J. Seigo committed
122
#include <kmimetypetrader.h>
Luboš Luňák's avatar
Luboš Luňák committed
123
#include <kwindowsystem.h>
124
#include <KJobWidgets>
Alexander Neundorf's avatar
 
Alexander Neundorf committed
125
#include <kfiledialog.h>
Laurent Montel's avatar
Laurent Montel committed
126
#include <KLocalizedString>
Laurent Montel's avatar
Laurent Montel committed
127
#include <QIcon>
David Faure's avatar
David Faure committed
128
#include <kiconloader.h>
Laurent Montel's avatar
Laurent Montel committed
129
#include <QMenu>
Christian Ehrlicher's avatar
Christian Ehrlicher committed
130
#include <kprocess.h>
131
#include <kio/scheduler.h>
132
#include <kio/netaccess.h>
133
#include <KIO/JobUiDelegate>
134
#include <KIO/CopyJob>
135
#include <KIO/Job>
136
#include <KIO/FileUndoManager>
137
#include <kparts/browseropenorsavequestion.h>
138 139 140
#include <KParts/OpenUrlEvent>
#include <KParts/BrowserHostExtension>
#include <KCompletionMatches>
Laurent Montel's avatar
Laurent Montel committed
141
#include <kacceleratormanager.h>
142
#include <kuser.h>
143
#include <kxmlguifactory.h>
144
#include <netwm.h>
145
#include <sonnet/configdialog.h>
146

Laurent Montel's avatar
Laurent Montel committed
147
#include <kauthorized.h>
148
#include <QtDBus/QtDBus>
149
#include <kconfiggroup.h>
150 151
#include <kglobalsettings.h>
#include <kurlauthorized.h>
Laurent Montel's avatar
Laurent Montel committed
152
#include <QFontDatabase>
153
#include <QMenuBar>
154 155
#include <QStandardPaths>
#include <KSharedConfig>
156

157 158
template class QList<QPixmap *>;
template class QList<KToggleAction *>;
159

160
static KBookmarkManager *s_bookmarkManager = 0;
161 162 163
QList<KonqMainWindow *> *KonqMainWindow::s_lstViews = 0;
KConfig *KonqMainWindow::s_comboConfig = 0;
KCompletion *KonqMainWindow::s_pCompletion = 0;
164

165 166
KonqOpenURLRequest KonqOpenURLRequest::null;

167
static const unsigned short int s_closedItemsListLength = 10;
168

169
static void raiseWindow(KonqMainWindow *window)
170
{
171
    if (!window) {
172
        return;
173
    }
174 175 176 177 178 179 180 181

    if (window->isMinimized()) {
        KWindowSystem::unminimizeWindow(window->winId());
    }
    window->activateWindow();
    window->raise();
}

182 183
KonqExtendedBookmarkOwner::KonqExtendedBookmarkOwner(KonqMainWindow *w)
{
184
    m_pKonqMainWindow = w;
185 186
}

187
KonqMainWindow::KonqMainWindow(const QUrl &initialURL)
188 189 190
    : KParts::MainWindow()
    , m_paClosedItems(0)
    , m_fullyConstructed(false)
Nick Shaforostoff's avatar
Nick Shaforostoff committed
191 192 193 194 195 196 197 198
    , m_bLocationBarConnected(false)
    , m_bURLEnterLock(false)
    , m_urlCompletionStarted(false)
    , m_prevMenuBarVisible(true)
    , m_goBuffer(0)
    , m_pBookmarkMenu(0)
    , m_configureDialog(0)
    , m_pURLCompletion(0)
199
    , m_isPopupWithProxyWindow(false)
200
{
201 202 203
    if (!s_lstViews) {
        s_lstViews = new QList<KonqMainWindow *>;
    }
204

205
    s_lstViews->append(this);
206

207
    KonqMouseEventFilter::self(); // create it
208

209 210 211 212 213
    m_pChildFrame = 0;
    m_pActiveChild = 0;
    m_workingTab = 0;
    (void) new KonqMainWindowAdaptor(this);
    m_paBookmarkBar = 0;
214

215 216 217 218 219
    m_viewModesGroup = new QActionGroup(this);
    m_viewModesGroup->setExclusive(true);
    connect(m_viewModesGroup, SIGNAL(triggered(QAction*)),
            this, SLOT(slotViewModeTriggered(QAction*)),
            Qt::QueuedConnection); // Queued so that we don't delete the action from the code that triggered it.
220

221
    // This has to be called before any action is created for this mainwindow
222
    setComponentData(KComponentData::mainComponent(), false /*don't load plugins yet*/);
223

224
    m_pViewManager = new KonqViewManager(this);
Simon Hausmann's avatar
Simon Hausmann committed
225

226 227 228 229 230
    m_viewModeMenu = 0;
    m_openWithMenu = 0;
    m_paCopyFiles = 0;
    m_paMoveFiles = 0;
    m_bookmarkBarInitialized = false;
231

232
    m_toggleViewGUIClient = new ToggleViewGUIClient(this);
233

234
    m_pBookmarksOwner = new KonqExtendedBookmarkOwner(this);
235

236 237 238
    // init history-manager, load history, get completion object
    if (!s_pCompletion) {
        s_bookmarkManager = KBookmarkManager::userBookmarksManager();
239

240
        // let the KBookmarkManager know that we are a browser, equals to "keditbookmarks --browser"
241
        s_bookmarkManager->setEditorOptions(QStringLiteral("konqueror"), true);
242

243 244
        KonqHistoryManager *mgr = new KonqHistoryManager(s_bookmarkManager);
        s_pCompletion = mgr->completionObject();
245

246 247 248 249 250 251
        // setup the completion object before createGUI(), so that the combo
        // picks up the correct mode from the HistoryManager (in slotComboPlugged)
        int mode = KonqSettings::settingsCompletionMode();
        s_pCompletion->setCompletionMode(static_cast<KCompletion::CompletionMode>(mode));
    }
    connect(KParts::HistoryProvider::self(), &KParts::HistoryProvider::cleared, this, &KonqMainWindow::slotClearComboHistory);
252

253 254
    KonqPixmapProvider *prov = KonqPixmapProvider::self();
    if (!s_comboConfig) {
255
        s_comboConfig = new KConfig(QStringLiteral("konq_history"), KConfig::NoGlobals);
256 257
        KonqCombo::setConfig(s_comboConfig);
        KConfigGroup locationBarGroup(s_comboConfig, "Location Bar");
258
        prov->load(locationBarGroup, QStringLiteral("ComboIconCache"));
259
    }
260

261
    connect(prov, SIGNAL(changed()), SLOT(slotIconsChanged()));
262

263 264 265
    m_pUndoManager = new KonqUndoManager(this);
    connect(m_pUndoManager, SIGNAL(undoAvailable(bool)),
            this, SLOT(slotUndoAvailable(bool)));
266

267 268
    initCombo();
    initActions();
269

270
    connect(KGlobalSettings::self(), &KGlobalSettings::kdisplayFontChanged, this, &KonqMainWindow::slotReconfigure);
271

272
    setXMLFile(QStringLiteral("konqueror.rc"));
273

274
    setStandardToolBarMenuEnabled(true);
275

276
    createGUI(Q_NULLPTR);
David Faure's avatar
David Faure committed
277

278
    m_combo->setParent(toolBar(QStringLiteral("locationToolBar")));
279 280
    m_combo->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
    m_combo->show();
281

282
    checkDisableClearButton();
David Faure's avatar
David Faure committed
283

284
    connect(toolBarMenuAction(), SIGNAL(triggered()), this, SLOT(slotForceSaveMainWindowSettings()));
David Faure's avatar
David Faure committed
285

286
    if (!m_toggleViewGUIClient->empty()) {
287
        plugActionList(QStringLiteral("toggleview"), m_toggleViewGUIClient->actions());
288 289 290 291
    } else {
        delete m_toggleViewGUIClient;
        m_toggleViewGUIClient = 0;
    }
292

293
    m_bNeedApplyKonqMainWindowSettings = true;
294

295 296 297 298 299 300
    if (!initialURL.isEmpty()) {
        openFilteredUrl(initialURL.url());
    } else {
        // silent
        m_bNeedApplyKonqMainWindowSettings = false;
    }
301

302
    resize(700, 480);
Albert Astals Cid's avatar
Albert Astals Cid committed
303
    setAutoSaveSettings();
304

305
    //qDebug() << this << "created";
Laurent Montel's avatar
Laurent Montel committed
306

307 308
    KonqSessionManager::self();
    m_fullyConstructed = true;
309
}
Matthias Welk's avatar
Matthias Welk committed
310

311
KonqMainWindow::~KonqMainWindow()
312
{
David Faure's avatar
David Faure committed
313
    //qDebug() << this;
Doug Hanley's avatar
 
Doug Hanley committed
314

315 316
    delete m_pViewManager;
    m_pViewManager = 0;
Doug Hanley's avatar
 
Doug Hanley committed
317

318 319 320 321 322 323
    if (s_lstViews) {
        s_lstViews->removeAll(this);
        if (s_lstViews->isEmpty()) {
            delete s_lstViews;
            s_lstViews = 0;
        }
Simon Hausmann's avatar
Simon Hausmann committed
324
    }
325

326 327
    qDeleteAll(m_openWithActions);
    m_openWithActions.clear();
328

329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
    delete m_pBookmarkMenu;
    delete m_paBookmarkBar;
    delete m_pBookmarksOwner;
    delete m_pURLCompletion;
    delete m_paClosedItems;

    if (s_lstViews == 0) {
        delete s_comboConfig;
        s_comboConfig = 0;
    }

    delete m_configureDialog;
    m_configureDialog = 0;
    delete m_combo;
    m_combo = 0;
    delete m_locationLabel;
    m_locationLabel = 0;
    m_pUndoManager->disconnect();
    delete m_pUndoManager;
348

349
    //qDebug() << this << "deleted";
350 351
}

352
QWidget *KonqMainWindow::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction)
353
{
354
    QWidget *res = KParts::MainWindow::createContainer(parent, index, element, containerAction);
355

356 357 358
    static QString nameBookmarkBar = QStringLiteral("bookmarkToolBar");
    static QString tagToolBar = QStringLiteral("ToolBar");
    if (res && (element.tagName() == tagToolBar) && (element.attribute(QStringLiteral("name")) == nameBookmarkBar)) {
359
        Q_ASSERT(::qobject_cast<KToolBar *>(res));
360
        if (!KAuthorized::authorizeAction(QStringLiteral("bookmarks"))) {
361 362 363
            delete res;
            return 0;
        }
364

365 366 367 368 369 370 371
        if (!m_bookmarkBarInitialized) {
            // The actual menu needs a different action collection, so that the bookmarks
            // don't appear in kedittoolbar
            m_bookmarkBarInitialized = true;
            DelayedInitializer *initializer = new DelayedInitializer(QEvent::Show, res);
            connect(initializer, &DelayedInitializer::initialize, this, &KonqMainWindow::initBookmarkBar);
        }
372
    }
373

374
    if (res && element.tagName() == QLatin1String("Menu")) {
375 376
        const QString &menuName = element.attribute(QStringLiteral("name"));
        if (menuName == QLatin1String("edit") || menuName == QLatin1String("tools")) {
377 378 379 380
            Q_ASSERT(qobject_cast<QMenu *>(res));
            KAcceleratorManager::manage(static_cast<QMenu *>(res));
        }
    }
381

382
    return res;
383
}
384

Maks Orlovich's avatar
 
Maks Orlovich committed
385 386
void KonqMainWindow::initBookmarkBar()
{
387
    KToolBar *bar = qFindChild<KToolBar *>(this, QStringLiteral("bookmarkToolBar"));
388

389 390 391
    if (!bar) {
        return;
    }
392

393
    const bool wasVisible = bar->isVisible();
394

395 396
    delete m_paBookmarkBar;
    m_paBookmarkBar = new KBookmarkBar(s_bookmarkManager, m_pBookmarksOwner, bar, this);
Maks Orlovich's avatar
 
Maks Orlovich committed
397

398 399 400 401
    // hide if empty
    if (bar->actions().count() == 0 || !wasVisible) {
        bar->hide();
    }
Maks Orlovich's avatar
 
Maks Orlovich committed
402 403
}

404
void KonqMainWindow::removeContainer(QWidget *container, QWidget *parent, QDomElement &element, QAction *containerAction)
405
{
406 407
    static QString nameBookmarkBar = QStringLiteral("bookmarkToolBar");
    static QString tagToolBar = QStringLiteral("ToolBar");
408

409
    if (element.tagName() == tagToolBar && element.attribute(QStringLiteral("name")) == nameBookmarkBar) {
410 411 412 413 414
        Q_ASSERT(::qobject_cast<KToolBar *>(container));
        if (m_paBookmarkBar) {
            m_paBookmarkBar->clear();
        }
    }
415

416
    KParts::MainWindow::removeContainer(container, parent, element, containerAction);
417 418
}

419 420 421
// Detect a name filter (e.g. *.txt) in the url.
// Note that KShortURIFilter does the same, but we have no way of getting it from there
//
422
// Note: this removes the filter from the URL.
423
QString KonqMainWindow::detectNameFilter(QUrl &url)
424
{
425
    if (!KProtocolManager::supportsListing(url)) {
Laurent Montel's avatar
Laurent Montel committed
426
        return QString();
427
    }
428

429 430
    // Look for wildcard selection
    QString nameFilter;
431
    QString path = url.path();
432 433 434
    int lastSlash = path.lastIndexOf('/');
    if (lastSlash > -1) {
        if (!url.query().isEmpty() && lastSlash == path.length() - 1) {  //  In /tmp/?foo, foo isn't a query
435 436
            path += url.query(); // includes the '?'
        }
437 438
        QString fileName = path.mid(lastSlash + 1);
        if (fileName.indexOf('*') != -1 || fileName.indexOf('[') != -1 || fileName.indexOf('?') != -1) {
439
            // Check that a file or dir with all the special chars in the filename doesn't exist
440 441
            // (NetAccess::exists has a fast path for local files)
            if (!KIO::NetAccess::exists(url, KIO::NetAccess::DestinationSide, this)) {
442
                nameFilter = fileName;
443
                url = url.adjusted(QUrl::RemoveFilename | QUrl::RemoveQuery);
David Faure's avatar
David Faure committed
444
                qDebug() << "Found wildcard. nameFilter=" << nameFilter << "  New url=" << url;
445
            }
446
        }
447
    }
448

449 450 451
    return nameFilter;
}

452
void KonqMainWindow::openFilteredUrl(const QString &url, const KonqOpenURLRequest &req)
453
{
454
    // Filter URL to build a correct one
455 456 457
    if (m_currentDir.isEmpty() && m_currentView) {
        m_currentDir = m_currentView->url();
    }
458

459
    QUrl filteredURL(KonqMisc::konqFilteredURL(this, url, m_currentDir));
David Faure's avatar
David Faure committed
460
    qDebug() << "url" << url << "filtered into" << filteredURL;
461

462
    if (filteredURL.isEmpty()) { // initially empty, or error (e.g. ~unknown_user)
463
        return;
464
    }
465

Laurent Montel's avatar
Laurent Montel committed
466
    m_currentDir.clear();
467

David Faure's avatar
David Faure committed
468
    openUrl(0, filteredURL, QString(), req);
469 470 471

    // #4070: Give focus to view after URL was entered manually
    // Note: we do it here if the view mode (i.e. part) wasn't changed
472
    // If it is changed, then it's done in KonqViewManager::doSetActivePart
473
    if (m_currentView) {
474
        m_currentView->setFocus();
475
    }
476 477
}

478
void KonqMainWindow::openFilteredUrl(const QString &_url, bool inNewTab, bool tempFile)
479
{
480
    KonqOpenURLRequest req(_url);
481
    req.browserArgs.setNewTab(inNewTab);
482 483
    req.newTabInFront = true;
    req.tempFile = tempFile;
484

485
    openFilteredUrl(_url, req);
486 487
}

488
void KonqMainWindow::openFilteredUrl(const QString &_url,  const QString &_mimeType, bool inNewTab, bool tempFile)
489
{
490
    KonqOpenURLRequest req(_url);
491 492 493 494 495
    req.browserArgs.setNewTab(inNewTab);
    req.newTabInFront = true;
    req.tempFile = tempFile;
    req.args.setMimeType(_mimeType);

496
    openFilteredUrl(_url, req);
497 498
}

499
void KonqMainWindow::openUrl(KonqView *_view, const QUrl &_url,
500
                             const QString &_mimeType, const KonqOpenURLRequest &_req,
David Faure's avatar
David Faure committed
501
                             bool trustedSource)
502
{
503
#ifndef NDEBUG // needed for req.debug()
David Faure's avatar
David Faure committed
504
    qDebug() << "url=" << _url << "mimeType=" << _mimeType
505
             << "_req=" << _req.debug() << "view=" << _view;
506
#endif
507

David Faure's avatar
David Faure committed
508
    // We like modifying args in this method :)
509
    QUrl url(_url);
David Faure's avatar
David Faure committed
510 511 512
    QString mimeType(_mimeType);
    KonqOpenURLRequest req(_req);

513
    if (mimeType.isEmpty()) {
514
        mimeType = req.args.mimeType();
515
    }
516

517
    if (url.scheme() != QLatin1String("error") && url.scheme() != QLatin1String("about")) {
518 519 520 521
        if (!url.isValid()) {
            // I think we can't really get here anymore; I tried and didn't succeed.
            // URL filtering catches this case before hand, and in cases without filtering
            // (e.g. HTML link), the url is empty here, not invalid.
David Faure's avatar
David Faure committed
522 523
            // But just to be safe, let's keep this code path
            url = KParts::BrowserRun::makeErrorUrl(KIO::ERR_MALFORMED_URL, url.url(), url);
524
        } else if (!KProtocolInfo::isKnownProtocol(url)) {
525
            url = KParts::BrowserRun::makeErrorUrl(KIO::ERR_UNSUPPORTED_PROTOCOL, url.scheme(), url);
526
        }
527
    }
528

529 530
    if (url.url() == QLatin1String("about:blank") || url.scheme() == QLatin1String("error")) {
        mimeType = QStringLiteral("text/html");
531
    }
532

533 534
    const QString nameFilter = detectNameFilter(url);
    if (!nameFilter.isEmpty()) {
535 536
        req.nameFilter = nameFilter;
        url = url.adjusted(QUrl::RemoveFilename);
537
    }
538

539
    QLineEdit *edit = comboEdit();
540
    if (edit) {
541
        edit->setModified(false);
542
    }
543

544
    KonqView *view = _view;
545

546 547
    // When clicking a 'follow active' view (e.g. view is the sidebar),
    // open the URL in the active view
548
    if (view && view->isFollowActive()) {
549
        view = m_currentView;
550
    }
551

552 553 554
    if (!view && !req.browserArgs.newTab()) {
        view = m_currentView;    /* Note, this can be 0, e.g. on startup */
    } else if (!view && req.browserArgs.newTab()) {
555

556 557 558 559 560
        // The URL should be opened in a new tab. Let's create the tab right away,
        // it gives faster user feedback (#163628). For a short while (kde-4.1-beta1)
        // I removed this entire block so that we wouldn't end up with a useless tab when
        // launching an external application for this mimetype. But user feedback
        // in all cases is more important than empty tabs in some cases.
561
        view = m_pViewManager->addTab(QStringLiteral("text/html"),
562
                                      QString(),
563 564 565
                                      false,
                                      req.openAfterCurrentPage);
        if (view) {
566
            view->setCaption(i18nc("@title:tab", "Loading..."));
567
            view->setLocationBarURL(_url);
568 569 570
            if (!req.browserArgs.frameName.isEmpty()) {
                view->setViewName(req.browserArgs.frameName);    // #44961
            }
571

572
            if (req.newTabInFront) {
573
                m_pViewManager->showTab(view);
574
            }
575 576 577

            updateViewActions(); //A new tab created -- we may need to enable the "remove tab" button (#56318)
        } else {
578
            req.browserArgs.setNewTab(false);
579 580
        }
    }
581

582 583 584 585 586 587 588 589 590
    const QString oldLocationBarURL = locationBarURL();
    if (view) {
        if (view == m_currentView) {
            //will do all the stuff below plus GUI stuff
            abortLoading();
        } else {
            view->stop();
            // Don't change location bar if not current view
        }
591
    }
592

593 594
    // Fast mode for local files: do the stat ourselves instead of letting KRun do it.
    if (mimeType.isEmpty() && url.isLocalFile()) {
595 596
        QMimeDatabase db;
        mimeType = db.mimeTypeForFile(url.toLocalFile()).name();
597
    }
598

599 600 601 602 603 604 605 606 607 608
    if (url.isLocalFile()) {
        // Generic mechanism for redirecting to tar:/<path>/ when clicking on a tar file,
        // zip:/<path>/ when clicking on a zip file, etc.
        // The .protocol file specifies the mimetype that the kioslave handles.
        // Note that we don't use mimetype inheritance since we don't want to
        // open OpenDocument files as zip folders...
        // Also note that we do this here and not in openView anymore,
        // because in the case of foo.bz2 we don't know the final mimetype, we need a konqrun...
        const QString protocol = KProtocolManager::protocolForArchiveMimetype(mimeType);
        if (!protocol.isEmpty() && KonqFMSettings::settings()->shouldEmbed(mimeType)) {
609
            url.setScheme(protocol);
610
            if (mimeType == QLatin1String("application/x-webarchive")) {
611
                url.setPath(url.path() + "/index.html");
612
                mimeType = QStringLiteral("text/html");
613 614
            } else {
                if (KProtocolManager::outputType(url) == KProtocolInfo::T_FILESYSTEM) {
615
                    if (!url.path().endsWith('/')) {
616
                        url.setPath(url.path() + '/');
617
                    }
618
                    mimeType = QStringLiteral("inode/directory");
619
                } else {
620
                    mimeType.clear();
621
                }
622 623
            }
        }
624 625

        // Redirect to the url in Type=Link desktop files
626
        if (mimeType == QLatin1String("application/x-desktop")) {
627 628
            KDesktopFile df(url.toLocalFile());
            if (df.hasLinkType()) {
629
                url = QUrl(df.readUrl());
630 631 632
                mimeType.clear(); // to be determined again
            }
        }
633 634
    }

635
    const bool hasMimeType = (!mimeType.isEmpty() && mimeType != QLatin1String("application/octet-stream"));
David Faure's avatar
David Faure committed
636 637 638
    KService::Ptr offer;
    bool associatedAppIsKonqueror = false;
    if (hasMimeType) {
639
        offer = KMimeTypeTrader::self()->preferredService(mimeType, QStringLiteral("Application"));
David Faure's avatar
David Faure committed
640
        associatedAppIsKonqueror = isMimeTypeAssociatedWithSelf(mimeType, offer);
641 642 643 644
        // If the associated app is konqueror itself, then make sure we try to embed before bailing out.
        if (associatedAppIsKonqueror) {
            req.forceAutoEmbed = true;
        }
David Faure's avatar
David Faure committed
645 646 647
    }

    //qDebug() << "trying openView for" << url << "( mimeType" << mimeType << ")";
648
    if (hasMimeType || url.url() == QLatin1String("about:") || url.url().startsWith(QLatin1String("about:konqueror")) || url.url() == QLatin1String("about:plugins")) {
649 650 651

        // Built-in view ?
        if (!openView(mimeType, url, view /* can be 0 */, req)) {
David Faure's avatar
David Faure committed
652
            //qDebug() << "openView returned false";
653 654
            // Are we following another view ? Then forget about this URL. Otherwise fire app.
            if (!req.followMode) {
David Faure's avatar
David Faure committed
655
                //qDebug() << "we were not following. Fire app.";
656 657 658 659 660 661
                // The logic below is similar to BrowserRun::handleNonEmbeddable(),
                // but we don't have a BrowserRun instance here, and since it uses
                // some virtual methods [like save, for KHTMLRun], we can't just
                // move all the logic to static methods... catch 22...

                if (!url.isLocalFile() && !trustedSource && KonqRun::isTextExecutable(mimeType)) {
662
                    mimeType = QStringLiteral("text/plain");    // view, don't execute
663
                }
664 665
                // Remote URL: save or open ?
                QString protClass = KProtocolInfo::protocolClass(url.scheme());
666
                bool open = url.isLocalFile() || protClass == QLatin1String(":local") || KProtocolInfo::isHelperProtocol(url);
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
                if (!open) {
                    KParts::BrowserOpenOrSaveQuestion dlg(this, url, mimeType);
                    dlg.setFeatures(KParts::BrowserOpenOrSaveQuestion::ServiceSelection);
                    const KParts::BrowserOpenOrSaveQuestion::Result res = dlg.askOpenOrSave();
                    if (res == KParts::BrowserOpenOrSaveQuestion::Save) {
                        KParts::BrowserRun::saveUrl(url, QString(), this, req.args);
                    }
                    open = (res == KParts::BrowserOpenOrSaveQuestion::Open);
                    if (open) {
                        offer = dlg.selectedService();
                    }
                }
                if (open) {
                    if (associatedAppIsKonqueror && refuseExecutingKonqueror(mimeType)) {
                        return;
                    }
                    QList<QUrl> lst;
                    lst.append(url);
David Faure's avatar
David Faure committed
685
                    //qDebug() << "Got offer" << (offer ? offer->name() : QString("0"));
686 687 688 689 690 691
                    const bool allowExecution = trustedSource || KParts::BrowserRun::allowExecution(mimeType, url);
                    if (allowExecution) {
                        const bool isExecutable = KonqRun::isExecutable(mimeType);
                        // Open with no offer means the user clicked on "Open With..." button.
                        if (!offer && !isExecutable) {
                            (void) KRun::displayOpenWithDialog(lst, this);
692
                        } else if (isExecutable || !KRun::runApplication(*offer, lst, this)) {
693 694 695
                            setLocationBarURL(oldLocationBarURL);   // Revert to previous locationbar URL
                            (void)new KRun(url, this);
                        }
696
                    }
697
                }
698
            }
699
        }
700 701 702 703 704 705 706 707 708 709 710 711 712
    } else { // no known mimeType, use KonqRun
        bool earlySetLocationBarURL = false;
        if (!view && !m_currentView) { // no view yet, e.g. starting with url as argument
            earlySetLocationBarURL = true;
        } else if (view == m_currentView && view->url().isEmpty()) { // opening in current view
            earlySetLocationBarURL = true;
        }
        if (req.browserArgs.newTab()) { // it's going into a new tab anyway
            earlySetLocationBarURL = false;
        }
        if (earlySetLocationBarURL) {
            // Show it for now in the location bar, but we'll need to store it in the view
            // later on (can't do it yet since either view == 0 or updateHistoryEntry will be called).
David Faure's avatar
David Faure committed
713
            qDebug() << "url=" << url;
714 715
            setLocationBarURL(url);
        }
716

David Faure's avatar
David Faure committed
717
        qDebug() << "Creating new konqrun for" << url << "req.typedUrl=" << req.typedUrl;
George Staikos's avatar
 
George Staikos committed
718

719
        KonqRun *run = new KonqRun(this, view /* can be 0 */, url, req, trustedSource);
720

721 722
        // Never start in external browser
        run->setEnableExternalBrowser(false);
723

724 725 726
        if (view) {
            view->setRun(run);
        }
727

728 729 730
        if (view == m_currentView) {
            startAnimation();
        }
731

732 733
        connect(run, &KonqRun::finished, this, &KonqMainWindow::slotRunFinished);
    }
734 735
}

736 737
// When opening a new view, for @p mimeType, prefer the part used in @p currentView, if possible.
// Testcase: window.open after manually switching to another web engine, and with "open popups in tabs" off.
738
static QString preferredService(KonqView *currentView, const QString &mimeType)
739 740 741 742 743 744 745
{
    if (currentView && !mimeType.isEmpty() && currentView->supportsMimeType(mimeType)) {
        return currentView->service()->desktopEntryName();
    }
    return QString();
}