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