aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominik Haumann <dhaumann@kde.org>2014-01-21 19:36:37 (GMT)
committerDominik Haumann <dhaumann@kde.org>2014-01-21 19:36:47 (GMT)
commitf952657a81e8154afa869fbb6e988f30c9255c06 (patch)
tree83ae2ebbe057f3ad41b882a20634f43a0802ed17
parentcc573ab35fc9f08e5f58e1670eeb077a3475d11d (diff)
start adding a tabbar to the view space
todo: - somehow show what view space is active - icons on buttons: modified state, close button - implement button "..." (quick open, ...) - fix which buttons are visible - width & height
-rw-r--r--kate/CMakeLists.txt6
-rw-r--r--kate/app/katetabbar.cpp745
-rw-r--r--kate/app/katetabbar.h171
-rw-r--r--kate/app/katetabbutton.cpp244
-rw-r--r--kate/app/katetabbutton.h158
-rw-r--r--kate/app/kateviewspace.cpp32
-rw-r--r--kate/app/kateviewspace.h6
7 files changed, 1361 insertions, 1 deletions
diff --git a/kate/CMakeLists.txt b/kate/CMakeLists.txt
index f8bf84c..52e67db 100644
--- a/kate/CMakeLists.txt
+++ b/kate/CMakeLists.txt
@@ -32,6 +32,9 @@ set (KATE_APPLICATION_SRCS
app/katesavemodifieddialog.cpp
app/katemwmodonhddialog.cpp
+ app/katetabbutton.cpp
+ app/katetabbar.cpp
+
# session
session/katesessionchooser.cpp
session/katesessionsaction.cpp
@@ -45,7 +48,8 @@ set (KATE_APPLICATION_SRCS
app/kateappcommands.cpp
app/katequickopen.cpp
app/katewaiter.h
- app/main.cpp)
+ app/main.cpp
+)
# create kde init executable
kf5_add_kdeinit_executable(kate ${KATE_APPLICATION_SRCS})
diff --git a/kate/app/katetabbar.cpp b/kate/app/katetabbar.cpp
new file mode 100644
index 0000000..5811a92
--- /dev/null
+++ b/kate/app/katetabbar.cpp
@@ -0,0 +1,745 @@
+/* This file is part of the KDE project
+ *
+ * Copyright (C) 2014 Dominik Haumann <dhauumann@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "katetabbar.h"
+#include "katetabbutton.h"
+
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <kiconloader.h>
+#include <kstringhandler.h>
+
+#include <QApplication> // QApplication::sendEvent
+#include <QtAlgorithms> // qSort
+#include <QDebug>
+
+KateTabBar::SortType global_sortType;
+
+// public operator < for two tab buttons
+bool tabLessThan(const KateTabButton *a, const KateTabButton *b)
+{
+ switch (global_sortType) {
+ case KateTabBar::OpeningOrder: {
+ return a->buttonID() < b->buttonID();
+ }
+
+ case KateTabBar::Name: {
+ // fall back to ID
+ if (a->text().toLower() == b->text().toLower()) {
+ return a->buttonID() < b->buttonID();
+ }
+
+ return a->text() < b->text();
+// return KStringHandler::naturalCompare(a->text(), b->text(), Qt::CaseInsensitive) < 0; // FIXME KF5
+ }
+
+ case KateTabBar::URL: {
+ // fall back, if infos not available
+ if (a->url().isEmpty() && b->url().isEmpty()) {
+ if (a->text().toLower() == b->text().toLower()) {
+ return a->buttonID() < b->buttonID();
+ }
+
+ return a->text() < b->text();
+// return KStringHandler::naturalCompare(a->text(), b->text(), Qt::CaseInsensitive) < 0; FIXME KF5
+ }
+
+ return a->url() < b->url();
+ // return KStringHandler::naturalCompare(a->url(), b->url(), Qt::CaseInsensitive) < 0; FIXME KF5
+ }
+
+ case KateTabBar::Extension: {
+ // sort by extension, but check whether the files have an
+ // extension first
+ const int apos = a->text().lastIndexOf(QLatin1Char('.'));
+ const int bpos = b->text().lastIndexOf(QLatin1Char('.'));
+
+ if (apos == -1 && bpos == -1) {
+ return a->text().toLower() < b->text().toLower();
+ } else if (apos == -1) {
+ return true;
+ } else if (bpos == -1) {
+ return false;
+ } else {
+ const int aright = a->text().size() - apos;
+ const int bright = b->text().size() - bpos;
+ QString aExt = a->text().right(aright).toLower();
+ QString bExt = b->text().right(bright).toLower();
+ QString aFile = a->text().left(apos).toLower();
+ QString bFile = b->text().left(bpos).toLower();
+
+ if (aExt == bExt)
+ return (aFile == bFile) ? a->buttonID() < b->buttonID()
+ : aFile < bFile;
+ else {
+ return aExt < bExt;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+//BEGIN public member functions
+/**
+ * Creates a new tab bar with the given \a parent and \a name.
+ *
+ * The default values are in detail:
+ * - minimum tab width: 150 pixel
+ * - maximum tab width: 200 pixel
+ * - fixed tab height : 22 pixel. Note that the icon's size is 16 pixel.
+ * .
+ */
+KateTabBar::KateTabBar(QWidget *parent)
+ : QWidget(parent)
+{
+ m_minimumTabWidth = 150;
+ m_maximumTabWidth = 200;
+
+ m_tabHeight = 22;
+
+ m_sortType = OpeningOrder;
+ m_nextID = 0;
+
+ m_activeButton = 0L;
+
+ // functions called in ::load() will set settings for the nav buttons
+ m_configureButton = new KateTabButton(QStringLiteral("Show Quick Open"), QStringLiteral("..."), -1, this);
+
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ updateFixedHeight();
+}
+
+/**
+ * Destroys the tab bar.
+ */
+KateTabBar::~KateTabBar()
+{
+}
+
+/**
+ * Loads the settings from \a config from section \a group.
+ * Remembered properties are:
+ * - minimum and maximum tab width
+ * - fixed tab height
+ * - button colors
+ * - much more!
+ * .
+ * The original group is saved and restored at the end of this function.
+ *
+ * \note Call @p load() immediately after you created the tabbar, otherwise
+ * some properties might not be restored correctly (like highlighted
+ * buttons).
+ */
+void KateTabBar::load(KConfigBase *config, const QString &group)
+{
+ KConfigGroup cg(config, group);
+
+ // tabbar properties
+ setMinimumTabWidth(cg.readEntry("minimum width", 150));
+ setMaximumTabWidth(cg.readEntry("maximum width", 300));
+ setTabHeight(cg.readEntry("fixed height", 20));
+ setTabSortType((SortType) cg.readEntry("sort type", (int)OpeningOrder));
+
+ // highlighted entries
+ QStringList documents = cg.readEntry("highlighted documents", QStringList());
+ QStringList colors = cg.readEntry("highlighted colors", QStringList());
+
+ // restore highlight map
+ m_highlightedTabs.clear();
+ for (int i = 0; i < documents.size() && i < colors.size(); ++i) {
+ m_highlightedTabs[documents[i]] = colors[i];
+ }
+
+ setHighlightMarks(highlightMarks());
+}
+
+/**
+ * Saves the settings to \a config into section \a group.
+ * The original group is saved and restored at the end of this function.
+ * See @p load() for more information.
+ */
+void KateTabBar::save(KConfigBase *config, const QString &group) const
+{
+ KConfigGroup cg(config, group);
+
+ // tabbar properties
+ cg.writeEntry("minimum width", minimumTabWidth());
+ cg.writeEntry("maximum width", maximumTabWidth());
+ cg.writeEntry("fixed height", tabHeight());
+ cg.writeEntry("sort type", (int)tabSortType());
+
+ // highlighted entries
+ cg.writeEntry("highlighted documents", m_highlightedTabs.keys());
+ cg.writeEntry("highlighted colors", m_highlightedTabs.values());
+}
+
+/**
+ * Set the minimum width in pixels a tab must have.
+ */
+void KateTabBar::setMinimumTabWidth(int min_pixel)
+{
+ if (m_minimumTabWidth == min_pixel) {
+ return;
+ }
+
+ m_minimumTabWidth = min_pixel;
+ triggerResizeEvent();
+}
+
+/**
+ * Set the maximum width in pixels a tab may have.
+ */
+void KateTabBar::setMaximumTabWidth(int max_pixel)
+{
+ if (m_maximumTabWidth == max_pixel) {
+ return;
+ }
+
+ m_maximumTabWidth = max_pixel;
+ triggerResizeEvent();
+}
+
+/**
+ * Get the minimum width in pixels a tab can have.
+ */
+int KateTabBar::minimumTabWidth() const
+{
+ return m_minimumTabWidth;
+}
+
+/**
+ * Get the maximum width in pixels a tab can have.
+ */
+int KateTabBar::maximumTabWidth() const
+{
+ return m_maximumTabWidth;
+}
+
+/**
+ * Set the fixed height in pixels all tabs have.
+ * \note If you also show icons use a height of iconheight + 2.
+ * E.g. for 16x16 pixel icons, a tab height of 18 pixel looks best.
+ * For 22x22 pixel icons a height of 24 pixel is best etc.
+ */
+void KateTabBar::setTabHeight(int height_pixel)
+{
+ if (m_tabHeight == height_pixel) {
+ return;
+ }
+
+ m_tabHeight = height_pixel;
+ updateFixedHeight();
+}
+
+/**
+ * Get the fixed tab height in pixels.
+ */
+int KateTabBar::tabHeight() const
+{
+ return m_tabHeight;
+}
+
+/**
+ * Adds a new tab with text \a text. Returns the new tab's ID. The document's
+ * url @p docurl is used to sort the documents by URL.
+ */
+int KateTabBar::addTab(const QString &docurl, const QString &text)
+{
+ return addTab(docurl, QIcon(), text);
+}
+
+/**
+ * This is an overloaded member function, provided for convenience.
+ * It behaves essentially like the above function.
+ *
+ * Adds a new tab with \a icon and \a text. Returns the new tab's index.
+ */
+int KateTabBar::addTab(const QString &docurl, const QIcon &icon, const QString &text)
+{
+ KateTabButton *tabButton = new KateTabButton(docurl, text, m_nextID, this);
+ tabButton->setIcon(icon);
+ if (m_highlightedTabs.contains(text)) {
+ tabButton->setHighlightColor(QColor(m_highlightedTabs[text]));
+ }
+
+ m_tabButtons.append(tabButton);
+ m_IDToTabButton[m_nextID] = tabButton;
+ connect(tabButton, SIGNAL(activated(KateTabButton *)),
+ this, SLOT(tabButtonActivated(KateTabButton *)));
+ connect(tabButton, SIGNAL(highlightChanged(KateTabButton *)),
+ this, SLOT(tabButtonHighlightChanged(KateTabButton *)));
+ connect(tabButton, SIGNAL(closeRequest(KateTabButton *)),
+ this, SLOT(tabButtonCloseRequest(KateTabButton *)));
+ connect(tabButton, SIGNAL(closeOtherTabsRequest(KateTabButton *)),
+ this, SLOT(tabButtonCloseOtherRequest(KateTabButton *)));
+ connect(tabButton, SIGNAL(closeAllTabsRequest()),
+ this, SLOT(tabButtonCloseAllRequest()));
+
+ if (!isVisible()) {
+ show();
+ }
+
+ updateSort();
+
+ return m_nextID++;
+}
+
+void KateTabBar::raiseTab(int buttonId)
+{
+ Q_ASSERT(m_IDToTabButton.contains(buttonId));
+
+ KateTabButton *button = m_IDToTabButton[buttonId];
+ int index = m_tabButtons.indexOf(button);
+ Q_ASSERT(index > -1);
+
+ m_tabButtons.move(index, 0);
+
+ triggerResizeEvent();
+}
+
+/**
+ * Get the ID of the tab bar's activated tab. Returns -1 if no tab is activated.
+ */
+int KateTabBar::currentTab() const
+{
+ if (m_activeButton != 0L) {
+ return m_activeButton->buttonID();
+ }
+
+ return -1;
+}
+
+/**
+ * Activate the tab with ID \a button_id. No signal is emitted.
+ */
+void KateTabBar::setCurrentTab(int button_id)
+{
+ if (!m_IDToTabButton.contains(button_id)) {
+ return;
+ }
+
+ KateTabButton *tabButton = m_IDToTabButton[button_id];
+ if (m_activeButton == tabButton) {
+ return;
+ }
+
+ if (m_activeButton) {
+ m_activeButton->setActivated(false);
+ }
+
+ m_activeButton = tabButton;
+ m_activeButton->setActivated(true);
+}
+
+/**
+ * Removes the tab with ID \a button_id.
+ */
+void KateTabBar::removeTab(int button_id)
+{
+ if (!m_IDToTabButton.contains(button_id)) {
+ return;
+ }
+
+ KateTabButton *tabButton = m_IDToTabButton[button_id];
+
+ if (tabButton == m_activeButton) {
+ m_activeButton = 0L;
+ }
+
+ m_IDToTabButton.remove(button_id);
+ m_tabButtons.removeAll(tabButton);
+ // delete the button with deleteLater() because the button itself might
+ // have send a close-request. So the app-execution is still in the
+ // button, a delete tabButton; would lead to a crash.
+ tabButton->hide();
+ tabButton->deleteLater();
+
+ if (m_tabButtons.count() == 0) {
+ hide();
+ }
+
+ triggerResizeEvent();
+}
+
+/**
+ * Returns whether a tab with ID \a button_id exists.
+ */
+bool KateTabBar::containsTab(int button_id) const
+{
+ return m_IDToTabButton.contains(button_id);
+}
+
+/**
+ * Sets the text of the tab with ID \a button_id to \a text.
+ * \see tabText()
+ */
+void KateTabBar::setTabText(int button_id, const QString &text)
+{
+ if (!m_IDToTabButton.contains(button_id)) {
+ return;
+ }
+
+ // change highlight key, if entry exists
+ if (m_highlightedTabs.contains(m_IDToTabButton[button_id]->text())) {
+ QString value = m_highlightedTabs[m_IDToTabButton[button_id]->text()];
+ m_highlightedTabs.remove(m_IDToTabButton[button_id]->text());
+ m_highlightedTabs[text] = value;
+
+ // do not emit highlightMarksChanged(), because every tabbar gets this
+ // change (usually)
+ // emit highlightMarksChanged( this );
+ }
+
+ m_IDToTabButton[button_id]->setText(text);
+
+ if (tabSortType() == Name || tabSortType() == URL || tabSortType() == Extension) {
+ updateSort();
+ }
+}
+
+/**
+ * Returns the text of the tab with ID \a button_id. If the button id does not
+ * exist \a QString() is returned.
+ * \see setTabText()
+ */
+QString KateTabBar::tabText(int button_id) const
+{
+ if (m_IDToTabButton.contains(button_id)) {
+ return m_IDToTabButton[button_id]->text();
+ }
+
+ return QString();
+}
+
+/**
+ * Set the button @p button_id's url to @p docurl.
+ */
+void KateTabBar::setTabURL(int button_id, const QString &docurl)
+{
+ if (!m_IDToTabButton.contains(button_id)) {
+ return;
+ }
+
+ m_IDToTabButton[button_id]->setURL(docurl);
+
+ if (tabSortType() == URL) {
+ updateSort();
+ }
+}
+
+/**
+ * Get the button @p button_id's url. Result is QStrint() if not available.
+ */
+QString KateTabBar::tabURL(int button_id) const
+{
+ if (m_IDToTabButton.contains(button_id)) {
+ return m_IDToTabButton[button_id]->url();
+ }
+
+ return QString();
+}
+
+
+/**
+ * Sets the icon of the tab with ID \a button_id to \a icon.
+ * \see tabIcon()
+ */
+void KateTabBar::setTabIcon(int button_id, const QIcon &icon)
+{
+ if (m_IDToTabButton.contains(button_id)) {
+ m_IDToTabButton[button_id]->setIcon(icon);
+ }
+}
+
+/**
+ * Returns the icon of the tab with ID \a button_id. If the button id does not
+ * exist \a QIcon() is returned.
+ * \see setTabIcon()
+ */
+QIcon KateTabBar::tabIcon(int button_id) const
+{
+ if (m_IDToTabButton.contains(button_id)) {
+ return m_IDToTabButton[button_id]->icon();
+ }
+
+ return QIcon();
+}
+
+/**
+ * Returns the number of tabs in the tab bar.
+ */
+int KateTabBar::count() const
+{
+ return m_tabButtons.count();
+}
+
+/**
+ * Set the sort tye to @p sort.
+ */
+void KateTabBar::setTabSortType(SortType sort)
+{
+ if (m_sortType == sort) {
+ return;
+ }
+
+ m_sortType = sort;
+ updateSort();
+}
+
+/**
+ * Get the sort type.
+ */
+KateTabBar::SortType KateTabBar::tabSortType() const
+{
+ return m_sortType;
+}
+
+void KateTabBar::setTabModified(int button_id, bool modified)
+{
+ if (m_IDToTabButton.contains(button_id)) {
+ m_IDToTabButton[button_id]->setModified(modified);
+ }
+}
+
+bool KateTabBar::isTabModified(int button_id) const
+{
+ if (m_IDToTabButton.contains(button_id)) {
+ return m_IDToTabButton[button_id]->isModified();
+ }
+
+ return false;
+}
+
+void KateTabBar::removeHighlightMarks()
+{
+ KateTabButton *tabButton;
+ foreach(tabButton, m_tabButtons) {
+ if (tabButton->highlightColor().isValid()) {
+ tabButton->setHighlightColor(QColor());
+ }
+ }
+
+ m_highlightedTabs.clear();
+ emit highlightMarksChanged(this);
+}
+
+void KateTabBar::setHighlightMarks(const QMap<QString, QString> &marks)
+{
+ m_highlightedTabs = marks;
+
+ KateTabButton *tabButton;
+ foreach(tabButton, m_tabButtons) {
+ if (marks.contains(tabButton->text())) {
+ if (tabButton->highlightColor().name() != marks[tabButton->text()]) {
+ tabButton->setHighlightColor(QColor(marks[tabButton->text()]));
+ }
+ } else if (tabButton->highlightColor().isValid()) {
+ tabButton->setHighlightColor(QColor());
+ }
+ }
+}
+
+QMap<QString, QString> KateTabBar::highlightMarks() const
+{
+ return m_highlightedTabs;
+}
+//END public member functions
+
+
+
+
+
+
+//BEGIN protected / private member functions
+
+/**
+ * Active button changed. Emit signal \p currentChanged() with the button's ID.
+ */
+void KateTabBar::tabButtonActivated(KateTabButton *tabButton)
+{
+ if (tabButton == m_activeButton) {
+ return;
+ }
+
+ if (m_activeButton) {
+ m_activeButton->setActivated(false);
+ }
+
+ m_activeButton = tabButton;
+ m_activeButton->setActivated(true);
+
+ emit currentChanged(tabButton->buttonID());
+}
+
+/**
+ * The \e tabButton's highlight color changed, so update the list of documents
+ * and colors.
+ */
+void KateTabBar::tabButtonHighlightChanged(KateTabButton *tabButton)
+{
+ if (tabButton->highlightColor().isValid()) {
+ m_highlightedTabs[tabButton->text()] = tabButton->highlightColor().name();
+ emit highlightMarksChanged(this);
+ } else if (m_highlightedTabs.contains(tabButton->text())) {
+ // invalid color, so remove the item
+ m_highlightedTabs.remove(tabButton->text());
+ emit highlightMarksChanged(this);
+ }
+}
+
+/**
+ * If the user wants to close a tab with the context menu, it sends a close
+ * request. Throw the close request by emitting the signal @p closeRequest().
+ */
+void KateTabBar::tabButtonCloseRequest(KateTabButton *tabButton)
+{
+ emit closeRequest(tabButton->buttonID());
+}
+
+/**
+ * If the user wants to close all tabs except the current one using the context
+ * menu, it sends multiple close requests.
+ * Throw the close requests by emitting the signal @p closeRequest().
+ */
+void KateTabBar::tabButtonCloseOtherRequest(KateTabButton *tabButton)
+{
+ QList <int> tabToCloseID;
+ for (int i = 0; i < m_tabButtons.size(); ++i) {
+ if ((m_tabButtons.at(i))->buttonID() != tabButton->buttonID()) {
+ tabToCloseID << (m_tabButtons.at(i))->buttonID();
+ }
+ }
+
+ for (int i = 0; i < tabToCloseID.size(); i++) {
+ emit closeRequest(tabToCloseID.at(i));
+ }
+}
+
+/**
+ * If the user wants to close all the tabs using the context menu, it sends
+ * multiple close requests.
+ * Throw the close requests by emitting the signal @p closeRequest().
+ */
+void KateTabBar::tabButtonCloseAllRequest()
+{
+ QList <int> tabToCloseID;
+ for (int i = 0; i < m_tabButtons.size(); ++i) {
+ tabToCloseID << (m_tabButtons.at(i))->buttonID();
+ }
+
+ for (int i = 0; i < tabToCloseID.size(); i++) {
+ emit closeRequest(tabToCloseID.at(i));
+ }
+}
+
+/**
+ * Recalculate geometry for all children.
+ */
+void KateTabBar::resizeEvent(QResizeEvent *event)
+{
+// qDebug() << "resizeEvent";
+ // if there are no tabs there is nothing to do. Do not delete otherwise
+ // division by zero is possible.
+ if (m_tabButtons.count() == 0) {
+ updateHelperButtons(event->size());
+ return;
+ }
+
+ int tabbar_width = event->size().width() - m_navigateSize;
+ int tabs_per_row = tabbar_width / minimumTabWidth();
+ if (tabs_per_row == 0) {
+ tabs_per_row = 1;
+ }
+
+ int tab_width = minimumTabWidth();
+
+ // On this point, we really know the value of tabs_per_row. So a final
+ // calculation gives us the tab_width. With this the width can even get
+ // greater than maximumTabWidth(), but that does not matter as it looks
+ // more ugly if there is a lot wasted space on the right.
+ tab_width = tabbar_width / tabs_per_row;
+
+ updateHelperButtons(event->size());
+
+ KateTabButton *tabButton;
+
+ foreach(tabButton, m_tabButtons)
+ tabButton->hide();
+
+ qDebug() << "---> tab width, items per row:" << tab_width << tabs_per_row;
+
+ for (int i = 0; i < 10; ++i) {
+ // value returns 0L, if index is out of bounds
+ tabButton = m_tabButtons.value(i);
+
+ if (tabButton) {
+ tabButton->setGeometry(i * tab_width, 0,
+ tab_width, tabHeight());
+ tabButton->show();
+ }
+ }
+}
+
+/**
+ * Updates the fixed height. Called when the tab height or the number of rows
+ * changed.
+ */
+void KateTabBar::updateFixedHeight()
+{
+ setFixedHeight(tabHeight());
+ triggerResizeEvent();
+}
+
+/**
+ * May modifies current row if more tabs fit into a row.
+ * Sets geometry for the buttons 'up', 'down' and 'configure'.
+ */
+void KateTabBar::updateHelperButtons(QSize new_size)
+{
+ m_configureButton->setGeometry(new_size.width() - m_configureButton->minimumSizeHint().width(),
+ 0, m_configureButton->minimumSizeHint().width(), tabHeight());
+}
+
+void KateTabBar::updateSort()
+{
+ global_sortType = tabSortType();
+ qSort(m_tabButtons.begin(), m_tabButtons.end(), tabLessThan);
+ triggerResizeEvent();
+}
+
+/**
+ * Triggers a resizeEvent. This is used whenever the tab buttons need
+ * a rearrange. By using \p QApplication::sendEvent() multiple calls are
+ * combined into only one call.
+ *
+ * \see addTab(), removeTab(), setMinimumWidth(), setMaximumWidth(),
+ * setFixedHeight()
+ */
+void KateTabBar::triggerResizeEvent()
+{
+ QResizeEvent ev(size(), size());
+ QApplication::sendEvent(this, &ev);
+}
+
+//END protected / private member functions
+
+// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix;
diff --git a/kate/app/katetabbar.h b/kate/app/katetabbar.h
new file mode 100644
index 0000000..e34d765
--- /dev/null
+++ b/kate/app/katetabbar.h
@@ -0,0 +1,171 @@
+/* This file is part of the KDE project
+ *
+ * Copyright (C) 2014 Dominik Haumann <dhauumann@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KATE_TAB_BAR_H
+#define KATE_TAB_BAR_H
+
+#include <QWidget>
+
+#include <QList>
+#include <QMap>
+#include <QIcon>
+#include <QResizeEvent>
+
+class KateTabButton;
+class KConfigBase;
+
+/**
+ * The \p KateTabBar class provides a tab bar, e.g. for tabbed documents and
+ * supports multiple rows. The tab bar hides itself if there are no tabs.
+ *
+ * It implements the API from TrollTech's \p QTabBar with some minor changes
+ * and additions.
+ *
+ * @author Dominik Haumann
+ */
+class KateTabBar : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Sort types.
+ */
+ enum SortType {
+ OpeningOrder = 0, ///< opening order
+ Name, ///< alphabetically
+ URL, ///< alphabetically URL based
+ Extension ///< by file extension (suffix)
+ };
+ Q_DECLARE_FLAGS(SortTypes, SortType)
+
+public:
+ // NOTE: as the API here is very self-explaining the docs are in the cpp
+ // file, more clean imho.
+
+ KateTabBar(QWidget *parent = 0);
+ virtual ~KateTabBar();
+
+ void load(KConfigBase *config, const QString &group);
+ void save(KConfigBase *config, const QString &group) const;
+
+ void setMinimumTabWidth(int min_pixel);
+ void setMaximumTabWidth(int max_pixel);
+
+ int minimumTabWidth() const;
+ int maximumTabWidth() const;
+
+ void setTabHeight(int height_pixel);
+ int tabHeight() const;
+
+ int addTab(const QString &docurl, const QString &text);
+ int addTab(const QString &docurl, const QIcon &pixmap, const QString &text);
+ void removeTab(int button_id);
+
+ int currentTab() const;
+ // corresponding SLOT: void setCurrentTab( int button_id );
+
+ bool containsTab(int button_id) const;
+
+ void setTabURL(int button_id, const QString &docurl);
+ QString tabURL(int button_id) const;
+
+ void setTabText(int button_id, const QString &text);
+ QString tabText(int button_id) const;
+
+ void setTabIcon(int button_id, const QIcon &pixmap);
+ QIcon tabIcon(int button_id) const;
+
+ void setTabModified(int button_id, bool modified);
+ bool isTabModified(int button_id) const;
+
+ int count() const;
+
+ void setTabSortType(SortType sort);
+ SortType tabSortType() const;
+
+ void setHighlightMarks(const QMap<QString, QString> &marks);
+ QMap<QString, QString> highlightMarks() const;
+
+public Q_SLOTS:
+ void setCurrentTab(int button_id); // does not emit signal
+ void removeHighlightMarks();
+ void raiseTab(int buttonId);
+
+Q_SIGNALS:
+ /**
+ * This signal is emitted whenever the current activated tab changes.
+ */
+ void currentChanged(int button_id);
+ /**
+ * This signal is emitted whenever a tab should be closed.
+ */
+ void closeRequest(int button_id);
+ /**
+ * This signal is emitted whenever a setting entry changes.
+ */
+ void settingsChanged(KateTabBar *tabbar);
+
+ /**
+ * This signal is emitted whenever a highlight mark changes.
+ * Usually this is used to synchronice several tabbars.
+ */
+ void highlightMarksChanged(KateTabBar *tabbar);
+
+protected Q_SLOTS:
+ void tabButtonActivated(KateTabButton *tabButton);
+ void tabButtonHighlightChanged(KateTabButton *tabButton);
+ void tabButtonCloseAllRequest();
+ void tabButtonCloseRequest(KateTabButton *tabButton);
+ void tabButtonCloseOtherRequest(KateTabButton *tabButton);
+
+protected:
+ virtual void resizeEvent(QResizeEvent *event);
+
+protected:
+ void updateFixedHeight();
+ void triggerResizeEvent();
+ void updateSort();
+ void updateHelperButtons(QSize new_size);
+
+private:
+ int m_minimumTabWidth;
+ int m_maximumTabWidth;
+ int m_tabHeight;
+
+ QList< KateTabButton * > m_tabButtons;
+ QMap< int, KateTabButton * > m_IDToTabButton;
+
+ KateTabButton *m_activeButton;
+
+ // buttons on the right to navigate and configure
+ KateTabButton *m_configureButton;
+ int m_navigateSize;
+
+ int m_nextID;
+
+ // map of highlighted tabs and colors
+ QMap< QString, QString > m_highlightedTabs;
+ SortType m_sortType;
+};
+
+#endif // KATE_TAB_BAR_H
+
+// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix;
diff --git a/kate/app/katetabbutton.cpp b/kate/app/katetabbutton.cpp
new file mode 100644
index 0000000..2d82ebf
--- /dev/null
+++ b/kate/app/katetabbutton.cpp
@@ -0,0 +1,244 @@
+/* This file is part of the KDE project
+ *
+ * Copyright (C) 2014 Dominik Haumann <dhauumann@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "katetabbutton.h"
+
+#include <klocalizedstring.h>
+
+#include <QApplication>
+#include <QColorDialog>
+#include <QContextMenuEvent>
+#include <QFontDatabase>
+#include <QIcon>
+#include <QMenu>
+#include <QPainter>
+
+QColor KateTabButton::s_predefinedColors[] = { Qt::red, Qt::yellow, Qt::green, Qt::cyan, Qt::blue, Qt::magenta };
+const int KateTabButton::s_colorCount = 6;
+int KateTabButton::s_currentColor = 0;
+
+
+KateTabButton::KateTabButton(const QString &docurl, const QString &caption,
+ int button_id, QWidget *parent)
+ : QPushButton(parent)
+{
+ setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
+ setCheckable(true);
+ setFocusPolicy(Qt::NoFocus);
+ setMinimumWidth(1);
+ setFlat(true);
+// setAutoFillBackground(true);
+
+ m_buttonId = button_id;
+ m_modified = false;
+
+ setIcon(QIcon());
+ setText(caption);
+ setURL(docurl);
+
+ connect(this, SIGNAL(clicked()), this, SLOT(buttonClicked()));
+}
+
+KateTabButton::~KateTabButton()
+{
+}
+
+void KateTabButton::setURL(const QString &docurl)
+{
+ m_url = docurl;
+ if (!m_url.isEmpty()) {
+ setToolTip(m_url);
+ } else {
+ setToolTip(text());
+ }
+}
+
+QString KateTabButton::url() const
+{
+ return m_url;
+}
+
+void KateTabButton::buttonClicked()
+{
+ // once down, stay down until another tab is activated
+ if (isChecked()) {
+ emit activated(this);
+ } else {
+ setChecked(true);
+ }
+}
+
+void KateTabButton::setActivated(bool active)
+{
+ if (isChecked() == active) {
+ return;
+ }
+ setChecked(active);
+ update();
+}
+
+bool KateTabButton::isActivated() const
+{
+ return isChecked();
+}
+
+void KateTabButton::paintEvent(QPaintEvent *ev)
+{
+ const int opac = 30;
+ const int comp = 100 - opac;
+
+ QPalette pal = QApplication::palette();
+ if (m_highlightColor.isValid()) {
+ QColor col(pal.button().color());
+ col.setRed((col.red()*comp + m_highlightColor.red()*opac) / 100);
+ col.setGreen((col.green()*comp + m_highlightColor.green()*opac) / 100);
+ col.setBlue((col.blue()*comp + m_highlightColor.blue()*opac) / 100);
+ pal.setColor(QPalette::Button, col);
+ pal.setColor(QPalette::Background, col);
+ }
+ setPalette(pal);
+ QPushButton::paintEvent(ev);
+}
+
+void KateTabButton::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QPixmap colorIcon(22, 22);
+ QMenu menu(/*text(),*/ this);
+ QMenu *colorMenu = menu.addMenu(i18n("&Highlight Tab"));
+ QAction *aNone = colorMenu->addAction(i18n("&None"));
+ colorMenu->addSeparator();
+ colorIcon.fill(Qt::red);
+ QAction *aRed = colorMenu->addAction(colorIcon, i18n("&Red"));
+ colorIcon.fill(Qt::yellow);
+ QAction *aYellow = colorMenu->addAction(colorIcon, i18n("&Yellow"));
+ colorIcon.fill(Qt::green);
+ QAction *aGreen = colorMenu->addAction(colorIcon, i18n("&Green"));
+ colorIcon.fill(Qt::cyan);
+ QAction *aCyan = colorMenu->addAction(colorIcon, i18n("&Cyan"));
+ colorIcon.fill(Qt::blue);
+ QAction *aBlue = colorMenu->addAction(colorIcon, i18n("&Blue"));
+ colorIcon.fill(Qt::magenta);
+ QAction *aMagenta = colorMenu->addAction(colorIcon, i18n("&Magenta"));
+ colorMenu->addSeparator();
+ QAction *aCustomColor = colorMenu->addAction(
+ QIcon::fromTheme(QStringLiteral("colors")), i18n("C&ustom Color..."));
+ menu.addSeparator();
+
+ QAction *aCloseTab = menu.addAction(i18n("&Close Tab"));
+ QAction *aCloseOtherTabs = menu.addAction(i18n("Close &Other Tabs"));
+ QAction *aCloseAllTabs = menu.addAction(i18n("Close &All Tabs"));
+
+ QAction *choice = menu.exec(ev->globalPos());
+
+ // process the result
+ if (choice == aNone) {
+ if (m_highlightColor.isValid()) {
+ setHighlightColor(QColor());
+ emit highlightChanged(this);
+ }
+ } else if (choice == aRed) {
+ setHighlightColor(Qt::red);
+ emit highlightChanged(this);
+ } else if (choice == aYellow) {
+ setHighlightColor(Qt::yellow);
+ emit highlightChanged(this);
+ } else if (choice == aGreen) {
+ setHighlightColor(Qt::green);
+ emit highlightChanged(this);
+ } else if (choice == aCyan) {
+ setHighlightColor(Qt::cyan);
+ emit highlightChanged(this);
+ } else if (choice == aBlue) {
+ setHighlightColor(Qt::blue);
+ emit highlightChanged(this);
+ } else if (choice == aMagenta) {
+ setHighlightColor(Qt::magenta);
+ emit highlightChanged(this);
+ } else if (choice == aCustomColor) {
+ QColor newColor = QColorDialog::getColor(m_highlightColor, this);
+ if (newColor.isValid()) {
+ setHighlightColor(newColor);
+ emit highlightChanged(this);
+ }
+ } else if (choice == aCloseTab) {
+ emit closeRequest(this);
+ } else if (choice == aCloseOtherTabs) {
+ emit closeOtherTabsRequest(this);
+ } else if (choice == aCloseAllTabs) {
+ emit closeAllTabsRequest();
+ }
+}
+
+void KateTabButton::mousePressEvent(QMouseEvent *ev)
+{
+ if (ev->button() == Qt::MidButton) {
+ if (ev->modifiers() & Qt::ControlModifier) {
+ // clear tab highlight
+ setHighlightColor(QColor());
+ } else {
+ setHighlightColor(s_predefinedColors[s_currentColor]);
+ if (++s_currentColor >= s_colorCount) {
+ s_currentColor = 0;
+ }
+ }
+ ev->accept();
+ } else {
+ QPushButton::mousePressEvent(ev);
+ }
+}
+
+void KateTabButton::setButtonID(int button_id)
+{
+ m_buttonId = button_id;
+}
+
+int KateTabButton::buttonID() const
+{
+ return m_buttonId;
+}
+
+void KateTabButton::setHighlightColor(const QColor &color)
+{
+ if (color.isValid()) {
+ m_highlightColor = color;
+ update();
+ } else if (m_highlightColor.isValid()) {
+ m_highlightColor = QColor();
+ update();
+ }
+}
+
+QColor KateTabButton::highlightColor() const
+{
+ return m_highlightColor;
+}
+
+void KateTabButton::setModified(bool modified)
+{
+ m_modified = modified;
+ update();
+}
+
+bool KateTabButton::isModified() const
+{
+ return m_modified;
+}
+
+// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix;
diff --git a/kate/app/katetabbutton.h b/kate/app/katetabbutton.h
new file mode 100644
index 0000000..3b8b46b
--- /dev/null
+++ b/kate/app/katetabbutton.h
@@ -0,0 +1,158 @@
+/* This file is part of the KDE project
+ *
+ * Copyright (C) 2014 Dominik Haumann <dhauumann@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KATE_TAB_BUTTON
+#define KATE_TAB_BUTTON
+
+#include <QPushButton>
+#include "katetabbar.h"
+
+/**
+ * A \p KateTabButton represents a button on the tab bar. It can either be
+ * \e activated or \e deactivated. If the state is \e deactivated it will
+ * be @e activated when the mouse is pressed. It then emits the signal
+ * @p activated(). The \p KateTabButton's caption can be set with \p setText()
+ * and an additional pixmap can be shown with \p setPixmap().
+ *
+ * @author Dominik Haumann
+ */
+class KateTabButton : public QPushButton
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructs a new tab bar button with \a caption and \a parent.
+ * If the @p docurl is unknown, pass QString().
+ */
+ KateTabButton(const QString &docurl, const QString &caption, int button_id,
+ QWidget *parent = 0);
+
+ virtual ~KateTabButton();
+
+ /**
+ * Activate or deactivate the button. If the button is already down
+ * and \a active is \e true nothing happens, otherwise the state toggles.
+ * \note The signal \p activated is \e not emitted.
+ */
+ void setActivated(bool active);
+
+ /**
+ * Check the button status. The return value is \e true, if the button is
+ * down.
+ */
+ bool isActivated() const;
+
+ /**
+ * Set a unique button id number.
+ */
+ void setButtonID(int button_id);
+
+ /**
+ * Get the unique id number.
+ */
+ int buttonID() const;
+
+ /**
+ * Set the document's url to @p docurl. If unknown, pass QString().
+ */
+ void setURL(const QString &docurl);
+
+ /**
+ * Get the document's url.
+ */
+ QString url() const;
+
+ /**
+ * Set the highlighted state. If @p color.isValid() is \e false the
+ * button is not highlighted. This does \e not emit the signal
+ * @p highlightChanged().
+ * @param color the color
+ */
+ void setHighlightColor(const QColor &color);
+
+ /**
+ * Get the highlight color. If the button is not highlighted then the color
+ * is invalid, i.e. \p QColor::isValid() returns \e flase.
+ */
+ QColor highlightColor() const;
+
+ void setModified(bool modified);
+ bool isModified() const;
+
+Q_SIGNALS:
+ /**
+ * Emitted whenever the button changes state from deactivated to activated.
+ * @param tabbutton the pressed button (this)
+ */
+ void activated(KateTabButton *tabbutton);
+
+ /**
+ * Emitted whenever the user changes the highlighted state. This can be
+ * done only via the context menu.
+ * @param tabbutton the changed button (this)
+ */
+ void highlightChanged(KateTabButton *tabbutton);
+
+ /**
+ * Emitted whenever the user wants to close the tab button.
+ * @param tabbutton the button that emitted this signal
+ */
+ void closeRequest(KateTabButton *tabbutton);
+
+ /**
+ * Emitted whenever the user wants to close all the tab button except the
+ * selected one.
+ * @param tabbutton the button that emitted this signal
+ */
+ void closeOtherTabsRequest(KateTabButton *tabbutton);
+
+ /**
+ * Emitted whenever the user wants to close all the tabs.
+ */
+ void closeAllTabsRequest();
+
+protected Q_SLOTS:
+ void buttonClicked();
+
+protected:
+ /** paint eyecandy rectangles around the button */
+ virtual void paintEvent(QPaintEvent *ev);
+ /** support for context menu */
+ virtual void contextMenuEvent(QContextMenuEvent *ev);
+ /** middle mouse button changes color */
+ virtual void mousePressEvent(QMouseEvent *ev);
+
+
+private:
+ QString m_url;
+ int m_buttonId;
+ bool m_modified;
+
+ QColor m_highlightColor;
+
+ static QColor s_predefinedColors[6];
+ static const int s_colorCount;
+ static int s_currentColor;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix;
diff --git a/kate/app/kateviewspace.cpp b/kate/app/kateviewspace.cpp
index 7965c21..9d4799f 100644
--- a/kate/app/kateviewspace.cpp
+++ b/kate/app/kateviewspace.cpp
@@ -27,6 +27,7 @@
#include "kateapp.h"
#include "katesessionmanager.h"
#include "katedebug.h"
+#include "katetabbar.h"
#include <KSqueezedTextLabel>
#include <KStringHandler>
@@ -53,6 +54,10 @@ KateViewSpace::KateViewSpace( KateViewManager *viewManager,
layout->setSpacing(0);
layout->setMargin(0);
+ m_tabBar = new KateTabBar(this);
+ layout->addWidget(m_tabBar);
+ connect(m_tabBar, &KateTabBar::currentChanged, this, &KateViewSpace::changeView);
+
stack = new QStackedWidget( this );
stack->setFocus();
stack->setSizePolicy (QSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
@@ -108,14 +113,27 @@ KTextEditor::View *KateViewSpace::createView (KTextEditor::Document *doc)
}
}
+ // add to tab bar
+ const int index = m_tabBar->addTab(doc->url().toString(), doc->documentName());
+ Q_ASSERT(index >= 0);
+ m_viewToTabId[v] = index;
+
+ // insert into stack
stack->addWidget(v);
mViewList.append(v);
showView( v );
+
return v;
}
void KateViewSpace::removeView(KTextEditor::View* v)
{
+ // remove from tab bar
+ Q_ASSERT(m_viewToTabId.contains(v));
+ m_tabBar->removeTab(m_viewToTabId[v]);
+ m_viewToTabId.remove(v);
+
+ // remove from view space
bool active = ( v == currentView() );
mViewList.removeAt ( mViewList.indexOf ( v ) );
@@ -145,12 +163,26 @@ bool KateViewSpace::showView(KTextEditor::Document *document)
stack->setCurrentWidget( kv );
kv->show();
+ // raise tab in tab bar
+ Q_ASSERT(m_viewToTabId.contains(kv));
+// m_tabBar->raiseTab(m_viewToTabId[kv]);
+ m_tabBar->setCurrentTab(m_viewToTabId[kv]);
+
return true;
}
}
return false;
}
+void KateViewSpace::changeView(int buttonId)
+{
+ KTextEditor::View * view = m_viewToTabId.key(buttonId);
+ Q_ASSERT(view);
+
+ if (view != currentView()) {
+ showView(view);
+ }
+}
KTextEditor::View* KateViewSpace::currentView()
{
diff --git a/kate/app/kateviewspace.h b/kate/app/kateviewspace.h
index 48bac87..b30f995 100644
--- a/kate/app/kateviewspace.h
+++ b/kate/app/kateviewspace.h
@@ -27,11 +27,14 @@
#include <QList>
#include <QFrame>
+#include <QHash>
class KConfigBase;
class KateViewManager;
class KateViewSpace;
class QStackedWidget;
+class QLabel;
+class KateTabBar;
class KateViewSpace : public QFrame
{
@@ -69,6 +72,7 @@ class KateViewSpace : public QFrame
private Q_SLOTS:
void statusBarToggled ();
+ void changeView(int buttonId);
private:
QStackedWidget* stack;
@@ -80,6 +84,8 @@ class KateViewSpace : public QFrame
QList<KTextEditor::View*> mViewList;
KateViewManager *m_viewManager;
QString m_group;
+ KateTabBar * m_tabBar;
+ QHash<KTextEditor::View*, int> m_viewToTabId;
};
#endif