diff --git a/krusader/KViewer/krviewer.cpp b/krusader/KViewer/krviewer.cpp index 0b7e2b22..2d76dbfc 100644 --- a/krusader/KViewer/krviewer.cpp +++ b/krusader/KViewer/krviewer.cpp @@ -1,715 +1,717 @@ /***************************************************************************** * 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), 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(&tabWidget, &QTabWidget::currentChanged, this, &KrViewer::tabChanged); connect(&tabWidget, &QTabWidget::tabCloseRequested, this, [=](int index) { tabCloseRequest(index, false); }); 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); 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(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 = 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 (tabWidget.count() > 1) { + if (tabWidget.count() > 1) detachAction->setEnabled(true); - } + tabWidget.adjustViewerTabBarVisibility(); 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 (tabWidget.currentWidget() == pvb) { manager.setActivePart(part); if (part->widget()) part->widget()->setFocus(); } } } else { tabCloseRequest(tabWidget.currentIndex(), false); } } void KrViewer::tabChanged(int 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(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(); tabWidget.removeTab(index); delete pvb; pvb = nullptr; if (tabWidget.count() <= 0) { if (returnFocusToThisWidget) { returnFocusToThisWidget->raise(); returnFocusToThisWidget->activateWindow(); } else { krMainWindow->raise(); krMainWindow->activateWindow(); } QTimer::singleShot(0, this, &KrViewer::close); } else if (tabWidget.count() == 1) { // no point in detaching only one tab.. detachAction->setEnabled(false); + tabWidget.adjustViewerTabBarVisibility(); } } void KrViewer::tabCloseRequest() { 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 != tabWidget.count(); i++) { auto* pvb = dynamic_cast(tabWidget.widget(i)); if (!pvb) continue; tabWidget.setCurrentIndex(i); if (!pvb->queryClose()) return false; } return true; } void KrViewer::viewGeneric() { auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) viewInternal(pvb->url(), Generic); } void KrViewer::viewText() { auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) viewInternal(pvb->url(), Text); } void KrViewer::viewLister() { auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) viewInternal(pvb->url(), Lister); } void KrViewer::viewHex() { auto* pvb = dynamic_cast(tabWidget.currentWidget()); if (pvb) viewInternal(pvb->url(), Hex); } void KrViewer::editText() { 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(tabWidget.currentWidget()); if (pvb) refreshTab(pvb); } void KrViewer::refreshTab(PanelViewerBase* 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 = (tabWidget.currentIndex() + 1) % tabWidget.count(); tabWidget.setCurrentIndex(index); } void KrViewer::prevTab() { int index = (tabWidget.currentIndex() - 1) % tabWidget.count(); while (index < 0) index += tabWidget.count(); tabWidget.setCurrentIndex(index); } void KrViewer::detachTab() { 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); tabWidget.removeTab(tabWidget.indexOf(pvb)); if (tabWidget.count() == 1) { //no point in detaching only one tab.. detachAction->setEnabled(false); + tabWidget.adjustViewerTabBarVisibility(); } 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(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(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(&tabWidget, mode); addTab(editWidget); editWidget->openUrl(std::move(url)); } diff --git a/krusader/KViewer/viewertabbar.cpp b/krusader/KViewer/viewertabbar.cpp index a551f611..ee64410b 100644 --- a/krusader/KViewer/viewertabbar.cpp +++ b/krusader/KViewer/viewertabbar.cpp @@ -1,53 +1,71 @@ /***************************************************************************** * 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 +#include + +#include "../defaults.h" +#include "../krglobal.h" + ViewerTabWidget::ViewerTabWidget(QWidget *parent) : QTabWidget(parent) { setTabBar(new ViewerTabBar(this)); } +void ViewerTabWidget::adjustViewerTabBarVisibility() +{ + if (count() > 1) { + tabBar()->show(); + } else if (count() == 1) { + KConfigGroup group(krConfig, "General"); + bool hideSingleTab = group.readEntry("Viewer Hide Single Tab", _ViewerHideSingleTab); + if (hideSingleTab) + tabBar()->hide(); + } + return; +} + + 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 index f197de37..ded7843b 100644 --- a/krusader/KViewer/viewertabbar.h +++ b/krusader/KViewer/viewertabbar.h @@ -1,62 +1,64 @@ /***************************************************************************** * 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); + void adjustViewerTabBarVisibility(); + 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 diff --git a/krusader/Konfigurator/kggeneral.cpp b/krusader/Konfigurator/kggeneral.cpp index 2ed039c2..97047845 100644 --- a/krusader/Konfigurator/kggeneral.cpp +++ b/krusader/Konfigurator/kggeneral.cpp @@ -1,332 +1,336 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * 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 "kggeneral.h" // QtCore #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #include "krresulttabledialog.h" #include "../defaults.h" #include "../icon.h" #include "../krglobal.h" #define PAGE_GENERAL 0 #define PAGE_VIEWER 1 #define PAGE_EXTENSIONS 2 KgGeneral::KgGeneral(bool first, QWidget* parent) : KonfiguratorPage(first, parent) { if (first) slotFindTools(); tabWidget = new QTabWidget(this); setWidget(tabWidget); setWidgetResizable(true); createGeneralTab(); createViewerTab(); createExtensionsTab(); } QWidget* KgGeneral::createTab(const QString& name) { auto *scrollArea = new QScrollArea(tabWidget); tabWidget->addTab(scrollArea, name); scrollArea->setFrameStyle(QFrame::NoFrame); scrollArea->setWidgetResizable(true); QWidget *tab = new QWidget(scrollArea); scrollArea->setWidget(tab); return tab; } void KgGeneral::createViewerTab() { QWidget *tab = createTab(i18n("Viewer/Editor")); auto *tabLayout = new QGridLayout(tab); tabLayout->setSpacing(6); tabLayout->setContentsMargins(11, 11, 11, 11); tabLayout->addWidget(createCheckBox("General", "View In Separate Window", _ViewInSeparateWindow, i18n("Internal editor and viewer opens each file in a separate window"), tab, false, i18n("If checked, each file will open in a separate window, otherwise, the viewer will work in a single, tabbed mode"), PAGE_VIEWER)); + tabLayout->addWidget(createCheckBox("General", "Viewer Hide Single Tab", _ViewerHideSingleTab, + i18n("Hide the tab bar when only one tab is opened"), tab, false, + i18n("If checked, the Viewer tab bar will hide if only one tab is open"), PAGE_VIEWER)); + // ------------------------- viewer ---------------------------------- QGroupBox *viewerGrp = createFrame(i18n("Viewer"), tab); - tabLayout->addWidget(viewerGrp, 1, 0); + tabLayout->addWidget(viewerGrp, 2, 0); QGridLayout *viewerGrid = createGridLayout(viewerGrp); QWidget * hboxWidget2 = new QWidget(viewerGrp); auto * hbox2 = new QHBoxLayout(hboxWidget2); QWidget * vboxWidget = new QWidget(hboxWidget2); auto * vbox = new QVBoxLayout(vboxWidget); vbox->addWidget(new QLabel(i18n("Default viewer mode:"), vboxWidget)); KONFIGURATOR_NAME_VALUE_TIP viewMode[] = // name value tooltip {{ i18n("Generic mode"), "generic", i18n("Use the system's default viewer") }, { i18n("Text mode"), "text", i18n("View the file in text-only mode") }, { i18n("Hex mode"), "hex", i18n("View the file in hex-mode (better for binary files)") }, { i18n("Lister mode"), "lister", i18n("View the file with lister (for huge text files)") } }; vbox->addWidget(createRadioButtonGroup("General", "Default Viewer Mode", "generic", 0, 4, viewMode, 4, vboxWidget, false, PAGE_VIEWER)); vbox->addWidget( createCheckBox("General", "UseOktetaViewer", _UseOktetaViewer, i18n("Use Okteta as Hex viewer"), vboxWidget, false, i18n("If available, use Okteta as Hex viewer instead of the internal viewer"), PAGE_VIEWER) ); QWidget * hboxWidget4 = new QWidget(vboxWidget); auto * hbox4 = new QHBoxLayout(hboxWidget4); const QString listerLimitTip = i18n("If a text file is bigger than this size then lister will be used."); QLabel *label5 = new QLabel(i18n("Use lister if the text file is bigger than:"), hboxWidget4); hbox4->addWidget(label5); KonfiguratorSpinBox *spinBox = createSpinBox("General", "Lister Limit", _ListerLimit, 0, 0x7FFFFFFF, label5, hboxWidget4, false, listerLimitTip, PAGE_VIEWER); hbox4->addWidget(spinBox); QLabel *label6 = new QLabel(i18n("MB"), hboxWidget4); hbox4->addWidget(label6); vbox->addWidget(hboxWidget4); hbox2->addWidget(vboxWidget); viewerGrid->addWidget(hboxWidget2, 0, 0); // ------------------------- editor ---------------------------------- QGroupBox *editorGrp = createFrame(i18n("Editor"), tab); - tabLayout->addWidget(editorGrp, 2, 0); + tabLayout->addWidget(editorGrp, 3, 0); QGridLayout *editorGrid = createGridLayout(editorGrp); QLabel *label1 = new QLabel(i18n("Editor:"), editorGrp); editorGrid->addWidget(label1, 0, 0); KonfiguratorURLRequester *urlReq = createURLRequester("General", "Editor", "internal editor", label1, editorGrp, false, QString(), PAGE_VIEWER, false); editorGrid->addWidget(urlReq, 0, 1); QLabel *label2 = new QLabel(i18n("Hint: use 'internal editor' if you want to use Krusader's fast built-in editor"), editorGrp); editorGrid->addWidget(label2, 1, 0, 1, 2); } void KgGeneral::createExtensionsTab() { // ------------------------- atomic extensions ---------------------------------- QWidget *tab = createTab(i18n("Atomic extensions")); auto *tabLayout = new QGridLayout(tab); tabLayout->setSpacing(6); tabLayout->setContentsMargins(11, 11, 11, 11); QWidget * vboxWidget2 = new QWidget(tab); tabLayout->addWidget(vboxWidget2); auto * vbox2 = new QVBoxLayout(vboxWidget2); QWidget * hboxWidget3 = new QWidget(vboxWidget2); vbox2->addWidget(hboxWidget3); auto * hbox3 = new QHBoxLayout(hboxWidget3); QLabel * atomLabel = new QLabel(i18n("Atomic extensions:"), hboxWidget3); hbox3->addWidget(atomLabel); int size = QFontMetrics(atomLabel->font()).height(); auto *addButton = new QToolButton(hboxWidget3); hbox3->addWidget(addButton); QPixmap iconPixmap = Icon("list-add").pixmap(size); addButton->setFixedSize(iconPixmap.width() + 4, iconPixmap.height() + 4); addButton->setIcon(QIcon(iconPixmap)); connect(addButton, &QToolButton::clicked, this, &KgGeneral::slotAddExtension); auto *removeButton = new QToolButton(hboxWidget3); hbox3->addWidget(removeButton); iconPixmap = Icon("list-remove").pixmap(size); removeButton->setFixedSize(iconPixmap.width() + 4, iconPixmap.height() + 4); removeButton->setIcon(QIcon(iconPixmap)); connect(removeButton, &QToolButton::clicked, this, &KgGeneral::slotRemoveExtension); QStringList defaultAtomicExtensions; defaultAtomicExtensions += ".tar.gz"; defaultAtomicExtensions += ".tar.bz2"; defaultAtomicExtensions += ".tar.lzma"; defaultAtomicExtensions += ".tar.xz"; defaultAtomicExtensions += ".moc.cpp"; listBox = createListBox("Look&Feel", "Atomic Extensions", defaultAtomicExtensions, vboxWidget2, true, QString(), PAGE_EXTENSIONS); vbox2->addWidget(listBox); } void KgGeneral::createGeneralTab() { QWidget *tab = createTab(i18n("General")); auto *kgGeneralLayout = new QGridLayout(tab); kgGeneralLayout->setSpacing(6); kgGeneralLayout->setContentsMargins(11, 11, 11, 11); // -------------------------- GENERAL GROUPBOX ---------------------------------- QGroupBox *generalGrp = createFrame(i18n("General"), tab); QGridLayout *generalGrid = createGridLayout(generalGrp); KONFIGURATOR_CHECKBOX_PARAM settings[] = { // cfg_class cfg_name default text restart tooltip {"Look&Feel", "Warn On Exit", _WarnOnExit, i18n("Warn on exit"), false, i18n("Display a warning when trying to close the main window.") }, // KDE4: move warn on exit to the other confirmations {"Look&Feel", "Minimize To Tray", _ShowTrayIcon, i18n("Show and close to tray"), false, i18n("Show an icon in the system tray and keep running in the background when the window is closed.") }, }; KonfiguratorCheckBoxGroup *cbs = createCheckBoxGroup(2, 0, settings, 2 /*count*/, generalGrp, PAGE_GENERAL); generalGrid->addWidget(cbs, 0, 0); // temp dir auto *hbox = new QHBoxLayout(); QLabel *labelGrp = new QLabel(i18n("Temp Folder:"), generalGrp); hbox->addWidget(labelGrp); KonfiguratorURLRequester *urlReq3 = createURLRequester("General", "Temp Directory", _TempDirectory, labelGrp, generalGrp, false, PAGE_GENERAL); urlReq3->setMode(KFile::Directory); connect(urlReq3->extension(), &KonfiguratorExtension::applyManually, this, &KgGeneral::applyTempDir); hbox->addWidget(urlReq3); generalGrid->addLayout(hbox, 13, 0, 1, 1); QLabel *label4 = new QLabel(i18n("Note: you must have full permissions for the temporary folder."), generalGrp); generalGrid->addWidget(label4, 14, 0, 1, 1); kgGeneralLayout->addWidget(generalGrp, 0 , 0); // ----------------------- delete mode -------------------------------------- QGroupBox *delGrp = createFrame(i18n("Delete mode"), tab); QGridLayout *delGrid = createGridLayout(delGrp); KONFIGURATOR_NAME_VALUE_TIP deleteMode[] = // name value tooltip {{i18n("Move to trash"), "true", i18n("Files will be moved to trash when deleted.")}, {i18n("Delete files"), "false", i18n("Files will be permanently deleted.")} }; KonfiguratorRadioButtons *trashRadio = createRadioButtonGroup("General", "Move To Trash", _MoveToTrash ? "true" : "false", 2, 0, deleteMode, 2, delGrp, false, PAGE_GENERAL); delGrid->addWidget(trashRadio); kgGeneralLayout->addWidget(delGrp, 1 , 0); // ----------------------- terminal ----------------------------------------- QGroupBox *terminalGrp = createFrame(i18n("Terminal"), tab); QGridLayout *terminalGrid = createGridLayout(terminalGrp); QLabel *label3 = new QLabel(i18n("External Terminal:"), generalGrp); terminalGrid->addWidget(label3, 0, 0); KonfiguratorURLRequester *urlReq2 = createURLRequester("General", "Terminal", _Terminal, label3, generalGrp, false, PAGE_GENERAL, false); terminalGrid->addWidget(urlReq2, 0, 1); QLabel *terminalLabel = new QLabel(i18n("%d will be replaced by the workdir."), terminalGrp); terminalGrid->addWidget(terminalLabel, 1, 1); KONFIGURATOR_CHECKBOX_PARAM terminal_settings[] = { // cfg_class cfg_name default text restart tooltip {"General", "Send CDs", _SendCDs, i18n("Embedded Terminal sends Chdir on panel change"), false, i18n("When checked, whenever the panel is changed (for example, by pressing Tab), Krusader changes the current folder in the embedded terminal.") }, }; cbs = createCheckBoxGroup(1, 0, terminal_settings, 1 /*count*/, terminalGrp, PAGE_GENERAL); terminalGrid->addWidget(cbs, 2, 0, 1, 2); kgGeneralLayout->addWidget(terminalGrp, 2 , 0); } void KgGeneral::applyTempDir(QObject *obj, const QString& configGroup, const QString& name) { auto *urlReq = qobject_cast(obj); QString value = urlReq->url().toDisplayString(QUrl::PreferLocalFile); KConfigGroup(krConfig, configGroup).writeEntry(name, value); } void KgGeneral::slotFindTools() { QPointer dlg = new KrResultTableDialog(this, KrResultTableDialog::Tool, i18n("Search results"), i18n("Searching for tools..."), "tools-wizard", i18n("Make sure to install new tools in your $PATH (e.g. /usr/bin)")); dlg->exec(); delete dlg; } void KgGeneral::slotAddExtension() { bool ok; QString atomExt = QInputDialog::getText(this, i18n("Add new atomic extension"), i18n("Extension:"), QLineEdit::Normal, QString(), &ok); if (ok) { if (!atomExt.startsWith('.') || atomExt.indexOf('.', 1) == -1) KMessageBox::error(krMainWindow, i18n("Atomic extensions must start with '.' and must contain at least one more '.' character."), i18n("Error")); else listBox->addItem(atomExt); } } void KgGeneral::slotRemoveExtension() { QList list = listBox->selectedItems(); for (int i = 0; i != list.count(); i++) listBox->removeItem(list[ i ]->text()); } diff --git a/krusader/defaults.h b/krusader/defaults.h index 27181bef..229033e2 100644 --- a/krusader/defaults.h +++ b/krusader/defaults.h @@ -1,336 +1,338 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 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 DEFAULTS_H #define DEFAULTS_H // QtGui #include /////////////////////// [Startup] // UI Save component Settings #define _UiSave true // Show Cmd Line #define _ShowCmdline false // Show status bar #define _ShowStatusBar true // Show actions tool bar #define _ShowActionsToolBar true // Show tool bar #define _ShowToolBar true // Show FN Keys #define _ShowFNkeys true // Show Terminal Emulator #define _ShowTerminalEmulator false // Remember Position #define _RememberPos true // Start to tray #define _StartToTray false // Left Tab Bar // Right Tab Bar // Size where lister is the default viewer #define _ListerLimit 10 ////////////////////////[Look&Feel] // Filelist Font /////// #define _FilelistFont QFontDatabase::systemFont(QFontDatabase::GeneralFont) // Warn On Exit //////// #define _WarnOnExit false // Minimize To Tray //// #define _ShowTrayIcon false // Mark Dirs /////////// #define _MarkDirs false // Show Hidden ///////// #define _ShowHidden true // Sort By Extension /// #define _SortByExt false // Case Sensative Sort / #define _CaseSensativeSort false // Html Min Font Size // #define _HtmlMinFontSize 12 // Filelist Icon Size // #define _FilelistIconSize QString("22") // Mouse Selection ///// #define _MouseSelection 0 // 0 - normal (shift+click, ctrl+click), 1 - left click, 2 - right click // Use fullpath tab names ///// #define _FullPathTabNames false // User defined folder icons #define _UserDefinedFolderIcons true // Always show current item decoration in panel #define _AlwaysShowCurrentItem true // Unslect files before copy/move #define _UnselectBeforeOperation false // Filter dialog remembers settings #define _FilterDialogRemembersSettings false // Panel Toolbar Checkboxes // Panel Toolbar Visible checkbox turned off #define _PanelToolBar true // cd / is turned on #define _cdRoot true // cd ~ is turned on #define _cdHome false // cd .. is turned on #define _cdUp true // cd other panel is turned on #define _cdOther false // syncBrowseButton is turned on #define _syncBrowseButton false // Use the default colors of KDE #define _KDEDefaultColors true // Enable Alternate Background colors #define _AlternateBackground true // Show current item even if not focused #define _ShowCurrentItemAlways false // Dim the colors of the inactive panel #define _DimInactiveColors false // Human Readable Size #define _HumanReadableSize true // With Icons #define _WithIcons true // Single Click Selects #define _SingleClickSelects false // Numeric Permissions #define _NumericPermissions false // Number of Columns in the Brief View #define _NumberOfBriefColumns 3 // Default Sort Method #define _DefaultSortMethod KrViewProperties::Krusader // Show splashscreen #define _ShowSplashScreen false // Single instance mode #define _SingleInstanceMode false /////////////////////// [General] // Move To Trash ////// #define _MoveToTrash true // Terminal /////////// #define _Terminal "konsole --separate" // Send CDs /////////// #define _SendCDs true // Editor ///////////// #define _Editor "internal editor" // Use Okteta as Hex viewer /////// #define _UseOktetaViewer false // Temp Directory ///// #define _TempDirectory "/tmp/krusader.tmp" // Classic Quicksearch #define _NewStyleQuicksearch true // Case Sensitive quick search, if _NewStyleQuicksearch is true #define _CaseSensitiveQuicksearch false // Special handling of Right Arrow in Quicksearch #define _NavigationWithRightArrowQuicksearch true // View In Separate Window #define _ViewInSeparateWindow false +// Hide Single Tab in Viewer +#define _ViewerHideSingleTab false /////////////////////// [Advanced] // Permission Check /// //#define _PermCheck true // AutoMount ////////// #define _AutoMount false // Preserving date ////////// #define _PreserveAttributes false // Nonmount Points //// #define _NonMountPoints "/, " // Confirm Unempty Dir // (for delete) #define _ConfirmUnemptyDir true // Confirm Delete ///// (for deleting files) #define _ConfirmDelete true // Confirm Copy /////// (for copying files) #define _ConfirmCopy true // Confirm Move /////// (for moving files) #define _ConfirmMove true // Icon Cache Size //// #define _IconCacheSize 2048 /////////////////////// [Archives] // Do Tar ///////////// #define _DoTar true // Do GZip //////////// #define _DoGZip true // Do Zip ///////////// #define _DoZip true // Do UnZip /////////// #define _DoUnZip true // Do BZip2 /////////// #define _DoBZip2 true // Do LZMA /////////// #define _DoLZMA true // Do XZ /////////// #define _DoXZ true // Do Rar ///////////// #define _DoRar true // Do UnRar /////////// #define _DoUnRar true // Do UnAce /////////// #define _DoUnAce true // Do Arj ///////////// #define _DoArj true // Do UnArj /////////// #define _DoUnarj true // Do RPM ///////////// #define _DoRPM true // Do DEB ///////////// ====> new #define _DoDEB true // Do Lha ///////////// #define _DoLha true // Do 7z ///////////// ====> new #define _Do7z true // Allow Move Into Archive // #define _MoveIntoArchive false // Test Archives ////// #define _TestArchives false // Test Before Unpack //// #define _TestBeforeUnpack true // Supported Packers // ====> a QStringList of SYSTEM supported archives ( also new ) // default compression level #define _defaultCompressionLevel 5 // treat Archives as Directories #define _ArchivesAsDirectories true /////////////////////// [UserActions] // Terminal for UserActions /////////// #define _UserActions_Terminal "konsole --noclose --workdir %d --title %t -e" // Normal font for output collection /////// #define _UserActions_NormalFont QFontDatabase::systemFont(QFontDatabase::GeneralFont) // Font for output collection with fixed width /////// #define _UserActions_FixedFont QFontDatabase::systemFont(QFontDatabase::FixedFont) // Use for output collection fixed width font as default /////// #define _UserActions_UseFixedFont false /////////////////////// [Private] // Start Position ///// #define _StartPosition QPoint(QApplication::desktop()->width()/2 - MAIN_VIEW->sizeHint().width()/2,QApplication::desktop()->height()/2 - 250) // Start Size ///////// #define _StartSize QSize(MAIN_VIEW->sizeHint().width(),500) // Panel Size ///////// #define _PanelSize 0 // Terminal Size ////// #define _TerminalSize 0 // Left Name Size - size of the left panel's name column // Left Size Size - size of the left panel's size column // Left Date Size - size of the left panel's date column // Right Name Size - size of the right panel's name column // Right Size Size - size of the left panel's size column // Right Date Size - size of the left panel's date column // Splitter Sizes - sizes of the splitter /////////////////////// [RemoteMan] // Connections //////// // the basic connections are defined internally /////////////////////// [Search] // Saved Searches ///// // holds an index of saved searches // Confirm Feed to Listbox ///// (costum-name on feed ti listbox) #define _ConfirmFeedToListbox true /////////// here are additional variables used internally by Krusader //////////// // BookmarkArchives - The infobox about not allowing bookmarks inside archives // BackArchiveWarning - The infobox about not allowing to back up into archives // SupermountWarning - Warning about mounting/unmounting supermount filesystems // lastHomeRight - Save the last place the right list panel was showing // lastHomeLeft - Save the last place the left list panel was showing // lastUsedPacker - used by packGUI to remember the last used packer /////////////////////// [Popular Urls] // PopularUrls - a string list containing the top urls // PopularUrlsRank - an int list contains the urls' ranking /////////////////////// [Synchronize directories] // Don't overwrite automatically ///////////// #define _ConfirmOverWrites false // Recursive search in the subdirectories ///////////// #define _RecurseSubdirs true // The searcher follows symlinks ///////////// #define _FollowSymlinks false // Files with similar size are compared by content ///////////// #define _CompareByContent false // The date information is ignored at synchronization ///////////// #define _IgnoreDate false // Asymmetric Client-File Server compare mode ///////////// #define _Asymmetric false // Case insensitive compare in synchronizer ///////////// #define _IgnoreCase false // Scrolls the results of the synchronization ///////////// #define _ScrollResults false // The right arrow button is turned on ///////////// #define _BtnLeftToRight true // The equals button is turned on ///////////// #define _BtnEquals true // The not equals button is turned on ///////////// #define _BtnDifferents true // The left arrow button is turned on ///////////// #define _BtnRightToLeft true // The trash button is turned on ///////////// #define _BtnDeletable true // The duplicates button is turned on ///////////// #define _BtnDuplicates true // The singles button is turned on ///////////// #define _BtnSingles true /////////////////////// [Custom Selection Mode] // QT Selection #define _QtSelection false // Left Selects #define _LeftSelects true // Left Preserves #define _LeftPreserves false // ShiftCtrl Left Selects #define _ShiftCtrlLeft false // Right Selects #define _RightSelects true // Right Preserves #define _RightPreserves false // ShiftCtrl Right Selects #define _ShiftCtrlRight false // Space Moves Down #define _SpaceMovesDown true // Space Calc Space #define _SpaceCalcSpace true // Insert Moves Down #define _InsertMovesDown true // Immediate Context Menu #define _ImmediateContextMenu true // Root directory #ifdef Q_OS_WIN #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "\\" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '\\' #define REPLACE_DIR_SEP2(x) x = x.replace( DIR_SEPARATOR2, DIR_SEPARATOR ); #define ROOT_DIR "C:\\" #define EXEC_SUFFIX ".exe" #else #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "/" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '/' #define REPLACE_DIR_SEP2(x) #define ROOT_DIR "/" #define EXEC_SUFFIX "" #endif #endif