1 /* This file is part of the KDE project
2     SPDX-FileCopyrightText: 1998, 1999 Simon Hausmann <hausmann@kde.org>
3     SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <pfeiffer@kde.org>
4     SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
5     SPDX-FileCopyrightText: 2007 Eduardo Robles Elvira <edulix@gmail.com>
6     SPDX-FileCopyrightText: 2007 Daniel García Moreno <danigm@gmail.com>
7 
8     SPDX-License-Identifier: GPL-2.0-or-later
9 */
10 
11 #include "konqmainwindow.h"
12 #include "konqmouseeventfilter.h"
13 #include "konqclosedwindowsmanager.h"
14 #include "konqsessionmanager.h"
15 #include "konqsessiondlg.h"
16 #include "konqdraggablelabel.h"
17 #include "konqcloseditem.h"
18 #include "konqapplication.h"
19 #include "konqguiclients.h"
20 #include "konqmainwindowfactory.h"
21 #include "KonqMainWindowAdaptor.h"
22 #include "KonquerorAdaptor.h"
23 #include "konqview.h"
24 #include "konqrun.h"
25 #include "konqmisc.h"
26 #include "konqviewmanager.h"
27 #include "konqframestatusbar.h"
28 #include "konqtabs.h"
29 #include "konqactions.h"
30 #include "konqsettingsxt.h"
31 #include "konqextensionmanager.h"
32 #include "konqueror_interface.h"
33 #include "delayedinitializer.h"
34 #include "konqextendedbookmarkowner.h"
35 #include "konqframevisitor.h"
36 #include "konqbookmarkbar.h"
37 #include "konqundomanager.h"
38 #include "konqhistorydialog.h"
39 #include <config-konqueror.h>
40 #include <kstringhandler.h>
41 #include "konqurl.h"
42 
43 #include <konq_events.h>
44 #include <konqpixmapprovider.h>
45 #include <konqsettings.h>
46 #include <konq_spellcheckingconfigurationdispatcher.h>
47 
48 #include <kwidgetsaddons_version.h>
49 #include <kxmlgui_version.h>
50 #include <kparts_version.h>
51 #include <kbookmarkmanager.h>
52 #include <klineedit.h>
53 #include <kzip.h>
54 #include <pwd.h>
55 // we define STRICT_ANSI to get rid of some warnings in glibc
56 #ifndef __STRICT_ANSI__
57 #define __STRICT_ANSI__
58 #define _WE_DEFINED_IT_
59 #endif
60 #include <netdb.h>
61 #ifdef _WE_DEFINED_IT_
62 #undef __STRICT_ANSI__
63 #undef _WE_DEFINED_IT_
64 #endif
65 #include <assert.h>
66 #include <stdlib.h>
67 #include <time.h>
68 #include <sys/types.h>
69 #include <sys/stat.h>
70 #include <KIO/ApplicationLauncherJob>
71 #include <KIO/OpenUrlJob>
72 
73 #include <QDesktopServices>
74 #include <QFile>
75 #include <QClipboard>
76 #include <QStackedWidget>
77 #include <QFileInfo>
78 #if KONQ_HAVE_X11
79 #include <QX11Info>
80 #endif
81 #include <QEvent>
82 #include <QKeyEvent>
83 #include <QByteRef>
84 #include <QPixmap>
85 #include <QLineEdit>
86 #include <QNetworkProxy>
87 
88 #if KPARTS_VERSION >= QT_VERSION_CHECK(5, 77, 0)
89 #include <KPluginMetaData>
90 #endif
91 #include <kaboutdata.h>
92 #include <ktoolbar.h>
93 #include <konqbookmarkmenu.h>
94 #include <kcmultidialog.h>
95 #include "konqdebug.h"
96 #include <kdesktopfile.h>
97 #include <kedittoolbar.h>
98 #include <klocalizedstring.h>
99 #include <kmessagebox.h>
100 #include <knewfilemenu.h>
101 #include <konq_popupmenu.h>
102 #include "konqsettings.h"
103 #include "konqanimatedlogo_p.h"
104 #include <kprotocolinfo.h>
105 #include <kprotocolmanager.h>
106 #include <kstandardshortcut.h>
107 #include <kstandardaction.h>
108 #include <ksycoca.h>
109 #include <QTemporaryFile>
110 #include <ktogglefullscreenaction.h>
111 #include <ktoolbarpopupaction.h>
112 #include <kurlcompletion.h>
113 #include <kurlrequesterdialog.h>
114 #include <kurlrequester.h>
115 #include <kmimetypetrader.h>
116 #include <KJobWidgets>
117 #include <KLocalizedString>
118 #include <QIcon>
119 #include <kiconloader.h>
120 #include <QMenu>
121 #include <kprocess.h>
122 #include <kio/scheduler.h>
123 #include <KIO/JobUiDelegate>
124 #include <KIO/CopyJob>
125 #include <KIO/Job>
126 #include <KIO/FileUndoManager>
127 #include <kparts/browseropenorsavequestion.h>
128 #include <KParts/OpenUrlEvent>
129 #include <KCompletionMatches>
130 #include <kacceleratormanager.h>
131 #include <kuser.h>
132 #include <kxmlguifactory.h>
133 #include <sonnet/configdialog.h>
134 #include <kwindowsystem.h>
135 #include <netwm.h>
136 
137 #include <kauthorized.h>
138 #include <QDBusConnection>
139 #include <QDBusMessage>
140 #include <kconfiggroup.h>
141 #include <kurlauthorized.h>
142 #include <QFontDatabase>
143 #include <QMenuBar>
144 #include <QStandardPaths>
145 #include <KSharedConfig>
146 
147 #include <KProtocolManager>
148 
149 template class QList<QPixmap *>;
150 template class QList<KToggleAction *>;
151 
152 static KBookmarkManager *s_bookmarkManager = nullptr;
153 QList<KonqMainWindow *> *KonqMainWindow::s_lstMainWindows = nullptr;
154 KConfig *KonqMainWindow::s_comboConfig = nullptr;
155 KCompletion *KonqMainWindow::s_pCompletion = nullptr;
156 
157 KonqOpenURLRequest KonqOpenURLRequest::null;
158 
159 static const unsigned short int s_closedItemsListLength = 10;
160 
raiseWindow(KonqMainWindow * window)161 static void raiseWindow(KonqMainWindow *window)
162 {
163     if (!window) {
164         return;
165     }
166 
167     if (window->isMinimized()) {
168         KWindowSystem::unminimizeWindow(window->winId());
169     }
170     window->activateWindow();
171     window->raise();
172 }
173 
KonqExtendedBookmarkOwner(KonqMainWindow * w)174 KonqExtendedBookmarkOwner::KonqExtendedBookmarkOwner(KonqMainWindow *w)
175 {
176     m_pKonqMainWindow = w;
177 }
178 
KonqMainWindow(const QUrl & initialURL)179 KonqMainWindow::KonqMainWindow(const QUrl &initialURL)
180     : KParts::MainWindow()
181     , m_paClosedItems(nullptr)
182     , m_fullyConstructed(false)
183     , m_bLocationBarConnected(false)
184     , m_bURLEnterLock(false)
185     , m_urlCompletionStarted(false)
186     , m_fullScreenData{FullScreenState::NoFullScreen, FullScreenState::NoFullScreen, true, true, false}
187     , m_goBuffer(0)
188     , m_pBookmarkMenu(nullptr)
189     , m_configureDialog(nullptr)
190     , m_pURLCompletion(nullptr)
191     , m_isPopupWithProxyWindow(false)
192 {
193     if (!s_lstMainWindows) {
194         s_lstMainWindows = new QList<KonqMainWindow *>;
195     }
196 
197     s_lstMainWindows->append(this);
198 
199     KonqMouseEventFilter::self(); // create it
200 
201     m_pChildFrame = nullptr;
202     m_pActiveChild = nullptr;
203     m_workingTab = 0;
204     (void) new KonqMainWindowAdaptor(this);
205     m_paBookmarkBar = nullptr;
206 
207     m_viewModesGroup = new QActionGroup(this);
208     m_viewModesGroup->setExclusive(true);
209     connect(m_viewModesGroup, SIGNAL(triggered(QAction*)),
210             this, SLOT(slotViewModeTriggered(QAction*)),
211             Qt::QueuedConnection); // Queued so that we don't delete the action from the code that triggered it.
212 
213     // This has to be called before any action is created for this mainwindow
214 #if KPARTS_VERSION >= QT_VERSION_CHECK(5, 77, 0)
215     const KAboutData applicationData = KAboutData::applicationData();
216     setComponentName(applicationData.componentName(), applicationData.displayName());
217 #else
218     setComponentData(KAboutData::applicationData(), false /*don't load plugins yet*/);
219 #endif
220 
221     m_pViewManager = new KonqViewManager(this);
222     m_viewModeMenu = nullptr;
223     m_openWithMenu = nullptr;
224     m_paCopyFiles = nullptr;
225     m_paMoveFiles = nullptr;
226     m_bookmarkBarInitialized = false;
227 
228     m_toggleViewGUIClient = new ToggleViewGUIClient(this);
229 
230     m_pBookmarksOwner = new KonqExtendedBookmarkOwner(this);
231 
232     // init history-manager, load history, get completion object
233     if (!s_pCompletion) {
234         s_bookmarkManager = KBookmarkManager::userBookmarksManager();
235 
236         // let the KBookmarkManager know that we are a browser, equals to "keditbookmarks --browser"
237         s_bookmarkManager->setEditorOptions(QStringLiteral("konqueror"), true);
238 
239         KonqHistoryManager *mgr = new KonqHistoryManager(s_bookmarkManager);
240         s_pCompletion = mgr->completionObject();
241 
242         // setup the completion object before createGUI(), so that the combo
243         // picks up the correct mode from the HistoryManager (in slotComboPlugged)
244         int mode = KonqSettings::settingsCompletionMode();
245         s_pCompletion->setCompletionMode(static_cast<KCompletion::CompletionMode>(mode));
246     }
247     connect(KParts::HistoryProvider::self(), &KParts::HistoryProvider::cleared, this, &KonqMainWindow::slotClearComboHistory);
248 
249     KonqPixmapProvider *prov = KonqPixmapProvider::self();
250     if (!s_comboConfig) {
251         s_comboConfig = new KConfig(QStringLiteral("konq_history"), KConfig::NoGlobals);
252         KonqCombo::setConfig(s_comboConfig);
253         KConfigGroup locationBarGroup(s_comboConfig, "Location Bar");
254         prov->load(locationBarGroup, QStringLiteral("ComboIconCache"));
255     }
256 
257     connect(prov, SIGNAL(changed()), SLOT(slotIconsChanged()));
258 
259     m_pUndoManager = new KonqUndoManager(KonqClosedWindowsManager::self(), this);
260     connect(m_pUndoManager, SIGNAL(undoAvailable(bool)),
261             this, SLOT(slotUndoAvailable(bool)));
262 
263     initCombo();
264     initActions();
265 
266     setXMLFile(QStringLiteral("konqueror.rc"));
267 
268     setStandardToolBarMenuEnabled(true);
269 
270     createGUI(nullptr);
271 
272     m_combo->setParent(toolBar(QStringLiteral("locationToolBar")));
273     m_combo->show();
274 
275     checkDisableClearButton();
276 
277     connect(toolBarMenuAction(), SIGNAL(triggered()), this, SLOT(slotForceSaveMainWindowSettings()));
278 
279     if (!m_toggleViewGUIClient->empty()) {
280         plugActionList(QStringLiteral("toggleview"), m_toggleViewGUIClient->actions());
281     } else {
282         delete m_toggleViewGUIClient;
283         m_toggleViewGUIClient = nullptr;
284     }
285 
286     m_bNeedApplyKonqMainWindowSettings = true;
287 
288     if (!initialURL.isEmpty()) {
289         openFilteredUrl(initialURL.url());
290     } else {
291         // silent
292         m_bNeedApplyKonqMainWindowSettings = false;
293     }
294 
295     resize(700, 480);
296 
297     updateProxyForWebEngine(false);
298     QDBusConnection::sessionBus().connect("", QStringLiteral("/KIO/Scheduler"), QStringLiteral("org.kde.KIO.Scheduler"),
299                                           QStringLiteral("reparseSlaveConfiguration"), this, SLOT(updateProxyForWebEngine()));
300     setAutoSaveSettings();
301 
302     //qCDebug(KONQUEROR_LOG) << this << "created";
303 
304     KonqSessionManager::self();
305     m_fullyConstructed = true;
306 }
307 
~KonqMainWindow()308 KonqMainWindow::~KonqMainWindow()
309 {
310     //qCDebug(KONQUEROR_LOG) << this;
311 
312     delete m_pViewManager;
313     m_pViewManager = nullptr;
314 
315     if (s_lstMainWindows) {
316         s_lstMainWindows->removeAll(this);
317         if (s_lstMainWindows->isEmpty()) {
318             delete s_lstMainWindows;
319             s_lstMainWindows = nullptr;
320         } else if (s_lstMainWindows->length() == 1 && s_lstMainWindows->first()->isPreloaded()) {
321             //If the only remaining window is preloaded, we want to close it. Otherwise,
322             //the application will remain open, even if there are no visible windows.
323             //This can be seen, for example, running Konqueror from a terminal with the
324             //"Always try to have a preloaded instance": even after closing the last
325             //window, Konqueror won't exit (see bug #258124). To avoid this situation,
326             //We close the preloaded window here, so that the application can exit,
327             //and launch another instance of Konqueror from konqmain.
328             s_lstMainWindows->first()->close();
329         }
330     }
331 
332     qDeleteAll(m_openWithActions);
333     m_openWithActions.clear();
334 
335     delete m_pBookmarkMenu;
336     delete m_paBookmarkBar;
337     delete m_pBookmarksOwner;
338     delete m_pURLCompletion;
339     delete m_paClosedItems;
340 
341     if (s_lstMainWindows == nullptr) {
342         delete s_comboConfig;
343         s_comboConfig = nullptr;
344     }
345 
346     delete m_configureDialog;
347     m_configureDialog = nullptr;
348     delete m_combo;
349     m_combo = nullptr;
350     delete m_locationLabel;
351     m_locationLabel = nullptr;
352     m_pUndoManager->disconnect();
353     delete m_pUndoManager;
354 
355     //qCDebug(KONQUEROR_LOG) << this << "deleted";
356 }
357 
createContainer(QWidget * parent,int index,const QDomElement & element,QAction * & containerAction)358 QWidget *KonqMainWindow::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction)
359 {
360     QWidget *res = KParts::MainWindow::createContainer(parent, index, element, containerAction);
361 
362     static QString nameBookmarkBar = QStringLiteral("bookmarkToolBar");
363     static QString tagToolBar = QStringLiteral("ToolBar");
364     if (res && (element.tagName() == tagToolBar) && (element.attribute(QStringLiteral("name")) == nameBookmarkBar)) {
365         Q_ASSERT(::qobject_cast<KToolBar *>(res));
366         if (!KAuthorized::authorizeAction(QStringLiteral("bookmarks"))) {
367             delete res;
368             return nullptr;
369         }
370 
371         if (!m_bookmarkBarInitialized) {
372             // The actual menu needs a different action collection, so that the bookmarks
373             // don't appear in kedittoolbar
374             m_bookmarkBarInitialized = true;
375             DelayedInitializer *initializer = new DelayedInitializer(QEvent::Show, res);
376             connect(initializer, &DelayedInitializer::initialize, this, &KonqMainWindow::initBookmarkBar);
377         }
378     }
379 
380     if (res && element.tagName() == QLatin1String("Menu")) {
381         const QString &menuName = element.attribute(QStringLiteral("name"));
382         if (menuName == QLatin1String("edit") || menuName == QLatin1String("tools")) {
383             Q_ASSERT(qobject_cast<QMenu *>(res));
384             KAcceleratorManager::manage(static_cast<QMenu *>(res));
385         }
386     }
387 
388     return res;
389 }
390 
initBookmarkBar()391 void KonqMainWindow::initBookmarkBar()
392 {
393     KToolBar *bar = this->findChild<KToolBar *>(QStringLiteral("bookmarkToolBar"));
394 
395     if (!bar) {
396         return;
397     }
398 
399     const bool wasVisible = bar->isVisible();
400 
401     delete m_paBookmarkBar;
402     m_paBookmarkBar = new KBookmarkBar(s_bookmarkManager, m_pBookmarksOwner, bar, this);
403 
404     // hide if empty
405     if (bar->actions().count() == 0 || !wasVisible) {
406         bar->hide();
407     }
408 }
409 
removeContainer(QWidget * container,QWidget * parent,QDomElement & element,QAction * containerAction)410 void KonqMainWindow::removeContainer(QWidget *container, QWidget *parent, QDomElement &element, QAction *containerAction)
411 {
412     static QString nameBookmarkBar = QStringLiteral("bookmarkToolBar");
413     static QString tagToolBar = QStringLiteral("ToolBar");
414 
415     if (element.tagName() == tagToolBar && element.attribute(QStringLiteral("name")) == nameBookmarkBar) {
416         Q_ASSERT(::qobject_cast<KToolBar *>(container));
417         if (m_paBookmarkBar) {
418             m_paBookmarkBar->clear();
419         }
420     }
421 
422     KParts::MainWindow::removeContainer(container, parent, element, containerAction);
423 }
424 
425 // Detect a name filter (e.g. *.txt) in the url.
426 // Note that KShortURIFilter does the same, but we have no way of getting it from there
427 //
428 // Note: this removes the filter from the URL.
detectNameFilter(QUrl & url)429 QString KonqMainWindow::detectNameFilter(QUrl &url)
430 {
431     if (!KProtocolManager::supportsListing(url)) {
432         return QString();
433     }
434 
435     // Look for wildcard selection
436     QString nameFilter;
437     QString path = url.path();
438     int lastSlash = path.lastIndexOf('/');
439     if (lastSlash > -1) {
440         if (!url.query().isEmpty() && lastSlash == path.length() - 1) {  //  In /tmp/?foo, foo isn't a query
441             path += url.query(); // includes the '?'
442         }
443         QString fileName = path.mid(lastSlash + 1);
444         if (fileName.indexOf('*') != -1 || fileName.indexOf('[') != -1 || fileName.indexOf('?') != -1) {
445             // Check that a file or dir with all the special chars in the filename doesn't exist
446             if (url.isLocalFile()) {
447                 if (!QFile(url.toLocalFile()).exists()) {
448                     nameFilter = fileName;
449                 }
450             } else { // not a local file
451                 KIO::StatJob *job = KIO::statDetails(url, KIO::StatJob::DestinationSide, KIO::StatBasic, KIO::HideProgressInfo);
452                 // if there's an error stat'ing url, then assume it doesn't exist
453                 nameFilter = !job->exec() ? fileName : QString();
454             }
455 
456             if (!nameFilter.isEmpty()) {
457                 url = url.adjusted(QUrl::RemoveFilename | QUrl::RemoveQuery);
458                 qCDebug(KONQUEROR_LOG) << "Found wildcard. nameFilter=" << nameFilter << "  New url=" << url;
459             }
460         }
461     }
462 
463     return nameFilter;
464 }
465 
openFilteredUrl(const QString & url,const KonqOpenURLRequest & req)466 void KonqMainWindow::openFilteredUrl(const QString &url, const KonqOpenURLRequest &req)
467 {
468     // Filter URL to build a correct one
469     if (m_currentDir.isEmpty() && m_currentView) {
470         m_currentDir = m_currentView->url();
471     }
472 
473     QUrl filteredURL(KonqMisc::konqFilteredURL(this, url, m_currentDir));
474     qCDebug(KONQUEROR_LOG) << "url" << url << "filtered into" << filteredURL;
475 
476     if (filteredURL.isEmpty()) { // initially empty, or error (e.g. ~unknown_user)
477         return;
478     }
479 
480     m_currentDir.clear();
481 
482     openUrl(nullptr, filteredURL, QString(), req);
483 
484     // #4070: Give focus to view after URL was entered manually
485     // Note: we do it here if the view mode (i.e. part) wasn't changed
486     // If it is changed, then it's done in KonqViewManager::doSetActivePart
487     if (m_currentView) {
488         m_currentView->setFocus();
489     }
490 }
491 
openFilteredUrl(const QString & _url,bool inNewTab,bool tempFile)492 void KonqMainWindow::openFilteredUrl(const QString &_url, bool inNewTab, bool tempFile)
493 {
494     KonqOpenURLRequest req(_url);
495     req.browserArgs.setNewTab(inNewTab);
496     req.newTabInFront = true;
497     req.tempFile = tempFile;
498 
499     openFilteredUrl(_url, req);
500 }
501 
openFilteredUrl(const QString & _url,const QString & _mimeType,bool inNewTab,bool tempFile)502 void KonqMainWindow::openFilteredUrl(const QString &_url,  const QString &_mimeType, bool inNewTab, bool tempFile)
503 {
504     KonqOpenURLRequest req(_url);
505     req.browserArgs.setNewTab(inNewTab);
506     req.newTabInFront = true;
507     req.tempFile = tempFile;
508     req.args.setMimeType(_mimeType);
509 
510     openFilteredUrl(_url, req);
511 }
512 
openUrl(KonqView * _view,const QUrl & _url,const QString & _mimeType,const KonqOpenURLRequest & _req,bool trustedSource)513 void KonqMainWindow::openUrl(KonqView *_view, const QUrl &_url,
514                              const QString &_mimeType, const KonqOpenURLRequest &_req,
515                              bool trustedSource)
516 {
517 #ifndef NDEBUG // needed for req.debug()
518     qCDebug(KONQUEROR_LOG) << "url=" << _url << "mimeType=" << _mimeType
519              << "_req=" << _req.debug() << "view=" << _view;
520 #endif
521     // We like modifying args in this method :)
522     QUrl url(_url);
523     QString mimeType(_mimeType);
524     KonqOpenURLRequest req(_req);
525     if (m_currentView && url.isLocalFile() && m_currentView->isWebEngineView()) {
526          //The value of the entry doesn't matter: what's important is that the key exists
527         req.args.metaData().insert("urlRequestedByApp", QString()) ;
528     }
529 
530     if (mimeType.isEmpty()) {
531         mimeType = req.args.mimeType();
532     }
533     if (!url.isValid()) {
534         // I think we can't really get here anymore; I tried and didn't succeed.
535         // URL filtering catches this case before hand, and in cases without filtering
536         // (e.g. HTML link), the url is empty here, not invalid.
537         // But just to be safe, let's keep this code path
538         url = KParts::BrowserRun::makeErrorUrl(KIO::ERR_MALFORMED_URL, url.url(), url);
539     } else if (!KProtocolInfo::isKnownProtocol(url) && url.scheme() != QLatin1String("error") && !KonqUrl::hasKonqScheme(url) && url.scheme() != QLatin1String("mailto")) {
540         url = KParts::BrowserRun::makeErrorUrl(KIO::ERR_UNSUPPORTED_PROTOCOL, url.scheme(), url);
541     }
542 
543     if (KonqUrl::isKonqBlank(url) || url.scheme() == QLatin1String("error")) {
544         mimeType = QStringLiteral("text/html");
545     }
546 
547     const QString nameFilter = detectNameFilter(url);
548     if (!nameFilter.isEmpty()) {
549         req.nameFilter = nameFilter;
550         url = url.adjusted(QUrl::RemoveFilename);
551     }
552 
553     QLineEdit *edit = comboEdit();
554     if (edit) {
555         edit->setModified(false);
556     }
557 
558     KonqView *view = _view;
559 
560     // When clicking a 'follow active' view (e.g. view is the sidebar),
561     // open the URL in the active view
562     if (view && view->isFollowActive()) {
563         view = m_currentView;
564     }
565 
566     if (!view && !req.browserArgs.newTab()) {
567         view = m_currentView;    /* Note, this can be 0, e.g. on startup */
568     } else if (!view && req.browserArgs.newTab()) {
569 
570         // The URL should be opened in a new tab. Let's create the tab right away,
571         // it gives faster user feedback (#163628). For a short while (kde-4.1-beta1)
572         // I removed this entire block so that we wouldn't end up with a useless tab when
573         // launching an external application for this mimetype. But user feedback
574         // in all cases is more important than empty tabs in some cases.
575         view = m_pViewManager->addTab(QStringLiteral("text/html"),
576                                       QString(),
577                                       false,
578                                       req.openAfterCurrentPage);
579         if (view) {
580             view->setCaption(i18nc("@title:tab", "Loading..."));
581             view->setLocationBarURL(_url);
582             if (!req.browserArgs.frameName.isEmpty()) {
583                 view->setViewName(req.browserArgs.frameName);    // #44961
584             }
585 
586             if (req.newTabInFront) {
587                 m_pViewManager->showTab(view);
588             }
589 
590             updateViewActions(); //A new tab created -- we may need to enable the "remove tab" button (#56318)
591         } else {
592             req.browserArgs.setNewTab(false);
593         }
594     }
595 
596     const QString oldLocationBarURL = locationBarURL();
597     if (view) {
598         if (view == m_currentView) {
599             //will do all the stuff below plus GUI stuff
600             abortLoading();
601         } else {
602             view->stop();
603             // Don't change location bar if not current view
604         }
605     }
606 
607     // Fast mode for local files: do the stat ourselves instead of letting OpenUrlJob do it.
608     if (mimeType.isEmpty() && url.isLocalFile()) {
609         QMimeDatabase db;
610         mimeType = db.mimeTypeForFile(url.toLocalFile()).name();
611     }
612 
613     if (url.isLocalFile()) {
614         // Generic mechanism for redirecting to tar:/<path>/ when clicking on a tar file,
615         // zip:/<path>/ when clicking on a zip file, etc.
616         // The .protocol file specifies the mimetype that the kioslave handles.
617         // Note that we don't use mimetype inheritance since we don't want to
618         // open OpenDocument files as zip folders...
619         // Also note that we do this here and not in openView anymore,
620         // because in the case of foo.bz2 we don't know the final mimetype, we need a konqrun...
621         const QString protocol = KProtocolManager::protocolForArchiveMimetype(mimeType);
622         if (!protocol.isEmpty() && KonqFMSettings::settings()->shouldEmbed(mimeType)) {
623             url.setScheme(protocol);
624             if (mimeType == QLatin1String("application/x-webarchive")) {
625                 url.setPath(url.path() + "/index.html");
626                 mimeType = QStringLiteral("text/html");
627             } else {
628                 if (KProtocolManager::outputType(url) == KProtocolInfo::T_FILESYSTEM) {
629                     if (!url.path().endsWith('/')) {
630                         url.setPath(url.path() + '/');
631                     }
632                     mimeType = QStringLiteral("inode/directory");
633                 } else {
634                     mimeType.clear();
635                 }
636             }
637         }
638 
639         // Redirect to the url in Type=Link desktop files
640         if (mimeType == QLatin1String("application/x-desktop")) {
641             KDesktopFile df(url.toLocalFile());
642             if (df.hasLinkType()) {
643                 url = QUrl(df.readUrl());
644                 mimeType.clear(); // to be determined again
645             }
646         }
647     }
648     const bool hasMimeType = (!mimeType.isEmpty() && mimeType != QLatin1String("application/octet-stream"));
649     KService::Ptr offer;
650     bool associatedAppIsKonqueror = false;
651     if (hasMimeType) {
652         offer = KMimeTypeTrader::self()->preferredService(mimeType, QStringLiteral("Application"));
653         associatedAppIsKonqueror = isMimeTypeAssociatedWithSelf(mimeType, offer);
654         // If the associated app is konqueror itself, then make sure we try to embed before bailing out.
655         if (associatedAppIsKonqueror) {
656             req.forceAutoEmbed = true;
657         }
658     }
659 
660     //qCDebug(KONQUEROR_LOG) << "trying openView for" << url << "( mimeType" << mimeType << ")";
661     if (hasMimeType || KonqUrl::isValidNotBlank(url)) {
662 
663         // Built-in view ?
664         if (!openView(mimeType, url, view /* can be 0 */, req)) {
665             //qCDebug(KONQUEROR_LOG) << "openView returned false";
666             // Are we following another view ? Then forget about this URL. Otherwise fire app.
667             if (!req.followMode) {
668                 //qCDebug(KONQUEROR_LOG) << "we were not following. Fire app.";
669                 // The logic below is similar to BrowserRun::handleNonEmbeddable(),
670                 // but we don't have a BrowserRun instance here, and since it uses
671                 // some virtual methods [like save, for KHTMLRun], we can't just
672                 // move all the logic to static methods... catch 22...
673 
674                 if (!url.isLocalFile() && !trustedSource && KonqRun::isTextExecutable(mimeType)) {
675                     mimeType = QStringLiteral("text/plain");    // view, don't execute
676                 }
677                 // Remote URL: save or open ?
678                 QString protClass = KProtocolInfo::protocolClass(url.scheme());
679                 bool open = url.isLocalFile() || protClass == QLatin1String(":local") || KProtocolInfo::isHelperProtocol(url);
680                 if (!open) {
681                     KParts::BrowserOpenOrSaveQuestion dlg(this, url, mimeType);
682                     dlg.setFeatures(KParts::BrowserOpenOrSaveQuestion::ServiceSelection);
683                     const KParts::BrowserOpenOrSaveQuestion::Result res = dlg.askOpenOrSave();
684                     if (res == KParts::BrowserOpenOrSaveQuestion::Save) {
685                         KParts::BrowserRun::saveUrl(url, QString(), this, req.args);
686                     }
687                     open = (res == KParts::BrowserOpenOrSaveQuestion::Open);
688                     if (open) {
689                         offer = dlg.selectedService();
690                     }
691                 }
692                 if (open) {
693                     if (associatedAppIsKonqueror && refuseExecutingKonqueror(mimeType)) {
694                         return;
695                     }
696                     //qCDebug(KONQUEROR_LOG) << "Got offer" << (offer ? offer->name() : QString("0"));
697                     const bool allowExecution = trustedSource || KParts::BrowserRun::allowExecution(mimeType, url);
698                     if (allowExecution) {
699                         const bool isExecutable = KonqRun::isExecutable(mimeType);
700                         if (isExecutable) {
701                             setLocationBarURL(oldLocationBarURL);   // Revert to previous locationbar URL
702                             KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url);
703                             job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
704                             job->setShowOpenOrExecuteDialog(true);
705                             job->start();
706                         } else {
707                             // If offer is null, it means the user clicked on "Open With..." button.
708                             KIO::ApplicationLauncherJob *job = new KIO::ApplicationLauncherJob(offer);
709                             job->setUrls({url});
710                             job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
711                             if (req.tempFile) {
712                                 job->setRunFlags(KIO::ApplicationLauncherJob::DeleteTemporaryFiles);
713                             }
714                             job->start();
715                         }
716                     }
717                 }
718             }
719         }
720     } else { // no known mimeType, use KonqRun
721         bool earlySetLocationBarURL = false;
722         if (!view && !m_currentView) { // no view yet, e.g. starting with url as argument
723             earlySetLocationBarURL = true;
724         } else if (view == m_currentView && view->url().isEmpty()) { // opening in current view
725             earlySetLocationBarURL = true;
726         }
727         if (req.browserArgs.newTab()) { // it's going into a new tab anyway
728             earlySetLocationBarURL = false;
729         }
730         if (earlySetLocationBarURL) {
731             // Show it for now in the location bar, but we'll need to store it in the view
732             // later on (can't do it yet since either view == 0 or updateHistoryEntry will be called).
733             qCDebug(KONQUEROR_LOG) << "url=" << url;
734             setLocationBarURL(url);
735         }
736 
737         qCDebug(KONQUEROR_LOG) << "Creating new konqrun for" << url << "req.typedUrl=" << req.typedUrl;
738 
739         KonqRun *run = new KonqRun(this, view /* can be 0 */, url, req, trustedSource);
740 
741         // Never start in external browser
742         run->setEnableExternalBrowser(false);
743 
744         if (view) {
745             view->setRun(run);
746         }
747 
748         if (view == m_currentView) {
749             startAnimation();
750         }
751 
752         connect(run, &KonqRun::finished, this, &KonqMainWindow::slotRunFinished);
753     }
754 }
755 
756 // When opening a new view, for @p mimeType, prefer the part used in @p currentView, if possible.
757 // Testcase: window.open after manually switching to another web engine, and with "open popups in tabs" off.
preferredService(KonqView * currentView,const QString & mimeType)758 static QString preferredService(KonqView *currentView, const QString &mimeType)
759 {
760     if (currentView && !mimeType.isEmpty() && currentView->supportsMimeType(mimeType)) {
761         return currentView->service()->desktopEntryName();
762     }
763     return QString();
764 }
765 
openView(QString mimeType,const QUrl & _url,KonqView * childView,const KonqOpenURLRequest & req)766 bool KonqMainWindow::openView(QString mimeType, const QUrl &_url, KonqView *childView, const KonqOpenURLRequest &req)
767 {
768     // Second argument is referring URL
769     if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("open"), childView ? childView->url() : QUrl(), _url)) {
770         QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, _url.toDisplayString());
771         QMessageBox::warning(this, i18n("Access denied"), msg);
772         return true; // Nothing else to do.
773     }
774 
775     if (KonqRun::isExecutable(mimeType)) {
776         return false;    // execute, don't open
777     }
778     // Contract: the caller of this method should ensure the view is stopped first.
779 
780 #ifndef NDEBUG
781     qCDebug(KONQUEROR_LOG) << mimeType << _url << "childView=" << childView << "req:" << req.debug();
782 #endif
783     bool bOthersFollowed = false;
784 
785     if (childView) {
786         // If we're not already following another view (and if we are not reloading)
787         if (!req.followMode && !req.args.reload() && !m_pViewManager->isLoadingProfile()) {
788             // When clicking a 'follow active' view (e.g. childView is the sidebar),
789             // open the URL in the active view
790             // (it won't do anything itself, since it's locked to its location)
791             if (childView->isFollowActive() && childView != m_currentView) {
792                 abortLoading();
793                 setLocationBarURL(_url);
794                 KonqOpenURLRequest newreq;
795                 newreq.forceAutoEmbed = true;
796                 newreq.followMode = true;
797                 newreq.args = req.args;
798                 newreq.browserArgs = req.browserArgs;
799                 bOthersFollowed = openView(mimeType, _url, m_currentView, newreq);
800             }
801             // "link views" feature, and "sidebar follows active view" feature
802             bOthersFollowed = makeViewsFollow(_url, req.args, req.browserArgs, mimeType, childView) || bOthersFollowed;
803         }
804         if (childView->isLockedLocation() && !req.args.reload() /* allow to reload a locked view*/) {
805             return bOthersFollowed;
806         }
807     }
808 
809     QUrl url(_url);
810 
811     // In case we open an index.html, we want the location bar
812     // to still display the original URL (so that 'up' uses that URL,
813     // and since that's what the user entered).
814     // changePart will take care of setting and storing that url.
815     QString originalURL = url.toDisplayString(QUrl::PreferLocalFile);
816     if (!req.nameFilter.isEmpty()) { // keep filter in location bar
817         if (!originalURL.endsWith('/')) {
818             originalURL += '/';
819         }
820         originalURL += req.nameFilter;
821     }
822 
823     QString serviceName = req.serviceName; // default: none provided
824 
825     //Force use of WebEnginePart when opening a konq: URL. If the user chose a different
826     //default HTML engine, they would get an error because they don't know how to handle
827     //such scheme. As a workaround, in this case we force the use of WebEnginePart
828     if (KonqUrl::hasKonqScheme(url)) {
829         serviceName = "webenginepart";
830     }
831 
832     const QString urlStr = url.url();
833     if (KonqUrl::isValidNotBlank(urlStr)) {
834         mimeType = QStringLiteral("text/html");
835         originalURL = req.typedUrl.isEmpty() ? QString() : req.typedUrl;
836     } else if (KonqUrl::isKonqBlank(urlStr) && req.typedUrl.isEmpty()) {
837         originalURL.clear();
838     }
839 
840     bool forceAutoEmbed = req.forceAutoEmbed || req.userRequestedReload;
841     if (!req.typedUrl.isEmpty()) { // the user _typed_ the URL, he wants it in Konq.
842         forceAutoEmbed = true;
843     }
844     if (KonqUrl::hasKonqScheme(url) || url.scheme() == QLatin1String("error")) {
845         forceAutoEmbed = true;
846     }
847     // Related to KonqFactory::createView
848     if (!forceAutoEmbed && !KonqFMSettings::settings()->shouldEmbed(mimeType)) {
849         qCDebug(KONQUEROR_LOG) << "KonqFMSettings says: don't embed this servicetype";
850         return false;
851     }
852     // Do we even have a part to embed? Otherwise don't ask, since we'd ask twice.
853     if (!forceAutoEmbed) {
854         KService::List partServiceOffers;
855         KonqFactory::getOffers(mimeType, &partServiceOffers);
856         if (partServiceOffers.isEmpty()) {
857             qCDebug(KONQUEROR_LOG) << "No part available for" << mimeType;
858             return false;
859         }
860     }
861 
862     // If the protocol doesn't support writing (e.g. HTTP) then we might want to save instead of just embedding.
863     // So (if embedding would succeed, hence the checks above) we ask the user
864     // Otherwise the user will get asked 'open or save' in openUrl anyway.
865     if (!forceAutoEmbed && !KProtocolManager::supportsWriting(url)) {
866         QString suggestedFileName;
867         KonqRun *run = childView ? childView->run() : nullptr;
868         int attachment = 0;
869         if (run) {
870             suggestedFileName = run->suggestedFileName();
871             attachment = (run->serverSuggestsSave()) ? KParts::BrowserRun::AttachmentDisposition : KParts::BrowserRun::InlineDisposition;
872         }
873 
874         KParts::BrowserOpenOrSaveQuestion dlg(this, url, mimeType);
875         dlg.setSuggestedFileName(suggestedFileName);
876         const KParts::BrowserOpenOrSaveQuestion::Result res = dlg.askEmbedOrSave(attachment);
877         if (res == KParts::BrowserOpenOrSaveQuestion::Embed) {
878             forceAutoEmbed = true;
879         } else if (res == KParts::BrowserOpenOrSaveQuestion::Cancel) {
880             return true;    // handled, don't do anything else
881         } else { // Save
882             KParts::BrowserRun::saveUrl(url, suggestedFileName, this, req.args);
883             return true; // handled
884         }
885     }
886 
887     bool ok = true;
888     if (!childView) {
889         if (req.browserArgs.newTab()) {
890             KonqFrameTabs *tabContainer = m_pViewManager->tabContainer();
891             int index = tabContainer->currentIndex();
892             childView = m_pViewManager->addTab(mimeType, serviceName, false, req.openAfterCurrentPage);
893 
894             if (req.newTabInFront && childView) {
895                 if (req.openAfterCurrentPage) {
896                     tabContainer->setCurrentIndex(index + 1);
897                 } else {
898                     tabContainer->setCurrentIndex(tabContainer->count() - 1);
899                 }
900             }
901         }
902 
903         else {
904             // Create a new view
905             // createFirstView always uses force auto-embed even if user setting is "separate viewer",
906             // since this window has no view yet - we don't want to keep an empty mainwindow.
907             // This can happen with e.g. application/pdf from a target="_blank" link, or window.open.
908             childView = m_pViewManager->createFirstView(mimeType, serviceName);
909 
910             if (childView) {
911                 enableAllActions(true);
912                 m_currentView = childView;
913             }
914         }
915 
916         if (!childView) {
917             return false;    // It didn't work out.
918         }
919 
920         childView->setViewName(m_initialFrameName.isEmpty() ? req.browserArgs.frameName : m_initialFrameName);
921         m_initialFrameName.clear();
922     } else { // We know the child view
923         if (!childView->isLockedViewMode()) {
924             if (ok) {
925 
926                 // When typing a new URL, the current context doesn't matter anymore
927                 // -> select the preferred part for a given mimetype (even if the current part can handle this mimetype).
928                 // This fixes the "get katepart and then type a website URL -> loaded into katepart" problem
929                 // (first fixed in r168902 from 2002!, see also unittest KonqHtmlTest::textThenHtml())
930 
931                 if (!req.typedUrl.isEmpty() || !serviceName.isEmpty()) {
932                     if (childView->isLoading()) { // Stop the view first, #282641.
933                         childView->stop();
934                     }
935                     ok = childView->changePart(mimeType, serviceName, forceAutoEmbed);
936                 } else {
937                     ok = childView->ensureViewSupports(mimeType, forceAutoEmbed);
938                 }
939             }
940         }
941     }
942 
943     if (ok) {
944         //qCDebug(KONQUEROR_LOG) << "req.nameFilter= " << req.nameFilter;
945         //qCDebug(KONQUEROR_LOG) << "req.typedUrl= " << req.typedUrl;
946         //qCDebug(KONQUEROR_LOG) << "Browser extension? " << (childView->browserExtension() ? "YES" : "NO");
947         //qCDebug(KONQUEROR_LOG) << "Referrer: " << req.args.metaData()["referrer"];
948         childView->setTypedURL(req.typedUrl);
949         if (childView->part()) {
950             childView->part()->setArguments(req.args);
951         }
952         if (childView->browserExtension()) {
953             childView->browserExtension()->setBrowserArguments(req.browserArgs);
954         }
955 
956         // see dolphinpart
957         childView->part()->setProperty("filesToSelect", QVariant::fromValue(req.filesToSelect));
958 
959         if (!url.isEmpty()) {
960             childView->openUrl(url, originalURL, req.nameFilter, req.tempFile);
961         }
962     }
963     //qCDebug(KONQUEROR_LOG) << "ok=" << ok << "bOthersFollowed=" << bOthersFollowed
964     //             << "returning" << (ok || bOthersFollowed);
965     return ok || bOthersFollowed;
966 }
967 
findChildView(KParts::ReadOnlyPart * callingPart,const QString & name,KonqMainWindow * & mainWindow,KParts::ReadOnlyPart ** part)968 static KonqView *findChildView(KParts::ReadOnlyPart *callingPart, const QString &name, KonqMainWindow *&mainWindow, KParts::ReadOnlyPart **part)
969 {
970     if (!KonqMainWindow::mainWindowList()) {
971         return nullptr;
972     }
973 
974     foreach (KonqMainWindow *window, *KonqMainWindow::mainWindowList()) {
975         KonqView *res = window->childView(callingPart, name, part);
976         if (res) {
977             mainWindow = window;
978             return res;
979         }
980     }
981 
982     return nullptr;
983 }
984 
slotOpenURLRequest(const QUrl & url,const KParts::OpenUrlArguments & args,const KParts::BrowserArguments & browserArgs)985 void KonqMainWindow::slotOpenURLRequest(const QUrl &url, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs)
986 {
987     //qCDebug(KONQUEROR_LOG) << "frameName=" << browserArgs.frameName;
988 
989     KParts::ReadOnlyPart *callingPart = static_cast<KParts::ReadOnlyPart *>(sender()->parent());
990     QString frameName = browserArgs.frameName;
991 
992     if (!frameName.isEmpty()) {
993         static QString _top = QStringLiteral("_top");
994         static QString _self = QStringLiteral("_self");
995         static QString _parent = QStringLiteral("_parent");
996         static QString _blank = QStringLiteral("_blank");
997 
998         if (frameName.toLower() == _blank) {
999             KonqMainWindow *mainWindow = (m_popupProxyWindow ? m_popupProxyWindow.data() : this);
1000             mainWindow->slotCreateNewWindow(url, args, browserArgs);
1001             if (m_isPopupWithProxyWindow) {
1002                 raiseWindow(mainWindow);
1003             }
1004             return;
1005         }
1006 
1007         if (frameName.toLower() != _top &&
1008                 frameName.toLower() != _self &&
1009                 frameName.toLower() != _parent) {
1010             KonqView *view = childView(callingPart, frameName, nullptr);
1011             if (!view) {
1012                 KonqMainWindow *mainWindow = nullptr;
1013                 view = findChildView(callingPart, frameName, mainWindow, nullptr);
1014 
1015                 if (!view || !mainWindow) {
1016                     slotCreateNewWindow(url, args, browserArgs);
1017                     return;
1018                 }
1019 
1020                 mainWindow->openUrlRequestHelper(view, url, args, browserArgs);
1021                 return;
1022             }
1023 
1024             openUrlRequestHelper(view, url, args, browserArgs);
1025             return;
1026         }
1027     }
1028 
1029     KonqView *view = browserArgs.newTab() ? nullptr : childView(callingPart);
1030     openUrlRequestHelper(view, url, args, browserArgs);
1031 }
1032 
1033 //Called by slotOpenURLRequest
openUrlRequestHelper(KonqView * childView,const QUrl & url,const KParts::OpenUrlArguments & args,const KParts::BrowserArguments & browserArgs)1034 void KonqMainWindow::openUrlRequestHelper(KonqView *childView, const QUrl &url, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs)
1035 {
1036     //qCDebug(KONQUEROR_LOG) << "url=" << url;
1037     KonqOpenURLRequest req;
1038     req.args = args;
1039     if (args.metaData().value("konq-temp-file") == "1") {
1040         req.tempFile = true;
1041     }
1042     req.browserArgs = browserArgs;
1043     openUrl(childView, url, args.mimeType(), req, browserArgs.trustedSource);
1044 }
1045 
lastFrame(KonqView * view)1046 QObject *KonqMainWindow::lastFrame(KonqView *view)
1047 {
1048     QObject *nextFrame, *viewFrame;
1049     nextFrame = view->frame();
1050     viewFrame = nullptr;
1051     while (nextFrame != nullptr && !::qobject_cast<QStackedWidget *>(nextFrame)) {
1052         viewFrame = nextFrame;
1053         nextFrame = nextFrame->parent();
1054     }
1055     return nextFrame ? viewFrame : nullptr;
1056 }
1057 
1058 // Linked-views feature, plus "sidebar follows URL opened in the active view" feature
makeViewsFollow(const QUrl & url,const KParts::OpenUrlArguments & args,const KParts::BrowserArguments & browserArgs,const QString & serviceType,KonqView * senderView)1059 bool KonqMainWindow::makeViewsFollow(const QUrl &url,
1060                                      const KParts::OpenUrlArguments &args,
1061                                      const KParts::BrowserArguments &browserArgs,
1062                                      const QString &serviceType, KonqView *senderView)
1063 {
1064     if (!senderView->isLinkedView() && senderView != m_currentView) {
1065         return false;    // none of those features apply -> return
1066     }
1067 
1068     bool res = false;
1069     //qCDebug(KONQUEROR_LOG) << senderView->metaObject()->className() << "url=" << url << "serviceType=" << serviceType;
1070     KonqOpenURLRequest req;
1071     req.forceAutoEmbed = true;
1072     req.followMode = true;
1073     req.args = args;
1074     req.browserArgs = browserArgs;
1075     // We can't iterate over the map here, and openUrl for each, because the map can get modified
1076     // (e.g. by part changes). Better copy the views into a list.
1077     const QList<KonqView *> listViews = m_mapViews.values();
1078 
1079     QObject *senderFrame = lastFrame(senderView);
1080 
1081     foreach (KonqView *view, listViews) {
1082         if (view == senderView) {
1083             continue;
1084         }
1085         bool followed = false;
1086         // Views that should follow this URL as both views are linked
1087         if (view->isLinkedView() && senderView->isLinkedView()) {
1088             QObject *viewFrame = lastFrame(view);
1089 
1090             // Only views in the same tab of the sender will follow
1091             if (senderFrame && viewFrame && viewFrame != senderFrame) {
1092                 continue;
1093             }
1094 
1095             qCDebug(KONQUEROR_LOG) << "sending openUrl to view" << view->part()->metaObject()->className() << "url=" << url;
1096 
1097             // XXX duplicate code from ::openUrl
1098             if (view == m_currentView) {
1099                 abortLoading();
1100                 setLocationBarURL(url);
1101             } else {
1102                 view->stop();
1103             }
1104 
1105             followed = openView(serviceType, url, view, req);
1106         } else {
1107             // Make the sidebar follow the URLs opened in the active view
1108             if (view->isFollowActive() && senderView == m_currentView) {
1109                 followed = openView(serviceType, url, view, req);
1110             }
1111         }
1112 
1113         // Ignore return value if the view followed but doesn't really
1114         // show the file contents. We still want to see that
1115         // file, e.g. in a separate viewer.
1116         // This happens in views locked to a directory mode,
1117         // like sidebar and konsolepart (#52161).
1118         const bool ignore = view->isLockedViewMode() && view->showsDirectory();
1119         //qCDebug(KONQUEROR_LOG) << "View " << view->service()->name()
1120         //              << " supports dirs: " << view->showsDirectory()
1121         //              << " is locked-view-mode:" << view->isLockedViewMode()
1122         //              << " ignore=" << ignore;
1123         if (!ignore) {
1124             res = followed || res;
1125         }
1126     }
1127 
1128     return res;
1129 }
1130 
abortLoading()1131 void KonqMainWindow::abortLoading()
1132 {
1133     if (m_currentView) {
1134         m_currentView->stop(); // will take care of the statusbar
1135         stopAnimation();
1136     }
1137 }
1138 
1139 // Are there any indications that this window has a strong popup
1140 // nature and should therefore not be embedded into a tab?
isPopupWindow(const KParts::WindowArgs & windowArgs)1141 static bool isPopupWindow(const KParts::WindowArgs &windowArgs)
1142 {
1143     // ### other settings to respect?
1144     return windowArgs.x() != -1 || windowArgs.y() != -1 ||
1145            windowArgs.width() != -1 || windowArgs.height() != -1 ||
1146            !windowArgs.isMenuBarVisible() ||
1147            !windowArgs.toolBarsVisible() ||
1148            !windowArgs.isStatusBarVisible();
1149 }
1150 
1151 // This is called for the javascript window.open call.
1152 // Also called for MMB on link, target="_blank" link, MMB on folder, etc.
slotCreateNewWindow(const QUrl & url,const KParts::OpenUrlArguments & args,const KParts::BrowserArguments & browserArgs,const KParts::WindowArgs & windowArgs,KParts::ReadOnlyPart ** part)1153 void KonqMainWindow::slotCreateNewWindow(const QUrl &url,
1154         const KParts::OpenUrlArguments &args,
1155         const KParts::BrowserArguments &browserArgs,
1156         const KParts::WindowArgs &windowArgs, KParts::ReadOnlyPart **part)
1157 {
1158     // NOTE: 'part' may be null
1159 
1160     qCDebug(KONQUEROR_LOG) << "url=" << url << "args.mimeType()=" << args.mimeType()
1161              << "browserArgs.frameName=" << browserArgs.frameName;
1162 
1163     // If we are a popup window, forward the request the proxy window.
1164     if (m_isPopupWithProxyWindow && m_popupProxyWindow) {
1165         m_popupProxyWindow->slotCreateNewWindow(url, args, browserArgs, windowArgs, part);
1166         raiseWindow(m_popupProxyWindow);
1167         return;
1168     }
1169 
1170     if (part) {
1171         *part = nullptr;    // Make sure to be initialized in case of failure...
1172     }
1173 
1174     KonqMainWindow *mainWindow = nullptr;
1175     if (!browserArgs.frameName.isEmpty() && browserArgs.frameName.toLower() != QLatin1String("_blank")) {
1176         KParts::ReadOnlyPart *ro_part = nullptr;
1177         KParts::BrowserExtension *be = ::qobject_cast<KParts::BrowserExtension *>(sender());
1178         if (be) {
1179             ro_part = ::qobject_cast<KParts::ReadOnlyPart *>(be->parent());
1180         }
1181         if (findChildView(ro_part, browserArgs.frameName, mainWindow, part)) {
1182             // Found a view. If url isn't empty, we should open it - but this never happens currently
1183             // findChildView put the resulting part in 'part', so we can just return now
1184             //qCDebug(KONQUEROR_LOG) << "frame=" << browserArgs.frameName << "-> found part=" << part << part->name();
1185             return;
1186         }
1187     }
1188 
1189     bool createTab = browserArgs.newTab();
1190     if (!createTab && !browserArgs.forcesNewWindow() /* explicit "Open in New Window" action, e.g. on frame or history item */) {
1191         if (args.actionRequestedByUser()) { // MMB or some RMB popupmenu action
1192             createTab = KonqSettings::mmbOpensTab();
1193         } else { // Javascript popup
1194             createTab = KonqSettings::popupsWithinTabs() &&
1195                         !isPopupWindow(windowArgs);
1196         }
1197     }
1198     qCDebug(KONQUEROR_LOG) << "createTab=" << createTab << "part=" << part;
1199 
1200     if (createTab && !m_isPopupWithProxyWindow) {
1201 
1202         bool newtabsinfront = !windowArgs.lowerWindow();
1203         if (KonqSettings::newTabsInFront()) {
1204             newtabsinfront = !newtabsinfront;
1205         }
1206         const bool aftercurrentpage = KonqSettings::openAfterCurrentPage();
1207 
1208         KonqOpenURLRequest req;
1209         req.args = args;
1210         req.browserArgs = browserArgs;
1211         // Can we use the standard way (openUrl), or do we need the part pointer immediately?
1212         if (!part) {
1213             req.browserArgs.setNewTab(true);
1214             req.forceAutoEmbed = true; // testcase: MMB on link-to-PDF, when pdf setting is "show file in external browser".
1215             req.newTabInFront = newtabsinfront;
1216             req.openAfterCurrentPage = aftercurrentpage;
1217             openUrl(nullptr, url, args.mimeType(), req);
1218         } else {
1219             KonqView *newView = m_pViewManager->addTab(QStringLiteral("text/html"), QString(), false, aftercurrentpage);
1220             if (newView == nullptr) {
1221                 return;
1222             }
1223 
1224             if (newtabsinfront) {
1225                 m_pViewManager->showTab(newView);
1226             }
1227 
1228             openUrl(newView, url.isEmpty() ? KonqUrl::url(KonqUrl::Type::Blank) : url, QString(), req);
1229             newView->setViewName(browserArgs.frameName);
1230 
1231             *part = newView->part();
1232         }
1233 
1234         // Raise the current window if the request to create the tab came from a popup
1235         // window, e.g. clicking on links with target = "_blank" in popup windows.
1236         KParts::BrowserExtension *be = qobject_cast<KParts::BrowserExtension *>(sender());
1237         KonqView *view = (be ? childView(qobject_cast<KParts::ReadOnlyPart *>(be->parent())) : nullptr);
1238         KonqMainWindow *window = view ? view->mainWindow() : nullptr;
1239         if (window && window->m_isPopupWithProxyWindow && !m_isPopupWithProxyWindow) {
1240             raiseWindow(this);
1241         }
1242 
1243         return;
1244     }
1245 
1246     KonqOpenURLRequest req;
1247     req.args = args;
1248     req.browserArgs = browserArgs;
1249     req.browserArgs.setNewTab(false); // we got a new window, no need for a new tab in that window
1250     req.forceAutoEmbed = true;
1251     req.serviceName = preferredService(m_currentView, args.mimeType());
1252 
1253     mainWindow = KonqMainWindowFactory::createEmptyWindow();
1254     mainWindow->resetAutoSaveSettings(); // Don't autosave
1255 
1256     // Do we know the mimetype? If not, go to generic openUrl which will use a KonqRun.
1257     if (args.mimeType().isEmpty()) {
1258         mainWindow->openUrl(nullptr, url, QString(), req);
1259     } else {
1260         if (!mainWindow->openView(args.mimeType(), url, nullptr, req)) {
1261             // we have problems. abort.
1262             delete mainWindow;
1263 
1264             if (part) {
1265                 *part = nullptr;
1266             }
1267             return;
1268         }
1269     }
1270 
1271     qCDebug(KONQUEROR_LOG) << "newWindow" << mainWindow << "currentView" << mainWindow->currentView() << "views" << mainWindow->viewMap().count();
1272 
1273     KonqView *view = nullptr;
1274     // cannot use activePart/currentView, because the activation through the partmanager
1275     // is delayed by a singleshot timer (see KonqViewManager::setActivePart)
1276     // ### TODO: not true anymore
1277     if (mainWindow->viewMap().count()) {
1278         MapViews::ConstIterator it = mainWindow->viewMap().begin();
1279         view = it.value();
1280 
1281         if (part) {
1282             *part = it.key();
1283         }
1284     }
1285 
1286     // activate the view now in order to make the menuBar() hide call work
1287     if (part && *part) {
1288         mainWindow->viewManager()->setActivePart(*part);
1289     }
1290 
1291 #if KONQ_HAVE_X11
1292     // WORKAROUND: Clear the window state information set by KMainWindow::restoreWindowSize
1293     // so that the size and location settings we set below always take effect.
1294     KWindowSystem::clearState(mainWindow->winId(), NET::Max);
1295 #endif
1296 
1297     // process the window args
1298     const int xPos = ((windowArgs.x() == -1) ?  mainWindow->x() : windowArgs.x());
1299     const int yPos = ((windowArgs.y() == -1) ?  mainWindow->y() : windowArgs.y());
1300     const int width = ((windowArgs.width() == -1) ?  mainWindow->width() : windowArgs.width());
1301     const int height = ((windowArgs.height() == -1) ?  mainWindow->height() : windowArgs.height());
1302 
1303     mainWindow->move(xPos, yPos);
1304     mainWindow->resize(width, height);
1305 
1306     // Make the window open properties configurable. This is equivalent to
1307     // Firefox's "dom.disable_window_open_feature.*" properties. For now
1308     // only LocationBar visibility is configurable.
1309     KSharedConfig::Ptr config = KSharedConfig::openConfig();
1310     KConfigGroup cfg(config, "DisableWindowOpenFeatures");
1311 
1312     if (!windowArgs.isMenuBarVisible()) {
1313         mainWindow->menuBar()->hide();
1314         mainWindow->m_paShowMenuBar->setChecked(false);
1315     }
1316 
1317     if (!windowArgs.toolBarsVisible()) {
1318         // For security reasons the address bar is NOT hidden by default. The
1319         // user can override the default setup by adding a config option
1320         // "LocationBar=false" to the [DisableWindowOpenFeatures] section of
1321         // konquerorrc.
1322         const bool showLocationBar = cfg.readEntry("LocationBar", true);
1323         KToolBar *locationToolBar = mainWindow->toolBar(QStringLiteral("locationToolBar"));
1324 
1325         Q_FOREACH (KToolBar *bar, mainWindow->findChildren<KToolBar *>()) {
1326             if (bar != locationToolBar || !showLocationBar) {
1327                 bar->hide();
1328             }
1329         }
1330 
1331         if (locationToolBar && showLocationBar && isPopupWindow(windowArgs)) {
1332             // Hide all the actions of the popup window
1333             KActionCollection *collection = mainWindow->actionCollection();
1334             for (int i = 0, count = collection->count(); i < count; ++i) {
1335                 collection->action(i)->setVisible(false);
1336             }
1337 
1338             // Show only those actions that are allowed in a popup window
1339             static const char *const s_allowedActions[] = {
1340                 "go_back", "go_forward", "go_up", "reload", "hard_reload",
1341                 "stop", "cut", "copy", "paste", "print", "fullscreen",
1342                 "add_bookmark", "new_window", nullptr
1343             };
1344             for (int i = 0; s_allowedActions[i]; ++i) {
1345                 if (QAction *action = collection->action(QLatin1String(s_allowedActions[i]))) {
1346                     action->setVisible(true);
1347                 }
1348             }
1349 
1350             // Make only the address widget available in the location toolbar
1351             locationToolBar->clear();
1352             QAction *action = locationToolBar->addWidget(mainWindow->m_combo);
1353             action->setVisible(true);
1354 
1355             // Make the combo box non editable and clear it of previous history
1356             QLineEdit *edit = (mainWindow->m_combo ? mainWindow->m_combo->lineEdit() : nullptr);
1357             if (edit) {
1358                 mainWindow->m_combo->clear();
1359                 mainWindow->m_combo->setCompletionMode(KCompletion::CompletionNone);
1360                 edit->setReadOnly(true);
1361             }
1362 
1363             // Store the originating window as the popup's proxy window so that
1364             // new tab requests in the popup window are forwarded to it.
1365             mainWindow->m_popupProxyWindow = this;
1366             mainWindow->m_isPopupWithProxyWindow = true;
1367         }
1368     }
1369 
1370     if (view) {
1371         if (!windowArgs.scrollBarsVisible()) {
1372             view->disableScrolling();
1373         }
1374         if (!windowArgs.isStatusBarVisible()) {
1375             view->frame()->statusbar()->hide();
1376             mainWindow->m_paShowStatusBar->setChecked(false);
1377         } else {
1378             mainWindow->m_paShowStatusBar->setChecked(true);
1379         }
1380     }
1381 
1382     if (!windowArgs.isResizable())
1383         // ### this doesn't seem to work :-(
1384     {
1385         mainWindow->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
1386     }
1387 
1388 // Trying to show the window initially behind the current window is a bit tricky,
1389 // as this involves the window manager, which may see things differently.
1390 // Many WMs raise and activate new windows, which means without WM support this won't work very
1391 // well. If the WM has support for _NET_WM_USER_TIME, it will be just set to 0 (=don't focus on show),
1392 // and the WM should take care of it itself.
1393     bool wm_usertime_support = false;
1394 
1395 #if KONQ_HAVE_X11
1396     if (KWindowSystem::platform() == KWindowSystem::Platform::X11) {
1397         auto saved_last_input_time = QX11Info::appUserTime();
1398         if (windowArgs.lowerWindow()) {
1399             NETRootInfo wm_info(QX11Info::connection(), NET::Supported);
1400             wm_usertime_support = wm_info.isSupported(NET::WM2UserTime);
1401             if (wm_usertime_support) {
1402                 // *sigh*, and I thought nobody would need QWidget::dontFocusOnShow().
1403                 // Avoid Qt's support for user time by setting it to 0, and
1404                 // set the property ourselves.
1405                 QX11Info::setAppUserTime(0);
1406                 KWindowSystem::setUserTime(mainWindow->winId(), 0);
1407             }
1408             // Put below the current window before showing, in case that actually works with the WM.
1409             // First do complete lower(), then stackUnder(), because the latter may not work with many WMs.
1410             mainWindow->lower();
1411             mainWindow->stackUnder(this);
1412         }
1413 
1414         mainWindow->show();
1415 
1416         if (windowArgs.lowerWindow()) {
1417             QX11Info::setAppUserTime(saved_last_input_time);
1418             if (!wm_usertime_support) {
1419                 // No WM support. Let's try ugly tricks.
1420                 mainWindow->lower();
1421                 mainWindow->stackUnder(this);
1422                 if (this->isActiveWindow()) {
1423                     this->activateWindow();
1424                 }
1425             }
1426         }
1427     }
1428 #else // KONQ_HAVE_X11
1429     mainWindow->show();
1430 #endif
1431 
1432     if (windowArgs.isFullScreen()) {
1433         mainWindow->action("fullscreen")->trigger();
1434     }
1435 }
1436 
slotNewWindow()1437 void KonqMainWindow::slotNewWindow()
1438 {
1439     KonqMainWindow *mainWin = KonqMainWindowFactory::createNewWindow();
1440     mainWin->show();
1441 }
1442 
slotDuplicateWindow()1443 void KonqMainWindow::slotDuplicateWindow()
1444 {
1445     m_pViewManager->duplicateWindow()->show();
1446 }
1447 
slotSendURL()1448 void KonqMainWindow::slotSendURL()
1449 {
1450     const QList<QUrl> lst = currentURLs();
1451     QString body;
1452     QString fileNameList;
1453     for (QList<QUrl>::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it) {
1454         if (!body.isEmpty()) {
1455             body += '\n';
1456         }
1457         body += (*it).toDisplayString();
1458         if (!fileNameList.isEmpty()) {
1459             fileNameList += QLatin1String(", ");
1460         }
1461         fileNameList += (*it).fileName();
1462     }
1463     QString subject;
1464     if (m_currentView && !m_currentView->showsDirectory()) {
1465         subject = m_currentView->caption();
1466     } else { // directory view
1467         subject = fileNameList;
1468     }
1469     QUrl mailtoUrl;
1470     mailtoUrl.setScheme(QStringLiteral("mailto"));
1471     QUrlQuery query;
1472     query.addQueryItem(QStringLiteral("subject"), subject);
1473     query.addQueryItem(QStringLiteral("body"), body);
1474     mailtoUrl.setQuery(query);
1475     QDesktopServices::openUrl(mailtoUrl);
1476 }
1477 
slotSendFile()1478 void KonqMainWindow::slotSendFile()
1479 {
1480     const QList<QUrl> lst = currentURLs();
1481     QStringList urls;
1482     QString fileNameList;
1483     for (QList<QUrl>::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it) {
1484         if (!fileNameList.isEmpty()) {
1485             fileNameList += QLatin1String(", ");
1486         }
1487         if ((*it).isLocalFile() && QFileInfo((*it).toLocalFile()).isDir()) {
1488             // Create a temp dir, so that we can put the ZIP file in it with a proper name
1489             // Problem: when to delete it?
1490             QTemporaryDir tempDir;
1491             tempDir.setAutoRemove(false);
1492             if (!tempDir.isValid()) {
1493                 qCWarning(KONQUEROR_LOG) << "Could not create temporary dir";
1494                 continue;
1495             }
1496             const QString zipFileName = tempDir.path() + '/' + (*it).fileName() + ".zip";
1497             KZip zip(zipFileName);
1498             if (!zip.open(QIODevice::WriteOnly)) {
1499                 qCWarning(KONQUEROR_LOG) << "Could not open" << zipFileName << "for writing";
1500                 continue;
1501             }
1502             zip.addLocalDirectory((*it).path(), QString());
1503             zip.close();
1504             fileNameList += (*it).fileName() + ".zip";
1505             urls.append(QUrl::fromLocalFile(zipFileName).url());
1506         } else {
1507             fileNameList += (*it).fileName();
1508             urls.append((*it).url());
1509         }
1510     }
1511     QString subject;
1512     if (m_currentView && !m_currentView->showsDirectory()) {
1513         subject = m_currentView->caption();
1514     } else {
1515         subject = fileNameList;
1516     }
1517     QUrl mailtoUrl;
1518     mailtoUrl.setScheme(QStringLiteral("mailto"));
1519     QUrlQuery query;
1520     query.addQueryItem(QStringLiteral("subject"), subject);
1521     for (const QString& url : urls) {
1522         query.addQueryItem(QStringLiteral("attachment"), url);
1523     }
1524     mailtoUrl.setQuery(query);
1525     QDesktopServices::openUrl(mailtoUrl);
1526 }
1527 
slotOpenLocation()1528 void KonqMainWindow::slotOpenLocation()
1529 {
1530     focusLocationBar();
1531     QLineEdit *edit = comboEdit();
1532     if (edit) {
1533         edit->selectAll();
1534     }
1535 }
1536 
slotOpenFile()1537 void KonqMainWindow::slotOpenFile()
1538 {
1539     QUrl currentUrl;
1540     if (m_currentView && m_currentView->url().isLocalFile()) {
1541         currentUrl = m_currentView->url();
1542     } else {
1543         currentUrl = QUrl::fromLocalFile(QDir::homePath());
1544     }
1545 
1546     QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open File"), currentUrl, QString());
1547     if (!url.isEmpty()) {
1548         openFilteredUrl(url.url().trimmed());
1549     }
1550 }
1551 
slotIconsChanged()1552 void KonqMainWindow::slotIconsChanged()
1553 {
1554     qCDebug(KONQUEROR_LOG);
1555     if (m_combo) {
1556         m_combo->updatePixmaps();
1557     }
1558     m_pViewManager->updatePixmaps();
1559     updateWindowIcon();
1560 }
1561 
slotOpenWith()1562 void KonqMainWindow::slotOpenWith()
1563 {
1564     if (!m_currentView) {
1565         return;
1566     }
1567 
1568     const QString serviceName = sender()->objectName();
1569     const KService::List offers = m_currentView->appServiceOffers();
1570     for (const KService::Ptr &service : offers) {
1571         if (service->desktopEntryName() == serviceName) {
1572             KIO::ApplicationLauncherJob *job = new KIO::ApplicationLauncherJob(service);
1573             job->setUrls({ m_currentView->url() });
1574             job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
1575             job->start();
1576             return;
1577         }
1578     }
1579 }
1580 
slotViewModeTriggered(QAction * action)1581 void KonqMainWindow::slotViewModeTriggered(QAction *action)
1582 {
1583     if (!m_currentView) {
1584         return;
1585     }
1586 
1587     // Gather data from the action, since the action will be deleted by changePart
1588     QString modeName = action->objectName();
1589     Q_ASSERT(modeName.endsWith("-viewmode"));
1590     modeName.chop(9);
1591     const QString internalViewMode = action->data().toString();
1592 
1593     if (m_currentView->service()->desktopEntryName() != modeName) {
1594         m_currentView->stop();
1595         m_currentView->lockHistory();
1596 
1597         // Save those, because changePart will lose them
1598         const QUrl url = m_currentView->url();
1599         const QString locationBarURL = m_currentView->locationBarURL();
1600 #if 0
1601         // Problem: dolphinpart doesn't currently implement it. But we don't need it that much
1602         // now that it's the main filemanagement part for all standard modes.
1603         QList<QUrl> filesToSelect = childView->part()->property("filesToSelect").value<QList<QUrl>>();
1604 #endif
1605 
1606         m_currentView->changePart(m_currentView->serviceType(), modeName);
1607         m_currentView->openUrl(url, locationBarURL);
1608     }
1609 
1610     if (!internalViewMode.isEmpty() && internalViewMode != m_currentView->internalViewMode()) {
1611         m_currentView->setInternalViewMode(internalViewMode);
1612     }
1613 }
1614 
slotLockView()1615 void KonqMainWindow::slotLockView()
1616 {
1617     if (!m_currentView) {
1618         return;
1619     }
1620 
1621     m_currentView->setLockedLocation(m_paLockView->isChecked());
1622 }
1623 
slotStop()1624 void KonqMainWindow::slotStop()
1625 {
1626     abortLoading();
1627     if (m_currentView) {
1628         m_currentView->frame()->statusbar()->message(i18n("Canceled."));
1629     }
1630 }
1631 
slotLinkView()1632 void KonqMainWindow::slotLinkView()
1633 {
1634     if (!m_currentView) {
1635         return;
1636     }
1637 
1638     // Can't access this action in passive mode anyway
1639     Q_ASSERT(!m_currentView->isPassiveMode());
1640     const bool mode = !m_currentView->isLinkedView();
1641 
1642     const QList<KonqView *> linkableViews = KonqLinkableViewsCollector::collect(this);
1643     if (linkableViews.count() == 2) {
1644         // Exactly two linkable views : link both
1645         linkableViews.at(0)->setLinkedView(mode);
1646         linkableViews.at(1)->setLinkedView(mode);
1647     } else { // Normal case : just this view
1648         m_currentView->setLinkedView(mode);
1649     }
1650 }
1651 
slotReload(KonqView * reloadView,bool softReload)1652 void KonqMainWindow::slotReload(KonqView *reloadView, bool softReload)
1653 {
1654     if (!reloadView) {
1655         reloadView = m_currentView;
1656     }
1657 
1658     if (!reloadView || (reloadView->url().isEmpty() && reloadView->locationBarURL().isEmpty())) {
1659         return;
1660     }
1661 
1662     if (reloadView->isModified()) {
1663         if (KMessageBox::warningContinueCancel(this,
1664                                                i18n("This page contains changes that have not been submitted.\nReloading the page will discard these changes."),
1665                                                i18nc("@title:window", "Discard Changes?"), KGuiItem(i18n("&Discard Changes"), QStringLiteral("view-refresh")), KStandardGuiItem::cancel(), QStringLiteral("discardchangesreload")) != KMessageBox::Continue) {
1666             return;
1667         }
1668     }
1669 
1670     KonqOpenURLRequest req(reloadView->typedUrl());
1671     req.userRequestedReload = true;
1672     if (reloadView->prepareReload(req.args, req.browserArgs, softReload)) {
1673         reloadView->lockHistory();
1674         // Reuse current servicetype for local files, but not for remote files (it could have changed, e.g. over HTTP)
1675         QString serviceType = reloadView->url().isLocalFile() ? reloadView->serviceType() : QString();
1676         // By using locationBarURL instead of url, we preserve name filters (#54687)
1677         QUrl reloadUrl = QUrl::fromUserInput(reloadView->locationBarURL(), QString(), QUrl::AssumeLocalFile);
1678         if (reloadUrl.isEmpty()) { // e.g. initial screen
1679             reloadUrl = reloadView->url();
1680         }
1681         openUrl(reloadView, reloadUrl, serviceType, req);
1682     }
1683 }
1684 
slotForceReload()1685 void KonqMainWindow::slotForceReload()
1686 {
1687     // A forced reload is simply a "hard" (i.e. - not soft!) reload.
1688     slotReload(nullptr /* Current view */, false /* Not softReload*/);
1689 }
1690 
slotReloadPopup()1691 void KonqMainWindow::slotReloadPopup()
1692 {
1693     KonqFrameBase *tab = m_pViewManager->tabContainer()->tabAt(m_workingTab);
1694     if (tab) {
1695         slotReload(tab->activeChildView());
1696     }
1697 }
1698 
slotHome()1699 void KonqMainWindow::slotHome()
1700 {
1701     const QString homeURL = m_paHomePopup->data().toString();
1702 
1703     KonqOpenURLRequest req;
1704     req.browserArgs.setNewTab(true);
1705     req.newTabInFront = KonqSettings::newTabsInFront();
1706 
1707     Qt::MouseButtons buttons = QApplication::mouseButtons();
1708     Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
1709 
1710     if (modifiers & Qt::ShiftModifier) {
1711         req.newTabInFront = !req.newTabInFront;
1712     }
1713 
1714     if (modifiers & Qt::ControlModifier) { // Ctrl Left/MMB
1715         openFilteredUrl(homeURL, req);
1716     } else if (buttons & Qt::MiddleButton) {
1717         if (KonqSettings::mmbOpensTab()) {
1718             openFilteredUrl(homeURL, req);
1719         } else {
1720             const QUrl finalURL = KonqMisc::konqFilteredURL(this, homeURL);
1721             KonqMainWindow *mw = KonqMainWindowFactory::createNewWindow(finalURL);
1722             mw->show();
1723         }
1724     } else {
1725         openFilteredUrl(homeURL, false);
1726     }
1727 }
1728 
slotHomePopupActivated(QAction * action)1729 void KonqMainWindow::slotHomePopupActivated(QAction *action)
1730 {
1731     openUrl(nullptr, QUrl(action->data().toString()));
1732 }
1733 
slotGoHistory()1734 void KonqMainWindow::slotGoHistory()
1735 {
1736     if (!m_historyDialog) {
1737         m_historyDialog = new KonqHistoryDialog(this);
1738         m_historyDialog->setAttribute(Qt::WA_DeleteOnClose);
1739         m_historyDialog->setModal(false);
1740     }
1741     m_historyDialog->show();
1742 }
1743 
slotConfigureExtensions()1744 void KonqMainWindow::slotConfigureExtensions()
1745 {
1746     KonqExtensionManager extensionManager(this, this, m_currentView ? m_currentView->part() : nullptr);
1747     extensionManager.exec();
1748 }
1749 
slotConfigure(const QString startingModule)1750 void KonqMainWindow::slotConfigure(const QString startingModule)
1751 {
1752     KPageWidgetItem *startingItem = nullptr;
1753     if (!m_configureDialog) {
1754         m_configureDialog = new KCMultiDialog(this);
1755         m_configureDialog->setObjectName(QStringLiteral("configureDialog"));
1756         m_configureDialog->setFaceType(KPageDialog::Tree);
1757         connect(m_configureDialog, &KCMultiDialog::finished, this, &KonqMainWindow::slotConfigureDone);
1758 
1759         // BEGIN SYNC with initActions()
1760         const char *const toplevelModules[] = {
1761             "konqueror_kcms/khtml_general",
1762 #ifndef Q_OS_WIN
1763             "konqueror_kcms/kcm_performance",
1764 #endif
1765             "konqueror_kcms/kcm_bookmarks",
1766         };
1767         for (uint i = 0; i < sizeof(toplevelModules) / sizeof(char *); ++i) {
1768             const QString kcmName = QString(toplevelModules[i]);
1769             if (KAuthorized::authorizeControlModule(kcmName)) {
1770                 m_configureDialog->addModule(KPluginMetaData(kcmName));
1771             }
1772         }
1773 
1774         if (KAuthorized::authorizeControlModule(QStringLiteral("filebehavior"))) {
1775             m_configureDialog->addModule(KPluginMetaData(QStringLiteral("konqueror_kcms/kcm_konq")));
1776             const char *const fmModules[] = {
1777                 "dolphin/kcms/kcm_dolphinviewmodes",
1778                 "dolphin/kcms/kcm_dolphinnavigation",
1779                 "dolphin/kcms/kcm_dolphingeneral",
1780                 "kcm_trash",
1781             };
1782             for (uint i = 0; i < sizeof(fmModules) / sizeof(char *); ++i)
1783                 if (KAuthorized::authorizeControlModule(fmModules[i])) {
1784                     KPageWidgetItem *it = m_configureDialog->addModule(KPluginMetaData(QString(fmModules[i])));
1785                     if (!startingItem && startingModule == fmModules[i]) {
1786                         startingItem = it;
1787                     }
1788                 }
1789             KPluginMetaData fileTypesData(QStringLiteral("plasma/kcms/systemsettings_qwidgets/kcm_filetypes"));
1790             if (!fileTypesData.isValid()) {
1791                 QString desktopFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/filetypes.desktop"));
1792                 fileTypesData = KPluginMetaData::fromDesktopFile(desktopFile, {QStringLiteral("kcmodule.desktop")});
1793             }
1794             m_configureDialog->addModule(fileTypesData);
1795         }
1796 
1797         if (KAuthorized::authorizeControlModule(QStringLiteral("konqueror_kcms/khtml_behavior"))) {
1798             m_configureDialog->addModule(KPluginMetaData(QStringLiteral("konqueror_kcms/khtml_behavior")));
1799 
1800             const char *const webModules[] = {
1801                 "konqueror_kcms/khtml_appearance",
1802                 "konqueror_kcms/khtml_filter",
1803                 "kcm_webshortcuts",
1804                 "kcm_proxy",
1805                 "konqueror_kcms/kcm_history",
1806                 "kcm_cookies",
1807                 "konqueror_kcms/khtml_java_js",
1808             };
1809             for (uint i = 0; i < sizeof(webModules) / sizeof(char *); ++i)
1810                 if (KAuthorized::authorizeControlModule(webModules[i])) {
1811                     KPageWidgetItem *it = m_configureDialog->addModule(KPluginMetaData(QString(webModules[i])));
1812                     if (!startingItem && startingModule == webModules[i]) {
1813                         startingItem = it;
1814                     }
1815                 }
1816         }
1817     }
1818     // END SYNC with initActions()
1819 
1820     if (startingItem) {
1821         m_configureDialog->setCurrentPage(startingItem);
1822     }
1823     m_configureDialog->show();
1824 
1825 }
1826 
slotConfigureDone()1827 void KonqMainWindow::slotConfigureDone()
1828 {
1829     // Cleanup the dialog so other instances can use it..
1830     if (m_configureDialog) {
1831         m_configureDialog->deleteLater();
1832         m_configureDialog = nullptr;
1833     }
1834 }
1835 
slotConfigureSpellChecking()1836 void KonqMainWindow::slotConfigureSpellChecking()
1837 {
1838     Sonnet::ConfigDialog dialog(this);
1839     dialog.setWindowIcon(QIcon::fromTheme("konqueror"));
1840     if (dialog.exec() == QDialog::Accepted) {
1841         updateSpellCheckConfiguration();
1842     }
1843 }
1844 
updateSpellCheckConfiguration()1845 void KonqMainWindow::updateSpellCheckConfiguration()
1846 {
1847     //HACK: since Sonnet doesn't allow to find out whether the spell checker should be enabled by default
1848     //we need to open its config file and read the setting from there. We then store it in our own configuration file
1849     //so that it can be read from there
1850     KSharedConfig::Ptr cfg = KSharedConfig::openConfig("KDE/Sonnet.conf");
1851     KConfigGroup grp = cfg->group("General");
1852     bool enabled = grp.readEntry("checkerEnabledByDefault", false);
1853     cfg = KSharedConfig::openConfig();
1854     grp = cfg->group("General");
1855     grp.writeEntry("SpellCheckingEnabled", enabled);
1856     cfg->sync();
1857     emit KonqSpellCheckingConfigurationDispatcher::self()->spellCheckingConfigurationChanged(enabled);
1858 }
1859 
slotConfigureToolbars()1860 void KonqMainWindow::slotConfigureToolbars()
1861 {
1862     slotForceSaveMainWindowSettings();
1863     KEditToolBar dlg(factory(), this);
1864     connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KonqMainWindow::slotNewToolbarConfig);
1865     connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KonqMainWindow::initBookmarkBar);
1866     dlg.exec();
1867     checkDisableClearButton();
1868 }
1869 
slotNewToolbarConfig()1870 void KonqMainWindow::slotNewToolbarConfig() // This is called when OK or Apply is clicked
1871 {
1872     if (m_toggleViewGUIClient) {
1873         plugActionList(QStringLiteral("toggleview"), m_toggleViewGUIClient->actions());
1874     }
1875     if (m_currentView && m_currentView->appServiceOffers().count() > 0) {
1876         plugActionList(QStringLiteral("openwith"), m_openWithActions);
1877     }
1878 
1879     plugViewModeActions();
1880 
1881     KConfigGroup cg = KSharedConfig::openConfig()->group("KonqMainWindow");
1882     applyMainWindowSettings(cg);
1883 }
1884 
slotUndoAvailable(bool avail)1885 void KonqMainWindow::slotUndoAvailable(bool avail)
1886 {
1887     m_paUndo->setEnabled(avail);
1888 }
1889 
slotPartChanged(KonqView * childView,KParts::ReadOnlyPart * oldPart,KParts::ReadOnlyPart * newPart)1890 void KonqMainWindow::slotPartChanged(KonqView *childView, KParts::ReadOnlyPart *oldPart, KParts::ReadOnlyPart *newPart)
1891 {
1892     m_mapViews.remove(oldPart);
1893     m_mapViews.insert(newPart, childView);
1894 
1895     // Remove the old part, and add the new part to the manager
1896     const bool wasActive = m_pViewManager->activePart() == oldPart;
1897 
1898     m_pViewManager->replacePart(oldPart, newPart, false);
1899 
1900     // Set active immediately - but only if the old part was the active one (#67956)
1901     if (wasActive) {
1902         // Note: this makes the new part active... so it calls slotPartActivated
1903         m_pViewManager->setActivePart(newPart);
1904     }
1905 
1906     viewsChanged();
1907 }
1908 
slotRunFinished()1909 void KonqMainWindow::slotRunFinished()
1910 {
1911     const KonqRun *run = static_cast<const KonqRun *>(sender());
1912 
1913     if (!run->mailtoURL().isEmpty()) {
1914         QDesktopServices::openUrl(run->mailtoURL());
1915     }
1916 
1917     if (run->hasError()) {   // we had an error
1918         QDBusMessage message = QDBusMessage::createSignal(KONQ_MAIN_PATH, QStringLiteral("org.kde.Konqueror.Main"), QStringLiteral("removeFromCombo"));
1919         message << run->url().toDisplayString();
1920         QDBusConnection::sessionBus().send(message);
1921     }
1922 
1923     KonqView *childView = run->childView();
1924 
1925     // Check if we found a mimetype _and_ we got no error (example: cancel in openwith dialog)
1926     if (run->wasMimeTypeFound() && !run->hasError()) {
1927 
1928         // We do this here and not in the constructor, because
1929         // we are waiting for the first view to be set up before doing this...
1930         // Note: this is only used when konqueror is started from command line.....
1931         if (m_bNeedApplyKonqMainWindowSettings) {
1932             m_bNeedApplyKonqMainWindowSettings = false; // only once
1933             applyKonqMainWindowSettings();
1934         }
1935 
1936         return;
1937     }
1938 
1939     // An error happened in KonqRun - stop wheel etc.
1940 
1941     if (childView) {
1942         childView->setLoading(false);
1943 
1944         if (childView == m_currentView) {
1945             stopAnimation();
1946 
1947             // Revert to working URL - unless the URL was typed manually
1948             if (run->typedUrl().isEmpty() && childView->currentHistoryEntry()) { // not typed
1949                 childView->setLocationBarURL(childView->currentHistoryEntry()->locationBarURL);
1950             }
1951         }
1952     } else { // No view, e.g. starting up empty
1953         stopAnimation();
1954     }
1955 }
1956 
applyKonqMainWindowSettings()1957 void KonqMainWindow::applyKonqMainWindowSettings()
1958 {
1959     const QStringList toggableViewsShown = KonqSettings::toggableViewsShown();
1960     QStringList::ConstIterator togIt = toggableViewsShown.begin();
1961     QStringList::ConstIterator togEnd = toggableViewsShown.end();
1962     for (; togIt != togEnd; ++togIt) {
1963         // Find the action by name
1964         //    QAction * act = m_toggleViewGUIClient->actionCollection()->action( (*togIt).toLatin1() );
1965         QAction *act = m_toggleViewGUIClient->action(*togIt);
1966         if (act) {
1967             act->trigger();
1968         } else {
1969             qCWarning(KONQUEROR_LOG) << "Unknown toggable view in ToggableViewsShown " << *togIt;
1970         }
1971     }
1972 }
1973 
slotSetStatusBarText(const QString &)1974 void KonqMainWindow::slotSetStatusBarText(const QString &)
1975 {
1976     // Reimplemented to disable KParts::MainWindow default behaviour
1977     // Does nothing here, see KonqFrame
1978 }
1979 
slotViewCompleted(KonqView * view)1980 void KonqMainWindow::slotViewCompleted(KonqView *view)
1981 {
1982     Q_ASSERT(view);
1983 
1984     // Need to update the current working directory
1985     // of the completion object every time the user
1986     // changes the directory!! (DA)
1987     if (m_pURLCompletion) {
1988         m_pURLCompletion->setDir(QUrl::fromUserInput(view->locationBarURL()));
1989     }
1990 }
1991 
slotPartActivated(KParts::Part * part)1992 void KonqMainWindow::slotPartActivated(KParts::Part *part)
1993 {
1994 #if KPARTS_VERSION >= QT_VERSION_CHECK(5, 77, 0)
1995     qCDebug(KONQUEROR_LOG) << part << (part ? part->metaData().pluginId() : QString());
1996 #else
1997     qCDebug(KONQUEROR_LOG) << part << (part ? part->componentData().componentName() : QLatin1String(""));
1998 #endif
1999 
2000     KonqView *newView = nullptr;
2001     KonqView *oldView = m_currentView;
2002 
2003     //Exit full screen when a new part is activated
2004     if (m_fullScreenData.currentState == FullScreenState::CompleteFullScreen) {
2005         if (oldView && oldView->part()) {
2006             QMetaObject::invokeMethod(oldView->part(), "exitFullScreen");
2007         } else { //This should never happen
2008             toggleCompleteFullScreen(false);
2009         }
2010     }
2011 
2012     if (part) {
2013         newView = m_mapViews.value(static_cast<KParts::ReadOnlyPart *>(part));
2014         Q_ASSERT(newView);
2015         if (newView->isPassiveMode()) {
2016             // Passive view. Don't connect anything, don't change m_currentView
2017             // Another view will become the current view very soon
2018             //qCDebug(KONQUEROR_LOG) << "Passive mode - return";
2019             return;
2020         }
2021     }
2022 
2023     KParts::BrowserExtension *ext = nullptr;
2024 
2025     if (oldView) {
2026         ext = oldView->browserExtension();
2027         if (ext) {
2028             //qCDebug(KONQUEROR_LOG) << "Disconnecting extension for view" << oldView;
2029             disconnectExtension(ext);
2030         }
2031     }
2032 
2033     qCDebug(KONQUEROR_LOG) << "New current view" << newView;
2034     m_currentView = newView;
2035     if (newView) {
2036         m_paShowStatusBar->setChecked(newView->frame()->statusbar()->isVisible());
2037     }
2038 
2039     if (!part) {
2040         //qCDebug(KONQUEROR_LOG) << "No part activated - returning";
2041         unplugViewModeActions();
2042         createGUI(nullptr);
2043         KParts::MainWindow::setCaption(QString());
2044         return;
2045     }
2046 
2047     ext = m_currentView->browserExtension();
2048 
2049     if (ext) {
2050         connectExtension(ext);
2051     } else {
2052         qCDebug(KONQUEROR_LOG) << "No Browser Extension for the new part";
2053         // Disable all browser-extension actions
2054 
2055         KParts::BrowserExtension::ActionSlotMap *actionSlotMap = KParts::BrowserExtension::actionSlotMapPtr();
2056         KParts::BrowserExtension::ActionSlotMap::ConstIterator it = actionSlotMap->constBegin();
2057         const KParts::BrowserExtension::ActionSlotMap::ConstIterator itEnd = actionSlotMap->constEnd();
2058         for (; it != itEnd; ++it) {
2059             QAction *act = actionCollection()->action(QString::fromLatin1(it.key()));
2060             Q_ASSERT(act);
2061             if (act) {
2062                 act->setEnabled(false);
2063             }
2064         }
2065 
2066         if (m_paCopyFiles) {
2067             m_paCopyFiles->setEnabled(false);
2068         }
2069         if (m_paMoveFiles) {
2070             m_paMoveFiles->setEnabled(false);
2071         }
2072     }
2073 
2074     m_paShowDeveloperTools->setEnabled(m_currentView && m_currentView->isWebEngineView());
2075 
2076     createGUI(part);
2077 
2078     // View-dependent GUI
2079 
2080     KParts::MainWindow::setCaption(KStringHandler::csqueeze(m_currentView->caption(), 128));
2081     // This line causes #170470 when removing the current tab, because QTabBar
2082     // emits currentChanged before calling tabRemoved, so KTabWidget gets confused.
2083     // I don't see a need for it anyway...
2084     //m_currentView->frame()->setTitle(m_currentView->caption(), 0);
2085 
2086     updateOpenWithActions();
2087     updateViewActions(); // undo, lock, link and other view-dependent actions
2088     updateViewModeActions();
2089 
2090     bool viewShowsDir = m_currentView->showsDirectory();
2091     bool buttonShowsFolder = m_paHomePopup->text() == i18n("Home Folder");
2092     if (m_paHomePopup->text() == i18n("Home") || viewShowsDir != buttonShowsFolder) {
2093         QAction *actHomeFolder = new QAction(this);
2094         QAction *actHomePage = new QAction(this);
2095 
2096         actHomeFolder->setIcon(QIcon::fromTheme(QStringLiteral("user-home")));
2097         actHomeFolder->setText(i18n("Home Folder"));
2098         actHomeFolder->setStatusTip(i18n("Navigate to your 'Home Folder'"));
2099         actHomeFolder->setWhatsThis(i18n("Navigate to your local 'Home Folder'"));
2100         actHomeFolder->setData(QUrl::fromLocalFile(QDir::homePath()));
2101         actHomePage->setIcon(QIcon::fromTheme(QStringLiteral("go-home")));
2102         actHomePage->setText(i18n("Home Page"));
2103 
2104         actHomePage->setStatusTip(i18n("Navigate to your 'Home Page'"));
2105         actHomePage->setWhatsThis(i18n("<html>Navigate to your 'Home Page'<br /><br />"
2106                                        "You can configure the location where this button takes you "
2107                                        "under <b>Settings -> Configure Konqueror -> General</b>.</html>"));
2108         actHomePage->setData(KonqSettings::homeURL());
2109 
2110         m_paHome->setIcon(viewShowsDir ? actHomeFolder->icon() : actHomePage->icon());
2111         m_paHome->setText(viewShowsDir ? actHomeFolder->text() : actHomePage->text());
2112         m_paHome->setStatusTip(viewShowsDir ? actHomeFolder->statusTip() : actHomePage->statusTip());
2113         m_paHome->setWhatsThis(viewShowsDir ? actHomeFolder->whatsThis() : actHomePage->whatsThis());
2114         m_paHomePopup->setIcon(viewShowsDir ? actHomeFolder->icon() : actHomePage->icon());
2115         m_paHomePopup->setText(viewShowsDir ? actHomeFolder->text() : actHomePage->text());
2116         m_paHomePopup->setStatusTip(viewShowsDir ? actHomeFolder->statusTip() : actHomePage->statusTip());
2117         m_paHomePopup->setWhatsThis(viewShowsDir ? actHomeFolder->whatsThis() : actHomePage->whatsThis());
2118         m_paHomePopup->setData(viewShowsDir ? actHomeFolder->data() : actHomePage->data());
2119         m_paHomePopup->menu()->clear();
2120         if (viewShowsDir) {
2121             m_paHomePopup->menu()->addAction(actHomePage);
2122             delete actHomeFolder;
2123         } else {
2124             m_paHomePopup->menu()->addAction(actHomeFolder);
2125             delete actHomePage;
2126         }
2127     }
2128 
2129     m_currentView->frame()->statusbar()->updateActiveStatus();
2130 
2131     if (oldView && oldView->frame()) {
2132         oldView->frame()->statusbar()->updateActiveStatus();
2133     }
2134 
2135     //qCDebug(KONQUEROR_LOG) << "setting location bar url to"
2136     //         << m_currentView->locationBarURL() << "m_currentView=" << m_currentView;
2137 
2138     // Make sure the location bar gets updated when the view(tab) is changed.
2139     if (oldView != newView && m_combo) {
2140         m_combo->lineEdit()->setModified(false);
2141     }
2142     m_currentView->setLocationBarURL(m_currentView->locationBarURL());
2143 
2144     updateToolBarActions();
2145 }
2146 
insertChildView(KonqView * childView)2147 void KonqMainWindow::insertChildView(KonqView *childView)
2148 {
2149     //qCDebug(KONQUEROR_LOG) << childView;
2150     m_mapViews.insert(childView->part(), childView);
2151 
2152     connect(childView, SIGNAL(viewCompleted(KonqView*)),
2153             this, SLOT(slotViewCompleted(KonqView*)));
2154 
2155     emit viewAdded(childView);
2156 }
2157 
2158 // Called by KonqViewManager, internal
removeChildView(KonqView * childView)2159 void KonqMainWindow::removeChildView(KonqView *childView)
2160 {
2161     //qCDebug(KONQUEROR_LOG) << childView;
2162 
2163     disconnect(childView, SIGNAL(viewCompleted(KonqView*)),
2164                this, SLOT(slotViewCompleted(KonqView*)));
2165 
2166 #ifndef NDEBUG
2167     //dumpViewList();
2168 #endif
2169 
2170     MapViews::Iterator it = m_mapViews.begin();
2171     const MapViews::Iterator end = m_mapViews.end();
2172 
2173     // find it in the map - can't use the key since childView->part() might be 0
2174 
2175     //qCDebug(KONQUEROR_LOG) << "Searching map";
2176 
2177     while (it != end && it.value() != childView) {
2178         ++it;
2179     }
2180 
2181     //qCDebug(KONQUEROR_LOG) << "Verifying search results";
2182 
2183     if (it == m_mapViews.end()) {
2184         qCWarning(KONQUEROR_LOG) << "KonqMainWindow::removeChildView childView " << childView << " not in map !";
2185         return;
2186     }
2187 
2188     //qCDebug(KONQUEROR_LOG) << "Removing view" << childView;
2189 
2190     m_mapViews.erase(it);
2191 
2192     emit viewRemoved(childView);
2193 
2194 #ifndef NDEBUG
2195     //dumpViewList();
2196 #endif
2197 
2198     // KonqViewManager takes care of m_currentView
2199 }
2200 
linkableViewCountChanged()2201 void KonqMainWindow::linkableViewCountChanged()
2202 {
2203     const QList<KonqView *> linkableViews = KonqLinkableViewsCollector::collect(this);
2204     const int lvc = linkableViews.count();
2205     m_paLinkView->setEnabled(lvc > 1);
2206     // Only one view -> unlink it
2207     if (lvc == 1) {
2208         linkableViews.at(0)->setLinkedView(false);
2209     }
2210     m_pViewManager->viewCountChanged();
2211 }
2212 
viewCountChanged()2213 void KonqMainWindow::viewCountChanged()
2214 {
2215     // This is called (by the view manager) when the number of views changes.
2216     linkableViewCountChanged();
2217     viewsChanged();
2218 }
2219 
viewsChanged()2220 void KonqMainWindow::viewsChanged()
2221 {
2222     // This is called when the number of views changes OR when
2223     // the type of some view changes.
2224 
2225     updateViewActions(); // undo, lock, link and other view-dependent actions
2226 }
2227 
childView(KParts::ReadOnlyPart * view)2228 KonqView *KonqMainWindow::childView(KParts::ReadOnlyPart *view)
2229 {
2230     return m_mapViews.value(view);
2231 }
2232 
childView(KParts::ReadOnlyPart * callingPart,const QString & name,KParts::ReadOnlyPart ** part)2233 KonqView *KonqMainWindow::childView(KParts::ReadOnlyPart *callingPart, const QString &name, KParts::ReadOnlyPart **part)
2234 {
2235     //qCDebug(KONQUEROR_LOG) << "this=" << this << "looking for" << name;
2236     QList<KonqView *> views = m_mapViews.values();
2237     KonqView *callingView = m_mapViews.value(callingPart);
2238     if (callingView) {
2239         // Move the callingView in front of the list, in case of duplicate frame names (#133967)
2240         if (views.removeAll(callingView)) {
2241             views.prepend(callingView);
2242         }
2243     }
2244 
2245     for (KonqView *view : qAsConst(views)) {
2246         QString viewName = view->viewName();
2247         //qCDebug(KONQUEROR_LOG) << "       - viewName=" << viewName
2248         //          << "frame names:" << view->frameNames();
2249 
2250         if (!viewName.isEmpty() && viewName == name) {
2251             qCDebug(KONQUEROR_LOG) << "found existing view by name:" << view;
2252             if (part) {
2253                 *part = view->part();
2254             }
2255             return view;
2256         }
2257     }
2258 
2259     return nullptr;
2260 }
2261 
activeViewsNotLockedCount() const2262 int KonqMainWindow::activeViewsNotLockedCount() const
2263 {
2264     int res = 0;
2265     MapViews::ConstIterator end = m_mapViews.constEnd();
2266     for (MapViews::ConstIterator it = m_mapViews.constBegin(); it != end; ++it) {
2267         if (!it.value()->isPassiveMode() && !it.value()->isLockedLocation()) {
2268             ++res;
2269         }
2270     }
2271 
2272     return res;
2273 }
2274 
linkableViewsCount() const2275 int KonqMainWindow::linkableViewsCount() const
2276 {
2277     return KonqLinkableViewsCollector::collect(const_cast<KonqMainWindow *>(this)).count();
2278 }
2279 
mainViewsCount() const2280 int KonqMainWindow::mainViewsCount() const
2281 {
2282     int res = 0;
2283     MapViews::ConstIterator it = m_mapViews.constBegin();
2284     const MapViews::ConstIterator end = m_mapViews.constEnd();
2285     for (; it != end; ++it) {
2286         if (!it.value()->isPassiveMode() && !it.value()->isToggleView()) {
2287             //qCDebug(KONQUEROR_LOG) << res << it.value() << it.value()->part()->widget();
2288             ++res;
2289         }
2290     }
2291 
2292     return res;
2293 }
2294 
slotURLEntered(const QString & text,Qt::KeyboardModifiers modifiers)2295 void KonqMainWindow::slotURLEntered(const QString &text, Qt::KeyboardModifiers modifiers)
2296 {
2297     if (m_bURLEnterLock || text.isEmpty()) {
2298         return;
2299     }
2300 
2301     m_bURLEnterLock = true;
2302 
2303     if ((modifiers & Qt::ControlModifier) || (modifiers & Qt::AltModifier)) {
2304         m_combo->setURL(m_currentView ? m_currentView->url().toDisplayString() : QString());
2305         const bool inNewTab = !m_isPopupWithProxyWindow; // do not open a new tab in popup window.
2306         openFilteredUrl(text.trimmed(), inNewTab);
2307     } else {
2308         openFilteredUrl(text.trimmed());
2309     }
2310 
2311     m_bURLEnterLock = false;
2312 }
2313 
splitCurrentView(Qt::Orientation orientation)2314 void KonqMainWindow::splitCurrentView(Qt::Orientation orientation)
2315 {
2316     if (!m_currentView) {
2317         return;
2318     }
2319     KonqView *oldView = m_currentView;
2320     KonqView *newView = m_pViewManager->splitView(m_currentView, orientation);
2321     if (newView == nullptr) {
2322         return;
2323     }
2324     KonqOpenURLRequest req;
2325     req.forceAutoEmbed = true;
2326 
2327     QString mime = oldView->serviceType();
2328     QUrl url = oldView->url();
2329     KSharedConfig::Ptr cfg = KSharedConfig::openConfig("konquerorrc");
2330     const bool alwaysDuplicateView = cfg->group("UserSettings").readEntry("AlwaysDuplicatePageWhenSplittingView", true);
2331     if (!alwaysDuplicateView && !url.isLocalFile()) {
2332         url = QUrl(KonqSettings::startURL());
2333         if (url.isLocalFile()) {
2334             QMimeDatabase db;
2335             mime = db.mimeTypeForUrl(url).name();
2336         } else {
2337             //We can't know the mimetype
2338             mime = "text/html";
2339         }
2340     }
2341     openView(mime, url, newView, req);
2342 }
2343 
slotSplitViewHorizontal()2344 void KonqMainWindow::slotSplitViewHorizontal()
2345 {
2346     splitCurrentView(Qt::Horizontal);
2347 }
2348 
slotSplitViewVertical()2349 void KonqMainWindow::slotSplitViewVertical()
2350 {
2351     splitCurrentView(Qt::Vertical);
2352 }
2353 
slotAddTab()2354 void KonqMainWindow::slotAddTab()
2355 {
2356     // we can hardcode text/html because this is what konq:blank will use anyway
2357     KonqView *newView = m_pViewManager->addTab(QStringLiteral("text/html"),
2358                         QString(),
2359                         false,
2360                         KonqSettings::openAfterCurrentPage());
2361     if (!newView) {
2362         return;
2363     }
2364 
2365     openUrl(newView, KonqUrl::url(KonqUrl::Type::Blank), QString());
2366 
2367     //HACK!! QTabBar likes to steal focus when changing widgets.  This can result
2368     //in a flicker since we don't want it to get focus we want the combo to get
2369     //or keep focus...
2370     // TODO: retest, and replace with the smaller hack from KTabWidget::moveTab
2371     QWidget *widget = newView->frame() && newView->frame()->part() ?
2372                       newView->frame()->part()->widget() : nullptr;
2373     QWidget *origFocusProxy = widget ? widget->focusProxy() : nullptr;
2374     if (widget) {
2375         widget->setFocusProxy(m_combo);
2376     }
2377 
2378     m_pViewManager->showTab(newView);
2379 
2380     if (widget) {
2381         widget->setFocusProxy(origFocusProxy);
2382     }
2383 
2384     m_workingTab = 0;
2385 }
2386 
slotDuplicateTab()2387 void KonqMainWindow::slotDuplicateTab()
2388 {
2389     m_pViewManager->duplicateTab(m_pViewManager->tabContainer()->currentIndex(), KonqSettings::openAfterCurrentPage());
2390 }
2391 
slotDuplicateTabPopup()2392 void KonqMainWindow::slotDuplicateTabPopup()
2393 {
2394     m_pViewManager->duplicateTab(m_workingTab, KonqSettings::openAfterCurrentPage());
2395 }
2396 
slotBreakOffTab()2397 void KonqMainWindow::slotBreakOffTab()
2398 {
2399     breakOffTab(m_pViewManager->tabContainer()->currentIndex());
2400 }
2401 
slotBreakOffTabPopup()2402 void KonqMainWindow::slotBreakOffTabPopup()
2403 {
2404     // Delay the call since it might delete the tabbar
2405     QMetaObject::invokeMethod(this, "breakOffTab", Qt::QueuedConnection, Q_ARG(int, m_workingTab));
2406 }
2407 
breakOffTab(int tabIndex)2408 void KonqMainWindow::breakOffTab(int tabIndex)
2409 {
2410     KonqFrameBase *tab = m_pViewManager->tabContainer()->tabAt(tabIndex);
2411     if (!tab) {
2412         return;
2413     }
2414     const int originalTabIndex = m_pViewManager->tabContainer()->currentIndex();
2415     // TODO: Why do we warn about breaking off a modified tab, since it seems to show the unsubmitted form data just fine?
2416     if (!KonqModifiedViewsCollector::collect(tab).isEmpty()) {
2417         m_pViewManager->showTab(tabIndex);
2418         if (KMessageBox::warningContinueCancel(
2419                     this,
2420                     i18n("This tab contains changes that have not been submitted.\nDetaching the tab will discard these changes."),
2421                     i18nc("@title:window", "Discard Changes?"), KGuiItem(i18n("&Discard Changes"), QStringLiteral("tab-detach")), KStandardGuiItem::cancel(), QStringLiteral("discardchangesdetach")) != KMessageBox::Continue) {
2422             m_pViewManager->showTab(originalTabIndex);
2423             return;
2424         }
2425     }
2426     m_pViewManager->showTab(originalTabIndex);
2427     m_pViewManager->breakOffTab(tabIndex, size());
2428     updateViewActions();
2429 }
2430 
slotPopupNewWindow()2431 void KonqMainWindow::slotPopupNewWindow()
2432 {
2433     KFileItemList::const_iterator it = m_popupItems.constBegin();
2434     const KFileItemList::const_iterator end = m_popupItems.constEnd();
2435     KonqOpenURLRequest req;
2436     req.args = m_popupUrlArgs;
2437     req.browserArgs = m_popupUrlBrowserArgs;
2438     for (; it != end; ++it) {
2439         KonqMainWindow *mw = KonqMainWindowFactory::createNewWindow((*it).targetUrl(), req);
2440         mw->show();
2441     }
2442 }
2443 
slotPopupThisWindow()2444 void KonqMainWindow::slotPopupThisWindow()
2445 {
2446     openUrl(nullptr, m_popupItems.first().url());
2447 }
2448 
slotPopupNewTab()2449 void KonqMainWindow::slotPopupNewTab()
2450 {
2451     if (m_isPopupWithProxyWindow && !m_popupProxyWindow) {
2452         slotPopupNewWindow();
2453         return;
2454     }
2455     bool openAfterCurrentPage = KonqSettings::openAfterCurrentPage();
2456     bool newTabsInFront = KonqSettings::newTabsInFront();
2457 
2458     if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
2459         newTabsInFront = !newTabsInFront;
2460     }
2461 
2462     popupNewTab(newTabsInFront, openAfterCurrentPage);
2463 }
2464 
popupNewTab(bool infront,bool openAfterCurrentPage)2465 void KonqMainWindow::popupNewTab(bool infront, bool openAfterCurrentPage)
2466 {
2467     KonqOpenURLRequest req;
2468     req.newTabInFront = false;
2469     req.forceAutoEmbed = true;
2470     req.openAfterCurrentPage = openAfterCurrentPage;
2471     req.args = m_popupUrlArgs;
2472     req.browserArgs = m_popupUrlBrowserArgs;
2473     req.browserArgs.setNewTab(true);
2474 
2475     KonqMainWindow *mainWindow = (m_popupProxyWindow ? m_popupProxyWindow.data() : this);
2476 
2477     for (int i = 0; i < m_popupItems.count(); ++i) {
2478         if (infront && i == m_popupItems.count() - 1) {
2479             req.newTabInFront = true;
2480         }
2481         mainWindow->openUrl(nullptr, m_popupItems[i].targetUrl(), QString(), req);
2482     }
2483 
2484     // Raise this window if the request to create the tab came from a popup window.
2485     if (m_isPopupWithProxyWindow) {
2486         raiseWindow(mainWindow);
2487     }
2488 }
2489 
openMultiURL(const QList<QUrl> & url)2490 void KonqMainWindow::openMultiURL(const QList<QUrl> &url)
2491 {
2492     QList<QUrl>::ConstIterator it = url.constBegin();
2493     const QList<QUrl>::ConstIterator end = url.constEnd();
2494     for (; it != end; ++it) {
2495         KonqView *newView = m_pViewManager->addTab(QStringLiteral("text/html"));
2496         Q_ASSERT(newView);
2497         if (newView == nullptr) {
2498             continue;
2499         }
2500         openUrl(newView, *it, QString());
2501         m_pViewManager->showTab(newView);
2502     }
2503 }
2504 
slotRemoveView()2505 void KonqMainWindow::slotRemoveView()
2506 {
2507     if (!m_currentView) {
2508         return;
2509     }
2510 
2511     if (m_currentView->isModified()) {
2512         if (KMessageBox::warningContinueCancel(this,
2513                                                i18n("This view contains changes that have not been submitted.\nClosing the view will discard these changes."),
2514                                                i18nc("@title:window", "Discard Changes?"), KGuiItem(i18n("&Discard Changes"), QStringLiteral("view-close")), KStandardGuiItem::cancel(), QStringLiteral("discardchangesclose")) != KMessageBox::Continue) {
2515             return;
2516         }
2517     }
2518 
2519     // takes care of choosing the new active view
2520     m_pViewManager->removeView(m_currentView);
2521 }
2522 
slotRemoveTab()2523 void KonqMainWindow::slotRemoveTab()
2524 {
2525     removeTab(m_pViewManager->tabContainer()->currentIndex());
2526 }
2527 
slotRemoveTabPopup()2528 void KonqMainWindow::slotRemoveTabPopup()
2529 {
2530     // Can't do immediately - may kill the tabbar, and we're in an event path down from it
2531     QMetaObject::invokeMethod(this, "removeTab", Qt::QueuedConnection, Q_ARG(int, m_workingTab));
2532 }
2533 
removeTab(int tabIndex)2534 void KonqMainWindow::removeTab(int tabIndex)
2535 {
2536     KonqFrameBase *tab = m_pViewManager->tabContainer()->tabAt(tabIndex);
2537     if (!tab) {
2538         return;
2539     }
2540     const int originalTabIndex = m_pViewManager->tabContainer()->currentIndex();
2541     if (!KonqModifiedViewsCollector::collect(tab).isEmpty()) {
2542         m_pViewManager->showTab(tabIndex);
2543         if (KMessageBox::warningContinueCancel(
2544                     this,
2545                     i18n("This tab contains changes that have not been submitted.\nClosing the tab will discard these changes."),
2546                     i18nc("@title:window", "Discard Changes?"), KGuiItem(i18n("&Discard Changes"), QStringLiteral("tab-close")), KStandardGuiItem::cancel(), QStringLiteral("discardchangesclose")) != KMessageBox::Continue) {
2547             m_pViewManager->showTab(originalTabIndex);
2548             return;
2549         }
2550     }
2551     m_pViewManager->showTab(originalTabIndex);
2552     m_pViewManager->removeTab(tab);
2553     updateViewActions();
2554 }
2555 
slotRemoveOtherTabs()2556 void KonqMainWindow::slotRemoveOtherTabs()
2557 {
2558     removeOtherTabs(m_pViewManager->tabContainer()->currentIndex());
2559 }
2560 
slotRemoveOtherTabsPopup()2561 void KonqMainWindow::slotRemoveOtherTabsPopup()
2562 {
2563     // Can't do immediately - kills the tabbar, and we're in an event path down from it
2564     QMetaObject::invokeMethod(this, "removeOtherTabs", Qt::QueuedConnection, Q_ARG(int, m_workingTab));
2565 }
2566 
removeOtherTabs(int tabToKeep)2567 void KonqMainWindow::removeOtherTabs(int tabToKeep)
2568 {
2569     if (KMessageBox::warningContinueCancel(
2570                 this,
2571                 i18n("Do you really want to close all other tabs?"),
2572                 i18nc("@title:window", "Close Other Tabs Confirmation"), KGuiItem(i18n("Close &Other Tabs"), QStringLiteral("tab-close-other")),
2573                 KStandardGuiItem::cancel(), QStringLiteral("CloseOtherTabConfirm")) != KMessageBox::Continue) {
2574         return;
2575     }
2576 
2577     KonqFrameTabs *tabContainer = m_pViewManager->tabContainer();
2578     const int originalTabIndex = tabContainer->currentIndex();
2579     for (int tabIndex = 0; tabIndex < tabContainer->count(); ++tabIndex) {
2580         if (tabIndex == tabToKeep) {
2581             continue;
2582         }
2583         KonqFrameBase *tab = tabContainer->tabAt(tabIndex);
2584         if (!KonqModifiedViewsCollector::collect(tab).isEmpty()) {
2585             m_pViewManager->showTab(tabIndex);
2586             if (KMessageBox::warningContinueCancel(
2587                         this,
2588                         i18n("This tab contains changes that have not been submitted.\nClosing other tabs will discard these changes."),
2589                         i18nc("@title:window", "Discard Changes?"), KGuiItem(i18n("&Discard Changes"), QStringLiteral("tab-close")),
2590                         KStandardGuiItem::cancel(), QStringLiteral("discardchangescloseother")) != KMessageBox::Continue) {
2591                 m_pViewManager->showTab(originalTabIndex);
2592                 return;
2593             }
2594         }
2595     }
2596     m_pViewManager->showTab(originalTabIndex);
2597     m_pViewManager->removeOtherTabs(tabToKeep);
2598     updateViewActions();
2599 }
2600 
slotReloadAllTabs()2601 void KonqMainWindow::slotReloadAllTabs()
2602 {
2603     KonqFrameTabs *tabContainer = m_pViewManager->tabContainer();
2604     const int originalTabIndex = tabContainer->currentIndex();
2605     for (int tabIndex = 0; tabIndex < tabContainer->count(); ++tabIndex) {
2606         KonqFrameBase *tab = tabContainer->tabAt(tabIndex);
2607         if (!KonqModifiedViewsCollector::collect(tab).isEmpty()) {
2608             m_pViewManager->showTab(tabIndex);
2609             if (KMessageBox::warningContinueCancel(this,
2610                                                    i18n("This tab contains changes that have not been submitted.\nReloading all tabs will discard these changes."),
2611                                                    i18nc("@title:window", "Discard Changes?"),
2612                                                    KGuiItem(i18n("&Discard Changes"), QStringLiteral("view-refresh")),
2613                                                    KStandardGuiItem::cancel(), QStringLiteral("discardchangesreload")) != KMessageBox::Continue) {
2614                 m_pViewManager->showTab(originalTabIndex);
2615                 return;
2616             }
2617         }
2618     }
2619     m_pViewManager->showTab(originalTabIndex);
2620     m_pViewManager->reloadAllTabs();
2621     updateViewActions();
2622 }
2623 
slotActivateNextTab()2624 void KonqMainWindow::slotActivateNextTab()
2625 {
2626     m_pViewManager->activateNextTab();
2627 }
2628 
slotActivatePrevTab()2629 void KonqMainWindow::slotActivatePrevTab()
2630 {
2631     m_pViewManager->activatePrevTab();
2632 }
2633 
slotActivateTab()2634 void KonqMainWindow::slotActivateTab()
2635 {
2636     m_pViewManager->activateTab(sender()->objectName().rightRef(2).toInt() - 1);
2637 }
2638 
slotDumpDebugInfo()2639 void KonqMainWindow::slotDumpDebugInfo()
2640 {
2641 #ifndef NDEBUG
2642     dumpViewList();
2643     m_pViewManager->printFullHierarchy();
2644 #endif
2645 }
2646 
askForTarget(const KLocalizedString & text,QUrl & url)2647 bool KonqMainWindow::askForTarget(const KLocalizedString &text, QUrl &url)
2648 {
2649     const QUrl initialUrl = (viewCount() == 2) ? otherView(m_currentView)->url() : m_currentView->url();
2650     QString label = text.subs(m_currentView->url().toDisplayString(QUrl::PreferLocalFile)).toString();
2651     KUrlRequesterDialog dlg(initialUrl, label, this);
2652     dlg.setWindowTitle(i18nc("@title:window", "Enter Target"));
2653     dlg.urlRequester()->setMode(KFile::File | KFile::ExistingOnly | KFile::Directory);
2654     if (dlg.exec()) {
2655         url = dlg.selectedUrl();
2656         if (url.isValid()) {
2657             return true;
2658         } else {
2659             KMessageBox::error(this, i18n("<qt><b>%1</b> is not valid</qt>", url.url()));
2660             return false;
2661         }
2662     }
2663     return false;
2664 }
2665 
slotCopyFiles()2666 void KonqMainWindow::slotCopyFiles()
2667 {
2668     QUrl dest;
2669     if (!askForTarget(ki18n("Copy selected files from %1 to:"), dest)) {
2670         return;
2671     }
2672 
2673     KIO::CopyJob *job = KIO::copy(currentURLs(), dest);
2674     KIO::FileUndoManager::self()->recordCopyJob(job);
2675     KJobWidgets::setWindow(job, this);
2676     job->uiDelegate()->setAutoErrorHandlingEnabled(true);
2677 }
2678 
slotMoveFiles()2679 void KonqMainWindow::slotMoveFiles()
2680 {
2681     QUrl dest;
2682     if (!askForTarget(ki18n("Move selected files from %1 to:"), dest)) {
2683         return;
2684     }
2685 
2686     KIO::CopyJob *job = KIO::move(currentURLs(), dest);
2687     KIO::FileUndoManager::self()->recordCopyJob(job);
2688     KJobWidgets::setWindow(job, this);
2689     job->uiDelegate()->setAutoErrorHandlingEnabled(true);
2690 }
2691 
currentURLs() const2692 QList<QUrl> KonqMainWindow::currentURLs() const
2693 {
2694     QList<QUrl> urls;
2695     if (m_currentView) {
2696         urls.append(m_currentView->url());
2697         if (!m_currentView->selectedItems().isEmpty()) { // Return list of selected items only if we have a selection
2698             urls = m_currentView->selectedItems().urlList();
2699         }
2700     }
2701     return urls;
2702 }
2703 
2704 // Only valid if there are one or two views
otherView(KonqView * view) const2705 KonqView *KonqMainWindow::otherView(KonqView *view) const
2706 {
2707     Q_ASSERT(viewCount() <= 2);
2708     MapViews::ConstIterator it = m_mapViews.constBegin();
2709     if ((*it) == view) {
2710         ++it;
2711     }
2712     if (it != m_mapViews.constEnd()) {
2713         return (*it);
2714     }
2715     return nullptr;
2716 }
2717 
slotUpAboutToShow()2718 void KonqMainWindow::slotUpAboutToShow()
2719 {
2720     if (!m_currentView) {
2721         return;
2722     }
2723 
2724     QMenu *popup = m_paUp->menu();
2725     popup->clear();
2726 
2727     int i = 0;
2728 
2729     // Use the location bar URL, because in case we display a index.html
2730     // we want to go up from the dir, not from the index.html
2731     QUrl u(QUrl::fromUserInput(m_currentView->locationBarURL()));
2732     u = KIO::upUrl(u);
2733     while (!u.path().isEmpty()) {
2734         QAction *action = new QAction(QIcon::fromTheme(KonqPixmapProvider::self()->iconNameFor(u)),
2735                                       u.toDisplayString(QUrl::PreferLocalFile),
2736                                       popup);
2737         action->setData(u);
2738         popup->addAction(action);
2739 
2740         if (u.path() == QLatin1String("/") || ++i > 10) {
2741             break;
2742         }
2743 
2744         u = KIO::upUrl(u);
2745     }
2746 }
2747 
slotUp()2748 void KonqMainWindow::slotUp()
2749 {
2750     if (!m_currentView) {
2751         return;
2752     }
2753 
2754     Qt::MouseButtons goMouseState = QApplication::mouseButtons();
2755     Qt::KeyboardModifiers goKeyboardState = QApplication::keyboardModifiers();
2756 
2757     KonqOpenURLRequest req;
2758     req.browserArgs.setNewTab(true);
2759     req.forceAutoEmbed = true;
2760 
2761     req.openAfterCurrentPage = KonqSettings::openAfterCurrentPage();
2762     req.newTabInFront = KonqSettings::newTabsInFront();
2763 
2764     if (goKeyboardState & Qt::ShiftModifier) {
2765         req.newTabInFront = !req.newTabInFront;
2766     }
2767 
2768     const QUrl &url = m_currentView->upUrl();
2769     if (goKeyboardState & Qt::ControlModifier) {
2770         openFilteredUrl(url.url(), req);
2771     } else if (goMouseState & Qt::MiddleButton) {
2772         if (KonqSettings::mmbOpensTab()) {
2773             openFilteredUrl(url.url(), req);
2774         } else {
2775             KonqMainWindow *mw = KonqMainWindowFactory::createNewWindow(url);
2776             mw->show();
2777         }
2778     } else {
2779         openFilteredUrl(url.url(), false);
2780     }
2781 }
2782 
slotUpActivated(QAction * action)2783 void KonqMainWindow::slotUpActivated(QAction *action)
2784 {
2785     openUrl(nullptr, action->data().value<QUrl>());
2786 }
2787 
slotGoHistoryActivated(int steps)2788 void KonqMainWindow::slotGoHistoryActivated(int steps)
2789 {
2790     if (!m_goBuffer) {
2791         // Only start 1 timer.
2792         m_goBuffer = steps;
2793         m_goMouseState = QApplication::mouseButtons();
2794         m_goKeyboardState = QApplication::keyboardModifiers();
2795         QTimer::singleShot(0, this, SLOT(slotGoHistoryDelayed()));
2796     }
2797 }
2798 
slotGoHistoryDelayed()2799 void KonqMainWindow::slotGoHistoryDelayed()
2800 {
2801     if (!m_currentView) {
2802         return;
2803     }
2804 
2805     bool openAfterCurrentPage = KonqSettings::openAfterCurrentPage();
2806     bool mmbOpensTab = KonqSettings::mmbOpensTab();
2807     bool inFront = KonqSettings::newTabsInFront();
2808     if (m_goKeyboardState & Qt::ShiftModifier) {
2809         inFront = !inFront;
2810     }
2811 
2812     if (m_goKeyboardState & Qt::ControlModifier) {
2813         KonqView *newView = m_pViewManager->addTabFromHistory(m_currentView, m_goBuffer, openAfterCurrentPage);
2814         if (newView && inFront) {
2815             m_pViewManager->showTab(newView);
2816         }
2817     } else if (m_goMouseState & Qt::MiddleButton) {
2818         if (mmbOpensTab) {
2819             KonqView *newView = m_pViewManager->addTabFromHistory(m_currentView, m_goBuffer, openAfterCurrentPage);
2820             if (newView && inFront) {
2821                 m_pViewManager->showTab(newView);
2822             }
2823         } else {
2824             KonqMisc::newWindowFromHistory(this->currentView(), m_goBuffer);
2825         }
2826     } else {
2827         m_currentView->go(m_goBuffer);
2828         makeViewsFollow(m_currentView->url(),
2829                         KParts::OpenUrlArguments(),
2830                         KParts::BrowserArguments(),
2831                         m_currentView->serviceType(),
2832                         m_currentView);
2833     }
2834 
2835     m_goBuffer = 0;
2836     m_goMouseState = Qt::LeftButton;
2837     m_goKeyboardState = Qt::NoModifier;
2838 }
2839 
slotBackAboutToShow()2840 void KonqMainWindow::slotBackAboutToShow()
2841 {
2842     m_paBack->menu()->clear();
2843     if (m_currentView) {
2844         KonqActions::fillHistoryPopup(m_currentView->history(), m_currentView->historyIndex(), m_paBack->menu(), true, false);
2845     }
2846 }
2847 
2848 /**
2849  * Fill the closed tabs action menu before it's shown
2850  */
slotClosedItemsListAboutToShow()2851 void KonqMainWindow::slotClosedItemsListAboutToShow()
2852 {
2853     QMenu *popup = m_paClosedItems->menu();
2854     // Clear the menu and fill it with a maximum of s_closedItemsListLength number of urls
2855     popup->clear();
2856     QAction *clearAction = popup->addAction(i18nc("This menu entry empties the closed items history", "Empty Closed Items History"));
2857     connect(clearAction, &QAction::triggered, m_pUndoManager, &KonqUndoManager::clearClosedItemsList);
2858     popup->insertSeparator(static_cast<QAction *>(nullptr));
2859 
2860     QList<KonqClosedItem *>::ConstIterator it = m_pUndoManager->closedItemsList().constBegin();
2861     const QList<KonqClosedItem *>::ConstIterator end = m_pUndoManager->closedItemsList().constEnd();
2862     for (int i = 0; it != end && i < s_closedItemsListLength; ++it, ++i) {
2863         const QString text = QString::number(i) + ' ' + (*it)->title();
2864         QAction *action = popup->addAction((*it)->icon(), text);
2865         action->setActionGroup(m_closedItemsGroup);
2866         action->setData(i);
2867     }
2868     KAcceleratorManager::manage(popup);
2869 }
2870 
2871 /**
2872  * Fill the sessions list action menu before it's shown
2873  */
slotSessionsListAboutToShow()2874 void KonqMainWindow::slotSessionsListAboutToShow()
2875 {
2876     QMenu *popup = m_paSessions->menu();
2877     // Clear the menu and fill it with a maximum of s_closedItemsListLength number of urls
2878     popup->clear();
2879     QAction *saveSessionAction = popup->addAction(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save As..."));
2880     connect(saveSessionAction, &QAction::triggered, this, &KonqMainWindow::saveCurrentSession);
2881     QAction *manageSessionsAction = popup->addAction(QIcon::fromTheme(QStringLiteral("view-choose")), i18n("Manage..."));
2882     connect(manageSessionsAction, &QAction::triggered, this, &KonqMainWindow::manageSessions);
2883     popup->addSeparator();
2884 
2885     QString dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/') + "sessions/";
2886     QDirIterator it(dir, QDir::Readable | QDir::NoDotAndDotDot | QDir::Dirs);
2887 
2888     while (it.hasNext()) {
2889         QFileInfo fileInfo(it.next());
2890 
2891         QAction *action = popup->addAction(KIO::decodeFileName(fileInfo.baseName()));
2892         action->setActionGroup(m_sessionsGroup);
2893         action->setData(fileInfo.filePath());
2894     }
2895     KAcceleratorManager::manage(popup);
2896 }
2897 
saveCurrentSession()2898 void KonqMainWindow::saveCurrentSession()
2899 {
2900     KonqNewSessionDlg dlg(this, this);
2901     dlg.exec();
2902 }
2903 
manageSessions()2904 void KonqMainWindow::manageSessions()
2905 {
2906     KonqSessionDlg dlg(m_pViewManager, this);
2907     dlg.exec();
2908 }
2909 
slotSessionActivated(QAction * action)2910 void KonqMainWindow::slotSessionActivated(QAction *action)
2911 {
2912     QString dirpath = action->data().toString();
2913     KonqSessionManager::self()->restoreSessions(dirpath);
2914 }
2915 
updateClosedItemsAction()2916 void KonqMainWindow::updateClosedItemsAction()
2917 {
2918     bool available = m_pUndoManager->undoAvailable();
2919     m_paClosedItems->setEnabled(available);
2920     m_paUndo->setText(m_pUndoManager->undoText());
2921 }
2922 
slotBack()2923 void KonqMainWindow::slotBack()
2924 {
2925     slotGoHistoryActivated(-1);
2926 }
2927 
slotBackActivated(QAction * action)2928 void KonqMainWindow::slotBackActivated(QAction *action)
2929 {
2930     slotGoHistoryActivated(action->data().toInt());
2931 }
2932 
slotForwardAboutToShow()2933 void KonqMainWindow::slotForwardAboutToShow()
2934 {
2935     m_paForward->menu()->clear();
2936     if (m_currentView) {
2937         KonqActions::fillHistoryPopup(m_currentView->history(), m_currentView->historyIndex(), m_paForward->menu(), false, true);
2938     }
2939 }
2940 
slotForward()2941 void KonqMainWindow::slotForward()
2942 {
2943     slotGoHistoryActivated(1);
2944 }
2945 
slotForwardActivated(QAction * action)2946 void KonqMainWindow::slotForwardActivated(QAction *action)
2947 {
2948     slotGoHistoryActivated(action->data().toInt());
2949 }
2950 
checkDisableClearButton()2951 void KonqMainWindow::checkDisableClearButton()
2952 {
2953     // if the location toolbar already has the clear_location action,
2954     // disable the combobox's embedded clear icon.
2955     KToolBar *ltb = toolBar(QStringLiteral("locationToolBar"));
2956     QAction *clearAction = action("clear_location");
2957     bool enable = true;
2958     const auto toolbuttons = ltb->findChildren<QToolButton *>();
2959     for (QToolButton *atb : toolbuttons) {
2960         if (atb->defaultAction() == clearAction) {
2961             enable = false;
2962             break;
2963         }
2964     }
2965     QLineEdit *lineEdit = comboEdit();
2966     if (lineEdit) {
2967         lineEdit->setClearButtonEnabled(enable);
2968     }
2969 }
2970 
initCombo()2971 void KonqMainWindow::initCombo()
2972 {
2973     m_combo = new KonqCombo(nullptr);
2974 
2975     m_combo->init(s_pCompletion);
2976 
2977     connect(m_combo, SIGNAL(activated(QString,Qt::KeyboardModifiers)),
2978             this, SLOT(slotURLEntered(QString,Qt::KeyboardModifiers)));
2979     connect(m_combo, SIGNAL(showPageSecurity()),
2980             this, SLOT(showPageSecurity()));
2981 
2982     m_pURLCompletion = new KUrlCompletion();
2983     m_pURLCompletion->setCompletionMode(s_pCompletion->completionMode());
2984 
2985     // This only turns completion off. ~ is still there in the result
2986     // We do want completion of user names, right?
2987     //m_pURLCompletion->setReplaceHome( false );  // Leave ~ alone! Will be taken care of by filters!!
2988 
2989     connect(m_combo, SIGNAL(completionModeChanged(KCompletion::CompletionMode)),
2990             SLOT(slotCompletionModeChanged(KCompletion::CompletionMode)));
2991     connect(m_combo, SIGNAL(completion(QString)),
2992             SLOT(slotMakeCompletion(QString)));
2993     connect(m_combo, SIGNAL(substringCompletion(QString)),
2994             SLOT(slotSubstringcompletion(QString)));
2995     connect(m_combo, SIGNAL(textRotation(KCompletionBase::KeyBindingType)),
2996             SLOT(slotRotation(KCompletionBase::KeyBindingType)));
2997     connect(m_combo, SIGNAL(cleared()),
2998             SLOT(slotClearHistory()));
2999     connect(m_pURLCompletion, SIGNAL(match(QString)),
3000             SLOT(slotMatch(QString)));
3001 
3002     m_combo->installEventFilter(this);
3003 
3004     static bool bookmarkCompletionInitialized = false;
3005     if (!bookmarkCompletionInitialized) {
3006         bookmarkCompletionInitialized = true;
3007         DelayedInitializer *initializer = new DelayedInitializer(QEvent::KeyPress, m_combo);
3008         connect(initializer, &DelayedInitializer::initialize, this, &KonqMainWindow::bookmarksIntoCompletion);
3009     }
3010 }
3011 
bookmarksIntoCompletion()3012 void KonqMainWindow::bookmarksIntoCompletion()
3013 {
3014     // add all bookmarks to the completion list for easy access
3015     addBookmarksIntoCompletion(s_bookmarkManager->root());
3016 }
3017 
3018 // the user changed the completion mode in the combo
slotCompletionModeChanged(KCompletion::CompletionMode m)3019 void KonqMainWindow::slotCompletionModeChanged(KCompletion::CompletionMode m)
3020 {
3021     s_pCompletion->setCompletionMode(m);
3022 
3023     KonqSettings::setSettingsCompletionMode(int(m_combo->completionMode()));
3024     KonqSettings::self()->save();
3025 
3026     // tell the other windows too (only this instance currently)
3027     foreach (KonqMainWindow *window, *s_lstMainWindows) {
3028         if (window && window->m_combo) {
3029             window->m_combo->setCompletionMode(m);
3030             window->m_pURLCompletion->setCompletionMode(m);
3031         }
3032     }
3033 }
3034 
3035 // at first, try to find a completion in the current view, then use the global
3036 // completion (history)
slotMakeCompletion(const QString & text)3037 void KonqMainWindow::slotMakeCompletion(const QString &text)
3038 {
3039     if (m_pURLCompletion) {
3040         m_urlCompletionStarted = true; // flag for slotMatch()
3041 
3042         // qCDebug(KONQUEROR_LOG) << "Local Completion object found!";
3043         QString completion = m_pURLCompletion->makeCompletion(text);
3044         m_currentDir.clear();
3045 
3046         if (completion.isNull() && !m_pURLCompletion->isRunning()) {
3047             // No match() signal will come from m_pURLCompletion
3048             // ask the global one
3049             // tell the static completion object about the current completion mode
3050             completion = s_pCompletion->makeCompletion(text);
3051 
3052             // some special handling necessary for CompletionPopup
3053             if (m_combo->completionMode() == KCompletion::CompletionPopup ||
3054                     m_combo->completionMode() == KCompletion::CompletionPopupAuto) {
3055                 m_combo->setCompletedItems(historyPopupCompletionItems(text));
3056             }
3057 
3058             else if (!completion.isNull()) {
3059                 m_combo->setCompletedText(completion);
3060             }
3061         } else {
3062             // To be continued in slotMatch()...
3063             if (!m_pURLCompletion->dir().isEmpty()) {
3064                 m_currentDir = m_pURLCompletion->dir();
3065             }
3066         }
3067     }
3068     // qCDebug(KONQUEROR_LOG) << "Current dir:" << m_currentDir << "Current text:" << text;
3069 }
3070 
slotSubstringcompletion(const QString & text)3071 void KonqMainWindow::slotSubstringcompletion(const QString &text)
3072 {
3073     if (!m_currentView) {
3074         return;
3075     }
3076 
3077     QString currentURL = m_currentView->url().toDisplayString();
3078     bool filesFirst = currentURL.startsWith('/') ||
3079                       currentURL.startsWith(QLatin1String("file:/"));
3080     QStringList items;
3081     if (filesFirst && m_pURLCompletion) {
3082         items = m_pURLCompletion->substringCompletion(text);
3083     }
3084 
3085     items += s_pCompletion->substringCompletion(text);
3086     if (!filesFirst && m_pURLCompletion) {
3087         items += m_pURLCompletion->substringCompletion(text);
3088     }
3089 
3090     m_combo->setCompletedItems(items);
3091 }
3092 
slotRotation(KCompletionBase::KeyBindingType type)3093 void KonqMainWindow::slotRotation(KCompletionBase::KeyBindingType type)
3094 {
3095     // Tell slotMatch() to do nothing
3096     m_urlCompletionStarted = false;
3097 
3098     bool prev = (type == KCompletionBase::PrevCompletionMatch);
3099     if (prev || type == KCompletionBase::NextCompletionMatch) {
3100         QString completion = prev ? m_pURLCompletion->previousMatch() :
3101                              m_pURLCompletion->nextMatch();
3102 
3103         if (completion.isNull()) {  // try the history KCompletion object
3104             completion = prev ? s_pCompletion->previousMatch() :
3105                          s_pCompletion->nextMatch();
3106         }
3107         if (completion.isEmpty() || completion == m_combo->currentText()) {
3108             return;
3109         }
3110 
3111         m_combo->setCompletedText(completion);
3112     }
3113 }
3114 
3115 // Handle match() from m_pURLCompletion
slotMatch(const QString & match)3116 void KonqMainWindow::slotMatch(const QString &match)
3117 {
3118     if (match.isEmpty() || !m_combo) {
3119         return;
3120     }
3121 
3122     // Check flag to avoid match() raised by rotation
3123     if (m_urlCompletionStarted) {
3124         m_urlCompletionStarted = false;
3125 
3126         // some special handling necessary for CompletionPopup
3127         if (m_combo->completionMode() == KCompletion::CompletionPopup ||
3128                 m_combo->completionMode() == KCompletion::CompletionPopupAuto) {
3129             QStringList items = m_pURLCompletion->allMatches();
3130             items += historyPopupCompletionItems(m_combo->currentText());
3131             items.removeDuplicates();  // when items from completion are also in history
3132             // items.sort(); // should we?
3133             m_combo->setCompletedItems(items);
3134         } else if (!match.isNull()) {
3135             m_combo->setCompletedText(match);
3136         }
3137     }
3138 }
3139 
slotCtrlTabPressed()3140 void KonqMainWindow::slotCtrlTabPressed()
3141 {
3142     KonqView *view = m_pViewManager->chooseNextView(m_currentView);
3143     //qCDebug(KONQUEROR_LOG) << m_currentView->url() << "->" << view->url();
3144     if (view) {
3145         m_pViewManager->setActivePart(view->part());
3146         KonqFrameTabs *tabs = m_pViewManager->tabContainer();
3147         m_pViewManager->showTab(tabs->tabIndexContaining(view->frame()));
3148     }
3149 }
3150 
slotClearHistory()3151 void KonqMainWindow::slotClearHistory()
3152 {
3153     KonqHistoryManager::kself()->emitClear();
3154 }
3155 
slotClearComboHistory()3156 void KonqMainWindow::slotClearComboHistory()
3157 {
3158     if (m_combo && m_combo->count()) {
3159         m_combo->clearHistory();
3160     }
3161 }
3162 
eventFilter(QObject * obj,QEvent * ev)3163 bool KonqMainWindow::eventFilter(QObject *obj, QEvent *ev)
3164 {
3165     if ((ev->type() == QEvent::FocusIn || ev->type() == QEvent::FocusOut) &&
3166             m_combo && m_combo->lineEdit() && m_combo == obj) {
3167         //qCDebug(KONQUEROR_LOG) << obj << obj->metaObject()->className() << obj->name();
3168 
3169         QFocusEvent *focusEv = static_cast<QFocusEvent *>(ev);
3170         if (focusEv->reason() == Qt::PopupFocusReason) {
3171             return KParts::MainWindow::eventFilter(obj, ev);
3172         }
3173 
3174         KParts::BrowserExtension *ext = nullptr;
3175         if (m_currentView) {
3176             ext = m_currentView->browserExtension();
3177         }
3178 
3179         if (ev->type() == QEvent::FocusIn) {
3180             //qCDebug(KONQUEROR_LOG) << "ComboBox got the focus...";
3181             if (m_bLocationBarConnected) {
3182                 //qCDebug(KONQUEROR_LOG) << "Was already connected...";
3183                 return KParts::MainWindow::eventFilter(obj, ev);
3184             }
3185             m_bLocationBarConnected = true;
3186 
3187             // Workaround for Qt issue: usually, QLineEdit reacts on Ctrl-D,
3188             // but the duplicatecurrenttab action also has Ctrl-D as accel and
3189             // prevents the lineedit from getting this event. IMHO the accel
3190             // should be disabled in favor of the focus-widget.
3191             // TODO: decide if the delete-character behaviour of QLineEdit
3192             // really is useful enough to warrant this workaround
3193             QAction *duplicate = actionCollection()->action(QStringLiteral("duplicatecurrenttab"));
3194             if (duplicate->shortcuts().contains(QKeySequence(Qt::CTRL | Qt::Key_D))) {
3195                 duplicate->setEnabled(false);
3196             }
3197 
3198             connect(m_paCut, SIGNAL(triggered()), m_combo->lineEdit(), SLOT(cut()));
3199             connect(m_paCopy, SIGNAL(triggered()), m_combo->lineEdit(), SLOT(copy()));
3200             connect(m_paPaste, SIGNAL(triggered()), m_combo->lineEdit(), SLOT(paste()));
3201             connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(slotClipboardDataChanged()));
3202             connect(m_combo->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotCheckComboSelection()));
3203             connect(m_combo->lineEdit(), SIGNAL(selectionChanged()), this, SLOT(slotCheckComboSelection()));
3204 
3205             slotClipboardDataChanged();
3206         } else if (ev->type() == QEvent::FocusOut) {
3207             //qCDebug(KONQUEROR_LOG) << "ComboBox lost focus...";
3208             if (!m_bLocationBarConnected) {
3209                 //qCDebug(KONQUEROR_LOG) << "Was already disconnected...";
3210                 return KParts::MainWindow::eventFilter(obj, ev);
3211             }
3212             m_bLocationBarConnected = false;
3213 
3214             // see above in FocusIn for explanation
3215             // action is reenabled if a view exists
3216             QAction *duplicate = actionCollection()->action(QStringLiteral("duplicatecurrenttab"));
3217             if (duplicate->shortcuts().contains(QKeySequence(Qt::CTRL | Qt::Key_D))) {
3218                 duplicate->setEnabled(currentView() && currentView()->frame());
3219             }
3220 
3221             disconnect(m_paCut, SIGNAL(triggered()), m_combo->lineEdit(), SLOT(cut()));
3222             disconnect(m_paCopy, SIGNAL(triggered()), m_combo->lineEdit(), SLOT(copy()));
3223             disconnect(m_paPaste, SIGNAL(triggered()), m_combo->lineEdit(), SLOT(paste()));
3224             disconnect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(slotClipboardDataChanged()));
3225             disconnect(m_combo->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotCheckComboSelection()));
3226             disconnect(m_combo->lineEdit(), SIGNAL(selectionChanged()), this, SLOT(slotCheckComboSelection()));
3227 
3228             if (ext) {
3229                 m_paCut->setEnabled(ext->isActionEnabled("cut"));
3230                 m_paCopy->setEnabled(ext->isActionEnabled("copy"));
3231                 m_paPaste->setEnabled(ext->isActionEnabled("paste"));
3232             } else {
3233                 m_paCut->setEnabled(false);
3234                 m_paCopy->setEnabled(false);
3235                 m_paPaste->setEnabled(false);
3236             }
3237         }
3238     } else if (ev->type() == QEvent::KeyPress) {
3239         QKeyEvent *keyEv = static_cast<QKeyEvent *>(ev);
3240         if ((keyEv->key() == Qt::Key_Tab) && (keyEv->modifiers() == Qt::ControlModifier)) {
3241             slotCtrlTabPressed();
3242             return true; // don't let QTabWidget see the event
3243         } else if (obj == m_combo && m_currentView && keyEv->key() == Qt::Key_Escape) {
3244             // reset url to current view's actual url on ESC
3245             m_combo->setURL(m_currentView->url().QUrl::toDisplayString(QUrl::PreferLocalFile));
3246             m_combo->lineEdit()->setModified(false);
3247             return true;
3248         }
3249     }
3250     return KParts::MainWindow::eventFilter(obj, ev);
3251 }
3252 
3253 // Only called when m_bLocationBarConnected, i.e. when the combobox has focus.
3254 // The rest of the time, the part handles the cut/copy/paste actions.
slotClipboardDataChanged()3255 void KonqMainWindow::slotClipboardDataChanged()
3256 {
3257     const QMimeData *data = QApplication::clipboard()->mimeData();
3258     m_paPaste->setEnabled(data->hasText());
3259     slotCheckComboSelection();
3260 }
3261 
slotCheckComboSelection()3262 void KonqMainWindow::slotCheckComboSelection()
3263 {
3264     QLineEdit *edit = comboEdit();
3265     if (edit) {
3266         const bool hasSelection = edit->hasSelectedText();
3267         //qCDebug(KONQUEROR_LOG) << "m_combo->lineEdit()->hasMarkedText():" << hasSelection;
3268         m_paCopy->setEnabled(hasSelection);
3269         m_paCut->setEnabled(hasSelection);
3270     }
3271 }
3272 
slotClearLocationBar()3273 void KonqMainWindow::slotClearLocationBar()
3274 {
3275     slotStop();
3276     if (m_combo) {
3277         m_combo->clearTemporary();
3278     }
3279     focusLocationBar();
3280 }
3281 
slotForceSaveMainWindowSettings()3282 void KonqMainWindow::slotForceSaveMainWindowSettings()
3283 {
3284     if (autoSaveSettings()) {   // don't do it on e.g. JS window.open windows with no toolbars!
3285         KConfigGroup config = KSharedConfig::openConfig()->group("MainWindow");
3286         saveMainWindowSettings(config);
3287     }
3288 }
3289 
slotShowMenuBar()3290 void KonqMainWindow::slotShowMenuBar()
3291 {
3292     menuBar()->setVisible(!menuBar()->isVisible());
3293     slotForceSaveMainWindowSettings();
3294 }
3295 
slotShowStatusBar()3296 void KonqMainWindow::slotShowStatusBar()
3297 {
3298     if (m_currentView) {
3299         m_currentView->frame()->statusbar()->setVisible(m_paShowStatusBar->isChecked());
3300     }
3301 
3302     // An alternative: this will change simultaneously all of the status bars on
3303     // all of the current views.
3304     //MapViews::const_iterator end = m_mapViews.constEnd();
3305     //for (MapViews::const_iterator it = m_mapViews.constBegin(); it != end; ++it) {
3306     //  KonqView* view = it.value();
3307     //  view->frame()->statusbar()->setVisible(on);
3308     //}
3309 
3310     slotForceSaveMainWindowSettings();
3311 }
3312 
slotUpdateFullScreen(bool set)3313 void KonqMainWindow::slotUpdateFullScreen(bool set)
3314 {
3315     if (m_fullScreenData.currentState == FullScreenState::CompleteFullScreen) {
3316         if (m_currentView && m_currentView->part()) {
3317             QMetaObject::invokeMethod(m_currentView->part(), "exitFullScreen");
3318         }
3319         return;
3320     }
3321 
3322     KToggleFullScreenAction::setFullScreen(this, set);
3323     if (set) {
3324         // Create toolbar button for exiting from full-screen mode
3325         // ...but only if there isn't one already...
3326 
3327         bool haveFullScreenButton = false;
3328 
3329         //Walk over the toolbars and check whether there is a show fullscreen button in any of them
3330         foreach (KToolBar *bar, findChildren<KToolBar *>()) {
3331             //Are we plugged here, in a visible toolbar?
3332             if (bar->isVisible() &&
3333                     action("fullscreen")->associatedWidgets().contains(bar)) {
3334                 haveFullScreenButton = true;
3335                 break;
3336             }
3337         }
3338 
3339         if (!haveFullScreenButton) {
3340             QList<QAction *> lst;
3341             lst.append(m_ptaFullScreen);
3342             plugActionList(QStringLiteral("fullscreen"), lst);
3343         }
3344 
3345         m_fullScreenData.wasMenuBarVisible = menuBar()->isVisible();
3346         menuBar()->hide();
3347         m_paShowMenuBar->setChecked(false);
3348     } else {
3349         unplugActionList(QStringLiteral("fullscreen"));
3350 
3351         if (m_fullScreenData.wasMenuBarVisible) {
3352             menuBar()->show();
3353             m_paShowMenuBar->setChecked(true);
3354         }
3355     }
3356     m_fullScreenData.switchToState(set ? FullScreenState::OrdinaryFullScreen : FullScreenState::NoFullScreen);
3357 }
3358 
setLocationBarURL(const QUrl & url)3359 void KonqMainWindow::setLocationBarURL(const QUrl &url)
3360 {
3361     setLocationBarURL(url.toString());
3362 }
3363 
setLocationBarURL(const QString & url)3364 void KonqMainWindow::setLocationBarURL(const QString &url)
3365 {
3366     // Don't set the location bar URL if it hasn't changed
3367     // or if the user had time to edit the url since the last call to openUrl (#64868)
3368     QLineEdit *edit = comboEdit();
3369     if (edit && url != edit->text() && !edit->isModified()) {
3370         //qCDebug(KONQUEROR_LOG) << "url=" << url;
3371         m_combo->setURL(url);
3372         updateWindowIcon();
3373     }
3374 }
3375 
setPageSecurity(PageSecurity pageSecurity)3376 void KonqMainWindow::setPageSecurity(PageSecurity pageSecurity)
3377 {
3378     if (m_combo) {
3379         m_combo->setPageSecurity(pageSecurity);
3380     }
3381 }
3382 
showPageSecurity()3383 void KonqMainWindow::showPageSecurity()
3384 {
3385     if (m_currentView && m_currentView->part()) {
3386         QAction *act = m_currentView->part()->action("security");
3387         if (act) {
3388             act->trigger();
3389         }
3390     }
3391 }
3392 
3393 // Called via DBUS from KonquerorApplication
comboAction(int action,const QString & url,const QString & senderId)3394 void KonqMainWindow::comboAction(int action, const QString &url, const QString &senderId)
3395 {
3396     if (!s_lstMainWindows) { // this happens in "konqueror --silent"
3397         return;
3398     }
3399 
3400     KonqCombo *combo = nullptr;
3401     foreach (KonqMainWindow *window, *s_lstMainWindows) {
3402         if (window && window->m_combo) {
3403             combo = window->m_combo;
3404 
3405             switch (action) {
3406             case ComboAdd:
3407                 combo->insertPermanent(url);
3408                 break;
3409             case ComboClear:
3410                 combo->clearHistory();
3411                 break;
3412             case ComboRemove:
3413                 combo->removeURL(url);
3414                 break;
3415             default:
3416                 break;
3417             }
3418         }
3419     }
3420 
3421     // only one instance should save...
3422     if (combo && senderId == QDBusConnection::sessionBus().baseService()) {
3423         combo->saveItems();
3424     }
3425 }
3426 
locationBarURL() const3427 QString KonqMainWindow::locationBarURL() const
3428 {
3429     return (m_combo ? m_combo->currentText() : QString());
3430 }
3431 
focusLocationBar()3432 void KonqMainWindow::focusLocationBar()
3433 {
3434     if (m_combo && (m_combo->isVisible() || !isVisible())) {
3435         m_combo->setFocus();
3436     }
3437 }
3438 
startAnimation()3439 void KonqMainWindow::startAnimation()
3440 {
3441     m_paAnimatedLogo->start();
3442     m_paStop->setEnabled(true);
3443 }
3444 
stopAnimation()3445 void KonqMainWindow::stopAnimation()
3446 {
3447     m_paAnimatedLogo->stop();
3448     m_paStop->setEnabled(false);
3449 }
3450 
setUpEnabled(const QUrl & url)3451 void KonqMainWindow::setUpEnabled(const QUrl &url)
3452 {
3453     bool bHasUpURL = ((!url.path().isEmpty() && url.path() != QLatin1String("/") && url.path()[0] == '/')
3454                       || !url.query().isEmpty() /*e.g. lists.kde.org*/);
3455 
3456     m_paUp->setEnabled(bHasUpURL);
3457 }
3458 
initActions()3459 void KonqMainWindow::initActions()
3460 {
3461     // Note about this method : don't call setEnabled() on any of the actions.
3462     // They are all disabled then re-enabled with enableAllActions
3463     // If any one needs to be initially disabled, put that code in enableAllActions
3464 
3465     // For the popup menu only.
3466     m_pMenuNew = new KNewFileMenu(actionCollection(), QStringLiteral("new_menu"), this);
3467 
3468     // File menu
3469 
3470     QAction *action = actionCollection()->addAction(QStringLiteral("new_window"));
3471     action->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
3472     action->setText(i18n("New &Window"));
3473     connect(action, &QAction::triggered, this, &KonqMainWindow::slotNewWindow);
3474     actionCollection()->setDefaultShortcuts(action, KStandardShortcut::shortcut(KStandardShortcut::New));
3475     action = actionCollection()->addAction(QStringLiteral("duplicate_window"));
3476     action->setIcon(QIcon::fromTheme(QStringLiteral("window-duplicate")));
3477     action->setText(i18n("&Duplicate Window"));
3478     connect(action, &QAction::triggered, this, &KonqMainWindow::slotDuplicateWindow);
3479     actionCollection()->setDefaultShortcut(action, Qt::CTRL+Qt::SHIFT+Qt::Key_D);
3480     action = actionCollection()->addAction(QStringLiteral("sendURL"));
3481     action->setIcon(QIcon::fromTheme(QStringLiteral("mail-message-new")));
3482     action->setText(i18n("Send &Link Address..."));
3483     connect(action, &QAction::triggered, this, &KonqMainWindow::slotSendURL);
3484     action = actionCollection()->addAction(QStringLiteral("sendPage"));
3485     action->setIcon(QIcon::fromTheme(QStringLiteral("mail-message-new")));
3486     action->setText(i18n("S&end File..."));
3487     connect(action, &QAction::triggered, this, &KonqMainWindow::slotSendFile);
3488     action = actionCollection()->addAction(QStringLiteral("open_location"));
3489     action->setIcon(QIcon::fromTheme(QStringLiteral("document-open-remote")));
3490     action->setText(i18n("&Open Location"));
3491     actionCollection()->setDefaultShortcut(action, Qt::ALT+Qt::Key_O);
3492     connect(action, &QAction::triggered, this, &KonqMainWindow::slotOpenLocation);
3493 
3494     action = actionCollection()->addAction(QStringLiteral("open_file"));
3495     action->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
3496     action->setText(i18n("&Open File..."));
3497     connect(action, &QAction::triggered, this, &KonqMainWindow::slotOpenFile);
3498     actionCollection()->setDefaultShortcuts(action, KStandardShortcut::shortcut(KStandardShortcut::Open));
3499 
3500 #if 0
3501     m_paFindFiles = new KToggleAction(QIcon::fromTheme("edit-find"), i18n("&Find File..."), this);
3502     actionCollection()->addAction("findfile", m_paFindFiles);
3503     connect(m_paFindFiles, &KToggleAction::triggered, this, &KonqMainWindow::slotToolFind);
3504     actionCollection()->setDefaultShortcuts(m_paFindFiles, KStandardShortcut::shortcut(KStandardShortcut::Find));
3505 #endif
3506 
3507     m_paPrint = actionCollection()->addAction(KStandardAction::Print, QStringLiteral("print"), nullptr, nullptr);
3508     actionCollection()->addAction(KStandardAction::Quit, QStringLiteral("quit"), this, SLOT(close()));
3509 
3510     m_paLockView = new KToggleAction(i18n("Lock to Current Location"), this);
3511     actionCollection()->addAction(QStringLiteral("lock"), m_paLockView);
3512     connect(m_paLockView, &KToggleAction::triggered, this, &KonqMainWindow::slotLockView);
3513     m_paLinkView = new KToggleAction(i18nc("This option links konqueror views", "Lin&k View"), this);
3514     actionCollection()->addAction(QStringLiteral("link"), m_paLinkView);
3515     connect(m_paLinkView, &KToggleAction::triggered, this, &KonqMainWindow::slotLinkView);
3516 
3517     // Go menu
3518     m_paUp = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("go-up")), i18n("&Up"), this);
3519     actionCollection()->addAction(QStringLiteral("go_up"), m_paUp);
3520     actionCollection()->setDefaultShortcuts(m_paUp, KStandardShortcut::shortcut(KStandardShortcut::Up));
3521     connect(m_paUp, SIGNAL(triggered()), this, SLOT(slotUp()));
3522     connect(m_paUp->menu(), SIGNAL(aboutToShow()), this, SLOT(slotUpAboutToShow()));
3523     connect(m_paUp->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotUpActivated(QAction*)));
3524 
3525     QPair< KGuiItem, KGuiItem > backForward = KStandardGuiItem::backAndForward();
3526 
3527     // Trash bin of closed tabs
3528     m_paClosedItems = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("edit-undo-closed-tabs")),  i18n("Closed Items"), this);
3529     actionCollection()->addAction(QStringLiteral("closeditems"), m_paClosedItems);
3530     m_closedItemsGroup = new QActionGroup(m_paClosedItems->menu());
3531 
3532     // set the closed tabs list shown
3533     connect(m_paClosedItems, &KToolBarPopupAction::triggered, m_pUndoManager, &KonqUndoManager::undoLastClosedItem);
3534     connect(m_paClosedItems->menu(), SIGNAL(aboutToShow()), this, SLOT(slotClosedItemsListAboutToShow()));
3535     connect(m_closedItemsGroup, &QActionGroup::triggered, m_pUndoManager, &KonqUndoManager::slotClosedItemsActivated);
3536     connect(m_pViewManager, &KonqViewManager::aboutToRemoveTab, this, &KonqMainWindow::slotAddClosedUrl);
3537     connect(m_pUndoManager, &KonqUndoManager::openClosedTab, m_pViewManager, &KonqViewManager::openClosedTab);
3538     connect(m_pUndoManager, &KonqUndoManager::openClosedWindow, m_pViewManager, &KonqViewManager::openClosedWindow);
3539     connect(m_pUndoManager, &KonqUndoManager::closedItemsListChanged, this, &KonqMainWindow::updateClosedItemsAction);
3540 
3541     m_paSessions = new KActionMenu(i18n("Sessions"), this);
3542     actionCollection()->addAction(QStringLiteral("sessions"), m_paSessions);
3543     m_sessionsGroup = new QActionGroup(m_paSessions->menu());
3544     connect(m_paSessions->menu(), SIGNAL(aboutToShow()), this, SLOT(slotSessionsListAboutToShow()));
3545     connect(m_sessionsGroup, &QActionGroup::triggered, this, &KonqMainWindow::slotSessionActivated);
3546 
3547     m_paBack = new KToolBarPopupAction(QIcon::fromTheme(backForward.first.iconName()), backForward.first.text(), this);
3548     actionCollection()->addAction(QStringLiteral("go_back"), m_paBack);
3549     actionCollection()->setDefaultShortcuts(m_paBack, KStandardShortcut::shortcut(KStandardShortcut::Back));
3550     connect(m_paBack, SIGNAL(triggered()), this, SLOT(slotBack()));
3551     connect(m_paBack->menu(), SIGNAL(aboutToShow()), this, SLOT(slotBackAboutToShow()));
3552     connect(m_paBack->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotBackActivated(QAction*)));
3553 
3554     m_paForward = new KToolBarPopupAction(QIcon::fromTheme(backForward.second.iconName()), backForward.second.text(), this);
3555     actionCollection()->addAction(QStringLiteral("go_forward"), m_paForward);
3556     actionCollection()->setDefaultShortcuts(m_paForward, KStandardShortcut::shortcut(KStandardShortcut::Forward));
3557     connect(m_paForward, SIGNAL(triggered()), this, SLOT(slotForward()));
3558     connect(m_paForward->menu(), SIGNAL(aboutToShow()), this, SLOT(slotForwardAboutToShow()));
3559     connect(m_paForward->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotForwardActivated(QAction*)));
3560 
3561     m_paHome = actionCollection()->addAction(KStandardAction::Home);
3562     connect(m_paHome, SIGNAL(triggered(bool)), this, SLOT(slotHome()));
3563     m_paHomePopup = new KToolBarPopupAction (QIcon::fromTheme(QStringLiteral("go-home")), i18n("Home"), this);
3564     actionCollection()->addAction(QStringLiteral("go_home_popup"), m_paHomePopup);
3565     connect(m_paHomePopup, SIGNAL(triggered()), this, SLOT(slotHome()));
3566     connect(m_paHomePopup->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotHomePopupActivated(QAction*)));
3567 
3568     KonqMostOftenURLSAction *mostOften = new KonqMostOftenURLSAction(i18nc("@action:inmenu Go", "Most Often Visited"), this);
3569     actionCollection()->addAction(QStringLiteral("go_most_often"), mostOften);
3570     connect(mostOften, &KonqMostOftenURLSAction::activated, this, &KonqMainWindow::slotOpenURL);
3571 
3572     KonqHistoryAction *historyAction = new KonqHistoryAction(i18nc("@action:inmenu Go", "Recently Visited"), this);
3573     actionCollection()->addAction(QStringLiteral("history"), historyAction);
3574     connect(historyAction, &KonqHistoryAction::activated, this, &KonqMainWindow::slotOpenURL);
3575 
3576     action = actionCollection()->addAction(QStringLiteral("go_history"));
3577     action->setIcon(QIcon::fromTheme(QStringLiteral("view-history")));
3578     // Ctrl+Shift+H, shortcut from firefox
3579     // TODO: and Ctrl+H should open the sidebar history module
3580     actionCollection()->setDefaultShortcut(action, Qt::CTRL+Qt::SHIFT+Qt::Key_H);
3581     action->setText(i18nc("@action:inmenu Go", "Show History"));
3582     connect(action, &QAction::triggered, this, &KonqMainWindow::slotGoHistory);
3583 
3584     // Settings menu
3585 
3586     // This list is just for the call to authorizeControlModule; see slotConfigure for the real code
3587     QStringList configureModules;
3588     configureModules << QStringLiteral("khtml_general") << QStringLiteral("bookmarks") <<
3589                      QStringLiteral("filebehavior") << QStringLiteral("filetypes") << QStringLiteral("kcmtrash") <<
3590                      QStringLiteral("khtml_appearance") << QStringLiteral("khtml_behavior") << QStringLiteral("khtml_java_js") <<
3591                      QStringLiteral("khtml_filter") << QStringLiteral("webshortcuts") <<
3592                      QStringLiteral("kcmhistory") << QStringLiteral("cookies") <<
3593                      QStringLiteral("cache") << QStringLiteral("proxy") <<
3594                      QStringLiteral("useragent") << QStringLiteral("khtml_plugins") <<
3595                      QStringLiteral("kcmkonqyperformance");
3596 
3597     if (!KAuthorized::authorizeControlModules(configureModules).isEmpty()) {
3598         actionCollection()->addAction(KStandardAction::Preferences, this, SLOT(slotConfigure()));
3599     }
3600 
3601 
3602 #if KXMLGUI_VERSION >= QT_VERSION_CHECK(5, 84, 0)
3603     KStandardAction::keyBindings(guiFactory(), &KXMLGUIFactory::showConfigureShortcutsDialog, actionCollection());
3604 #else
3605     actionCollection()->addAction(KStandardAction::KeyBindings, guiFactory(), SLOT(configureShortcuts()));
3606 #endif
3607 
3608     actionCollection()->addAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars()));
3609 
3610     m_paConfigureExtensions = actionCollection()->addAction(QStringLiteral("options_configure_extensions"));
3611     m_paConfigureExtensions->setIcon(QIcon::fromTheme(QStringLiteral("plugins")));
3612     m_paConfigureExtensions->setText(i18n("Configure Extensions..."));
3613     connect(m_paConfigureExtensions, &QAction::triggered, this, &KonqMainWindow::slotConfigureExtensions);
3614     m_paConfigureSpellChecking = actionCollection()->addAction(QStringLiteral("configurespellcheck"));
3615     m_paConfigureSpellChecking->setIcon(QIcon::fromTheme(QStringLiteral("tools-check-spelling")));
3616     m_paConfigureSpellChecking->setText(i18n("Configure Spell Checking..."));
3617     connect(m_paConfigureSpellChecking, &QAction::triggered, this, &KonqMainWindow::slotConfigureSpellChecking);
3618 
3619     // Window menu
3620     m_paSplitViewHor = actionCollection()->addAction(QStringLiteral("splitviewh"));
3621     m_paSplitViewHor->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
3622     m_paSplitViewHor->setText(i18n("Split View &Left/Right"));
3623     connect(m_paSplitViewHor, &QAction::triggered, this, &KonqMainWindow::slotSplitViewHorizontal);
3624     actionCollection()->setDefaultShortcut(m_paSplitViewHor, Qt::CTRL+Qt::SHIFT+Qt::Key_L);
3625     m_paSplitViewVer = actionCollection()->addAction(QStringLiteral("splitviewv"));
3626     m_paSplitViewVer->setIcon(QIcon::fromTheme(QStringLiteral("view-split-top-bottom")));
3627     m_paSplitViewVer->setText(i18n("Split View &Top/Bottom"));
3628     connect(m_paSplitViewVer, &QAction::triggered, this, &KonqMainWindow::slotSplitViewVertical);
3629     actionCollection()->setDefaultShortcut(m_paSplitViewVer, Qt::CTRL+Qt::SHIFT+Qt::Key_T);
3630     m_paAddTab = actionCollection()->addAction(QStringLiteral("newtab"));
3631     m_paAddTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
3632     m_paAddTab->setText(i18n("&New Tab"));
3633     connect(m_paAddTab, &QAction::triggered, this, &KonqMainWindow::slotAddTab);
3634     QList<QKeySequence> addTabShortcuts;
3635     addTabShortcuts.append(QKeySequence(Qt::CTRL+Qt::Key_T));
3636     addTabShortcuts.append(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_N));
3637     actionCollection()->setDefaultShortcuts(m_paAddTab, addTabShortcuts);
3638 
3639     m_paDuplicateTab = actionCollection()->addAction(QStringLiteral("duplicatecurrenttab"));
3640     m_paDuplicateTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-duplicate")));
3641     m_paDuplicateTab->setText(i18n("&Duplicate Current Tab"));
3642     connect(m_paDuplicateTab, &QAction::triggered, this, &KonqMainWindow::slotDuplicateTab);
3643     actionCollection()->setDefaultShortcut(m_paDuplicateTab, Qt::CTRL+Qt::Key_D);
3644     m_paBreakOffTab = actionCollection()->addAction(QStringLiteral("breakoffcurrenttab"));
3645     m_paBreakOffTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-detach")));
3646     m_paBreakOffTab->setText(i18n("Detach Current Tab"));
3647     connect(m_paBreakOffTab, &QAction::triggered, this, &KonqMainWindow::slotBreakOffTab);
3648     actionCollection()->setDefaultShortcut(m_paBreakOffTab, Qt::CTRL+Qt::SHIFT+Qt::Key_B);
3649     m_paRemoveView = actionCollection()->addAction(QStringLiteral("removeview"));
3650     m_paRemoveView->setIcon(QIcon::fromTheme(QStringLiteral("view-close")));
3651     m_paRemoveView->setText(i18n("&Close Active View"));
3652     connect(m_paRemoveView, &QAction::triggered, this, &KonqMainWindow::slotRemoveView);
3653     actionCollection()->setDefaultShortcut(m_paRemoveView, Qt::CTRL+Qt::SHIFT+Qt::Key_W);
3654     m_paRemoveTab = actionCollection()->addAction(QStringLiteral("removecurrenttab"));
3655     m_paRemoveTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
3656     m_paRemoveTab->setText(i18n("Close Current Tab"));
3657     connect(m_paRemoveTab, &QAction::triggered, this, &KonqMainWindow::slotRemoveTab, Qt::QueuedConnection /* exit Ctrl+W handler before deleting */);
3658     actionCollection()->setDefaultShortcut(m_paRemoveTab, Qt::CTRL+Qt::Key_W);
3659     m_paRemoveTab->setAutoRepeat(false);
3660     m_paRemoveOtherTabs = actionCollection()->addAction(QStringLiteral("removeothertabs"));
3661     m_paRemoveOtherTabs->setIcon(QIcon::fromTheme(QStringLiteral("tab-close-other")));
3662     m_paRemoveOtherTabs->setText(i18n("Close &Other Tabs"));
3663     connect(m_paRemoveOtherTabs, &QAction::triggered, this, &KonqMainWindow::slotRemoveOtherTabs);
3664 
3665     m_paActivateNextTab = actionCollection()->addAction(QStringLiteral("activatenexttab"));
3666     m_paActivateNextTab->setText(i18n("Activate Next Tab"));
3667     connect(m_paActivateNextTab, &QAction::triggered, this, &KonqMainWindow::slotActivateNextTab);
3668     actionCollection()->setDefaultShortcuts(m_paActivateNextTab, QApplication::isRightToLeft() ? KStandardShortcut::tabPrev() : KStandardShortcut::tabNext());
3669     m_paActivatePrevTab = actionCollection()->addAction(QStringLiteral("activateprevtab"));
3670     m_paActivatePrevTab->setText(i18n("Activate Previous Tab"));
3671     connect(m_paActivatePrevTab, &QAction::triggered, this, &KonqMainWindow::slotActivatePrevTab);
3672     actionCollection()->setDefaultShortcuts(m_paActivatePrevTab, QApplication::isRightToLeft() ? KStandardShortcut::tabNext() : KStandardShortcut::tabPrev());
3673 
3674     for (int i = 1; i < 13; i++) {
3675         const QString actionname = QString::asprintf("activate_tab_%02d", i);
3676         QAction *action = actionCollection()->addAction(actionname);
3677         action->setText(i18n("Activate Tab %1", i));
3678         connect(action, &QAction::triggered, this, &KonqMainWindow::slotActivateTab);
3679     }
3680 
3681     m_paMoveTabLeft = actionCollection()->addAction(QStringLiteral("tab_move_left"));
3682     m_paMoveTabLeft->setText(i18n("Move Tab Left"));
3683     m_paMoveTabLeft->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
3684     connect(m_paMoveTabLeft, &QAction::triggered, this, &KonqMainWindow::slotMoveTabLeft);
3685     actionCollection()->setDefaultShortcut(m_paMoveTabLeft, Qt::CTRL+Qt::SHIFT+Qt::Key_Left);
3686     m_paMoveTabRight = actionCollection()->addAction(QStringLiteral("tab_move_right"));
3687     m_paMoveTabRight->setText(i18n("Move Tab Right"));
3688     m_paMoveTabRight->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
3689     connect(m_paMoveTabRight, &QAction::triggered, this, &KonqMainWindow::slotMoveTabRight);
3690     actionCollection()->setDefaultShortcut(m_paMoveTabRight, Qt::CTRL+Qt::SHIFT+Qt::Key_Right);
3691 
3692 #ifndef NDEBUG
3693     action = actionCollection()->addAction(QStringLiteral("dumpdebuginfo"));
3694     action->setIcon(QIcon::fromTheme(QStringLiteral("view-dump-debug-info")));
3695     action->setText(i18n("Dump Debug Info"));
3696     connect(action, &QAction::triggered, this, &KonqMainWindow::slotDumpDebugInfo);
3697 #endif
3698 
3699     m_ptaFullScreen = KStandardAction::fullScreen(nullptr, nullptr, this, this);
3700     actionCollection()->addAction(m_ptaFullScreen->objectName(), m_ptaFullScreen);
3701     QList<QKeySequence> fullScreenShortcut = m_ptaFullScreen->shortcuts();
3702     fullScreenShortcut.append(Qt::Key_F11);
3703     actionCollection()->setDefaultShortcuts(m_ptaFullScreen, fullScreenShortcut);
3704     connect(m_ptaFullScreen, &KToggleFullScreenAction::toggled, this, &KonqMainWindow::slotUpdateFullScreen);
3705 
3706     QList<QKeySequence> reloadShortcut = KStandardShortcut::shortcut(KStandardShortcut::Reload);
3707     QKeySequence reloadAlternate(Qt::CTRL | Qt::Key_R);
3708     if (!reloadShortcut.contains(reloadAlternate)) {
3709         reloadShortcut.append(reloadAlternate);
3710     }
3711     m_paReload = actionCollection()->addAction(QStringLiteral("reload"));
3712     m_paReload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
3713     m_paReload->setText(i18n("&Reload"));
3714     connect(m_paReload, SIGNAL(triggered()), SLOT(slotReload()));
3715     actionCollection()->setDefaultShortcuts(m_paReload, reloadShortcut);
3716     m_paReloadAllTabs = actionCollection()->addAction(QStringLiteral("reload_all_tabs"));
3717     m_paReloadAllTabs->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh-all")));
3718     m_paReloadAllTabs->setText(i18n("&Reload All Tabs"));
3719     connect(m_paReloadAllTabs, &QAction::triggered, this, &KonqMainWindow::slotReloadAllTabs);
3720     actionCollection()->setDefaultShortcut(m_paReloadAllTabs, Qt::SHIFT+Qt::Key_F5);
3721     // "Forced"/ "Hard" reload action - re-downloads all e.g. images even if a cached
3722     // version already exists.
3723     m_paForceReload = actionCollection()->addAction(QStringLiteral("hard_reload"));
3724     // TODO - request new icon? (view-refresh will do for the time being)
3725     m_paForceReload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
3726     m_paForceReload->setText(i18n("&Force Reload"));
3727     connect(m_paForceReload, &QAction::triggered, this, &KonqMainWindow::slotForceReload);
3728     QList<QKeySequence> forceReloadShortcuts;
3729     forceReloadShortcuts.append(QKeySequence(Qt::CTRL+Qt::Key_F5));
3730     forceReloadShortcuts.append(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_R));
3731     actionCollection()->setDefaultShortcuts(m_paForceReload, forceReloadShortcuts);
3732 
3733     m_paUndo = KStandardAction::undo(m_pUndoManager, SLOT(undo()), this);
3734     actionCollection()->addAction(QStringLiteral("undo"), m_paUndo);
3735     connect(m_pUndoManager, SIGNAL(undoTextChanged(QString)),
3736             this, SLOT(slotUndoTextChanged(QString)));
3737 
3738     // Those are connected to the browserextension directly
3739     m_paCut = KStandardAction::cut(nullptr, nullptr, this);
3740     actionCollection()->addAction(QStringLiteral("cut"), m_paCut);
3741 
3742     QList<QKeySequence> cutShortcuts(m_paCut->shortcuts());
3743     cutShortcuts.removeAll(QKeySequence(Qt::SHIFT+Qt::Key_Delete)); // used for deleting files
3744     actionCollection()->setDefaultShortcuts(m_paCut, cutShortcuts);
3745 
3746     m_paCopy = KStandardAction::copy(nullptr, nullptr, this);
3747     actionCollection()->addAction(QStringLiteral("copy"), m_paCopy);
3748     m_paPaste = KStandardAction::paste(nullptr, nullptr, this);
3749     actionCollection()->addAction(QStringLiteral("paste"), m_paPaste);
3750     m_paStop = actionCollection()->addAction(QStringLiteral("stop"));
3751     m_paStop->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
3752     m_paStop->setText(i18n("&Stop"));
3753     connect(m_paStop, &QAction::triggered, this, &KonqMainWindow::slotStop);
3754     actionCollection()->setDefaultShortcut(m_paStop, Qt::Key_Escape);
3755 
3756     m_paAnimatedLogo = new KonqAnimatedLogo;
3757     QWidgetAction *logoAction = new QWidgetAction(this);
3758     actionCollection()->addAction(QStringLiteral("konq_logo"), logoAction);
3759     logoAction->setDefaultWidget(m_paAnimatedLogo);
3760     // Set icon and text so that it's easier to figure out what the action is in the toolbar editor
3761     logoAction->setText(i18n("Throbber"));
3762     logoAction->setIcon(QIcon::fromTheme(QStringLiteral("kde")));
3763 
3764     // Location bar
3765     m_locationLabel = new KonqDraggableLabel(this, i18n("L&ocation: "));
3766     QWidgetAction *locationAction = new QWidgetAction(this);
3767     actionCollection()->addAction(QStringLiteral("location_label"), locationAction);
3768     locationAction->setText(i18n("L&ocation: "));
3769     connect(locationAction, &QWidgetAction::triggered, this, &KonqMainWindow::slotLocationLabelActivated);
3770     locationAction->setDefaultWidget(m_locationLabel);
3771     m_locationLabel->setBuddy(m_combo);
3772 
3773     QWidgetAction *comboAction = new QWidgetAction(this);
3774     actionCollection()->addAction(QStringLiteral("toolbar_url_combo"), comboAction);
3775     comboAction->setText(i18n("Location Bar"));
3776     actionCollection()->setDefaultShortcut(comboAction, Qt::Key_F6);
3777     connect(comboAction, &QWidgetAction::triggered, this, &KonqMainWindow::slotLocationLabelActivated);
3778     comboAction->setDefaultWidget(m_combo);
3779     actionCollection()->setShortcutsConfigurable(comboAction, false);
3780 
3781     m_combo->setWhatsThis(i18n("<html>Location Bar<br /><br />Enter a web address or search term.</html>"));
3782 
3783     QAction *clearLocation = actionCollection()->addAction(QStringLiteral("clear_location"));
3784     clearLocation->setIcon(QIcon::fromTheme(QApplication::isRightToLeft() ? "edit-clear-locationbar-rtl" : "edit-clear-locationbar-ltr"));
3785     clearLocation->setText(i18n("Clear Location Bar"));
3786     actionCollection()->setDefaultShortcut(clearLocation, Qt::CTRL+Qt::Key_L);
3787     connect(clearLocation, SIGNAL(triggered()),
3788             SLOT(slotClearLocationBar()));
3789     clearLocation->setWhatsThis(i18n("<html>Clear Location bar<br /><br />"
3790                                      "Clears the contents of the location bar.</html>"));
3791 
3792     // Bookmarks menu
3793     m_pamBookmarks = new KBookmarkActionMenu(s_bookmarkManager->root(),
3794             i18n("&Bookmarks"), this);
3795     actionCollection()->addAction(QStringLiteral("bookmarks"), m_pamBookmarks);
3796 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 77, 0)
3797     m_pamBookmarks->setPopupMode(QToolButton::InstantPopup);
3798 #else
3799     m_pamBookmarks->setDelayed(false);
3800 #endif
3801 
3802     // The actual menu needs a different action collection, so that the bookmarks
3803     // don't appear in kedittoolbar
3804     m_bookmarksActionCollection = new KActionCollection(static_cast<QWidget *>(this));
3805 
3806     m_pBookmarkMenu = new Konqueror::KonqBookmarkMenu(s_bookmarkManager, m_pBookmarksOwner, m_pamBookmarks, m_bookmarksActionCollection);
3807 
3808     QAction *addBookmark = m_bookmarksActionCollection->action(QStringLiteral("add_bookmark"));
3809     if (addBookmark) {
3810         // Keep the "Add bookmark" action visible though (#153835)
3811         // -> We should think of a way to mark actions as "not configurable in toolbars" and
3812         // "should not appear in shortcut dialog (!= isShortcutConfigurable)" instead, and use
3813         // a single actionCollection.
3814         actionCollection()->addAction(QStringLiteral("add_bookmark"), m_bookmarksActionCollection->takeAction(addBookmark));
3815     } else {
3816         qCDebug(KONQUEROR_LOG) << "Action add_bookmark not found!";
3817     }
3818 
3819     m_paShowMenuBar = KStandardAction::showMenubar(this, SLOT(slotShowMenuBar()), this);
3820     actionCollection()->addAction(KStandardAction::name(KStandardAction::ShowMenubar), m_paShowMenuBar);
3821 
3822     m_paShowStatusBar = KStandardAction::showStatusbar(this, SLOT(slotShowStatusBar()), this);
3823     actionCollection()->addAction(KStandardAction::name(KStandardAction::ShowStatusbar), m_paShowStatusBar);
3824 
3825     action = actionCollection()->addAction(QStringLiteral("konqintro"));
3826     action->setText(i18n("Kon&queror Introduction"));
3827     connect(action, &QAction::triggered, this, &KonqMainWindow::slotIntro);
3828 
3829     QAction *goUrl = actionCollection()->addAction(QStringLiteral("go_url"));
3830     goUrl->setIcon(QIcon::fromTheme(QStringLiteral("go-jump-locationbar")));
3831     goUrl->setText(i18n("Go"));
3832     connect(goUrl, &QAction::triggered, this, &KonqMainWindow::goURL);
3833     goUrl->setWhatsThis(i18n("<html>Go<br /><br />"
3834                              "Goes to the page that has been entered into the location bar.</html>"));
3835 
3836     enableAllActions(false);
3837 
3838     // help stuff
3839     m_paUp->setWhatsThis(i18n("<html>Enter the parent folder<br /><br />"
3840                               "For instance, if the current location is file:/home/%1 clicking this "
3841                               "button will take you to file:/home.</html>",  KUser().loginName()));
3842     m_paUp->setStatusTip(i18n("Enter the parent folder"));
3843 
3844     m_paBack->setWhatsThis(i18n("Move backwards one step in the browsing history"));
3845     m_paBack->setStatusTip(i18n("Move backwards one step in the browsing history"));
3846 
3847     m_paForward->setWhatsThis(i18n("Move forward one step in the browsing history"));
3848     m_paForward->setStatusTip(i18n("Move forward one step in the browsing history"));
3849 
3850     m_paClosedItems->setWhatsThis(i18n("Move backwards one step in the closed tabs history"));
3851     m_paClosedItems->setStatusTip(i18n("Move backwards one step in the closed tabs history"));
3852 
3853     m_paReload->setWhatsThis(i18n("<html>Reload the currently displayed document<br /><br />"
3854                                   "This may, for example, be needed to refresh web pages that have been "
3855                                   "modified since they were loaded, in order to make the changes visible.</html>"));
3856     m_paReload->setStatusTip(i18n("Reload the currently displayed document"));
3857 
3858     m_paReloadAllTabs->setWhatsThis(i18n("<html>Reload all currently displayed documents in tabs<br /><br />"
3859                                          "This may, for example, be needed to refresh web pages that have been "
3860                                          "modified since they were loaded, in order to make the changes visible.</html>"));
3861     m_paReloadAllTabs->setStatusTip(i18n("Reload all currently displayed document in tabs"));
3862 
3863     m_paStop->setWhatsThis(i18n("<html>Stop loading the document<br /><br />"
3864                                 "All network transfers will be stopped and Konqueror will display the content "
3865                                 "that has been received so far.</html>"));
3866 
3867     m_paForceReload->setWhatsThis(i18n("<html>Reload the currently displayed document<br /><br />"
3868                                        "This may, for example, be needed to refresh web pages that have been "
3869                                        "modified since they were loaded, in order to make the changes visible. Any images on the page are downloaded again, even if cached copies exist.</html>"));
3870 
3871     m_paForceReload->setStatusTip(i18n("Force a reload of the currently displayed document and any contained images"));
3872 
3873     m_paStop->setStatusTip(i18n("Stop loading the document"));
3874 
3875     m_paCut->setWhatsThis(i18n("<html>Cut the currently selected text or item(s) and move it "
3876                                "to the system clipboard<br /><br />"
3877                                "This makes it available to the <b>Paste</b> command in Konqueror "
3878                                "and other KDE applications.</html>"));
3879     m_paCut->setStatusTip(i18n("Move the selected text or item(s) to the clipboard"));
3880 
3881     m_paCopy->setWhatsThis(i18n("<html>Copy the currently selected text or item(s) to the "
3882                                 "system clipboard<br /><br />"
3883                                 "This makes it available to the <b>Paste</b> command in Konqueror "
3884                                 "and other KDE applications.</html>"));
3885     m_paCopy->setStatusTip(i18n("Copy the selected text or item(s) to the clipboard"));
3886 
3887     m_paPaste->setWhatsThis(i18n("<html>Paste the previously cut or copied clipboard "
3888                                  "contents<br /><br />"
3889                                  "This also works for text copied or cut from other KDE applications.</html>"));
3890     m_paPaste->setStatusTip(i18n("Paste the clipboard contents"));
3891 
3892     m_paPrint->setWhatsThis(i18n("<html>Print the currently displayed document<br /><br />"
3893                                  "You will be presented with a dialog where you can set various "
3894                                  "options, such as the number of copies to print and which printer "
3895                                  "to use.<br /><br />"
3896                                  "This dialog also provides access to special KDE printing "
3897                                  "services such as creating a PDF file from the current document.</html>"));
3898     m_paPrint->setStatusTip(i18n("Print the current document"));
3899 
3900     m_paLockView->setStatusTip(i18n("A locked view cannot change folders. Use in combination with 'link view' to explore many files from one folder"));
3901     m_paLinkView->setStatusTip(i18n("Sets the view as 'linked'. A linked view follows folder changes made in other linked views."));
3902 
3903     m_paShowDeveloperTools = actionCollection()->addAction("inspect_page", this, &KonqMainWindow::inspectCurrentPage);
3904     actionCollection()->setDefaultShortcut(m_paShowDeveloperTools, QKeySequence("Ctrl+Shift+I"));
3905     m_paShowDeveloperTools->setText(i18n("&Inspect Current Page"));
3906     m_paShowDeveloperTools->setWhatsThis(i18n("<html>Shows the developer tools for the current page<br/><br/>The current view is split in two and the developer tools are displayed in the second half"));
3907     m_paShowDeveloperTools->setStatusTip(i18n("Shows the developer tools for the current page"));
3908 }
3909 
openBookmark(const KBookmark & bm,Qt::MouseButtons mb,Qt::KeyboardModifiers km)3910 void KonqExtendedBookmarkOwner::openBookmark(const KBookmark &bm, Qt::MouseButtons mb, Qt::KeyboardModifiers km)
3911 {
3912     qCDebug(KONQUEROR_LOG) << bm.url() << km << mb;
3913 
3914     const QString url = bm.url().url();
3915 
3916     KonqOpenURLRequest req;
3917     req.browserArgs.setNewTab(true);
3918     req.newTabInFront = KonqSettings::newTabsInFront();
3919     req.forceAutoEmbed = true;
3920 
3921     if (km & Qt::ShiftModifier) {
3922         req.newTabInFront = !req.newTabInFront;
3923     }
3924 
3925     if (km & Qt::ControlModifier) {  // Ctrl Left/MMB
3926         m_pKonqMainWindow->openFilteredUrl(url, req);
3927     } else if (mb & Qt::MiddleButton) {
3928         if (KonqSettings::mmbOpensTab()) {
3929             m_pKonqMainWindow->openFilteredUrl(url, req);
3930         } else {
3931             const QUrl finalURL = KonqMisc::konqFilteredURL(m_pKonqMainWindow, url);
3932             KonqMainWindow *mw = KonqMainWindowFactory::createNewWindow(finalURL);
3933             mw->show();
3934         }
3935     } else {
3936         m_pKonqMainWindow->openFilteredUrl(url, false);
3937     }
3938 }
3939 
slotMoveTabLeft()3940 void KonqMainWindow::slotMoveTabLeft()
3941 {
3942     if (QApplication::isRightToLeft()) {
3943         m_pViewManager->moveTabForward();
3944     } else {
3945         m_pViewManager->moveTabBackward();
3946     }
3947 
3948     updateViewActions();
3949 }
3950 
slotMoveTabRight()3951 void KonqMainWindow::slotMoveTabRight()
3952 {
3953     if (QApplication::isRightToLeft()) {
3954         m_pViewManager->moveTabBackward();
3955     } else {
3956         m_pViewManager->moveTabForward();
3957     }
3958 
3959     updateViewActions();
3960 }
3961 
updateHistoryActions()3962 void KonqMainWindow::updateHistoryActions()
3963 {
3964     if (m_currentView) {
3965         m_paBack->setEnabled(m_currentView->canGoBack());
3966         m_paForward->setEnabled(m_currentView->canGoForward());
3967     }
3968 }
3969 
isPreloaded() const3970 bool KonqMainWindow::isPreloaded() const
3971 {
3972     return !isVisible() && m_mapViews.count() == 1 && KonqUrl::isKonqBlank(m_currentView->url().toString());
3973 }
3974 
updateToolBarActions(bool pendingAction)3975 void KonqMainWindow::updateToolBarActions(bool pendingAction /*=false*/)
3976 {
3977     if (!m_currentView) {
3978         return;
3979     }
3980 
3981     // Enables/disables actions that depend on the current view & url (mostly toolbar)
3982     // Up, back, forward, the edit extension, stop button, wheel
3983     setUpEnabled(m_currentView->url());
3984     m_paBack->setEnabled(m_currentView->canGoBack());
3985     m_paForward->setEnabled(m_currentView->canGoForward());
3986 
3987     if (m_currentView->isLoading()) {
3988         startAnimation(); // takes care of m_paStop
3989     } else {
3990         m_paAnimatedLogo->stop();
3991         m_paStop->setEnabled(pendingAction);    //enable/disable based on any pending actions...
3992     }
3993 }
3994 
updateViewActions()3995 void KonqMainWindow::updateViewActions()
3996 {
3997     // Update actions that depend on the current view and its mode, or on the number of views etc.
3998 
3999     // Don't do things in this method that depend on m_currentView->url().
4000     // When going 'back' in history this will be called before opening the url.
4001     // Use updateToolBarActions instead.
4002 
4003     bool enable = false;
4004 
4005     if (m_currentView && m_currentView->part()) {
4006         // Avoid qWarning from QObject::property if it doesn't exist
4007         if (m_currentView->part()->metaObject()->indexOfProperty("supportsUndo") != -1) {
4008             QVariant prop = m_currentView->part()->property("supportsUndo");
4009             if (prop.isValid() && prop.toBool()) {
4010                 enable = true;
4011             }
4012         }
4013     }
4014 
4015     m_pUndoManager->updateSupportsFileUndo(enable);
4016 
4017 //   slotUndoAvailable( m_pUndoManager->undoAvailable() );
4018 
4019     m_paLockView->setEnabled(true);
4020     m_paLockView->setChecked(m_currentView && m_currentView->isLockedLocation());
4021 
4022     // Can remove view if we'll still have a main view after that
4023     m_paRemoveView->setEnabled(mainViewsCount() > 1 ||
4024                                (m_currentView && m_currentView->isToggleView()));
4025 
4026     if (!currentView() || !currentView()->frame()) {
4027         m_paAddTab->setEnabled(false);
4028         m_paDuplicateTab->setEnabled(false);
4029         m_paRemoveOtherTabs->setEnabled(false);
4030         m_paBreakOffTab->setEnabled(false);
4031         m_paActivateNextTab->setEnabled(false);
4032         m_paActivatePrevTab->setEnabled(false);
4033         m_paMoveTabLeft->setEnabled(false);
4034         m_paMoveTabRight->setEnabled(false);
4035     } else {
4036         m_paAddTab->setEnabled(true);
4037         m_paDuplicateTab->setEnabled(true);
4038         KonqFrameTabs *tabContainer = m_pViewManager->tabContainer();
4039         bool state = (tabContainer->count() > 1);
4040         m_paRemoveOtherTabs->setEnabled(state);
4041         m_paBreakOffTab->setEnabled(state);
4042         m_paActivateNextTab->setEnabled(state);
4043         m_paActivatePrevTab->setEnabled(state);
4044 
4045         QList<KonqFrameBase *> childFrameList = tabContainer->childFrameList();
4046         Q_ASSERT(!childFrameList.isEmpty());
4047         m_paMoveTabLeft->setEnabled(currentView() ? currentView()->frame() !=
4048                                     (QApplication::isRightToLeft() ? childFrameList.last() : childFrameList.first()) : false);
4049         m_paMoveTabRight->setEnabled(currentView() ? currentView()->frame() !=
4050                                      (QApplication::isRightToLeft() ? childFrameList.first() : childFrameList.last()) : false);
4051     }
4052 
4053     // Can split a view if it's not a toggle view (because a toggle view can be here only once)
4054     bool isNotToggle = m_currentView && !m_currentView->isToggleView();
4055     m_paSplitViewHor->setEnabled(isNotToggle);
4056     m_paSplitViewVer->setEnabled(isNotToggle);
4057 
4058     m_paLinkView->setChecked(m_currentView && m_currentView->isLinkedView());
4059 
4060 #if 0
4061     if (m_currentView && m_currentView->part() &&
4062             ::qobject_cast<KonqDirPart *>(m_currentView->part())) {
4063         KonqDirPart *dirPart = static_cast<KonqDirPart *>(m_currentView->part());
4064         m_paFindFiles->setEnabled(dirPart->findPart() == 0);
4065 
4066         // Create the copy/move options if not already done
4067         // TODO: move that stuff to dolphin(part)
4068         if (!m_paCopyFiles) {
4069             // F5 is the default key binding for Reload.... a la Windows.
4070             // mc users want F5 for Copy and F6 for move, but I can't make that default.
4071 
4072             m_paCopyFiles = actionCollection()->addAction("copyfiles");
4073             m_paCopyFiles->setText(i18n("Copy &Files..."));
4074             connect(m_paCopyFiles, &QAction::triggered, this, &KonqMainWindow::slotCopyFiles);
4075             m_paCopyFiles->setShortcut(Qt::Key_F7);
4076             m_paMoveFiles = actionCollection()->addAction("movefiles");
4077             m_paMoveFiles->setText(i18n("M&ove Files..."));
4078             connect(m_paMoveFiles, &QAction::triggered, this, &KonqMainWindow::slotMoveFiles);
4079             m_paMoveFiles->setShortcut(Qt::Key_F8);
4080 
4081             QList<QAction *> lst;
4082             lst.append(m_paCopyFiles);
4083             lst.append(m_paMoveFiles);
4084             m_paCopyFiles->setEnabled(false);
4085             m_paMoveFiles->setEnabled(false);
4086             plugActionList("operations", lst);
4087         }
4088     } else {
4089         m_paFindFiles->setEnabled(false);
4090 
4091         if (m_paCopyFiles) {
4092             unplugActionList("operations");
4093             delete m_paCopyFiles;
4094             m_paCopyFiles = 0;
4095             delete m_paMoveFiles;
4096             m_paMoveFiles = 0;
4097         }
4098     }
4099 #endif
4100 }
4101 
findIndexFile(const QString & dir)4102 QString KonqMainWindow::findIndexFile(const QString &dir)
4103 {
4104     QDir d(dir);
4105 
4106     QString f = d.filePath(QStringLiteral("index.html"));
4107     if (QFile::exists(f)) {
4108         return f;
4109     }
4110 
4111     f = d.filePath(QStringLiteral("index.htm"));
4112     if (QFile::exists(f)) {
4113         return f;
4114     }
4115 
4116     f = d.filePath(QStringLiteral("index.HTML"));
4117     if (QFile::exists(f)) {
4118         return f;
4119     }
4120 
4121     return QString();
4122 }
4123 
connectExtension(KParts::BrowserExtension * ext)4124 void KonqMainWindow::connectExtension(KParts::BrowserExtension *ext)
4125 {
4126     KParts::BrowserExtension::ActionSlotMap *actionSlotMap = KParts::BrowserExtension::actionSlotMapPtr();
4127     KParts::BrowserExtension::ActionSlotMap::ConstIterator it = actionSlotMap->constBegin();
4128     KParts::BrowserExtension::ActionSlotMap::ConstIterator itEnd = actionSlotMap->constEnd();
4129 
4130     for (; it != itEnd; ++it) {
4131         QAction *act = actionCollection()->action(it.key().data());
4132         //qCDebug(KONQUEROR_LOG) << it.key();
4133         if (act) {
4134             // Does the extension have a slot with the name of this action ?
4135             if (ext->metaObject()->indexOfSlot(QByteArray(it.key() + "()").constData()) != -1) {
4136                 connect(act, SIGNAL(triggered()), ext, it.value() /* SLOT(slot name) */);
4137                 act->setEnabled(ext->isActionEnabled(it.key()));
4138                 const QString text = ext->actionText(it.key());
4139                 if (!text.isEmpty()) {
4140                     act->setText(text);
4141                 }
4142                 // TODO how to re-set the original action text, when switching to a part that didn't call setAction?
4143                 // Can't test with Paste...
4144             } else {
4145                 act->setEnabled(false);
4146             }
4147 
4148         } else {
4149             qCWarning(KONQUEROR_LOG) << "Error in BrowserExtension::actionSlotMap(), unknown action : " << it.key();
4150         }
4151     }
4152 
4153 }
4154 
disconnectExtension(KParts::BrowserExtension * ext)4155 void KonqMainWindow::disconnectExtension(KParts::BrowserExtension *ext)
4156 {
4157     KParts::BrowserExtension::ActionSlotMap *actionSlotMap = KParts::BrowserExtension::actionSlotMapPtr();
4158     KParts::BrowserExtension::ActionSlotMap::ConstIterator it = actionSlotMap->constBegin();
4159     KParts::BrowserExtension::ActionSlotMap::ConstIterator itEnd = actionSlotMap->constEnd();
4160 
4161     for (; it != itEnd; ++it) {
4162         QAction *act = actionCollection()->action(it.key().data());
4163         //qCDebug(KONQUEROR_LOG) << it.key();
4164         if (act && ext->metaObject()->indexOfSlot(QByteArray(it.key() + "()").constData()) != -1) {
4165             //qCDebug(KONQUEROR_LOG) << act << act->name();
4166             act->disconnect(ext);
4167         }
4168     }
4169 }
4170 
enableAction(const char * name,bool enabled)4171 void KonqMainWindow::enableAction(const char *name, bool enabled)
4172 {
4173     QAction *act = actionCollection()->action(name);
4174     if (!act) {
4175         qCWarning(KONQUEROR_LOG) << "Unknown action " << name << " - can't enable";
4176     } else {
4177         if (m_bLocationBarConnected && (
4178                     act == m_paCopy || act == m_paCut || act == m_paPaste))
4179             // Don't change action state while the location bar has focus.
4180         {
4181             return;
4182         }
4183         //qCDebug(KONQUEROR_LOG) << name << enabled;
4184         act->setEnabled(enabled);
4185     }
4186 
4187     // Update "copy files" and "move files" accordingly
4188     if (m_paCopyFiles && !strcmp(name, "copy")) {
4189         m_paCopyFiles->setEnabled(enabled);
4190     } else if (m_paMoveFiles && !strcmp(name, "cut")) {
4191         m_paMoveFiles->setEnabled(enabled);
4192     }
4193 }
4194 
setActionText(const char * name,const QString & text)4195 void KonqMainWindow::setActionText(const char *name, const QString &text)
4196 {
4197     QAction *act = actionCollection()->action(name);
4198     if (!act) {
4199         qCWarning(KONQUEROR_LOG) << "Unknown action " << name << "- can't enable";
4200     } else {
4201         //qCDebug(KONQUEROR_LOG) << name << " text=" << text;
4202         act->setText(text);
4203     }
4204 }
4205 
enableAllActions(bool enable)4206 void KonqMainWindow::enableAllActions(bool enable)
4207 {
4208     //qCDebug(KONQUEROR_LOG) << enable;
4209     KParts::BrowserExtension::ActionSlotMap *actionSlotMap = KParts::BrowserExtension::actionSlotMapPtr();
4210 
4211     const QList<QAction *> actions = actionCollection()->actions();
4212     QList<QAction *>::ConstIterator it = actions.constBegin();
4213     QList<QAction *>::ConstIterator end = actions.constEnd();
4214     for (; it != end; ++it) {
4215         QAction *act = *it;
4216         if (!act->objectName().startsWith(QLatin1String("options_configure"))  /* do not touch the configureblah actions */
4217                 && (!enable || !actionSlotMap->contains(act->objectName().toLatin1()))) {    /* don't enable BE actions */
4218             act->setEnabled(enable);
4219         }
4220     }
4221     // This method is called with enable=false on startup, and
4222     // then only once with enable=true when the first view is setup.
4223     // So the code below is where actions that should initially be disabled are disabled.
4224     if (enable) {
4225         setUpEnabled(m_currentView ? m_currentView->url() : QUrl());
4226         // we surely don't have any history buffers at this time
4227         m_paBack->setEnabled(false);
4228         m_paForward->setEnabled(false);
4229 
4230         updateViewActions(); // undo, lock, link and other view-dependent actions
4231         updateClosedItemsAction();
4232 
4233         m_paStop->setEnabled(m_currentView && m_currentView->isLoading());
4234 
4235         if (m_toggleViewGUIClient) {
4236             QList<QAction *> actions = m_toggleViewGUIClient->actions();
4237             for (int i = 0; i < actions.size(); ++i) {
4238                 actions.at(i)->setEnabled(true);
4239             }
4240         }
4241 
4242     }
4243     actionCollection()->action(QStringLiteral("quit"))->setEnabled(true);
4244     actionCollection()->action(QStringLiteral("link"))->setEnabled(false);
4245 }
4246 
disableActionsNoView()4247 void KonqMainWindow::disableActionsNoView()
4248 {
4249     // No view -> there are some things we can't do
4250     m_paUp->setEnabled(false);
4251     m_paReload->setEnabled(false);
4252     m_paReloadAllTabs->setEnabled(false);
4253     m_paBack->setEnabled(false);
4254     m_paForward->setEnabled(false);
4255     m_paLockView->setEnabled(false);
4256     m_paLockView->setChecked(false);
4257     m_paSplitViewVer->setEnabled(false);
4258     m_paSplitViewHor->setEnabled(false);
4259     m_paRemoveView->setEnabled(false);
4260     m_paLinkView->setEnabled(false);
4261     if (m_toggleViewGUIClient) {
4262         QList<QAction *> actions = m_toggleViewGUIClient->actions();
4263         for (int i = 0; i < actions.size(); ++i) {
4264             actions.at(i)->setEnabled(false);
4265         }
4266     }
4267     // There are things we can do, though : bookmarks, view profile, location bar, new window,
4268     // settings, etc.
4269     static const char *const s_enActions[] = { "new_window", "duplicate_window", "open_location",
4270                                                "toolbar_url_combo", "clear_location", "animated_logo",
4271                                                "konqintro", "go_most_often", "go_applications",
4272                                                "go_trash", "go_settings", "go_network_folders", "go_autostart",
4273                                                "go_url", "go_media", "go_history", "options_configure_extensions", nullptr
4274                                              };
4275     for (int i = 0; s_enActions[i]; ++i) {
4276         QAction *act = action(s_enActions[i]);
4277         if (act) {
4278             act->setEnabled(true);
4279         }
4280     }
4281     m_combo->clearTemporary();
4282 }
4283 
setCaption(const QString & caption)4284 void KonqMainWindow::setCaption(const QString &caption)
4285 {
4286     // KParts sends us empty captions when activating a brand new part
4287     // We can't change it there (in case of apps removing all parts altogether)
4288     // but here we never do that.
4289     if (!caption.isEmpty() && m_currentView) {
4290         //qCDebug(KONQUEROR_LOG) << caption;
4291 
4292         // Keep an unmodified copy of the caption (before squeezing and KComponentData::makeStdCaption are applied)
4293         m_currentView->setCaption(caption);
4294         KParts::MainWindow::setCaption(KStringHandler::csqueeze(m_currentView->caption(), 128));
4295     }
4296 }
4297 
showEvent(QShowEvent * event)4298 void KonqMainWindow::showEvent(QShowEvent *event)
4299 {
4300     //qCDebug(KONQUEROR_LOG) << QTime::currentTime();
4301     // We need to check if our toolbars are shown/hidden here, and set
4302     // our menu items accordingly. We can't do it in the constructor because
4303     // view profiles store toolbar info, and that info is read after
4304     // construct time.
4305     m_paShowMenuBar->setChecked(!menuBar()->isHidden());
4306     if (m_currentView) {
4307         m_paShowStatusBar->setChecked(m_currentView->frame()->statusbar()->isVisible());
4308     }
4309     updateBookmarkBar(); // hide if empty
4310 
4311     // Call parent method
4312     KParts::MainWindow::showEvent(event);
4313 }
4314 
currentUrl() const4315 QUrl KonqExtendedBookmarkOwner::currentUrl() const
4316 {
4317     const KonqView *view = m_pKonqMainWindow->currentView();
4318     return view ? view->url() : QUrl();
4319 }
4320 
currentURL() const4321 QString KonqMainWindow::currentURL() const
4322 {
4323     if (!m_currentView) {
4324         return QString();
4325     }
4326     QString url = m_currentView->url().toDisplayString();
4327 
4328 #if 0 // do we want this?
4329     // Add the name filter (*.txt) at the end of the URL again
4330     if (m_currentView->part()) {
4331         const QString nameFilter = m_currentView->nameFilter();
4332         if (!nameFilter.isEmpty()) {
4333             if (!url.endsWith('/')) {
4334                 url += '/';
4335             }
4336             url += nameFilter;
4337         }
4338     }
4339 #endif
4340     return url;
4341 }
4342 
supportsTabs() const4343 bool KonqExtendedBookmarkOwner::supportsTabs() const
4344 {
4345     return true;
4346 }
4347 
currentBookmarkList() const4348 QList<KBookmarkOwner::FutureBookmark> KonqExtendedBookmarkOwner::currentBookmarkList() const
4349 {
4350     QList<KBookmarkOwner::FutureBookmark> list;
4351     KonqFrameTabs *tabContainer = m_pKonqMainWindow->viewManager()->tabContainer();
4352 
4353     foreach (KonqFrameBase *frame, tabContainer->childFrameList()) {
4354         if (!frame || !frame->activeChildView()) {
4355             continue;
4356         }
4357         KonqView *view = frame->activeChildView();
4358         if (view->locationBarURL().isEmpty()) {
4359             continue;
4360         }
4361         list << KBookmarkOwner::FutureBookmark(view->caption(), view->url(), KIO::iconNameForUrl(view->url()));
4362     }
4363     return list;
4364 }
4365 
currentTitle() const4366 QString KonqExtendedBookmarkOwner::currentTitle() const
4367 {
4368     return m_pKonqMainWindow->currentTitle();
4369 }
4370 
openInNewTab(const KBookmark & bm)4371 void KonqExtendedBookmarkOwner::openInNewTab(const KBookmark &bm)
4372 {
4373     bool newTabsInFront = KonqSettings::newTabsInFront();
4374     if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
4375         newTabsInFront = !newTabsInFront;
4376     }
4377 
4378     KonqOpenURLRequest req;
4379     req.browserArgs.setNewTab(true);
4380     req.newTabInFront = newTabsInFront;
4381     req.openAfterCurrentPage = false;
4382     req.forceAutoEmbed = true;
4383 
4384     m_pKonqMainWindow->openFilteredUrl(bm.url().url(), req);
4385 }
4386 
openFolderinTabs(const KBookmarkGroup & grp)4387 void KonqExtendedBookmarkOwner::openFolderinTabs(const KBookmarkGroup &grp)
4388 {
4389     bool newTabsInFront = KonqSettings::newTabsInFront();
4390     if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
4391         newTabsInFront = !newTabsInFront;
4392     }
4393     KonqOpenURLRequest req;
4394     req.browserArgs.setNewTab(true);
4395     req.newTabInFront = false;
4396     req.openAfterCurrentPage = false;
4397     req.forceAutoEmbed = true;
4398 
4399     const QList<QUrl> list = grp.groupUrlList();
4400     if (list.isEmpty()) {
4401         return;
4402     }
4403 
4404     if (list.size() > 20) {
4405         if (KMessageBox::questionYesNo(m_pKonqMainWindow,
4406                                        i18n("You have requested to open more than 20 bookmarks in tabs. "
4407                                             "This might take a while. Continue?"),
4408                                        i18nc("@title:window", "Open bookmarks folder in new tabs")) != KMessageBox::Yes) {
4409             return;
4410         }
4411     }
4412 
4413     QList<QUrl>::ConstIterator it = list.constBegin();
4414     QList<QUrl>::ConstIterator end = list.constEnd();
4415     --end;
4416     for (; it != end; ++it) {
4417         m_pKonqMainWindow->openFilteredUrl((*it).toString(), req);
4418     }
4419     if (newTabsInFront) {
4420         req.newTabInFront = true;
4421     }
4422     m_pKonqMainWindow->openFilteredUrl((*end).toString(), req);
4423 }
4424 
openInNewWindow(const KBookmark & bm)4425 void KonqExtendedBookmarkOwner::openInNewWindow(const KBookmark &bm)
4426 {
4427     const QUrl finalURL(KonqMisc::konqFilteredURL(m_pKonqMainWindow, bm.url().url()));
4428     KonqMainWindow *mw = KonqMainWindowFactory::createNewWindow(finalURL);
4429     mw->show();
4430 }
4431 
currentTitle() const4432 QString KonqMainWindow::currentTitle() const
4433 {
4434     return m_currentView ? m_currentView->caption() : QString();
4435 }
4436 
4437 // Convert between deprecated string-based KParts::BrowserExtension::ActionGroupMap
4438 // to newer enum-based KonqPopupMenu::ActionGroupMap
convertActionGroups(const KParts::BrowserExtension::ActionGroupMap & input)4439 static KonqPopupMenu::ActionGroupMap convertActionGroups(const KParts::BrowserExtension::ActionGroupMap &input)
4440 {
4441     KonqPopupMenu::ActionGroupMap agm;
4442     agm.insert(KonqPopupMenu::TopActions, input.value(QStringLiteral("topactions")));
4443     agm.insert(KonqPopupMenu::TabHandlingActions, input.value(QStringLiteral("tabhandling")));
4444     agm.insert(KonqPopupMenu::EditActions, input.value(QStringLiteral("editactions")));
4445     agm.insert(KonqPopupMenu::PreviewActions, input.value(QStringLiteral("preview")));
4446     agm.insert(KonqPopupMenu::CustomActions, input.value(QStringLiteral("partactions")));
4447     agm.insert(KonqPopupMenu::LinkActions, input.value(QStringLiteral("linkactions")));
4448     return agm;
4449 }
4450 
slotPopupMenu(const QPoint & global,const QUrl & url,mode_t mode,const KParts::OpenUrlArguments & args,const KParts::BrowserArguments & browserArgs,KParts::BrowserExtension::PopupFlags flags,const KParts::BrowserExtension::ActionGroupMap & actionGroups)4451 void KonqMainWindow::slotPopupMenu(const QPoint &global, const QUrl &url, mode_t mode, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs, KParts::BrowserExtension::PopupFlags flags, const KParts::BrowserExtension::ActionGroupMap &actionGroups)
4452 {
4453     KFileItem item(url, args.mimeType(), mode);
4454     KFileItemList items;
4455     items.append(item);
4456     slotPopupMenu(global, items, args, browserArgs, flags, actionGroups);
4457 }
4458 
slotPopupMenu(const QPoint & global,const KFileItemList & items,const KParts::OpenUrlArguments & args,const KParts::BrowserArguments & browserArgs,KParts::BrowserExtension::PopupFlags itemFlags,const KParts::BrowserExtension::ActionGroupMap & actionGroups)4459 void KonqMainWindow::slotPopupMenu(const QPoint &global, const KFileItemList &items, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs, KParts::BrowserExtension::PopupFlags itemFlags, const KParts::BrowserExtension::ActionGroupMap &actionGroups)
4460 {
4461     KonqView *m_oldView = m_currentView;
4462     KonqView *currentView = childView(static_cast<KParts::ReadOnlyPart *>(sender()->parent()));
4463 
4464     //qCDebug(KONQUEROR_LOG) << "m_oldView=" << m_oldView << "new currentView=" << currentView << "passive:" << currentView->isPassiveMode();
4465 
4466     if ((m_oldView != currentView) && currentView->isPassiveMode()) {
4467         // Make this view active only temporarily (because it's passive)
4468         m_currentView = currentView;
4469 
4470         if (m_oldView && m_oldView->browserExtension()) {
4471             disconnectExtension(m_oldView->browserExtension());
4472         }
4473         if (m_currentView->browserExtension()) {
4474             connectExtension(m_currentView->browserExtension());
4475         }
4476     }
4477     // Note that if m_oldView!=currentView and currentView isn't passive,
4478     // then the KParts mechanism has already noticed the click in it,
4479     // but KonqViewManager delays the GUI-rebuilding with a single-shot timer.
4480     // Right after the popup shows up, currentView _will_ be m_currentView.
4481 
4482     //qCDebug(KONQUEROR_LOG) << "current view=" << m_currentView << m_currentView->part()->metaObject()->className();
4483 
4484     // This action collection is used to pass actions to KonqPopupMenu.
4485     // It has to be a KActionCollection instead of a QList<QAction *> because we need
4486     // the actionStatusText signal...
4487     KActionCollection popupMenuCollection(static_cast<QWidget *>(nullptr));
4488 
4489     popupMenuCollection.addAction(QStringLiteral("closeditems"), m_paClosedItems);
4490 
4491 #if 0
4492     popupMenuCollection.addAction("find", m_paFindFiles);
4493 #endif
4494 
4495     popupMenuCollection.addAction(QStringLiteral("undo"), m_paUndo);
4496     popupMenuCollection.addAction(QStringLiteral("cut"), m_paCut);
4497     popupMenuCollection.addAction(QStringLiteral("copy"), m_paCopy);
4498     popupMenuCollection.addAction(QStringLiteral("paste"), m_paPaste);
4499 
4500     // The pasteto action is used when clicking on a dir, to paste into it.
4501     QAction *actPaste = KStandardAction::paste(this, SLOT(slotPopupPasteTo()), this);
4502     actPaste->setEnabled(m_paPaste->isEnabled());
4503     popupMenuCollection.addAction(QStringLiteral("pasteto"), actPaste);
4504 
4505     prepareForPopupMenu(items, args, browserArgs);
4506 
4507     bool sReading = false;
4508     if (!m_popupUrl.isEmpty()) {
4509         sReading = KProtocolManager::supportsReading(m_popupUrl);
4510     }
4511 
4512     QUrl viewURL = currentView->url();
4513     qCDebug(KONQUEROR_LOG) << "viewURL=" << viewURL;
4514 
4515     bool openedForViewURL = false;
4516     //bool dirsSelected = false;
4517     bool devicesFile = false;
4518 
4519     if (items.count() == 1) {
4520         const QUrl firstURL = items.first().url();
4521         if (!viewURL.isEmpty()) {
4522             //firstURL.cleanPath();
4523             openedForViewURL = firstURL.matches(viewURL, QUrl::StripTrailingSlash);
4524         }
4525         devicesFile = firstURL.scheme().indexOf(QLatin1String("device"), 0, Qt::CaseInsensitive) == 0;
4526         //dirsSelected = S_ISDIR( items.first()->mode() );
4527     }
4528     //qCDebug(KONQUEROR_LOG) << "viewURL=" << viewURL;
4529 
4530     QUrl url = viewURL;
4531     bool isIntoTrash = url.scheme() == QLatin1String("trash") || url.url().startsWith(QLatin1String("system:/trash"));
4532     const bool doTabHandling = !openedForViewURL && !isIntoTrash && sReading;
4533     const bool showEmbeddingServices = items.count() == 1 && !m_popupMimeType.isEmpty() &&
4534                                        !isIntoTrash && !devicesFile &&
4535                                        (itemFlags & KParts::BrowserExtension::ShowTextSelectionItems) == 0;
4536 
4537     KService::List embeddingServices;
4538     if (showEmbeddingServices) {
4539         const QString currentServiceName = currentView->service()->desktopEntryName();
4540 
4541         // List of services for the "Preview In" submenu.
4542         embeddingServices = KMimeTypeTrader::self()->query(
4543                                 m_popupMimeType,
4544                                 QStringLiteral("KParts/ReadOnlyPart"),
4545                                 // Obey "HideFromMenus". It defaults to false so we want "absent or true"
4546                                 // (wow, testing for 'true' if absent doesn't work, so order matters)
4547                                 "(not exist [X-KDE-BrowserView-HideFromMenus] or not [X-KDE-BrowserView-HideFromMenus]) "
4548                                 "and DesktopEntryName != '" +currentServiceName + "' "
4549                                 // I had an old local dirtree.desktop without lib, no need for invalid entries
4550                                 "and exist [Library]");
4551     }
4552 
4553     // TODO: get rid of KParts::BrowserExtension::PopupFlags
4554     KonqPopupMenu::Flags popupFlags = static_cast<KonqPopupMenu::Flags>(static_cast<int>(itemFlags));
4555 
4556     KonqPopupMenu::ActionGroupMap popupActionGroups = convertActionGroups(actionGroups);
4557 
4558     PopupMenuGUIClient *konqyMenuClient = new PopupMenuGUIClient(embeddingServices,
4559             popupActionGroups,
4560             !menuBar()->isVisible() ? m_paShowMenuBar : nullptr,
4561             fullScreenMode() ? m_ptaFullScreen : nullptr
4562                                                                 );
4563     qRegisterMetaType<KService::Ptr>("KService::Ptr");
4564     connect(konqyMenuClient, SIGNAL(openEmbedded(KService::Ptr)),
4565             this, SLOT(slotOpenEmbedded(KService::Ptr)), Qt::QueuedConnection);
4566 
4567     // Those actions go into the PopupMenuGUIClient, since that's the one defining them.
4568     QList<QAction *> tabHandlingActions;
4569     if (doTabHandling) {
4570         if (browserArgs.forcesNewWindow()) {
4571             QAction *act = konqyMenuClient->actionCollection()->addAction(QStringLiteral("sameview"));
4572             act->setText(i18n("Open in T&his Window"));
4573             act->setStatusTip(i18n("Open the document in current window"));
4574             connect(act, &QAction::triggered, this, &KonqMainWindow::slotPopupThisWindow);
4575             tabHandlingActions.append(act);
4576         }
4577         QAction *actNewWindow = konqyMenuClient->actionCollection()->addAction(QStringLiteral("newview"));
4578         actNewWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
4579         actNewWindow->setText(i18n("Open in New &Window"));
4580         actNewWindow->setStatusTip(i18n("Open the document in a new window"));
4581         connect(actNewWindow, &QAction::triggered, this, &KonqMainWindow::slotPopupNewWindow);
4582         tabHandlingActions.append(actNewWindow);
4583 
4584         QAction *actNewTab = konqyMenuClient->actionCollection()->addAction(QStringLiteral("openintab"));
4585         actNewTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
4586         actNewTab->setText(i18n("Open in &New Tab"));
4587         connect(actNewTab, &QAction::triggered, this, &KonqMainWindow::slotPopupNewTab);
4588         actNewTab->setStatusTip(i18n("Open the document in a new tab"));
4589         tabHandlingActions.append(actNewTab);
4590 
4591         QAction *separator = new QAction(konqyMenuClient->actionCollection());
4592         separator->setSeparator(true);
4593         tabHandlingActions.append(separator);
4594     }
4595 
4596     if (doTabHandling) {
4597         popupActionGroups.insert(KonqPopupMenu::TabHandlingActions, tabHandlingActions);
4598     }
4599 
4600     QPointer<KonqPopupMenu> pPopupMenu = new KonqPopupMenu(
4601         items,
4602         viewURL,
4603         popupMenuCollection,
4604         popupFlags,
4605         // This parent ensures that if the part destroys itself (e.g. KHTML redirection),
4606         // it will close the popupmenu
4607         currentView->part()->widget());
4608     pPopupMenu->setNewFileMenu(m_pMenuNew);
4609     pPopupMenu->setBookmarkManager(s_bookmarkManager);
4610     pPopupMenu->setActionGroups(popupActionGroups);
4611 
4612     if (openedForViewURL && !viewURL.isLocalFile()) {
4613         pPopupMenu->setURLTitle(currentView->caption());
4614     }
4615 
4616     QPointer<KParts::BrowserExtension> be = ::qobject_cast<KParts::BrowserExtension *>(sender());
4617 
4618     if (be) {
4619         QObject::connect(this, &KonqMainWindow::popupItemsDisturbed, pPopupMenu.data(), &KonqPopupMenu::close);
4620         QObject::connect(be, SIGNAL(itemsRemoved(KFileItemList)),
4621                          this, SLOT(slotItemsRemoved(KFileItemList)));
4622     }
4623 
4624     QPointer<QObject> guard(this);   // #149736, window could be deleted inside popupmenu event loop
4625     pPopupMenu->exec(global);
4626 
4627     delete pPopupMenu;
4628 
4629     // We're sort of misusing KActionCollection here, but we need it for the actionStatusText signal...
4630     // Anyway. If the action belonged to the view, and the view got deleted, we don't want ~KActionCollection
4631     // to iterate over those deleted actions
4632     /*KActionPtrList lst = popupMenuCollection.actions();
4633     KActionPtrList::iterator it = lst.begin();
4634     for ( ; it != lst.end() ; ++it )
4635         popupMenuCollection.take( *it );*/
4636 
4637     if (guard.isNull()) { // the placement of this test is very important, double-check #149736 if moving stuff around
4638         return;
4639     }
4640 
4641     if (be) {
4642         QObject::disconnect(be, SIGNAL(itemsRemoved(KFileItemList)),
4643                             this, SLOT(slotItemsRemoved(KFileItemList)));
4644     }
4645 
4646     delete konqyMenuClient;
4647     m_popupItems.clear();
4648 
4649     // Deleted by konqyMenuClient's actioncollection
4650     //delete actNewTab;
4651     //delete actNewWindow;
4652 
4653     delete actPaste;
4654 
4655     // Restore current view if current is passive
4656     if ((m_oldView != currentView) && (currentView == m_currentView) && currentView->isPassiveMode()) {
4657         //qCDebug(KONQUEROR_LOG) << "restoring active view" << m_oldView;
4658         if (m_currentView->browserExtension()) {
4659             disconnectExtension(m_currentView->browserExtension());
4660         }
4661         if (m_oldView) {
4662             if (m_oldView->browserExtension()) {
4663                 connectExtension(m_oldView->browserExtension());
4664                 m_currentView = m_oldView;
4665             }
4666             // Special case: RMB + renaming in sidebar; setFocus would abort editing.
4667             QWidget *fw = focusWidget();
4668             if (!fw || !::qobject_cast<QLineEdit *>(fw)) {
4669                 m_oldView->part()->widget()->setFocus();
4670                 m_pViewManager->setActivePart(m_oldView->part());
4671             }
4672         }
4673     }
4674 }
4675 
prepareForPopupMenu(const KFileItemList & items,const KParts::OpenUrlArguments & args,const KParts::BrowserArguments & browserArgs)4676 void KonqMainWindow::prepareForPopupMenu(const KFileItemList &items, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs)
4677 {
4678     if (!items.isEmpty()) {
4679         m_popupUrl = items.first().url();
4680         m_popupMimeType = items.first().mimetype();
4681     } else {
4682         m_popupUrl = QUrl();
4683         m_popupMimeType.clear();
4684     }
4685     // We will need these if we call the newTab slot
4686     m_popupItems = items;
4687     m_popupUrlArgs = args;
4688     m_popupUrlArgs.setMimeType(QString());   // Reset so that Open in New Window/Tab does mimetype detection
4689     m_popupUrlBrowserArgs = browserArgs;
4690 }
4691 
slotItemsRemoved(const KFileItemList & items)4692 void KonqMainWindow::slotItemsRemoved(const KFileItemList &items)
4693 {
4694     QListIterator<KFileItem> it(items);
4695     while (it.hasNext()) {
4696         if (m_popupItems.contains(it.next())) {
4697             emit popupItemsDisturbed();
4698             return;
4699         }
4700     }
4701 }
4702 
slotOpenEmbedded(KService::Ptr service)4703 void KonqMainWindow::slotOpenEmbedded(KService::Ptr service)
4704 {
4705     if (!m_currentView) {
4706         return;
4707     }
4708 
4709     m_currentView->stop();
4710     m_currentView->setLocationBarURL(m_popupUrl);
4711     m_currentView->setTypedURL(QString());
4712     if (m_currentView->changePart(m_popupMimeType,
4713                                   service->desktopEntryName(), true)) {
4714         m_currentView->openUrl(m_popupUrl, m_popupUrl.toDisplayString(QUrl::PreferLocalFile));
4715     }
4716 }
4717 
slotPopupPasteTo()4718 void KonqMainWindow::slotPopupPasteTo()
4719 {
4720     if (!m_currentView || m_popupUrl.isEmpty()) {
4721         return;
4722     }
4723     m_currentView->callExtensionURLMethod("pasteTo", m_popupUrl);
4724 }
4725 
slotReconfigure()4726 void KonqMainWindow::slotReconfigure()
4727 {
4728     reparseConfiguration();
4729 }
4730 
reparseConfiguration()4731 void KonqMainWindow::reparseConfiguration()
4732 {
4733     qCDebug(KONQUEROR_LOG);
4734 
4735     KonqSettings::self()->load();
4736     m_pViewManager->applyConfiguration();
4737     KonqMouseEventFilter::self()->reparseConfiguration();
4738 
4739     MapViews::ConstIterator it = m_mapViews.constBegin();
4740     MapViews::ConstIterator end = m_mapViews.constEnd();
4741     for (; it != end; ++it) {
4742         (*it)->reparseConfiguration();
4743     }
4744 }
4745 
saveProperties(KConfigGroup & config)4746 void KonqMainWindow::saveProperties(KConfigGroup &config)
4747 {
4748     // Ensure no crash if the sessionmanager timer fires before the ctor is done
4749     // This can happen via ToggleViewGUIClient -> KServiceTypeTrader::query
4750     // -> KSycoca running kbuildsycoca -> nested event loop.
4751     if (m_fullyConstructed) {
4752         KonqFrameBase::Options flags = KonqFrameBase::SaveHistoryItems;
4753         m_pViewManager->saveViewConfigToGroup(config, flags);
4754     }
4755 }
4756 
readProperties(const KConfigGroup & configGroup)4757 void KonqMainWindow::readProperties(const KConfigGroup &configGroup)
4758 {
4759     m_pViewManager->loadViewConfigFromGroup(configGroup, QString() /*no profile name*/);
4760     // read window settings
4761     applyMainWindowSettings(configGroup);
4762 }
4763 
applyMainWindowSettings(const KConfigGroup & config)4764 void KonqMainWindow::applyMainWindowSettings(const KConfigGroup &config)
4765 {
4766     KParts::MainWindow::applyMainWindowSettings(config);
4767     if (m_currentView) {
4768         /// @Note status bar isn't direct child to main window
4769         QString entry = config.readEntry("StatusBar", "Enabled");
4770         m_currentView->frame()->statusbar()->setVisible(entry != QLatin1String("Disabled"));
4771     }
4772 }
4773 
saveMainWindowSettings(KConfigGroup & config)4774 void KonqMainWindow::saveMainWindowSettings(KConfigGroup &config)
4775 {
4776     KParts::MainWindow::saveMainWindowSettings(config);
4777     if (m_currentView) {
4778         /// @Note status bar isn't direct child to main window
4779         config.writeEntry("StatusBar", m_currentView->frame()->statusbar()->isHidden() ? "Disabled" : "Enabled");
4780         config.sync();
4781     }
4782 }
4783 
setInitialFrameName(const QString & name)4784 void KonqMainWindow::setInitialFrameName(const QString &name)
4785 {
4786     m_initialFrameName = name;
4787 }
4788 
updateOpenWithActions()4789 void KonqMainWindow::updateOpenWithActions()
4790 {
4791     unplugActionList(QStringLiteral("openwithbase"));
4792     unplugActionList(QStringLiteral("openwith"));
4793 
4794     qDeleteAll(m_openWithActions);
4795     m_openWithActions.clear();
4796 
4797     delete m_openWithMenu;
4798     m_openWithMenu = nullptr;
4799 
4800     if (!KAuthorized::authorizeAction(QStringLiteral("openwith"))) {
4801         return;
4802     }
4803 
4804     m_openWithMenu = new KActionMenu(i18n("&Open With"), this);
4805 
4806     const KService::List &services = m_currentView->appServiceOffers();
4807     KService::List::ConstIterator it = services.constBegin();
4808     const KService::List::ConstIterator end = services.constEnd();
4809 
4810     const int baseOpenWithItems = qMax(KonqSettings::openWithItems(), 0);
4811 
4812     int idxService = 0;
4813     for (; it != end; ++it, ++idxService) {
4814         QAction *action;
4815 
4816         if (idxService < baseOpenWithItems) {
4817             action = new QAction(i18n("Open with %1", (*it)->name()), this);
4818         } else {
4819             action = new QAction((*it)->name(), this);
4820         }
4821         action->setIcon(QIcon::fromTheme((*it)->icon()));
4822 
4823         connect(action, SIGNAL(triggered()),
4824                 this, SLOT(slotOpenWith()));
4825 
4826         actionCollection()->addAction((*it)->desktopEntryName(), action);
4827         if (idxService < baseOpenWithItems) {
4828             m_openWithActions.append(action);
4829         } else {
4830             m_openWithMenu->addAction(action);
4831         }
4832     }
4833 
4834     if (services.count() > 0) {
4835         plugActionList(QStringLiteral("openwithbase"), m_openWithActions);
4836         QList<QAction *> openWithActionsMenu;
4837         if (idxService > baseOpenWithItems) {
4838             openWithActionsMenu.append(m_openWithMenu);
4839         }
4840         QAction *sep = new QAction(this);
4841         sep->setSeparator(true);
4842         openWithActionsMenu.append(sep);
4843         plugActionList(QStringLiteral("openwith"), openWithActionsMenu);
4844     }
4845 }
4846 
updateViewModeActions()4847 void KonqMainWindow::updateViewModeActions()
4848 {
4849     unplugViewModeActions();
4850     Q_FOREACH (QAction *action, m_viewModesGroup->actions()) {
4851         Q_FOREACH (QWidget *w, action->associatedWidgets()) {
4852             w->removeAction(action);
4853         }
4854         delete action;
4855     }
4856 
4857     delete m_viewModeMenu;
4858     m_viewModeMenu = nullptr;
4859 
4860     const KService::List services = m_currentView->partServiceOffers();
4861     if (services.count() <= 1) {
4862         return;
4863     }
4864 
4865     m_viewModeMenu = new KActionMenu(i18nc("@action:inmenu View", "&View Mode"), this);
4866     //actionCollection()->addAction( "viewModeMenu", m_viewModeMenu );
4867 
4868     KService::List::ConstIterator it = services.constBegin();
4869     const KService::List::ConstIterator end = services.constEnd();
4870     for (; it != end; ++it) {
4871         const KService::Ptr service = *it;
4872 
4873         const QString desktopEntryName = service->desktopEntryName();
4874         bool bIsCurrentView = desktopEntryName == m_currentView->service()->desktopEntryName();
4875 
4876         const QList<KServiceAction> actions = service->actions();
4877         if (!actions.isEmpty()) {
4878 
4879             // The service provides several view modes, like DolphinPart
4880             // -> create one action per view mode
4881             Q_FOREACH (const KServiceAction &serviceAction, actions) {
4882                 // Create a KToggleAction for each view mode, and plug it into the menu
4883                 KToggleAction *action = new KToggleAction(QIcon::fromTheme(serviceAction.icon()), serviceAction.text(), this);
4884                 //actionCollection()->addAction(desktopEntryName /*not unique!*/, action);
4885                 action->setObjectName(desktopEntryName + QLatin1String("-viewmode"));
4886                 action->setData(QVariant(serviceAction.name()));
4887                 action->setActionGroup(m_viewModesGroup);
4888                 m_viewModeMenu->menu()->addAction(action);
4889                 if (bIsCurrentView && m_currentView->internalViewMode() == serviceAction.name()) {
4890                     action->setChecked(true);
4891                 }
4892             }
4893 
4894         } else {
4895             // The service only provides one view mode (common case)
4896 
4897             QString serviceText = service->genericName();
4898             if (serviceText.isEmpty()) {
4899                 serviceText = service->name();
4900             }
4901 
4902             // Create a KToggleAction for this view mode, and plug it into the menu
4903             KToggleAction *action = new KToggleAction(QIcon::fromTheme(service->icon()), serviceText, this);
4904             // NOTE: "-viewmode" is appended to desktopEntryName to avoid overwritting existing
4905             // action, e.g. konsolepart added through ToggleViewGUIClient in the ctor will be
4906             // overwritten by the view mode konsolepart action added here.  #266517.
4907             actionCollection()->addAction(desktopEntryName + QLatin1String("-viewmode"), action);
4908             action->setActionGroup(m_viewModesGroup);
4909             m_viewModeMenu->menu()->addAction(action);
4910 
4911             action->setChecked(bIsCurrentView);
4912         }
4913     }
4914 
4915     // No view mode for actions toggable views
4916     // (The other way would be to enforce a better servicetype for them, than Browser/View)
4917     if (!m_currentView->isToggleView()
4918             /* already tested: && services.count() > 1 */
4919             && m_viewModeMenu) {
4920         plugViewModeActions();
4921     }
4922 }
4923 
slotInternalViewModeChanged()4924 void KonqMainWindow::slotInternalViewModeChanged()
4925 {
4926     KParts::ReadOnlyPart *part = static_cast<KParts::ReadOnlyPart *>(sender());
4927     KonqView *view = m_mapViews.value(part);
4928     if (view) {
4929         const QString actionName = view->service()->desktopEntryName();
4930         const QString actionData = view->internalViewMode();
4931         Q_FOREACH (QAction *action, m_viewModesGroup->actions()) {
4932             if (action->objectName() == actionName + QLatin1String("-viewmode") &&
4933                     action->data().toString() == actionData) {
4934                 action->setChecked(true);
4935                 break;
4936             }
4937         }
4938     }
4939 }
4940 
plugViewModeActions()4941 void KonqMainWindow::plugViewModeActions()
4942 {
4943     QList<QAction *> lst;
4944 
4945     if (m_viewModeMenu) {
4946         lst.append(m_viewModeMenu);
4947     }
4948 
4949     plugActionList(QStringLiteral("viewmode"), lst);
4950 }
4951 
unplugViewModeActions()4952 void KonqMainWindow::unplugViewModeActions()
4953 {
4954     unplugActionList(QStringLiteral("viewmode"));
4955 }
4956 
updateBookmarkBar()4957 void KonqMainWindow::updateBookmarkBar()
4958 {
4959     KToolBar *bar = this->findChild<KToolBar *>(QStringLiteral("bookmarkToolBar"));
4960     if (!bar) {
4961         return;
4962     }
4963     if (m_paBookmarkBar && bar->actions().isEmpty()) {
4964         bar->hide();
4965     }
4966 
4967 }
4968 
closeEvent(QCloseEvent * e)4969 void KonqMainWindow::closeEvent(QCloseEvent *e)
4970 {
4971     // This breaks session management (the window is withdrawn in kwin)
4972     // so let's do this only when closed by the user.
4973 
4974     if (!qApp->isSavingSession()) {
4975         KonqFrameTabs *tabContainer = m_pViewManager->tabContainer();
4976         if (tabContainer->count() > 1) {
4977             KSharedConfig::Ptr config = KSharedConfig::openConfig();
4978             KConfigGroup cs(config, QStringLiteral("Notification Messages"));
4979 
4980             if (!cs.hasKey("MultipleTabConfirm")) {
4981                 switch (
4982                     KMessageBox::warningYesNoCancel(
4983                         this,
4984                         i18n("You have multiple tabs open in this window, "
4985                              "are you sure you want to quit?"),
4986                         i18nc("@title:window", "Confirmation"),
4987                         KStandardGuiItem::closeWindow(),
4988                         KGuiItem(i18n("C&lose Current Tab"), QStringLiteral("tab-close")),
4989                         KStandardGuiItem::cancel(),
4990                         QStringLiteral("MultipleTabConfirm")
4991                     )
4992                 ) {
4993                 case KMessageBox::Yes :
4994                     break;
4995                 case KMessageBox::No :
4996                     e->ignore();
4997                     slotRemoveTab();
4998                     return;
4999                 case KMessageBox::Cancel :
5000                     e->ignore();
5001                     return;
5002                 default:
5003                     Q_UNREACHABLE();
5004                 }
5005             }
5006         }
5007 
5008         const int originalTabIndex = tabContainer->currentIndex();
5009         for (int tabIndex = 0; tabIndex < tabContainer->count(); ++tabIndex) {
5010             KonqFrameBase *tab = tabContainer->tabAt(tabIndex);
5011             if (!KonqModifiedViewsCollector::collect(tab).isEmpty()) {
5012                 m_pViewManager->showTab(tabIndex);
5013                 const QString question = m_pViewManager->isTabBarVisible()
5014                                          ? i18n("This tab contains changes that have not been submitted.\nClosing the window will discard these changes.")
5015                                          : i18n("This page contains changes that have not been submitted.\nClosing the window will discard these changes.");
5016                 if (KMessageBox::warningContinueCancel(
5017                             this, question,
5018                             i18nc("@title:window", "Discard Changes?"), KGuiItem(i18n("&Discard Changes"), QStringLiteral("application-exit")),
5019                             KStandardGuiItem::cancel(), QStringLiteral("discardchangesclose")) != KMessageBox::Continue) {
5020                     e->ignore();
5021                     m_pViewManager->showTab(originalTabIndex);
5022                     return;
5023                 }
5024             }
5025         }
5026 
5027         if (settingsDirty() && autoSaveSettings()) {
5028             saveAutoSaveSettings();
5029         }
5030 
5031         addClosedWindowToUndoList();
5032     }
5033     // We're going to close - tell the parts
5034     MapViews::ConstIterator it = m_mapViews.constBegin();
5035     MapViews::ConstIterator end = m_mapViews.constEnd();
5036     for (; it != end; ++it) {
5037         if ((*it)->part() && (*it)->part()->widget()) {
5038             QApplication::sendEvent((*it)->part()->widget(), e);
5039         }
5040     }
5041     KParts::MainWindow::closeEvent(e);
5042 }
5043 
addClosedWindowToUndoList()5044 void KonqMainWindow::addClosedWindowToUndoList()
5045 {
5046     qCDebug(KONQUEROR_LOG);
5047 
5048     // 1. We get the current title
5049     int numTabs = m_pViewManager->tabContainer()->childFrameList().count();
5050     QString title(i18n("no name"));
5051 
5052     if (m_currentView) {
5053         title = m_currentView->caption();
5054     }
5055 
5056     // 2. Create the KonqClosedWindowItem and  save its config
5057     KonqClosedWindowItem *closedWindowItem = new KonqClosedWindowItem(title, KonqClosedWindowsManager::self()->memoryStore(),
5058                                                                       m_pUndoManager->newCommandSerialNumber(), numTabs);
5059     saveProperties(closedWindowItem->configGroup());
5060 
5061     // 3. Add the KonqClosedWindowItem to the undo list
5062     m_paClosedItems->setEnabled(true);
5063     m_pUndoManager->addClosedWindowItem(closedWindowItem);
5064 
5065     qCDebug(KONQUEROR_LOG) << "done";
5066 }
5067 
updateWindowIcon()5068 void KonqMainWindow::updateWindowIcon()
5069 {
5070     KParts::MainWindow::setWindowIcon(KonqPixmapProvider::self()->iconForUrl(m_combo->currentText()));
5071 }
5072 
slotIntro()5073 void KonqMainWindow::slotIntro()
5074 {
5075     openUrl(nullptr, KonqUrl::url(KonqUrl::Type::NoPath));
5076 }
5077 
goURL()5078 void KonqMainWindow::goURL()
5079 {
5080     QLineEdit *lineEdit = comboEdit();
5081     if (!lineEdit) {
5082         return;
5083     }
5084 
5085     QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, QChar('\n'));
5086     QApplication::sendEvent(lineEdit, &event);
5087 }
5088 
5089 /**
5090  * Adds the URL of a KonqView to the closed tabs list.
5091  * This slot gets called each time a View is closed.
5092  */
slotAddClosedUrl(KonqFrameBase * tab)5093 void KonqMainWindow::slotAddClosedUrl(KonqFrameBase *tab)
5094 {
5095     qCDebug(KONQUEROR_LOG);
5096     QString title(i18n("no name")), url(KonqUrl::string(KonqUrl::Type::Blank));
5097 
5098     // Did the tab contain a single frame, or a splitter?
5099     KonqFrame *frame = dynamic_cast<KonqFrame *>(tab);
5100     if (!frame) {
5101         KonqFrameContainer *frameContainer = dynamic_cast<KonqFrameContainer *>(tab);
5102         if (frameContainer->activeChildView()) {
5103             frame = frameContainer->activeChildView()->frame();
5104         }
5105     }
5106 
5107     KParts::ReadOnlyPart *part = frame ? frame->part() : nullptr;
5108     if (part) {
5109         url = part->url().url();
5110     }
5111     if (frame) {
5112         title = frame->title().trimmed();
5113     }
5114     if (title.isEmpty()) {
5115         title = url;
5116     }
5117     title = KStringHandler::csqueeze(title, 50);
5118 
5119     // Now we get the position of the tab
5120     const int index =  m_pViewManager->tabContainer()->childFrameList().indexOf(tab);
5121 
5122     KonqClosedTabItem *closedTabItem = new KonqClosedTabItem(url, KonqClosedWindowsManager::self()->memoryStore(),
5123                                                              title, index, m_pUndoManager->newCommandSerialNumber());
5124 
5125     QString prefix = KonqFrameBase::frameTypeToString(tab->frameType()) + QString::number(0);
5126     closedTabItem->configGroup().writeEntry("RootItem", prefix);
5127     prefix.append(QLatin1Char('_'));
5128     KonqFrameBase::Options flags = KonqFrameBase::SaveHistoryItems;
5129     tab->saveConfig(closedTabItem->configGroup(), prefix, flags, nullptr, 0, 1);
5130 
5131     m_paClosedItems->setEnabled(true);
5132     m_pUndoManager->addClosedTabItem(closedTabItem);
5133 
5134     qCDebug(KONQUEROR_LOG) << "done";
5135 }
5136 
slotLocationLabelActivated()5137 void KonqMainWindow::slotLocationLabelActivated()
5138 {
5139     focusLocationBar();
5140     QLineEdit *edit = comboEdit();
5141     if (edit) {
5142         edit->selectAll();
5143     }
5144 }
5145 
slotOpenURL(const QUrl & url)5146 void KonqMainWindow::slotOpenURL(const QUrl &url)
5147 {
5148     openUrl(nullptr, url);
5149 }
5150 
sidebarVisible() const5151 bool KonqMainWindow::sidebarVisible() const
5152 {
5153     QAction *a = m_toggleViewGUIClient->action(QStringLiteral("konq_sidebartng"));
5154     return (a && static_cast<KToggleAction *>(a)->isChecked());
5155 }
5156 
fullScreenMode() const5157 bool KonqMainWindow::fullScreenMode() const
5158 {
5159     return m_ptaFullScreen->isChecked();
5160 }
5161 
slotAddWebSideBar(const QUrl & url,const QString & name)5162 void KonqMainWindow::slotAddWebSideBar(const QUrl &url, const QString &name)
5163 {
5164     if (url.isEmpty() && name.isEmpty()) {
5165         return;
5166     }
5167 
5168     qCDebug(KONQUEROR_LOG) << "Requested to add URL" << url << " [" << name << "] to the sidebar!";
5169 
5170     QAction *a = m_toggleViewGUIClient->action(QStringLiteral("konq_sidebartng"));
5171     if (!a) {
5172         KMessageBox::sorry(nullptr, i18n("Your sidebar is not functional or unavailable. A new entry cannot be added."), i18nc("@title:window", "Web Sidebar"));
5173         return;
5174     }
5175 
5176     int rc = KMessageBox::questionYesNo(nullptr,
5177                                         i18n("Add new web extension \"%1\" to your sidebar?",
5178                                                 name.isEmpty() ? name : url.toDisplayString()),
5179                                         i18nc("@title:window", "Web Sidebar"), KGuiItem(i18n("Add")), KGuiItem(i18n("Do Not Add")));
5180 
5181     if (rc == KMessageBox::Yes) {
5182         // Show the sidebar
5183         if (!static_cast<KToggleAction *>(a)->isChecked()) {
5184             a->trigger();
5185         }
5186 
5187         // Tell it to add a new panel
5188         MapViews::ConstIterator it;
5189         for (it = viewMap().constBegin(); it != viewMap().constEnd(); ++it) {
5190             KonqView *view = it.value();
5191             if (view) {
5192                 KService::Ptr svc = view->service();
5193                 if (svc->desktopEntryName() == QLatin1String("konq_sidebartng")) {
5194                     emit view->browserExtension()->addWebSideBar(url, name);
5195                     break;
5196                 }
5197             }
5198         }
5199     }
5200 }
5201 
addBookmarksIntoCompletion(const KBookmarkGroup & group)5202 void KonqMainWindow::addBookmarksIntoCompletion(const KBookmarkGroup &group)
5203 {
5204     const QString http = QStringLiteral("http");
5205     const QString ftp = QStringLiteral("ftp");
5206 
5207     if (group.isNull()) {
5208         return;
5209     }
5210 
5211     for (KBookmark bm = group.first();
5212             !bm.isNull(); bm = group.next(bm)) {
5213         if (bm.isGroup()) {
5214             addBookmarksIntoCompletion(bm.toGroup());
5215             continue;
5216         }
5217 
5218         QUrl url = bm.url();
5219         if (!url.isValid()) {
5220             continue;
5221         }
5222 
5223         QString u = url.toDisplayString();
5224         s_pCompletion->addItem(u);
5225 
5226         if (url.isLocalFile()) {
5227             s_pCompletion->addItem(url.toLocalFile());
5228         } else if (url.scheme() == http) {
5229             s_pCompletion->addItem(u.mid(7));
5230         } else if (url.scheme() == ftp &&
5231                    url.host().startsWith(ftp)) {
5232             s_pCompletion->addItem(u.mid(6));
5233         }
5234     }
5235 }
5236 
5237 //
5238 // the smart popup completion code , <l.lunak@kde.org>
5239 //
5240 
5241 // prepend http://www. or http:// if there's no protocol in 's'
5242 // This is used only when there are no completion matches
hp_tryPrepend(const QString & s)5243 static QString hp_tryPrepend(const QString &s)
5244 {
5245     if (s.isEmpty() || s[0] == QLatin1Char('/') || s[0] == QLatin1Char('~')) {
5246         return QString();
5247     }
5248 
5249     bool containsSpace = false;
5250 
5251     for (int pos = 0;
5252             pos < s.length() - 2; // 4 = ://x
5253             ++pos) {
5254         if (s[ pos ] == ':' && s[ pos + 1 ] == '/' && s[ pos + 2 ] == '/') {
5255             return QString();
5256         }
5257         if (!s[ pos ].isLetter()) {
5258             break;
5259         }
5260         if (s[pos].isSpace()) {
5261             containsSpace = true;
5262             break;
5263         }
5264     }
5265 
5266     if (containsSpace || s.at(s.length() - 1).isSpace()) {
5267         return QString();
5268     }
5269 
5270     return (s.startsWith(QLatin1String("www.")) ? "http://" : "http://www.") + s;
5271 }
5272 
hp_removeDupe(KCompletionMatches & l,const QString & dupe,KCompletionMatches::Iterator it_orig)5273 static void hp_removeDupe(KCompletionMatches &l, const QString &dupe,
5274                           KCompletionMatches::Iterator it_orig)
5275 {
5276     KCompletionMatches::Iterator it = it_orig + 1;
5277     while (it != l.end()) {
5278         if ((*it).value() == dupe) {
5279             (*it_orig).first = qMax((*it_orig).first, (*it).key());
5280             it = l.erase(it);
5281             continue;
5282         }
5283         ++it;
5284     }
5285 }
5286 
5287 // remove duplicates like 'http://www.kde.org' and 'http://www.kde.org/'
5288 // (i.e. the trailing slash)
5289 // some duplicates are also created by prepending protocols
hp_removeDuplicates(KCompletionMatches & l)5290 static void hp_removeDuplicates(KCompletionMatches &l)
5291 {
5292     QString http = QStringLiteral("http://");
5293     QString ftp = QStringLiteral("ftp://ftp.");
5294     QString file = QStringLiteral("file:");
5295     QString file2 = QStringLiteral("file://");
5296     l.removeDuplicates();
5297     for (KCompletionMatches::Iterator it = l.begin();
5298             it != l.end();
5299             ++it) {
5300         QString str = (*it).value();
5301         if (str.startsWith(http)) {
5302             if (str.indexOf('/', 7) < 0) {    // http://something<noslash>
5303                 hp_removeDupe(l, str + '/', it);
5304                 hp_removeDupe(l, str.mid(7) + '/', it);
5305             } else if (str[ str.length() - 1 ] == '/') {
5306                 hp_removeDupe(l, str.left(str.length() - 1), it);
5307                 hp_removeDupe(l, str.left(str.length() - 1).mid(7), it);
5308             }
5309             hp_removeDupe(l, str.mid(7), it);
5310         } else if (str.startsWith(ftp)) { // ftp://ftp.
5311             hp_removeDupe(l, str.mid(6), it);    // remove dupes without ftp://
5312         } else if (str.startsWith(file2)) {
5313             hp_removeDupe(l, str.mid(7), it);    // remove dupes without file://
5314         } else if (str.startsWith(file)) {
5315             hp_removeDupe(l, str.mid(5), it);    // remove dupes without file:
5316         }
5317     }
5318 }
5319 
hp_removeCommonPrefix(KCompletionMatches & l,const QString & prefix)5320 static void hp_removeCommonPrefix(KCompletionMatches &l, const QString &prefix)
5321 {
5322     for (KCompletionMatches::Iterator it = l.begin();
5323             it != l.end();
5324         ) {
5325         if ((*it).value().startsWith(prefix)) {
5326             it = l.erase(it);
5327             continue;
5328         }
5329         ++it;
5330     }
5331 }
5332 
5333 // don't include common prefixes like 'http://', i.e. when s == 'h', include
5334 // http://hotmail.com but don't include everything just starting with 'http://'
hp_checkCommonPrefixes(KCompletionMatches & matches,const QString & s)5335 static void hp_checkCommonPrefixes(KCompletionMatches &matches, const QString &s)
5336 {
5337     static const char *const prefixes[] = {
5338         "http://",
5339         "https://",
5340         "www.",
5341         "ftp://",
5342         "http://www.",
5343         "https://www.",
5344         "ftp://ftp.",
5345         "file:",
5346         "file://",
5347         nullptr
5348     };
5349     for (const char *const *pos = prefixes;
5350             *pos != nullptr;
5351             ++pos) {
5352         QString prefix = *pos;
5353         if (prefix.startsWith(s)) {
5354             hp_removeCommonPrefix(matches, prefix);
5355         }
5356     }
5357 }
5358 
historyPopupCompletionItems(const QString & s)5359 QStringList KonqMainWindow::historyPopupCompletionItems(const QString &s)
5360 {
5361     const QString http = QStringLiteral("http://");
5362     const QString https = QStringLiteral("https://");
5363     const QString www = QStringLiteral("http://www.");
5364     const QString wwws = QStringLiteral("https://www.");
5365     const QString ftp = QStringLiteral("ftp://");
5366     const QString ftpftp = QStringLiteral("ftp://ftp.");
5367     const QString file = QStringLiteral("file:"); // without /, because people enter /usr etc.
5368     const QString file2 = QStringLiteral("file://");
5369     if (s.isEmpty()) {
5370         return QStringList();
5371     }
5372     KCompletionMatches matches = s_pCompletion->allWeightedMatches(s);
5373     hp_checkCommonPrefixes(matches, s);
5374     bool checkDuplicates = false;
5375     if (!s.startsWith(ftp)) {
5376         matches += s_pCompletion->allWeightedMatches(ftp + s);
5377         if (QStringLiteral("ftp.").startsWith(s)) {
5378             hp_removeCommonPrefix(matches, ftpftp);
5379         }
5380         checkDuplicates = true;
5381     }
5382     if (!s.startsWith(https)) {
5383         matches += s_pCompletion->allWeightedMatches(https + s);
5384         if (QStringLiteral("www.").startsWith(s)) {
5385             hp_removeCommonPrefix(matches, wwws);
5386         }
5387         checkDuplicates = true;
5388     }
5389     if (!s.startsWith(http)) {
5390         matches += s_pCompletion->allWeightedMatches(http + s);
5391         if (QStringLiteral("www.").startsWith(s)) {
5392             hp_removeCommonPrefix(matches, www);
5393         }
5394         checkDuplicates = true;
5395     }
5396     if (!s.startsWith(www)) {
5397         matches += s_pCompletion->allWeightedMatches(www + s);
5398         checkDuplicates = true;
5399     }
5400     if (!s.startsWith(wwws)) {
5401         matches += s_pCompletion->allWeightedMatches(wwws + s);
5402         checkDuplicates = true;
5403     }
5404     if (!s.startsWith(ftpftp)) {
5405         matches += s_pCompletion->allWeightedMatches(ftpftp + s);
5406         checkDuplicates = true;
5407     }
5408     if (!s.startsWith(file)) {
5409         matches += s_pCompletion->allWeightedMatches(file + s);
5410         checkDuplicates = true;
5411     }
5412     if (!s.startsWith(file2)) {
5413         matches += s_pCompletion->allWeightedMatches(file2 + s);
5414         checkDuplicates = true;
5415     }
5416     if (checkDuplicates) {
5417         hp_removeDuplicates(matches);
5418     }
5419     QStringList items = matches.list();
5420     if (items.count() == 0
5421             && !s.contains(':') && !s.isEmpty() && s[ 0 ] != '/') {
5422         QString pre = hp_tryPrepend(s);
5423         if (!pre.isNull()) {
5424             items += pre;
5425         }
5426     }
5427     return items;
5428 }
5429 
5430 #ifndef NDEBUG
dumpViewList()5431 void KonqMainWindow::dumpViewList()
5432 {
5433     qCDebug(KONQUEROR_LOG) << m_mapViews.count() << "views:";
5434 
5435     MapViews::Iterator end = m_mapViews.end();
5436     for (MapViews::Iterator it = m_mapViews.begin(); it != end; ++it) {
5437         KonqView *view = it.value();
5438         qCDebug(KONQUEROR_LOG) << view << view->part();
5439     }
5440 }
5441 #endif
5442 
insertChildFrame(KonqFrameBase * frame,int)5443 void KonqMainWindow::insertChildFrame(KonqFrameBase *frame, int /*index*/)
5444 {
5445     m_pChildFrame = frame;
5446     m_pActiveChild = frame;
5447     frame->setParentContainer(this);
5448     if (centralWidget() && centralWidget() != frame->asQWidget()) {
5449         centralWidget()->setParent(nullptr);   // workaround Qt-4.1.2 crash (reported)
5450         setCentralWidget(nullptr);
5451     }
5452     setCentralWidget(frame->asQWidget());
5453 }
5454 
childFrameRemoved(KonqFrameBase * frame)5455 void KonqMainWindow::childFrameRemoved(KonqFrameBase *frame)
5456 {
5457     Q_ASSERT(frame == m_pChildFrame);
5458     Q_UNUSED(frame)
5459     m_pChildFrame = nullptr;
5460     m_pActiveChild = nullptr;
5461 }
5462 
saveConfig(KConfigGroup & config,const QString & prefix,const KonqFrameBase::Options & options,KonqFrameBase * docContainer,int id,int depth)5463 void KonqMainWindow::saveConfig(KConfigGroup &config, const QString &prefix, const KonqFrameBase::Options &options, KonqFrameBase *docContainer, int id, int depth)
5464 {
5465     if (m_pChildFrame) {
5466         m_pChildFrame->saveConfig(config, prefix, options, docContainer, id, depth);
5467     }
5468 }
5469 
copyHistory(KonqFrameBase * other)5470 void KonqMainWindow::copyHistory(KonqFrameBase *other)
5471 {
5472     if (m_pChildFrame) {
5473         m_pChildFrame->copyHistory(other);
5474     }
5475 }
5476 
setTitle(const QString &,QWidget *)5477 void KonqMainWindow::setTitle(const QString &/*title*/, QWidget * /*sender*/)
5478 {
5479 }
5480 
setTabIcon(const QUrl &,QWidget *)5481 void KonqMainWindow::setTabIcon(const QUrl &/*url*/, QWidget * /*sender*/)
5482 {
5483 }
5484 
asQWidget()5485 QWidget *KonqMainWindow::asQWidget()
5486 {
5487     return this;
5488 }
5489 
frameType() const5490 KonqFrameBase::FrameType KonqMainWindow::frameType() const
5491 {
5492     return KonqFrameBase::MainWindow;
5493 }
5494 
childFrame() const5495 KonqFrameBase *KonqMainWindow::childFrame()const
5496 {
5497     return m_pChildFrame;
5498 }
5499 
setActiveChild(KonqFrameBase *)5500 void KonqMainWindow::setActiveChild(KonqFrameBase * /*activeChild*/)
5501 {
5502 }
5503 
setWorkingTab(int index)5504 void KonqMainWindow::setWorkingTab(int index)
5505 {
5506     m_workingTab = index;
5507 }
5508 
isMimeTypeAssociatedWithSelf(const QString & mimeType)5509 bool KonqMainWindow::isMimeTypeAssociatedWithSelf(const QString &mimeType)
5510 {
5511     return isMimeTypeAssociatedWithSelf(mimeType, KMimeTypeTrader::self()->preferredService(mimeType, QStringLiteral("Application")));
5512 }
5513 
isMimeTypeAssociatedWithSelf(const QString &,const KService::Ptr & offer)5514 bool KonqMainWindow::isMimeTypeAssociatedWithSelf(const QString &/*mimeType*/, const KService::Ptr &offer)
5515 {
5516     // Prevention against user stupidity : if the associated app for this mimetype
5517     // is konqueror/kfmclient, then we'll loop forever. So we have to
5518     // 1) force embedding first, if that works we're ok
5519     // 2) check what OpenUrlJob is going to do before calling it.
5520     return (offer && (offer->desktopEntryName() == QLatin1String("konqueror") ||
5521                       offer->exec().trimmed().startsWith(QLatin1String("kfmclient"))));
5522 }
5523 
refuseExecutingKonqueror(const QString & mimeType)5524 bool KonqMainWindow::refuseExecutingKonqueror(const QString &mimeType)
5525 {
5526     if (activeViewsNotLockedCount() > 0) {   // if I lock the only view, then there's no error: open links in a new window
5527         KMessageBox::error(this, i18n("There appears to be a configuration error. You have associated Konqueror with %1, but it cannot handle this file type.", mimeType));
5528         return true; // we refuse indeed
5529     }
5530     return false; // no error
5531 }
5532 
event(QEvent * e)5533 bool KonqMainWindow::event(QEvent *e)
5534 {
5535     if (e->type() == QEvent::StatusTip) {
5536         if (m_currentView && m_currentView->frame()->statusbar()) {
5537             KonqFrameStatusBar *statusBar = m_currentView->frame()->statusbar();
5538             statusBar->message(static_cast<QStatusTipEvent *>(e)->tip());
5539         }
5540     }
5541 
5542     if (KonqFileSelectionEvent::test(e) ||
5543             KonqFileMouseOverEvent::test(e) ||
5544             KParts::PartActivateEvent::test(e)) {
5545         // Forward the event to all views
5546         MapViews::ConstIterator it = m_mapViews.constBegin();
5547         MapViews::ConstIterator end = m_mapViews.constEnd();
5548         for (; it != end; ++it) {
5549             QApplication::sendEvent((*it)->part(), e);
5550         }
5551         return true;
5552     }
5553 
5554     if (KParts::OpenUrlEvent::test(e)) {
5555         KParts::OpenUrlEvent *ev = static_cast<KParts::OpenUrlEvent *>(e);
5556 
5557         // Forward the event to all views
5558         MapViews::ConstIterator it = m_mapViews.constBegin();
5559         MapViews::ConstIterator end = m_mapViews.constEnd();
5560         for (; it != end; ++it) {
5561             // Don't resend to sender
5562             if (it.key() != ev->part()) {
5563                 //qCDebug(KONQUEROR_LOG) << "Sending event to view" << it.key()->metaObject()->className();
5564                 QApplication::sendEvent(it.key(), e);
5565             }
5566         }
5567     }
5568     return KParts::MainWindow::event(e);
5569 }
5570 
slotUndoTextChanged(const QString & newText)5571 void KonqMainWindow::slotUndoTextChanged(const QString &newText)
5572 {
5573     m_paUndo->setText(newText);
5574 }
5575 
currentView() const5576 KonqView *KonqMainWindow::currentView() const
5577 {
5578     return m_currentView;
5579 }
5580 
accept(KonqFrameVisitor * visitor)5581 bool KonqMainWindow::accept(KonqFrameVisitor *visitor)
5582 {
5583     return visitor->visit(this)
5584            && (!m_pChildFrame || m_pChildFrame->accept(visitor))
5585            && visitor->endVisit(this);
5586 }
5587 
comboEdit()5588 QLineEdit *KonqMainWindow::comboEdit()
5589 {
5590     return m_combo ? m_combo->lineEdit() : nullptr;
5591 }
5592 
updateProxyForWebEngine(bool updateProtocolManager)5593 void KonqMainWindow::updateProxyForWebEngine(bool updateProtocolManager)
5594 {
5595     if (updateProtocolManager) {
5596         KProtocolManager::reparseConfiguration();
5597     }
5598 
5599     KService::Ptr service = KMimeTypeTrader::self()->preferredService("text/html", "KParts/ReadOnlyPart");
5600     Q_ASSERT(service);
5601     const bool webengineIsDefault = service->desktopEntryName() == "webenginepart";
5602     if (!webengineIsDefault) {
5603         return;
5604     }
5605 
5606     KProtocolManager::ProxyType proxyType = KProtocolManager::proxyType();
5607     if (proxyType == KProtocolManager::WPADProxy || proxyType == KProtocolManager::PACProxy) {
5608         QString msg = i18n("Your proxy configuration can't be used with the QtWebEngine HTML engine. "
5609                            "No proxy will be used\n\n QtWebEngine only support a fixed proxy, so proxy auto-configuration (PAC) "
5610                            "and Web Proxy Auto-Discovery protocol can't be used with QtWebEngine. If you need a proxy, please select "
5611                            "the system proxy configuration or specify a proxy URL manually in the settings dialog. Do you want to "
5612                            "change proxy settings now?");
5613         KMessageBox::ButtonCode ans = KMessageBox::warningYesNo(this, msg, i18n("Unsupported proxy configuration"), KGuiItem(i18n("Don't use a proxy")),
5614                                                                 KGuiItem(i18n("Show proxy configuration dialog")), "WebEngineUnsupportedProxyType");
5615         QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
5616         if (ans == KMessageBox::No) {
5617             slotConfigure("proxy");
5618             return;
5619         }
5620     }
5621     QString httpProxy = KProtocolManager::proxyForUrl(QUrl("http://fakeurl.test.com"));
5622     QString httpsProxy = KProtocolManager::proxyForUrl(QUrl("https://fakeurl.test.com"));
5623     if (httpProxy == "DIRECT" && httpsProxy == "DIRECT") {
5624         QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
5625     } else {
5626         QUrl url(httpsProxy);
5627         if (httpProxy != httpsProxy) {
5628             QString msg =  i18n("Your proxy configuration can't be used with the QtWebEngine HTML engine because it doesn't support having different proxies for the HTTP and HTTPS protocols. Your current settings are:"
5629             "<p><b>HTTP proxy:</b> <tt>%1</tt></p><p><b>HTTPS proxy: </b><tt>%2</tt></p>"
5630             "What do you want to do?", httpProxy, httpsProxy);
5631             KMessageBox::ButtonCode ans = KMessageBox::questionYesNoCancel(this, msg, i18n("Conflicting proxy configuration"),
5632                 KGuiItem(i18n("Use HTTP proxy (only this time)")), KGuiItem(i18n("Use HTTPS proxy (only this time)")), KGuiItem(i18n("Show proxy configuration dialog")), "WebEngineConflictingProxy");
5633             if (ans == KMessageBox::Yes) {
5634                 url = QUrl(httpProxy);
5635             } else if (ans == KMessageBox::Cancel) {
5636                 slotConfigure("proxy");
5637                 return;
5638             }
5639         }
5640         QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, url.host(), url.port(), url.userName(), url.password()));
5641     }
5642 }
5643 
toggleCompleteFullScreen(bool on)5644 void KonqMainWindow::toggleCompleteFullScreen(bool on)
5645 {
5646     //Do nothing if already in complete full screen mode and on is true or not in complete full screen mode and on is false
5647     if (on == (m_fullScreenData.currentState == FullScreenState::CompleteFullScreen)) {
5648         return;
5649     }
5650     if (on) {
5651         slotForceSaveMainWindowSettings();
5652         resetAutoSaveSettings();
5653 
5654         //Hide the menu bar
5655         menuBar()->setVisible(false);
5656 
5657         //Hide the side bar
5658         QAction *a = m_toggleViewGUIClient->action(QStringLiteral("konq_sidebartng"));
5659         if (a) {
5660             KToggleAction *ta = static_cast<KToggleAction*>(a);
5661             if (ta){
5662                 m_fullScreenData.wasSidebarVisible = ta->isChecked();
5663                 a->setChecked(false);
5664             }
5665         }
5666 
5667         //Hide the tool bars
5668         const QList<QAction*> actions = toolBarMenuAction()->menu()->actions();
5669         for (QAction *a : actions) {
5670             a->setChecked(false);
5671         }
5672     } else {
5673         setAutoSaveSettings();
5674     }
5675 
5676     //Status bar and side bar are not managed by autoSaveSettings
5677 
5678     //Hide or show the sidebar
5679     QAction *a = m_toggleViewGUIClient->action(QStringLiteral("konq_sidebartng"));
5680     KToggleAction *sideBarAction = qobject_cast<KToggleAction*>(a);
5681     if (sideBarAction) {
5682         if (on) {
5683             m_fullScreenData.wasSidebarVisible = sideBarAction ->isChecked();
5684             sideBarAction ->setChecked(false);
5685         } else if (m_fullScreenData.wasSidebarVisible) {
5686             sideBarAction->setChecked(true);
5687         }
5688     }
5689 
5690     //Hide or show the status bar
5691     if (m_currentView) {
5692         QStatusBar *statusBar = m_currentView->frame()->statusbar();
5693         if (on) {
5694             m_fullScreenData.wasStatusBarVisible = statusBar->isVisible();
5695             statusBar->setVisible(false);
5696         } else if (m_fullScreenData.wasStatusBarVisible) {
5697             statusBar->setVisible(true);
5698         }
5699     }
5700 
5701     if (on || m_fullScreenData.previousState == FullScreenState::NoFullScreen) {
5702         disconnect(m_ptaFullScreen, &KToggleAction::toggled, this, &KonqMainWindow::slotUpdateFullScreen);
5703         KToggleFullScreenAction::setFullScreen(this, on);
5704         connect(m_ptaFullScreen, &KToggleAction::toggled, this, &KonqMainWindow::slotUpdateFullScreen);
5705     }
5706     m_pViewManager->forceHideTabBar(on);
5707 
5708     if (on) {
5709     QString msg = i18n("You have entered Complete Full Screen mode (the user interface is completely hidden)."
5710     " You can exit it by pressing the keyboard shortcut for Full Screen Mode (%1)", m_ptaFullScreen->shortcut().toString());
5711         KMessageBox::information(this, msg, QString(), "Complete Full Screen Warning");
5712     }
5713 
5714     m_fullScreenData.switchToState(on ? FullScreenState::CompleteFullScreen : m_fullScreenData.previousState);
5715 }
5716 
switchToState(KonqMainWindow::FullScreenState newState)5717 void KonqMainWindow::FullScreenData::switchToState(KonqMainWindow::FullScreenState newState)
5718 {
5719     if (newState != currentState) {
5720         previousState = currentState;
5721         currentState = newState;
5722     }
5723 }
5724 
inspectCurrentPage()5725 void KonqMainWindow::inspectCurrentPage()
5726 {
5727     if (!m_currentView || !m_currentView->isWebEngineView()) {
5728         return;
5729     }
5730     KParts::ReadOnlyPart *partToInspect = m_currentView->part();
5731     KonqView *devToolsView = m_pViewManager->splitView(m_currentView, Qt::Vertical);
5732     if (devToolsView == nullptr) {
5733         return;
5734     }
5735     KonqOpenURLRequest req;
5736     req.forceAutoEmbed = true;
5737 
5738     openView("text/html", QUrl(), devToolsView, req);
5739     QMetaObject::invokeMethod(devToolsView->part(), "setInspectedPart", Qt::DirectConnection, Q_ARG(KParts::ReadOnlyPart*, partToInspect));
5740 }
5741