diff --git a/src/ViewContainer.cpp b/src/ViewContainer.cpp index 0534db35..68f3379d 100644 --- a/src/ViewContainer.cpp +++ b/src/ViewContainer.cpp @@ -1,400 +1,394 @@ /* This file is part of the Konsole Terminal. Copyright 2006-2008 Robert Knight This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Own #include "ViewContainer.h" #include // Qt #include #include #include // KDE #include #include #include #include // Konsole #include "IncrementalSearchBar.h" #include "ViewProperties.h" #include "ProfileList.h" #include "ViewManager.h" #include "KonsoleSettings.h" #include "SessionController.h" // TODO Perhaps move everything which is Konsole-specific into different files using namespace Konsole; TabbedViewContainer::TabbedViewContainer(ViewManager *connectedViewManager, QWidget *parent) : QTabWidget(parent), _connectedViewManager(connectedViewManager), _newTabButton(new QToolButton()) { setDocumentMode(true); tabBar()->setContextMenuPolicy(Qt::CustomContextMenu); _newTabButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); _newTabButton->setAutoRaise(true); connect(_newTabButton, &QToolButton::clicked, this, [this]{ emit newViewRequest(); }); connect(tabBar(), &QTabBar::tabCloseRequested, this, &TabbedViewContainer::closeTerminalTab); connect(tabBar(), &QTabBar::tabBarDoubleClicked, this, &Konsole::TabbedViewContainer::tabDoubleClicked); connect(tabBar(), &QTabBar::customContextMenuRequested, this, &Konsole::TabbedViewContainer::openTabContextMenu); connect(this, &TabbedViewContainer::currentChanged, this, [this](int index) { if (index != -1) { widget(index)->setFocus(); } }); // The context menu of tab bar _contextPopupMenu = new QMenu(tabBar()); connect(_contextPopupMenu, &QMenu::aboutToHide, this, [this]() { // Remove the read-only action when the popup closes for (auto &action : _contextPopupMenu->actions()) { if (action->objectName() == QStringLiteral("view-readonly")) { _contextPopupMenu->removeAction(action); break; } } }); #if defined(ENABLE_DETACHING) // _contextPopupMenu->addAction(QIcon::fromTheme(QStringLiteral("tab-detach")), // i18nc("@action:inmenu", "&Detach Tab"), this, // SLOT(tabContextMenuDetachTab())); #endif auto editAction = _contextPopupMenu->addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18nc("@action:inmenu", "&Rename Tab..."), this, SLOT(tabContextMenuRenameTab())); editAction->setObjectName(QStringLiteral("edit-rename")); auto profileMenu = new QMenu(); auto profileList = new ProfileList(false, profileMenu); profileList->syncWidgetActions(profileMenu, true); connect(profileList, &Konsole::ProfileList::profileSelected, this, static_cast(&Konsole::TabbedViewContainer::newViewRequest)); // setNewViewMenu(profileMenu); konsoleConfigChanged(); connect(KonsoleSettings::self(), &KonsoleSettings::configChanged, this, &TabbedViewContainer::konsoleConfigChanged); } TabbedViewContainer::~TabbedViewContainer() { for(int i = 0, end = count(); i < end; i++) { auto view = widget(i); disconnect(view, &QWidget::destroyed, this, &Konsole::TabbedViewContainer::viewDestroyed); } emit destroyed(this); } void TabbedViewContainer::konsoleConfigChanged() { setTabBarAutoHide((bool) KonsoleSettings::tabBarVisibility()); setTabPosition((QTabWidget::TabPosition) KonsoleSettings::tabBarPosition()); setTabsClosable(KonsoleSettings::showQuickButtons()); setCornerWidget( KonsoleSettings::showQuickButtons() ? _newTabButton : nullptr, Qt::TopLeftCorner); if (KonsoleSettings::tabBarUseUserStyleSheet()) { setCssFromFile(KonsoleSettings::tabBarUserStyleSheetFile()); } } void TabbedViewContainer::setCssFromFile(const QUrl &url) { // Let's only deal w/ local files for now if (!url.isLocalFile()) { setStyleSheet(KonsoleSettings::tabBarStyleSheet()); } QFile file(url.toLocalFile()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { setStyleSheet(KonsoleSettings::tabBarStyleSheet()); } QString styleSheetText; QTextStream in(&file); while (!in.atEnd()) { styleSheetText.append(in.readLine()); } setStyleSheet(styleSheetText); } void TabbedViewContainer::moveActiveView(MoveDirection direction) { const int currentIndex = indexOf(currentWidget()); int newIndex = -1; switch (direction) { case MoveViewLeft: newIndex = qMax(currentIndex - 1, 0); break; case MoveViewRight: newIndex = qMin(currentIndex + 1, count() - 1); break; } Q_ASSERT(newIndex != -1); auto swappedWidget = widget(newIndex); auto currentWidget = widget(currentIndex); removeTab(newIndex); removeTab(currentIndex); } void TabbedViewContainer::addView(QWidget *view, ViewProperties *item, int index) { if (index == -1) { addTab(view, item->icon(), item->title()); } else { insertTab(index, view, item->icon(), item->title()); } _navigation[view] = item; - connect(view, &QWidget::destroyed, this, &Konsole::TabbedViewContainer::viewDestroyed); + connect(item, &Konsole::ViewProperties::titleChanged, this, + &Konsole::TabbedViewContainer::updateTitle); + connect(item, &Konsole::ViewProperties::iconChanged, this, + &Konsole::TabbedViewContainer::updateIcon); + connect(item, &Konsole::ViewProperties::activity, this, + &Konsole::TabbedViewContainer::updateActivity); + connect(view, &QWidget::destroyed, this, + &Konsole::TabbedViewContainer::viewDestroyed); emit viewAdded(view, item); } void TabbedViewContainer::viewDestroyed(QObject *view) { auto widget = static_cast(view); const auto idx = indexOf(widget); removeTab(idx); forgetView(widget); } void TabbedViewContainer::forgetView(QWidget *view) { _navigation.remove(view); emit viewRemoved(view); if (count() == 0) { emit empty(this); } } void TabbedViewContainer::removeView(QWidget *view) { const int idx = indexOf(view); disconnect(view, &QWidget::destroyed, this, &Konsole::TabbedViewContainer::viewDestroyed); removeTab(idx); forgetView(view); } void TabbedViewContainer::activateNextView() { QWidget *active = currentWidget(); int index = indexOf(active); setCurrentIndex(index == count() - 1 ? 0 : index + 1); } void TabbedViewContainer::activateLastView() { setCurrentIndex(count() - 1); } void TabbedViewContainer::activatePreviousView() { QWidget *active = currentWidget(); int index = indexOf(active); setCurrentIndex(index == 0 ? count() - 1 : index - 1); } ViewProperties *TabbedViewContainer::viewProperties(QWidget *view) const { Q_ASSERT(_navigation.contains(view)); return _navigation[view]; } QList TabbedViewContainer::widgetsForItem(ViewProperties *item) const { return _navigation.keys(item); } void TabbedViewContainer::closeCurrentTab() { if (currentIndex() != -1) { closeTerminalTab(currentIndex()); } } void TabbedViewContainer::tabDoubleClicked(int index) { if (index >= 0) { renameTab(index); } else { emit newViewRequest(); } } void TabbedViewContainer::renameTab(int index) { if (index != -1) { _navigation[widget(index)]->rename(); } } void TabbedViewContainer::openTabContextMenu(const QPoint &point) { if (point.isNull()) { return; } const int contextMenuTabIndex = tabBar()->tabAt(point); if (contextMenuTabIndex < 0) { return; } #if defined(ENABLE_DETACHING) // Enable 'Detach Tab' menu item only if there is more than 1 tab // Note: the code is coupled with that action's position within the menu QAction *detachAction = _contextPopupMenu->actions().at(0); detachAction->setEnabled(count() > 1); #endif // Add the read-only action auto controller = _navigation[widget(contextMenuTabIndex)]; auto sessionController = qobject_cast(controller); if (sessionController != nullptr) { auto collection = sessionController->actionCollection(); auto readonlyAction = collection->action(QStringLiteral("view-readonly")); if (readonlyAction != nullptr) { const auto readonlyActions = _contextPopupMenu->actions(); _contextPopupMenu->insertAction(readonlyActions.last(), readonlyAction); } // Disable tab rename for (auto &action : _contextPopupMenu->actions()) { if (action->objectName() == QStringLiteral("edit-rename")) { action->setEnabled(!sessionController->isReadOnly()); break; } } } _contextPopupMenu->exec(tabBar()->mapToGlobal(point)); } void TabbedViewContainer::currentTabChanged(int index) { setCurrentIndex(index); if (widget(index) != nullptr) { emit activeViewChanged(widget(index)); } // clear activity indicators setTabActivity(index, false); } void TabbedViewContainer::wheelScrolled(int delta) { if (delta < 0) { activateNextView(); } else { activatePreviousView(); } } -void TabbedViewContainer::addViewWidget(QWidget *view, int index) -{ - ViewProperties *item = viewProperties(view); - connect(item, &Konsole::ViewProperties::titleChanged, this, - &Konsole::TabbedViewContainer::updateTitle); - connect(item, &Konsole::ViewProperties::iconChanged, this, - &Konsole::TabbedViewContainer::updateIcon); - connect(item, &Konsole::ViewProperties::activity, this, - &Konsole::TabbedViewContainer::updateActivity); - - insertTab(index, view, item->icon(), item->title()); -} - void TabbedViewContainer::setTabActivity(int index, bool activity) { const QPalette &palette = tabBar()->palette(); KColorScheme colorScheme(palette.currentColorGroup()); const QColor colorSchemeActive = colorScheme.foreground(KColorScheme::ActiveText).color(); const QColor normalColor = palette.text().color(); const QColor activityColor = KColorUtils::mix(normalColor, colorSchemeActive); QColor color = activity ? activityColor : QColor(); if (color != tabBar()->tabTextColor(index)) { tabBar()->setTabTextColor(index, color); } } void TabbedViewContainer::updateActivity(ViewProperties *item) { foreach (QWidget *widget, widgetsForItem(item)) { const int index = indexOf(widget); if (index != currentIndex()) { setTabActivity(index, true); } } } void TabbedViewContainer::updateTitle(ViewProperties *item) { foreach (QWidget *widget, widgetsForItem(item)) { const int index = indexOf(widget); QString tabText = item->title(); setTabToolTip(index, tabText); // To avoid having & replaced with _ (shortcut indicator) tabText.replace(QLatin1Char('&'), QLatin1String("&&")); setTabText(index, tabText); } } void TabbedViewContainer::updateIcon(ViewProperties *item) { foreach (QWidget *widget, widgetsForItem(item)) { const int index = indexOf(widget); setTabIcon(index, item->icon()); } } void TabbedViewContainer::closeTerminalTab(int idx) { auto currWidget = widget(idx); auto controller = qobject_cast(_navigation[currWidget]); controller->closeSession(); } ViewManager *TabbedViewContainer::connectedViewManager() { return _connectedViewManager; } diff --git a/src/ViewContainer.h b/src/ViewContainer.h index 42b958c3..ba79a131 100644 --- a/src/ViewContainer.h +++ b/src/ViewContainer.h @@ -1,211 +1,200 @@ /* This file is part of the Konsole Terminal. Copyright 2006-2008 Robert Knight This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef VIEWCONTAINER_H #define VIEWCONTAINER_H // Qt #include #include #include #include #include // Konsole #include "Profile.h" // Qt class QPoint; class QToolButton; class QMenu; class QDropEvent; namespace Konsole { class IncrementalSearchBar; class ViewProperties; class ViewManager; class TabbedViewContainer; /** * An interface for container widgets which can hold one or more views. * * The container widget typically displays a list of the views which * it has and provides a means of switching between them. * * Subclasses should reimplement the addViewWidget() and removeViewWidget() functions * to actually add or remove view widgets from the container widget, as well * as updating any navigation aids. */ class TabbedViewContainer : public QTabWidget { Q_OBJECT public: /** * Constructs a new view container with the specified parent. * * @param position The initial position of the navigation widget * @param parent The parent object of the container */ TabbedViewContainer(ViewManager *connectedViewManager, QWidget *parent); /** * Called when the ViewContainer is destroyed. When reimplementing this in * subclasses, use object->deleteLater() to delete any widgets or other objects * instead of 'delete object'. */ ~TabbedViewContainer() Q_DECL_OVERRIDE; /** Adds a new view to the container widget */ void addView(QWidget *view, ViewProperties *navigationItem, int index = -1); /** Removes a view from the container */ void removeView(QWidget *view); /** Returns the ViewProperties instance associated with a particular view in the container */ ViewProperties *viewProperties(QWidget *view) const; void setTabActivity(int index, bool activity); void updateTitle(ViewProperties *item); void updateIcon(ViewProperties *item); void updateActivity(ViewProperties *item); /** Changes the active view to the next view */ void activateNextView(); /** Changes the active view to the previous view */ void activatePreviousView(); /** Changes the active view to the last view */ void activateLastView(); void setCssFromFile(const QUrl& url); /** * This enum describes the directions * in which views can be re-arranged within the container * using the moveActiveView() method. */ enum MoveDirection { /** Moves the view to the left. */ MoveViewLeft, /** Moves the view to the right. */ MoveViewRight }; /** * Moves the active view within the container and * updates the order in which the views are shown * in the container's navigation widget. * * The default implementation does nothing. */ void moveActiveView(MoveDirection direction); /** Sets the menu to be shown when the new view button is clicked. * Only valid if the QuickNewView feature is enabled. * The default implementation does nothing. */ // TODO: Reenable this later. // void setNewViewMenu(QMenu *menu); void renameTab(int index); ViewManager *connectedViewManager(); void currentTabChanged(int index); void closeCurrentTab(); void wheelScrolled(int delta); void tabDoubleClicked(int index); void openTabContextMenu(const QPoint &point); Q_SIGNALS: /** Emitted when the container has no more children */ void empty(TabbedViewContainer *container); /** Emitted when the user requests to open a new view */ void newViewRequest(); /** Requests creation of a new view, with the selected profile. */ void newViewRequest(Profile::Ptr); /** * Emitted when the user requests to move a view from another container * into this container. If 'success' is set to true by a connected slot * then the original view will be removed. * * @param index Index at which to insert the new view in the container or -1 * to append it. This index should be passed to addView() when the new view * has been created. * @param id The identifier of the view. * @param success The slot handling this signal should set this to true if the * new view was successfully created. * @param sourceContainer Initial move event Tabbed view container. */ void moveViewRequest(int index, int id, bool &success, TabbedViewContainer *sourceContainer); /** Emitted when the active view changes */ void activeViewChanged(QWidget *view); /** Emitted when a view is added to the container. */ void viewAdded(QWidget *view, ViewProperties *properties); /** Emitted when a view is removed from the container. */ void viewRemoved(QWidget *view); protected: - /** - * Performs the task of adding the view widget - * to the container widget. - */ - void addViewWidget(QWidget *view, int index); - /** - * Performs the task of removing the view widget - * from the container widget. - */ - void removeViewWidget(QWidget *view); - /** Returns the widgets which are associated with a particular navigation item */ QList widgetsForItem(ViewProperties *item) const; /** * Rearranges the order of widgets in the container. * * @param fromIndex Current index of the widget to move * @param toIndex New index for the widget */ void moveViewWidget(int fromIndex, int toIndex); // close tabs and unregister void closeTerminalTab(int index); private Q_SLOTS: void viewDestroyed(QObject *view); void konsoleConfigChanged(); private: void forgetView(QWidget *view); QHash _navigation; ViewManager *_connectedViewManager; QMenu *_contextPopupMenu; QToolButton *_newTabButton; }; } #endif //VIEWCONTAINER_H