diff --git a/krusader/KViewer/CMakeLists.txt b/krusader/KViewer/CMakeLists.txt index e2aba09a..2d449318 100644 --- a/krusader/KViewer/CMakeLists.txt +++ b/krusader/KViewer/CMakeLists.txt @@ -1,20 +1,21 @@ set(KViewer_SRCS krviewer.cpp panelviewer.cpp diskusageviewer.cpp - lister.cpp) + lister.cpp + viewertabbar.cpp) add_library(KViewer STATIC ${KViewer_SRCS}) target_link_libraries(KViewer Qt5::PrintSupport KF5::ConfigCore KF5::ConfigWidgets KF5::CoreAddons KF5::IconThemes KF5::KIOCore KF5::KIOFileWidgets KF5::Parts KF5::WidgetsAddons KF5::XmlGui ) diff --git a/krusader/KViewer/krviewer.cpp b/krusader/KViewer/krviewer.cpp index f21fabb9..0b7e2b22 100644 --- a/krusader/KViewer/krviewer.cpp +++ b/krusader/KViewer/krviewer.cpp @@ -1,712 +1,715 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2020 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krviewer.h" // QtCore #include #include #include #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../defaults.h" #include "../icon.h" #include "panelviewer.h" +#include "viewertabbar.h" #define VIEW_ICON "document-preview" #define EDIT_ICON "document-edit" #define MODIFIED_ICON "document-save-as" #define CHECK_MODFIED_INTERVAL 500 /* NOTE: Currently the code expects PanelViewer::openUrl() to be called only once in the panel viewer's life time - otherwise unexpected things might happen. */ QList KrViewer::viewers; KrViewer::KrViewer(QWidget *parent) : KParts::MainWindow(parent, (Qt::WindowFlags)KDE_DEFAULT_WINDOWFLAGS), manager(this, this), - tabBar(this), sizeX(-1), sizeY(-1) + tabWidget(this), sizeX(-1), sizeY(-1) { //setWFlags(Qt::WType_TopLevel | WDestructiveClose); setXMLFile("krviewer.rc"); // kpart-related xml file setHelpMenuEnabled(false); connect(&manager, &KParts::PartManager::activePartChanged, this, &KrViewer::createGUI); - connect(&tabBar, &QTabWidget::currentChanged, this, &KrViewer::tabChanged); - connect(&tabBar, &QTabWidget::tabCloseRequested, this, [=](int index) { tabCloseRequest(index, false); }); + connect(&tabWidget, &QTabWidget::currentChanged, this, &KrViewer::tabChanged); + connect(&tabWidget, &QTabWidget::tabCloseRequested, this, [=](int index) { tabCloseRequest(index, false); }); - tabBar.setDocumentMode(true); - tabBar.setMovable(true); - setCentralWidget(&tabBar); + tabWidget.setDocumentMode(true); + tabWidget.setMovable(true); + if (ViewerTabBar *tabBar = tabWidget.tabBar()) + connect(tabBar, &ViewerTabBar::closeTabSignal, this, [=](int index) { tabCloseRequest(index, false); }); + setCentralWidget(&tabWidget); printAction = KStandardAction::print(this, SLOT(print()), nullptr); copyAction = KStandardAction::copy(this, SLOT(copy()), nullptr); viewerMenu = new QMenu(this); QAction *tempAction; KActionCollection *ac = actionCollection(); #define addCustomMenuAction(name, text, slot, shortcut)\ tempAction = ac->addAction(name, this, slot);\ tempAction->setText(text);\ ac->setDefaultShortcut(tempAction, shortcut);\ viewerMenu->addAction(tempAction); addCustomMenuAction("genericViewer", i18n("&Generic Viewer"), SLOT(viewGeneric()), Qt::CTRL + Qt::SHIFT + Qt::Key_G); addCustomMenuAction("textViewer", i18n("&Text Viewer"), SLOT(viewText()), Qt::CTRL + Qt::SHIFT + Qt::Key_T); addCustomMenuAction("hexViewer", i18n("&Hex Viewer"), SLOT(viewHex()), Qt::CTRL + Qt::SHIFT + Qt::Key_H); addCustomMenuAction("lister", i18n("&Lister"), SLOT(viewLister()), Qt::CTRL + Qt::SHIFT + Qt::Key_L); viewerMenu->addSeparator(); addCustomMenuAction("textEditor", i18n("Text &Editor"), SLOT(editText()), Qt::CTRL + Qt::SHIFT + Qt::Key_E); viewerMenu->addSeparator(); QList actList = menuBar()->actions(); bool hasPrint = false, hasCopy = false; foreach(QAction *a, actList) { if (a->shortcut().matches(printAction->shortcut()) != QKeySequence::NoMatch) hasPrint = true; if (a->shortcut().matches(copyAction->shortcut()) != QKeySequence::NoMatch) hasCopy = true; } QAction *printAct = viewerMenu->addAction(printAction->icon(), printAction->text(), this, SLOT(print())); if (hasPrint) printAct->setShortcut(printAction->shortcut()); QAction *copyAct = viewerMenu->addAction(copyAction->icon(), copyAction->text(), this, SLOT(copy())); if (hasCopy) copyAct->setShortcut(copyAction->shortcut()); viewerMenu->addSeparator(); configKeysAction = ac->addAction(KStandardAction::KeyBindings, this, SLOT(configureShortcuts())); viewerMenu->addAction(configKeysAction); viewerMenu->addSeparator(); detachAction = ac->addAction("detachTab", this, SLOT(detachTab())); detachAction->setText(i18n("&Detach Tab")); //no point in detaching only one tab.. detachAction->setEnabled(false); ac->setDefaultShortcut(detachAction, Qt::META + Qt::Key_D); viewerMenu->addAction(detachAction); quitAction = ac->addAction(KStandardAction::Quit, this, SLOT(close())); viewerMenu->addAction(quitAction); QList shortcuts; tabCloseAction = ac->addAction("closeTab", this, SLOT(tabCloseRequest())); tabCloseAction->setText(i18n("&Close Current Tab")); shortcuts = KStandardShortcut::close(); shortcuts.append(Qt::Key_Escape); ac->setDefaultShortcuts(tabCloseAction, shortcuts); tabNextAction = ac->addAction("nextTab", this, SLOT(nextTab())); tabNextAction->setText(i18n("&Next Tab")); shortcuts = KStandardShortcut::tabNext(); shortcuts.append(Qt::CTRL + Qt::Key_Tab); // reenforce QTabWidget shortcut ac->setDefaultShortcuts(tabNextAction, shortcuts); tabPrevAction = ac->addAction("prevTab", this, SLOT(prevTab())); tabPrevAction->setText(i18n("&Previous Tab")); shortcuts = KStandardShortcut::tabPrev(); shortcuts.append(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab); // reenforce QTabWidget shortcut ac->setDefaultShortcuts(tabPrevAction, shortcuts); - tabBar.setTabsClosable(true); + tabWidget.setTabsClosable(true); checkModified(); KConfigGroup group(krConfig, "KrViewerWindow"); int sx = group.readEntry("Window Width", -1); int sy = group.readEntry("Window Height", -1); if (sx != -1 && sy != -1) resize(sx, sy); else resize(900, 700); if (group.readEntry("Window Maximized", false)) { setWindowState(windowState() | Qt::WindowMaximized); } // filtering out the key events menuBar() ->installEventFilter(this); } KrViewer::~KrViewer() { disconnect(&manager, SIGNAL(activePartChanged(KParts::Part*)), this, SLOT(createGUI(KParts::Part*))); viewers.removeAll(this); // close tabs before deleting tab bar - this avoids Qt bug 26115 // https://bugreports.qt-project.org/browse/QTBUG-26115 - while(tabBar.count()) - tabCloseRequest(tabBar.currentIndex(), true); + while(tabWidget.count()) + tabCloseRequest(tabWidget.currentIndex(), true); delete printAction; delete copyAction; } void KrViewer::configureDeps() { PanelEditor::configureDeps(); } void KrViewer::createGUI(KParts::Part* part) { KParts::MainWindow::createGUI(part); updateActions(); toolBar() ->show(); statusBar() ->show(); // the KParts part may override the viewer shortcuts. We prevent it // by installing an event filter on the menuBar() and the part reservedKeys.clear(); reservedKeyActions.clear(); QList list = viewerMenu->actions(); // also add the actions that are not in the menu... list << tabCloseAction << tabNextAction << tabPrevAction; // getting the key sequences of the viewer menu for (int w = 0; w != list.count(); w++) { QAction *act = list[ w ]; QList sequences = act->shortcuts(); foreach(QKeySequence keySeq, sequences) { for(int i = 0; i < keySeq.count(); ++i) { reservedKeys.push_back(keySeq[i]); reservedKeyActions.push_back(act); //the same action many times in case of multiple shortcuts } } } // and "fix" the menubar QList actList = menuBar()->actions(); viewerMenu->setTitle(i18n("&KrViewer")); QAction * act = menuBar() ->addMenu(viewerMenu); act->setData(QVariant(70)); menuBar() ->show(); } void KrViewer::configureShortcuts() { KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this); } bool KrViewer::eventFilter(QObject * /* watched */, QEvent * e) { // TODO: after porting to Qt5/KF5 we never catch *ANY* KeyPress or ShortcutOverride events here anymore. // Should look into if there is any way to fix it. Currently if a KPart has same shortcut as KrViewer then // it causes a conflict, messagebox shown to user and no action triggered. if (e->type() == QEvent::ShortcutOverride) { auto* ke = dynamic_cast( e); if (reservedKeys.contains(ke->key())) { ke->accept(); QAction *act = reservedKeyActions[ reservedKeys.indexOf(ke->key())]; if (act != nullptr) { // don't activate the close functions immediately! // it can cause crash if (act == tabCloseAction || act == quitAction) { QTimer::singleShot(0, act, &QAction::trigger); } else { act->activate(QAction::Trigger); } } return true; } } else if (e->type() == QEvent::KeyPress) { auto* ke = dynamic_cast( e); if (reservedKeys.contains(ke->key())) { ke->accept(); return true; } } return false; } KrViewer* KrViewer::getViewer(bool new_window) { if (!new_window) { if (viewers.isEmpty()) { viewers.prepend(new KrViewer()); // add to first (active) } else { if (viewers.first()->isMinimized()) { // minimized? -> show it again viewers.first()->setWindowState((viewers.first()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); viewers.first()->show(); } viewers.first()->raise(); viewers.first()->activateWindow(); } return viewers.first(); } else { auto *newViewer = new KrViewer(); viewers.prepend(newViewer); return newViewer; } } void KrViewer::view(QUrl url, QWidget * parent) { KConfigGroup group(krConfig, "General"); bool defaultWindow = group.readEntry("View In Separate Window", _ViewInSeparateWindow); view(std::move(url), Default, defaultWindow, parent); } void KrViewer::view(QUrl url, Mode mode, bool new_window, QWidget * parent) { KrViewer* viewer = getViewer(new_window); viewer->viewInternal(std::move(url), mode, parent); viewer->show(); } void KrViewer::edit(QUrl url, QWidget * parent) { edit(std::move(url), Text, -1, parent); } void KrViewer::edit(const QUrl& url, Mode mode, int new_window, QWidget * parent) { KConfigGroup group(krConfig, "General"); QString editor = group.readEntry("Editor", _Editor); if (new_window == -1) new_window = group.readEntry("View In Separate Window", _ViewInSeparateWindow); if (editor != "internal editor" && !editor.isEmpty()) { KProcess proc; QStringList cmdArgs = KShell::splitArgs(editor, KShell::TildeExpand); if (cmdArgs.isEmpty()) { KMessageBox::error(krMainWindow, i18nc("Arg is a string containing the bad quoting.", "Bad quoting in editor command:\n%1", editor)); return; } // if the file is local, pass a normal path and not a url. this solves // the problem for editors that aren't url-aware proc << cmdArgs << url.toDisplayString(QUrl::PreferLocalFile); if (!proc.startDetached()) KMessageBox::sorry(krMainWindow, i18n("Can not open \"%1\"", editor)); return; } KrViewer* viewer = getViewer(new_window); viewer->editInternal(url, mode, parent); viewer->show(); } void KrViewer::addTab(PanelViewerBase *pvb) { - int tabIndex = tabBar.addTab(pvb, makeTabIcon(pvb), makeTabText(pvb)); - tabBar.setCurrentIndex(tabIndex); - tabBar.setTabToolTip(tabIndex, makeTabToolTip(pvb)); + int tabIndex = tabWidget.addTab(pvb, makeTabIcon(pvb), makeTabText(pvb)); + tabWidget.setCurrentIndex(tabIndex); + tabWidget.setTabToolTip(tabIndex, makeTabToolTip(pvb)); updateActions(); // now we can offer the option to detach tabs (we have more than one) - if (tabBar.count() > 1) { + if (tabWidget.count() > 1) { detachAction->setEnabled(true); } - tabBar.show(); + tabWidget.show(); connect(pvb, &PanelViewerBase::openUrlFinished, this, &KrViewer::openUrlFinished); connect(pvb, &PanelViewerBase::urlChanged, this, &KrViewer::tabURLChanged); } void KrViewer::tabURLChanged(PanelViewerBase *pvb, const QUrl &url) { Q_UNUSED(url) refreshTab(pvb); } void KrViewer::openUrlFinished(PanelViewerBase *pvb, bool success) { if (success) { KParts::ReadOnlyPart *part = pvb->part(); if (part) { if (!isPartAdded(part)) addPart(part); - if (tabBar.currentWidget() == pvb) { + if (tabWidget.currentWidget() == pvb) { manager.setActivePart(part); if (part->widget()) part->widget()->setFocus(); } } } else { - tabCloseRequest(tabBar.currentIndex(), false); + tabCloseRequest(tabWidget.currentIndex(), false); } } void KrViewer::tabChanged(int index) { - QWidget *w = tabBar.widget(index); + QWidget *w = tabWidget.widget(index); if(!w) return; KParts::ReadOnlyPart *part = dynamic_cast(w)->part(); if (part && isPartAdded(part)) { manager.setActivePart(part); if (part->widget()) part->widget()->setFocus(); } else manager.setActivePart(nullptr); // set this viewer to be the main viewer if (viewers.removeAll(this)) viewers.prepend(this); // move to first } void KrViewer::tabCloseRequest(int index, bool force) { // important to save as returnFocusTo will be cleared at removePart QWidget *returnFocusToThisWidget = returnFocusTo; - auto *pvb = dynamic_cast(tabBar.widget(index)); + auto *pvb = dynamic_cast(tabWidget.widget(index)); if (!pvb) return; if (!force && !pvb->queryClose()) return; if (pvb->part() && isPartAdded(pvb->part())) removePart(pvb->part()); disconnect(pvb, nullptr, this, nullptr); pvb->closeUrl(); - tabBar.removeTab(index); + tabWidget.removeTab(index); delete pvb; pvb = nullptr; - if (tabBar.count() <= 0) { + if (tabWidget.count() <= 0) { if (returnFocusToThisWidget) { returnFocusToThisWidget->raise(); returnFocusToThisWidget->activateWindow(); } else { krMainWindow->raise(); krMainWindow->activateWindow(); } QTimer::singleShot(0, this, &KrViewer::close); - } else if (tabBar.count() == 1) { + } else if (tabWidget.count() == 1) { // no point in detaching only one tab.. detachAction->setEnabled(false); } } void KrViewer::tabCloseRequest() { - tabCloseRequest(tabBar.currentIndex()); + tabCloseRequest(tabWidget.currentIndex()); } bool KrViewer::queryClose() { KConfigGroup group(krConfig, "KrViewerWindow"); group.writeEntry("Window Width", sizeX); group.writeEntry("Window Height", sizeY); group.writeEntry("Window Maximized", isMaximized()); - for (int i = 0; i != tabBar.count(); i++) { - auto* pvb = dynamic_cast(tabBar.widget(i)); + for (int i = 0; i != tabWidget.count(); i++) { + auto* pvb = dynamic_cast(tabWidget.widget(i)); if (!pvb) continue; - tabBar.setCurrentIndex(i); + tabWidget.setCurrentIndex(i); if (!pvb->queryClose()) return false; } return true; } void KrViewer::viewGeneric() { - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) viewInternal(pvb->url(), Generic); } void KrViewer::viewText() { - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) viewInternal(pvb->url(), Text); } void KrViewer::viewLister() { - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) viewInternal(pvb->url(), Lister); } void KrViewer::viewHex() { - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) viewInternal(pvb->url(), Hex); } void KrViewer::editText() { - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) editInternal(pvb->url(), Text); } void KrViewer::checkModified() { QTimer::singleShot(CHECK_MODFIED_INTERVAL, this, &KrViewer::checkModified); - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) refreshTab(pvb); } void KrViewer::refreshTab(PanelViewerBase* pvb) { - int ndx = tabBar.indexOf(pvb); - tabBar.setTabText(ndx, makeTabText(pvb)); - tabBar.setTabIcon(ndx, makeTabIcon(pvb)); - tabBar.setTabToolTip(ndx, makeTabToolTip(pvb)); + int ndx = tabWidget.indexOf(pvb); + tabWidget.setTabText(ndx, makeTabText(pvb)); + tabWidget.setTabIcon(ndx, makeTabIcon(pvb)); + tabWidget.setTabToolTip(ndx, makeTabToolTip(pvb)); } void KrViewer::nextTab() { - int index = (tabBar.currentIndex() + 1) % tabBar.count(); - tabBar.setCurrentIndex(index); + int index = (tabWidget.currentIndex() + 1) % tabWidget.count(); + tabWidget.setCurrentIndex(index); } void KrViewer::prevTab() { - int index = (tabBar.currentIndex() - 1) % tabBar.count(); - while (index < 0) index += tabBar.count(); - tabBar.setCurrentIndex(index); + int index = (tabWidget.currentIndex() - 1) % tabWidget.count(); + while (index < 0) index += tabWidget.count(); + tabWidget.setCurrentIndex(index); } void KrViewer::detachTab() { - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (!pvb) return; KrViewer* viewer = getViewer(true); bool wasPartAdded = false; KParts::ReadOnlyPart *part = pvb->part(); if (part && isPartAdded(part)) { wasPartAdded = true; removePart(part); } disconnect(pvb, nullptr, this, nullptr); - tabBar.removeTab(tabBar.indexOf(pvb)); + tabWidget.removeTab(tabWidget.indexOf(pvb)); - if (tabBar.count() == 1) { + if (tabWidget.count() == 1) { //no point in detaching only one tab.. detachAction->setEnabled(false); } - pvb->setParent(&viewer->tabBar); + pvb->setParent(&viewer->tabWidget); pvb->move(QPoint(0, 0)); viewer->addTab(pvb); if (wasPartAdded) { viewer->addPart(part); if (part->widget()) part->widget()->setFocus(); } viewer->show(); } void KrViewer::changeEvent(QEvent *e) { if (e->type() == QEvent::ActivationChange && isActiveWindow()) if (viewers.removeAll(this)) viewers.prepend(this); // move to first } void KrViewer::print() { - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (!pvb || !pvb->part() || !isPartAdded(pvb->part())) return; KParts::BrowserExtension * ext = KParts::BrowserExtension::childObject(pvb->part()); if (ext && ext->isActionEnabled("print")) Invoker(ext, SLOT(print())).invoke(); } void KrViewer::copy() { - auto* pvb = dynamic_cast(tabBar.currentWidget()); + auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (!pvb || !pvb->part() || !isPartAdded(pvb->part())) return; KParts::BrowserExtension * ext = KParts::BrowserExtension::childObject(pvb->part()); if (ext && ext->isActionEnabled("copy")) Invoker(ext, SLOT(copy())).invoke(); } void KrViewer::updateActions() { QList actList = toolBar()->actions(); bool hasPrint = false, hasCopy = false; foreach(QAction *a, actList) { if (a->text() == printAction->text()) hasPrint = true; if (a->text() == copyAction->text()) hasCopy = true; } if (!hasPrint) toolBar()->addAction(printAction->icon(), printAction->text(), this, SLOT(print())); if (!hasCopy) toolBar()->addAction(copyAction->icon(), copyAction->text(), this, SLOT(copy())); } bool KrViewer::isPartAdded(KParts::Part* part) { return manager.parts().contains(part); } void KrViewer::resizeEvent(QResizeEvent *e) { if (!isMaximized()) { sizeX = e->size().width(); sizeY = e->size().height(); } KParts::MainWindow::resizeEvent(e); } QString KrViewer::makeTabText(PanelViewerBase *pvb) { QString fileName = pvb->url().fileName(); if (pvb->isModified()) fileName.prepend("*"); return pvb->isEditor() ? i18nc("filename (filestate)", "%1 (Editing)", fileName) : i18nc("filename (filestate)", "%1 (Viewing)", fileName); } QString KrViewer::makeTabToolTip(PanelViewerBase *pvb) { QString url = pvb->url().toDisplayString(QUrl::PreferLocalFile); return pvb->isEditor() ? i18nc("filestate: filename", "Editing: %1", url) : i18nc("filestate: filename", "Viewing: %1", url); } QIcon KrViewer::makeTabIcon(PanelViewerBase *pvb) { QString iconName; if (pvb->isModified()) iconName = MODIFIED_ICON; else if (pvb->isEditor()) iconName = EDIT_ICON; else iconName = VIEW_ICON; return Icon(iconName); } void KrViewer::addPart(KParts::ReadOnlyPart *part) { Q_ASSERT(part); Q_ASSERT(!isPartAdded(part)); if (isPartAdded(part)) { qDebug()<<"part already added:"<installEventFilter(this); manager.addPart(part, false); // don't automatically set active part } void KrViewer::removePart(KParts::ReadOnlyPart *part) { Q_ASSERT(part); Q_ASSERT(isPartAdded(part)); if (isPartAdded(part)) { disconnect(part, nullptr, this, nullptr); part->removeEventFilter(this); manager.removePart(part); } else qDebug()<<"part hasn't been added:"<openUrl(std::move(url)); } void KrViewer::editInternal(QUrl url, Mode mode, QWidget * parent) { returnFocusTo = parent; - PanelViewerBase* editWidget = new PanelEditor(&tabBar, mode); + PanelViewerBase* editWidget = new PanelEditor(&tabWidget, mode); addTab(editWidget); editWidget->openUrl(std::move(url)); } diff --git a/krusader/KViewer/krviewer.h b/krusader/KViewer/krviewer.h index 64ff86f7..9daffc3a 100644 --- a/krusader/KViewer/krviewer.h +++ b/krusader/KViewer/krviewer.h @@ -1,154 +1,155 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2020 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRVIEWER_H #define KRVIEWER_H // QtCore #include #include #include #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include "../krglobal.h" +#include "viewertabbar.h" class PanelViewerBase; class KrViewer : public KParts::MainWindow { Q_OBJECT public: virtual ~KrViewer(); enum Mode {Generic, Text, Hex, Lister, Default}; static void view(QUrl url, QWidget * parent = krMainWindow); static void view(QUrl url, Mode mode, bool new_window, QWidget * parent = krMainWindow); static void edit(QUrl url, QWidget * parent); static void edit(const QUrl& url, Mode mode = Text, int new_window = -1, QWidget * parent = krMainWindow); static void configureDeps(); virtual bool eventFilter(QObject * watched, QEvent * e) override; public slots: void createGUI(KParts::Part*); void configureShortcuts(); void viewGeneric(); void viewText(); void viewHex(); void viewLister(); void editText(); void print(); void copy(); void tabChanged(int index); void tabURLChanged(PanelViewerBase * pvb, const QUrl &url); void tabCloseRequest(int index, bool force = false); void tabCloseRequest(); void nextTab(); void prevTab(); void detachTab(); void checkModified(); protected: virtual bool queryClose() override; virtual void changeEvent(QEvent *e) override; virtual void resizeEvent(QResizeEvent *e) override; virtual void focusInEvent(QFocusEvent *) override { if (viewers.removeAll(this)) viewers.prepend(this); } // move to first private slots: void openUrlFinished(PanelViewerBase *pvb, bool success); private: explicit KrViewer(QWidget *parent = 0); void addTab(PanelViewerBase* pvb); void updateActions(); void refreshTab(PanelViewerBase* pvb); void viewInternal(QUrl url, Mode mode, QWidget * parent = krMainWindow); void editInternal(QUrl url, Mode mode, QWidget * parent = krMainWindow); void addPart(KParts::ReadOnlyPart *part); void removePart(KParts::ReadOnlyPart *part); bool isPartAdded(KParts::Part* part); static KrViewer* getViewer(bool new_window); static QString makeTabText(PanelViewerBase *pvb); static QString makeTabToolTip(PanelViewerBase *pvb); static QIcon makeTabIcon(PanelViewerBase *pvb); KParts::PartManager manager; QMenu* viewerMenu; - QTabWidget tabBar; + ViewerTabWidget tabWidget; QPointer returnFocusTo; QAction *detachAction; QAction *printAction; QAction *copyAction; QAction *quitAction; QAction *configKeysAction; QAction *tabCloseAction; QAction *tabNextAction; QAction *tabPrevAction; static QList viewers; // the first viewer is the active one QList reservedKeys; // the reserved key sequences QList reservedKeyActions; // the IDs of the reserved keys int sizeX; int sizeY; }; class Invoker : public QObject { Q_OBJECT public: Invoker(QObject *recv, const char * slot) { connect(this, SIGNAL(invokeSignal()), recv, slot); } void invoke() { emit invokeSignal(); } signals: void invokeSignal(); }; #endif diff --git a/krusader/KViewer/viewertabbar.cpp b/krusader/KViewer/viewertabbar.cpp new file mode 100644 index 00000000..a551f611 --- /dev/null +++ b/krusader/KViewer/viewertabbar.cpp @@ -0,0 +1,53 @@ +/***************************************************************************** + * Copyright (C) 2020 Krusader Krew [https://krusader.org] * + * * + * This file is part of Krusader [https://krusader.org]. * + * * + * Krusader is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Krusader is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * + *****************************************************************************/ + +#include "viewertabbar.h" + +#include +// QtGui +#include + +ViewerTabWidget::ViewerTabWidget(QWidget *parent) : QTabWidget(parent) +{ + setTabBar(new ViewerTabBar(this)); +} + +ViewerTabBar *ViewerTabWidget::tabBar() const +{ + return dynamic_cast(QTabWidget::tabBar()); +} + +void ViewerTabBar::mousePressEvent(QMouseEvent *e) +{ + int clickedTab = tabAt(e->pos()); + + if (-1 == clickedTab) { // clicked on nothing ... + QTabBar::mousePressEvent(e); + return; + } + + setCurrentIndex(clickedTab); + + if (e->button() == Qt::MidButton) { + // close the current tab + emit closeTabSignal(clickedTab); + } + + QTabBar::mousePressEvent(e); +} diff --git a/krusader/KViewer/viewertabbar.h b/krusader/KViewer/viewertabbar.h new file mode 100644 index 00000000..f197de37 --- /dev/null +++ b/krusader/KViewer/viewertabbar.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * Copyright (C) 2020 Krusader Krew [https://krusader.org] * + * * + * This file is part of Krusader [https://krusader.org]. * + * * + * Krusader is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Krusader is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * + *****************************************************************************/ + +#ifndef VIEWERTABBAR_H +#define VIEWERTABBAR_H + +// QtWidgets +#include +#include +#include + +class ViewerTabBar; + +/** + * This class extends QTabWidget such that we can use a custom QTabBar on it + */ +class ViewerTabWidget : public QTabWidget +{ + Q_OBJECT +public: + explicit ViewerTabWidget(QWidget *parent); + ViewerTabBar *tabBar() const; +}; + +/** + * This class extends QTabBar such that right-clicking on a tab pops-up a menu + * containing relevant actions for the tab. It also emits signals to close the + * current tab. + */ +class ViewerTabBar : public QTabBar +{ + Q_OBJECT +public: + explicit ViewerTabBar(QWidget *parent) : QTabBar(parent) {}; + +protected: + void mousePressEvent(QMouseEvent *) override; + +signals: + /** + * emitted when the user mid-clicks on the tab + */ + void closeTabSignal(int index); +}; + +#endif