File indexing completed on 2024-05-12 05:54:34

0001 /*
0002     SPDX-FileCopyrightText: 2002 Shie Erlich <erlich@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2002 Rafi Yanai <yanai@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "krviewer.h"
0010 
0011 // QtCore
0012 #include <QDataStream>
0013 #include <QDebug>
0014 #include <QEvent>
0015 #include <QFile>
0016 #include <QTimer>
0017 // QtGui
0018 #include <QKeyEvent>
0019 // QtWidgets
0020 #include <QMenuBar>
0021 #include <QStatusBar>
0022 
0023 #include <KConfigCore/KSharedConfig>
0024 #include <KConfigWidgets/KStandardAction>
0025 #include <KCoreAddons/KProcess>
0026 #include <KCoreAddons/KShell>
0027 #include <KI18n/KLocalizedString>
0028 #include <KIOCore/KFileItem>
0029 #include <KParts/Part>
0030 #include <KWidgetsAddons/KMessageBox>
0031 #include <KXmlGui/KActionCollection>
0032 #include <KXmlGui/KShortcutsDialog>
0033 #include <KXmlGui/KToolBar>
0034 #include <kxmlgui_version.h>
0035 #include <utility>
0036 
0037 #include "../defaults.h"
0038 #include "../icon.h"
0039 #include "panelviewer.h"
0040 #include "viewertabbar.h"
0041 
0042 #define VIEW_ICON "document-preview"
0043 #define EDIT_ICON "document-edit"
0044 #define MODIFIED_ICON "document-save-as"
0045 
0046 #define CHECK_MODFIED_INTERVAL 500
0047 
0048 /*
0049 NOTE: Currently the code expects PanelViewer::openUrl() to be called only once
0050       in the panel viewer's life time - otherwise unexpected things might happen.
0051 */
0052 
0053 QList<KrViewer *> KrViewer::viewers;
0054 
0055 KrViewer::KrViewer(QWidget *parent)
0056     : KParts::MainWindow(parent, Qt::WindowFlags())
0057     , manager(this, this)
0058     , tabWidget(this)
0059     , sizeX(-1)
0060     , sizeY(-1)
0061 {
0062     // setWFlags(Qt::WType_TopLevel | WDestructiveClose);
0063     setXMLFile("krviewer.rc"); // kpart-related xml file
0064     setHelpMenuEnabled(false);
0065 
0066     connect(&manager, &KParts::PartManager::activePartChanged, this, &KrViewer::createGUI);
0067     connect(&tabWidget, &QTabWidget::currentChanged, this, &KrViewer::tabChanged);
0068     connect(&tabWidget, &QTabWidget::tabCloseRequested, this, [=](int index) {
0069         tabCloseRequest(index, false);
0070     });
0071 
0072     tabWidget.setDocumentMode(true);
0073     tabWidget.setMovable(true);
0074     if (ViewerTabBar *tabBar = tabWidget.tabBar())
0075         connect(tabBar, &ViewerTabBar::closeTabSignal, this, [=](int index) {
0076             tabCloseRequest(index, false);
0077         });
0078     setCentralWidget(&tabWidget);
0079 
0080     printAction = KStandardAction::print(this, SLOT(print()), nullptr);
0081     copyAction = KStandardAction::copy(this, SLOT(copy()), nullptr);
0082 
0083     viewerMenu = new QMenu(this);
0084     QAction *tempAction;
0085     KActionCollection *ac = actionCollection();
0086 
0087 #define addCustomMenuAction(name, text, slot, shortcut)                                                                                                        \
0088     tempAction = ac->addAction(name, this, slot);                                                                                                              \
0089     tempAction->setText(text);                                                                                                                                 \
0090     ac->setDefaultShortcut(tempAction, shortcut);                                                                                                              \
0091     viewerMenu->addAction(tempAction);
0092 
0093     addCustomMenuAction("genericViewer", i18n("&Generic Viewer"), SLOT(viewGeneric()), Qt::CTRL + Qt::SHIFT + Qt::Key_G);
0094     addCustomMenuAction("textViewer", i18n("&Text Viewer"), SLOT(viewText()), Qt::CTRL + Qt::SHIFT + Qt::Key_T);
0095     addCustomMenuAction("hexViewer", i18n("&Hex Viewer"), SLOT(viewHex()), Qt::CTRL + Qt::SHIFT + Qt::Key_H);
0096     addCustomMenuAction("lister", i18n("&Lister"), SLOT(viewLister()), Qt::CTRL + Qt::SHIFT + Qt::Key_L);
0097     viewerMenu->addSeparator();
0098 
0099     addCustomMenuAction("textEditor", i18n("Text &Editor"), SLOT(editText()), Qt::CTRL + Qt::SHIFT + Qt::Key_E);
0100     viewerMenu->addSeparator();
0101 
0102     QList<QAction *> actList = menuBar()->actions();
0103     bool hasPrint = false, hasCopy = false;
0104     foreach (QAction *a, actList) {
0105         if (a->shortcut().matches(printAction->shortcut()) != QKeySequence::NoMatch)
0106             hasPrint = true;
0107         if (a->shortcut().matches(copyAction->shortcut()) != QKeySequence::NoMatch)
0108             hasCopy = true;
0109     }
0110     QAction *printAct = viewerMenu->addAction(printAction->icon(), printAction->text(), this, SLOT(print()));
0111     if (hasPrint)
0112         printAct->setShortcut(printAction->shortcut());
0113     QAction *copyAct = viewerMenu->addAction(copyAction->icon(), copyAction->text(), this, SLOT(copy()));
0114     if (hasCopy)
0115         copyAct->setShortcut(copyAction->shortcut());
0116     viewerMenu->addSeparator();
0117 
0118     configKeysAction = ac->addAction(KStandardAction::KeyBindings, this, SLOT(configureShortcuts()));
0119     viewerMenu->addAction(configKeysAction);
0120     viewerMenu->addSeparator();
0121 
0122     detachAction = ac->addAction("detachTab", this, SLOT(detachTab()));
0123     detachAction->setText(i18n("&Detach Tab"));
0124     // no point in detaching only one tab..
0125     detachAction->setEnabled(false);
0126     ac->setDefaultShortcut(detachAction, Qt::META + Qt::Key_D);
0127     viewerMenu->addAction(detachAction);
0128 
0129     quitAction = ac->addAction(KStandardAction::Quit, this, SLOT(close()));
0130     viewerMenu->addAction(quitAction);
0131 
0132     QList<QKeySequence> shortcuts;
0133 
0134     tabCloseAction = ac->addAction("closeTab", this, SLOT(tabCloseRequest()));
0135     tabCloseAction->setText(i18n("&Close Current Tab"));
0136     shortcuts = KStandardShortcut::close();
0137     shortcuts.append(Qt::Key_Escape);
0138     ac->setDefaultShortcuts(tabCloseAction, shortcuts);
0139 
0140     tabNextAction = ac->addAction("nextTab", this, SLOT(nextTab()));
0141     tabNextAction->setText(i18n("&Next Tab"));
0142     shortcuts = KStandardShortcut::tabNext();
0143     shortcuts.append(Qt::CTRL + Qt::Key_Tab); // reenforce QTabWidget shortcut
0144     ac->setDefaultShortcuts(tabNextAction, shortcuts);
0145 
0146     tabPrevAction = ac->addAction("prevTab", this, SLOT(prevTab()));
0147     tabPrevAction->setText(i18n("&Previous Tab"));
0148     shortcuts = KStandardShortcut::tabPrev();
0149     shortcuts.append(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab); // reenforce QTabWidget shortcut
0150     ac->setDefaultShortcuts(tabPrevAction, shortcuts);
0151 
0152     tabWidget.setTabsClosable(true);
0153 
0154     checkModified();
0155 
0156     KConfigGroup group(krConfig, "KrViewerWindow");
0157     int sx = group.readEntry("Window Width", -1);
0158     int sy = group.readEntry("Window Height", -1);
0159 
0160     if (sx != -1 && sy != -1)
0161         resize(sx, sy);
0162     else
0163         resize(900, 700);
0164 
0165     if (group.readEntry("Window Maximized", false)) {
0166         setWindowState(windowState() | Qt::WindowMaximized);
0167     }
0168 
0169     // filtering out the key events
0170     menuBar()->installEventFilter(this);
0171 }
0172 
0173 KrViewer::~KrViewer()
0174 {
0175     disconnect(&manager, SIGNAL(activePartChanged(KParts::Part *)), this, SLOT(createGUI(KParts::Part *)));
0176 
0177     viewers.removeAll(this);
0178 
0179     // close tabs before deleting tab bar - this avoids Qt bug 26115
0180     // https://bugreports.qt-project.org/browse/QTBUG-26115
0181     while (tabWidget.count())
0182         tabCloseRequest(tabWidget.currentIndex(), true);
0183 
0184     delete printAction;
0185     delete copyAction;
0186 }
0187 
0188 void KrViewer::configureDeps()
0189 {
0190     PanelEditor::configureDeps();
0191 }
0192 
0193 void KrViewer::createGUI(KParts::Part *part)
0194 {
0195     KParts::MainWindow::createGUI(part);
0196 
0197     updateActions();
0198 
0199     toolBar()->show();
0200     statusBar()->show();
0201 
0202     // the KParts part may override the viewer shortcuts. We prevent it
0203     // by installing an event filter on the menuBar() and the part
0204     reservedKeys.clear();
0205     reservedKeyActions.clear();
0206 
0207     QList<QAction *> list = viewerMenu->actions();
0208     // also add the actions that are not in the menu...
0209     list << tabCloseAction << tabNextAction << tabPrevAction;
0210     // getting the key sequences of the viewer menu
0211     for (int w = 0; w != list.count(); w++) {
0212         QAction *act = list[w];
0213         QList<QKeySequence> sequences = act->shortcuts();
0214         foreach (QKeySequence keySeq, sequences) {
0215             for (int i = 0; i < keySeq.count(); ++i) {
0216                 reservedKeys.push_back(keySeq[i]);
0217                 reservedKeyActions.push_back(act); // the same action many times in case of multiple shortcuts
0218             }
0219         }
0220     }
0221 
0222     // and "fix" the menubar
0223     viewerMenu->setTitle(i18n("&KrViewer"));
0224     QAction *act = menuBar()->addMenu(viewerMenu);
0225     act->setData(QVariant(70));
0226     menuBar()->show();
0227 }
0228 
0229 void KrViewer::configureShortcuts()
0230 {
0231 #if KXMLGUI_VERSION >= QT_VERSION_CHECK(5, 84, 0)
0232     KShortcutsDialog::showDialog(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
0233 #else
0234     KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
0235 #endif
0236 }
0237 
0238 bool KrViewer::eventFilter(QObject * /* watched */, QEvent *e)
0239 {
0240     // TODO: after porting to Qt5/KF5 we never catch *ANY* KeyPress or ShortcutOverride events here anymore.
0241     // Should look into if there is any way to fix it. Currently if a KPart has same shortcut as KrViewer then
0242     // it causes a conflict, messagebox shown to user and no action triggered.
0243     if (e->type() == QEvent::ShortcutOverride) {
0244         auto *ke = dynamic_cast<QKeyEvent *>(e);
0245         if (reservedKeys.contains(ke->key())) {
0246             ke->accept();
0247 
0248             QAction *act = reservedKeyActions[reservedKeys.indexOf(ke->key())];
0249             if (act != nullptr) {
0250                 // don't activate the close functions immediately!
0251                 // it can cause crash
0252                 if (act == tabCloseAction || act == quitAction) {
0253                     QTimer::singleShot(0, act, &QAction::trigger);
0254                 } else {
0255                     act->activate(QAction::Trigger);
0256                 }
0257             }
0258             return true;
0259         }
0260     } else if (e->type() == QEvent::KeyPress) {
0261         auto *ke = dynamic_cast<QKeyEvent *>(e);
0262         if (reservedKeys.contains(ke->key())) {
0263             ke->accept();
0264             return true;
0265         }
0266     }
0267     return false;
0268 }
0269 
0270 KrViewer *KrViewer::getViewer(bool new_window)
0271 {
0272     if (!new_window) {
0273         if (viewers.isEmpty()) {
0274             viewers.prepend(new KrViewer()); // add to first (active)
0275         } else {
0276             if (viewers.first()->isMinimized()) { // minimized? -> show it again
0277                 viewers.first()->setWindowState((viewers.first()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
0278                 viewers.first()->show();
0279             }
0280             viewers.first()->raise();
0281             viewers.first()->activateWindow();
0282         }
0283         return viewers.first();
0284     } else {
0285         auto *newViewer = new KrViewer();
0286         viewers.prepend(newViewer);
0287         return newViewer;
0288     }
0289 }
0290 
0291 void KrViewer::view(QUrl url, QWidget *parent)
0292 {
0293     KConfigGroup group(krConfig, "General");
0294     bool defaultWindow = group.readEntry("View In Separate Window", _ViewInSeparateWindow);
0295 
0296     view(std::move(url), Default, defaultWindow, parent);
0297 }
0298 
0299 void KrViewer::view(QUrl url, Mode mode, bool new_window, QWidget *parent)
0300 {
0301     KrViewer *viewer = getViewer(new_window);
0302     viewer->viewInternal(std::move(url), mode, parent);
0303     viewer->show();
0304 }
0305 
0306 void KrViewer::edit(QUrl url, QWidget *parent)
0307 {
0308     edit(std::move(url), Text, -1, parent);
0309 }
0310 
0311 void KrViewer::edit(const QUrl &url, Mode mode, int new_window, QWidget *parent)
0312 {
0313     KConfigGroup group(krConfig, "General");
0314     QString editor = group.readEntry("Editor", _Editor);
0315 
0316     if (new_window == -1)
0317         new_window = group.readEntry("View In Separate Window", _ViewInSeparateWindow);
0318 
0319     if (editor != "internal editor" && !editor.isEmpty()) {
0320         KProcess proc;
0321         QStringList cmdArgs = KShell::splitArgs(editor, KShell::TildeExpand);
0322         if (cmdArgs.isEmpty()) {
0323             KMessageBox::error(krMainWindow, i18nc("Arg is a string containing the bad quoting.", "Bad quoting in editor command:\n%1", editor));
0324             return;
0325         }
0326         // if the file is local, pass a normal path and not a url. this solves
0327         // the problem for editors that aren't url-aware
0328         proc << cmdArgs << url.toDisplayString(QUrl::PreferLocalFile);
0329         if (!proc.startDetached())
0330             KMessageBox::error(krMainWindow, i18n("Can not open \"%1\"", editor));
0331         return;
0332     }
0333 
0334     KrViewer *viewer = getViewer(new_window);
0335     viewer->editInternal(url, mode, parent);
0336     viewer->show();
0337 }
0338 
0339 void KrViewer::addTab(PanelViewerBase *pvb)
0340 {
0341     int tabIndex = tabWidget.addTab(pvb, makeTabIcon(pvb), makeTabText(pvb));
0342     tabWidget.setCurrentIndex(tabIndex);
0343     tabWidget.setTabToolTip(tabIndex, makeTabToolTip(pvb));
0344 
0345     updateActions();
0346 
0347     // now we can offer the option to detach tabs (we have more than one)
0348     if (tabWidget.count() > 1)
0349         detachAction->setEnabled(true);
0350     tabWidget.adjustViewerTabBarVisibility();
0351 
0352     tabWidget.show();
0353 
0354     connect(pvb, &PanelViewerBase::openUrlFinished, this, &KrViewer::openUrlFinished);
0355 
0356     connect(pvb, &PanelViewerBase::urlChanged, this, &KrViewer::tabURLChanged);
0357 }
0358 
0359 void KrViewer::tabURLChanged(PanelViewerBase *pvb, const QUrl &url)
0360 {
0361     Q_UNUSED(url)
0362     refreshTab(pvb);
0363 }
0364 
0365 void KrViewer::openUrlFinished(PanelViewerBase *pvb, bool success)
0366 {
0367     if (success) {
0368         KParts::ReadOnlyPart *part = pvb->part();
0369         if (part) {
0370             if (!isPartAdded(part))
0371                 addPart(part);
0372             if (tabWidget.currentWidget() == pvb) {
0373                 manager.setActivePart(part);
0374                 if (part->widget())
0375                     part->widget()->setFocus();
0376             }
0377         }
0378     } else {
0379         tabCloseRequest(tabWidget.currentIndex(), false);
0380     }
0381 }
0382 
0383 void KrViewer::tabChanged(int index)
0384 {
0385     QWidget *w = tabWidget.widget(index);
0386     if (!w)
0387         return;
0388     KParts::ReadOnlyPart *part = dynamic_cast<PanelViewerBase *>(w)->part();
0389     if (part && isPartAdded(part)) {
0390         manager.setActivePart(part);
0391         if (part->widget())
0392             part->widget()->setFocus();
0393     } else
0394         manager.setActivePart(nullptr);
0395 
0396     // set this viewer to be the main viewer
0397     if (viewers.removeAll(this))
0398         viewers.prepend(this); // move to first
0399 }
0400 
0401 void KrViewer::tabCloseRequest(int index, bool force)
0402 {
0403     // important to save as returnFocusTo will be cleared at removePart
0404     QWidget *returnFocusToThisWidget = returnFocusTo;
0405 
0406     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.widget(index));
0407     if (!pvb)
0408         return;
0409 
0410     if (!force && !pvb->queryClose())
0411         return;
0412 
0413     if (pvb->part() && isPartAdded(pvb->part()))
0414         removePart(pvb->part());
0415 
0416     disconnect(pvb, nullptr, this, nullptr);
0417 
0418     pvb->closeUrl();
0419 
0420     tabWidget.removeTab(index);
0421 
0422     delete pvb;
0423     pvb = nullptr;
0424 
0425     if (tabWidget.count() <= 0) {
0426         if (returnFocusToThisWidget) {
0427             returnFocusToThisWidget->raise();
0428             returnFocusToThisWidget->activateWindow();
0429         } else {
0430             krMainWindow->raise();
0431             krMainWindow->activateWindow();
0432         }
0433 
0434         QTimer::singleShot(0, this, &KrViewer::close);
0435     } else if (tabWidget.count() == 1) {
0436         // no point in detaching only one tab..
0437         detachAction->setEnabled(false);
0438         tabWidget.adjustViewerTabBarVisibility();
0439     }
0440 }
0441 
0442 void KrViewer::tabCloseRequest()
0443 {
0444     tabCloseRequest(tabWidget.currentIndex());
0445 }
0446 
0447 bool KrViewer::queryClose()
0448 {
0449     KConfigGroup group(krConfig, "KrViewerWindow");
0450 
0451     group.writeEntry("Window Width", sizeX);
0452     group.writeEntry("Window Height", sizeY);
0453     group.writeEntry("Window Maximized", isMaximized());
0454 
0455     for (int i = 0; i != tabWidget.count(); i++) {
0456         auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.widget(i));
0457         if (!pvb)
0458             continue;
0459 
0460         tabWidget.setCurrentIndex(i);
0461 
0462         if (!pvb->queryClose())
0463             return false;
0464     }
0465     return true;
0466 }
0467 
0468 void KrViewer::viewGeneric()
0469 {
0470     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0471     if (pvb)
0472         viewInternal(pvb->url(), Generic);
0473 }
0474 
0475 void KrViewer::viewText()
0476 {
0477     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0478     if (pvb)
0479         viewInternal(pvb->url(), Text);
0480 }
0481 
0482 void KrViewer::viewLister()
0483 {
0484     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0485     if (pvb)
0486         viewInternal(pvb->url(), Lister);
0487 }
0488 
0489 void KrViewer::viewHex()
0490 {
0491     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0492     if (pvb)
0493         viewInternal(pvb->url(), Hex);
0494 }
0495 
0496 void KrViewer::editText()
0497 {
0498     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0499     if (pvb)
0500         editInternal(pvb->url(), Text);
0501 }
0502 
0503 void KrViewer::checkModified()
0504 {
0505     QTimer::singleShot(CHECK_MODFIED_INTERVAL, this, &KrViewer::checkModified);
0506 
0507     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0508     if (pvb)
0509         refreshTab(pvb);
0510 }
0511 
0512 void KrViewer::refreshTab(PanelViewerBase *pvb)
0513 {
0514     int ndx = tabWidget.indexOf(pvb);
0515     tabWidget.setTabText(ndx, makeTabText(pvb));
0516     tabWidget.setTabIcon(ndx, makeTabIcon(pvb));
0517     tabWidget.setTabToolTip(ndx, makeTabToolTip(pvb));
0518 }
0519 
0520 void KrViewer::nextTab()
0521 {
0522     int index = (tabWidget.currentIndex() + 1) % tabWidget.count();
0523     tabWidget.setCurrentIndex(index);
0524 }
0525 
0526 void KrViewer::prevTab()
0527 {
0528     int index = (tabWidget.currentIndex() - 1) % tabWidget.count();
0529     while (index < 0)
0530         index += tabWidget.count();
0531     tabWidget.setCurrentIndex(index);
0532 }
0533 
0534 void KrViewer::detachTab()
0535 {
0536     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0537     if (!pvb)
0538         return;
0539 
0540     KrViewer *viewer = getViewer(true);
0541 
0542     bool wasPartAdded = false;
0543     KParts::ReadOnlyPart *part = pvb->part();
0544 
0545     if (part && isPartAdded(part)) {
0546         wasPartAdded = true;
0547         removePart(part);
0548     }
0549 
0550     disconnect(pvb, nullptr, this, nullptr);
0551 
0552     tabWidget.removeTab(tabWidget.indexOf(pvb));
0553 
0554     if (tabWidget.count() == 1) {
0555         // no point in detaching only one tab..
0556         detachAction->setEnabled(false);
0557         tabWidget.adjustViewerTabBarVisibility();
0558     }
0559 
0560     pvb->setParent(&viewer->tabWidget);
0561     pvb->move(QPoint(0, 0));
0562 
0563     viewer->addTab(pvb);
0564 
0565     if (wasPartAdded) {
0566         viewer->addPart(part);
0567         if (part->widget())
0568             part->widget()->setFocus();
0569     }
0570 
0571     viewer->show();
0572 }
0573 
0574 void KrViewer::changeEvent(QEvent *e)
0575 {
0576     if (e->type() == QEvent::ActivationChange && isActiveWindow())
0577         if (viewers.removeAll(this))
0578             viewers.prepend(this); // move to first
0579 }
0580 
0581 void KrViewer::print()
0582 {
0583     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0584     if (!pvb || !pvb->part() || !isPartAdded(pvb->part()))
0585         return;
0586 
0587     KParts::BrowserExtension *ext = KParts::BrowserExtension::childObject(pvb->part());
0588     if (ext && ext->isActionEnabled("print"))
0589         Invoker(ext, SLOT(print())).invoke();
0590 }
0591 
0592 void KrViewer::copy()
0593 {
0594     auto *pvb = dynamic_cast<PanelViewerBase *>(tabWidget.currentWidget());
0595     if (!pvb || !pvb->part() || !isPartAdded(pvb->part()))
0596         return;
0597 
0598     KParts::BrowserExtension *ext = KParts::BrowserExtension::childObject(pvb->part());
0599     if (ext && ext->isActionEnabled("copy"))
0600         Invoker(ext, SLOT(copy())).invoke();
0601 }
0602 
0603 void KrViewer::updateActions()
0604 {
0605     QList<QAction *> actList = toolBar()->actions();
0606     bool hasPrint = false, hasCopy = false;
0607     foreach (QAction *a, actList) {
0608         if (a->text() == printAction->text())
0609             hasPrint = true;
0610         if (a->text() == copyAction->text())
0611             hasCopy = true;
0612     }
0613     if (!hasPrint)
0614         toolBar()->addAction(printAction->icon(), printAction->text(), this, SLOT(print()));
0615     if (!hasCopy)
0616         toolBar()->addAction(copyAction->icon(), copyAction->text(), this, SLOT(copy()));
0617 }
0618 
0619 bool KrViewer::isPartAdded(KParts::Part *part)
0620 {
0621     return manager.parts().contains(part);
0622 }
0623 
0624 void KrViewer::resizeEvent(QResizeEvent *e)
0625 {
0626     if (!isMaximized()) {
0627         sizeX = e->size().width();
0628         sizeY = e->size().height();
0629     }
0630 
0631     KParts::MainWindow::resizeEvent(e);
0632 }
0633 
0634 QString KrViewer::makeTabText(PanelViewerBase *pvb)
0635 {
0636     QString fileName = pvb->url().fileName();
0637     if (pvb->isModified())
0638         fileName.prepend("*");
0639 
0640     return pvb->isEditor() ? i18nc("filename (filestate)", "%1 (Editing)", fileName) : i18nc("filename (filestate)", "%1 (Viewing)", fileName);
0641 }
0642 
0643 QString KrViewer::makeTabToolTip(PanelViewerBase *pvb)
0644 {
0645     QString url = pvb->url().toDisplayString(QUrl::PreferLocalFile);
0646     return pvb->isEditor() ? i18nc("filestate: filename", "Editing: %1", url) : i18nc("filestate: filename", "Viewing: %1", url);
0647 }
0648 
0649 QIcon KrViewer::makeTabIcon(PanelViewerBase *pvb)
0650 {
0651     QString iconName;
0652     if (pvb->isModified())
0653         iconName = MODIFIED_ICON;
0654     else if (pvb->isEditor())
0655         iconName = EDIT_ICON;
0656     else
0657         iconName = VIEW_ICON;
0658 
0659     return Icon(iconName);
0660 }
0661 
0662 void KrViewer::addPart(KParts::ReadOnlyPart *part)
0663 {
0664     Q_ASSERT(part);
0665     Q_ASSERT(!isPartAdded(part));
0666 
0667     if (isPartAdded(part)) {
0668         qDebug() << "part already added:" << part;
0669         return;
0670     }
0671 
0672     connect(part, SIGNAL(setStatusBarText(QString)), this, SLOT(slotSetStatusBarText(QString)));
0673     // filtering out the key events
0674     part->installEventFilter(this);
0675 
0676     manager.addPart(part, false); // don't automatically set active part
0677 }
0678 
0679 void KrViewer::removePart(KParts::ReadOnlyPart *part)
0680 {
0681     Q_ASSERT(part);
0682     Q_ASSERT(isPartAdded(part));
0683 
0684     if (isPartAdded(part)) {
0685         disconnect(part, nullptr, this, nullptr);
0686         part->removeEventFilter(this);
0687         manager.removePart(part);
0688     } else
0689         qDebug() << "part hasn't been added:" << part;
0690 }
0691 
0692 void KrViewer::viewInternal(QUrl url, Mode mode, QWidget *parent)
0693 {
0694     returnFocusTo = parent;
0695 
0696     PanelViewerBase *viewWidget = new PanelViewer(&tabWidget, mode);
0697 
0698     addTab(viewWidget);
0699     viewWidget->openUrl(std::move(url));
0700 }
0701 
0702 void KrViewer::editInternal(QUrl url, Mode mode, QWidget *parent)
0703 {
0704     returnFocusTo = parent;
0705 
0706     PanelViewerBase *editWidget = new PanelEditor(&tabWidget, mode);
0707 
0708     addTab(editWidget);
0709     editWidget->openUrl(std::move(url));
0710 }