diff --git a/src/iconsidepane.cpp b/src/iconsidepane.cpp index 9e11b393..9f867650 100644 --- a/src/iconsidepane.cpp +++ b/src/iconsidepane.cpp @@ -1,568 +1,589 @@ /* This file is part of KDE Kontact. Copyright (C) 2003 Cornelius Schumacher Copyright (C) 2008 Rafael Fernández López 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "iconsidepane.h" +#include "mainwindow.h" #include "prefs.h" using namespace Kontact; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Kontact { class SelectionModel : public QItemSelectionModel { Q_OBJECT public: SelectionModel(QAbstractItemModel *model, QObject *parent) : QItemSelectionModel(model, parent) { } public Q_SLOTS: void clear() override { // Don't allow the current selection to be cleared. QListView doesn't call to this method // nowadays, but just to cover of future change of implementation, since QTreeView does call // to this one when clearing the selection. } void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) override { // Don't allow the current selection to be cleared if (!index.isValid() && (command & QItemSelectionModel::Clear)) { return; } QItemSelectionModel::select(index, command); } void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override { // Don't allow the current selection to be cleared if (!selection.count() && (command & QItemSelectionModel::Clear)) { return; } QItemSelectionModel::select(selection, command); } }; class Model : public QStringListModel { Q_OBJECT public: enum SpecialRoles { PluginName = Qt::UserRole }; explicit Model(Navigator *parentNavigator = nullptr) : QStringListModel(parentNavigator), mNavigator(parentNavigator) { } void emitReset() { - //FIXME + //FIXME //Q_EMIT reset(); } void setPluginList(const QList &list) { pluginList = list; } Qt::ItemFlags flags(const QModelIndex &index) const override { Qt::ItemFlags flags = QStringListModel::flags(index); flags &= ~Qt::ItemIsEditable; if (index.isValid()) { if (static_cast(index.internalPointer())->disabled()) { flags &= ~Qt::ItemIsEnabled; flags &= ~Qt::ItemIsSelectable; flags &= ~Qt::ItemIsDropEnabled; } else { flags |= Qt::ItemIsDropEnabled; } } else { flags &= ~Qt::ItemIsDropEnabled; } return flags; } QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { Q_UNUSED(parent); if (row < 0 || row >= pluginList.count()) { return QModelIndex(); } return createIndex(row, column, pluginList[row]); } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { if (!index.isValid() || !index.internalPointer()) { return QVariant(); } if (role == Qt::DisplayRole) { if (!mNavigator->showText()) { return QVariant(); } return static_cast(index.internalPointer())->title(); } else if (role == Qt::DecorationRole) { if (!mNavigator->showIcons()) { return QVariant(); } return QIcon::fromTheme(static_cast(index.internalPointer())->icon()); } else if (role == Qt::TextAlignmentRole) { return Qt::AlignCenter; } else if (role == Qt::ToolTipRole) { if (!mNavigator->showText()) { return static_cast(index.internalPointer())->title(); } return QVariant(); } else if (role == PluginName) { return static_cast(index.internalPointer())->identifier(); } return QStringListModel::data(index, role); } private: QList pluginList; Navigator *mNavigator; }; class SortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit SortFilterProxyModel(QObject *parent = nullptr): QSortFilterProxyModel(parent) { setDynamicSortFilter(true); sort(0); } protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const override { KontactInterface::Plugin *leftPlugin = static_cast(left.internalPointer()); KontactInterface::Plugin *rightPlugin = static_cast(right.internalPointer()); if (leftPlugin->weight() == rightPlugin->weight()) { //Optimize it QCollator col; return col.compare(leftPlugin->title(), rightPlugin->title()) < 0; } return leftPlugin->weight() < rightPlugin->weight(); } }; class Delegate : public QStyledItemDelegate { Q_OBJECT public: explicit Delegate(Navigator *parentNavigator = nullptr) : QStyledItemDelegate(parentNavigator), mNavigator(parentNavigator) { } void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (!index.isValid() || !index.internalPointer()) { return; } QStyleOptionViewItem optionCopy(*static_cast(&option)); optionCopy.decorationPosition = QStyleOptionViewItem::Top; optionCopy.decorationSize = QSize(mNavigator->iconSize(), mNavigator->iconSize()); optionCopy.textElideMode = Qt::ElideNone; QStyledItemDelegate::paint(painter, optionCopy, index); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (!index.isValid() || !index.internalPointer()) { return QSize(); } QStyleOptionViewItem optionCopy(*static_cast(&option)); optionCopy.decorationPosition = QStyleOptionViewItem::Top; optionCopy.decorationSize = mNavigator->showIcons() ? QSize(mNavigator->iconSize(), mNavigator->iconSize()) : QSize(); optionCopy.textElideMode = Qt::ElideNone; return QStyledItemDelegate::sizeHint(optionCopy, index); } private: Navigator *mNavigator; }; } Navigator::Navigator(SidePaneBase *parent) - : QListView(parent), mSidePane(parent) + : QListView(parent), mSidePane(parent), mMainWindow(nullptr) { setViewport(new QWidget(this)); setVerticalScrollMode(ScrollPerPixel); setHorizontalScrollMode(ScrollPerPixel); mIconSize = Prefs::self()->sidePaneIconSize(); mShowIcons = Prefs::self()->sidePaneShowIcons(); mShowText = Prefs::self()->sidePaneShowText(); QActionGroup *viewMode = new QActionGroup(this); + connect(viewMode, &QActionGroup::triggered, this, &Navigator::slotActionTriggered); mShowIconsAction = new QAction(i18nc("@action:inmenu", "Show Icons Only"), this); mShowIconsAction->setCheckable(true); mShowIconsAction->setActionGroup(viewMode); mShowIconsAction->setChecked(!mShowText && mShowIcons); setHelpText(mShowIconsAction, i18nc("@info:status", "Show sidebar items with icons and without text")); mShowIconsAction->setWhatsThis( i18nc("@info:whatsthis", "Choose this option if you want the sidebar items to have icons without text.")); mShowTextAction = new QAction(i18nc("@action:inmenu", "Show Text Only"), this); mShowTextAction->setCheckable(true); mShowTextAction->setActionGroup(viewMode); mShowTextAction->setChecked(mShowText && !mShowIcons); setHelpText(mShowTextAction, i18nc("@info:status", "Show sidebar items with text and without icons")); mShowTextAction->setWhatsThis( i18nc("@info:whatsthis", "Choose this option if you want the sidebar items to have text without icons.")); mShowBothAction = new QAction(i18nc("@action:inmenu", "Show Icons && Text"), this); mShowBothAction->setCheckable(true); mShowBothAction->setActionGroup(viewMode); mShowBothAction->setChecked(mShowText && mShowIcons); setHelpText(mShowBothAction, i18nc("@info:status", "Show sidebar items with icons and text")); mShowBothAction->setWhatsThis( i18nc("@info:whatsthis", "Choose this option if you want the sidebar items to have icons and text.")); - QAction *sep = new QAction(this); - sep->setSeparator(true); - connect(viewMode, &QActionGroup::triggered, this, &Navigator::slotActionTriggered); - QActionGroup *iconSize = new QActionGroup(this); connect(iconSize, &QActionGroup::triggered, this, &Navigator::slotActionTriggered); mBigIconsAction = new QAction(i18nc("@action:inmenu", "Big Icons"), this); mBigIconsAction->setCheckable(true); mBigIconsAction->setActionGroup(iconSize); mBigIconsAction->setChecked(mIconSize == KIconLoader::SizeLarge); setHelpText(mBigIconsAction, i18nc("@info:status", "Show large size sidebar icons")); mBigIconsAction->setWhatsThis( i18nc("@info:whatsthis", "Choose this option if you want the sidebar icons to be extra big.")); mNormalIconsAction = new QAction(i18nc("@action:inmenu", "Normal Icons"), this); mNormalIconsAction->setCheckable(true); mNormalIconsAction->setActionGroup(iconSize); mNormalIconsAction->setChecked(mIconSize == KIconLoader::SizeMedium); setHelpText(mNormalIconsAction, i18nc("@info:status", "Show normal size sidebar icons")); mNormalIconsAction->setWhatsThis( i18nc("@info:whatsthis", "Choose this option if you want the sidebar icons to be normal size.")); mSmallIconsAction = new QAction(i18nc("@action:inmenu", "Small Icons"), this); mSmallIconsAction->setCheckable(true); mSmallIconsAction->setActionGroup(iconSize); mSmallIconsAction->setChecked(mIconSize == KIconLoader::SizeSmallMedium); setHelpText(mSmallIconsAction, i18nc("@info:status", "Show small size sidebar icons")); mSmallIconsAction->setWhatsThis( i18nc("@info:whatsthis", "Choose this option if you want the sidebar icons to be extra small.")); + mHideSideBarAction = new QAction(i18nc("@action:inmenu", "Hide Sidebar"), this); + mHideSideBarAction->setCheckable(true); + mHideSideBarAction->setChecked(false); + setHelpText(mHideSideBarAction, i18nc("@info:status", "Hide the icon sidebar")); + mHideSideBarAction->setWhatsThis( + i18nc("@info:whatsthis", + "Choose this option if you to hide the icon sidebar. Press F9 to unhide.")); + connect(mHideSideBarAction, &QAction::triggered, this, &Navigator::slotHideSideBarTriggered); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + QAction *sep2 = new QAction(this); + sep2->setSeparator(true); + QList actionList; actionList << mShowIconsAction << mShowTextAction << mShowBothAction << sep - << mBigIconsAction << mNormalIconsAction << mSmallIconsAction; + << mBigIconsAction << mNormalIconsAction << mSmallIconsAction << sep2 + << mHideSideBarAction; insertActions(nullptr, actionList); setContextMenuPolicy(Qt::ActionsContextMenu); setViewMode(ListMode); setItemDelegate(new Delegate(this)); mModel = new Model(this); SortFilterProxyModel *sortFilterProxyModel = new SortFilterProxyModel; sortFilterProxyModel->setSourceModel(mModel); setModel(sortFilterProxyModel); setSelectionModel(new SelectionModel(sortFilterProxyModel, this)); setDragDropMode(DropOnly); viewport()->setAcceptDrops(true); setDropIndicatorShown(true); connect(selectionModel(), &QItemSelectionModel::currentChanged, this, &Navigator::slotCurrentChanged); } void Navigator::updatePlugins(const QList &plugins_) { QString currentPlugin; if (currentIndex().isValid()) { currentPlugin = currentIndex().model()->data(currentIndex(), Model::PluginName).toString(); } QList pluginsToShow; for (KontactInterface::Plugin *plugin : plugins_) { if (plugin->showInSideBar()) { pluginsToShow << plugin; } } mModel->setPluginList(pluginsToShow); mModel->removeRows(0, mModel->rowCount()); mModel->insertRows(0, pluginsToShow.count()); // Restore the previous selected index, if any if (!currentPlugin.isEmpty()) { setCurrentPlugin(currentPlugin); } } void Navigator::setCurrentPlugin(const QString &plugin) { const int numberOfRows(model()->rowCount()); for (int i = 0; i < numberOfRows; ++i) { const QModelIndex index = model()->index(i, 0); const QString pluginName = model()->data(index, Model::PluginName).toString(); if (plugin == pluginName) { selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent); break; } } } QSize Navigator::sizeHint() const { //### TODO: We can cache this value, so this reply is faster. Since here we won't // have too many elements, it is not that important. When caching this value // make sure it is updated correctly when new rows have been added or // removed. (ereslibre) int maxWidth = 0; const int numberOfRows(model()->rowCount()); for (int i = 0; i < numberOfRows; ++i) { const QModelIndex index = model()->index(i, 0); maxWidth = qMax(maxWidth, sizeHintForIndex(index).width()); } // Take vertical scrollbar into account maxWidth += qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); int viewHeight = QListView::sizeHint().height(); QSize size(maxWidth + rect().width() - contentsRect().width(), viewHeight); return size; } void Navigator::dragEnterEvent(QDragEnterEvent *event) { if (event->proposedAction() == Qt::IgnoreAction) { return; } event->acceptProposedAction(); } void Navigator::dragMoveEvent(QDragMoveEvent *event) { if (event->proposedAction() == Qt::IgnoreAction) { return; } const QModelIndex dropIndex = indexAt(event->pos()); if (!dropIndex.isValid() || !(dropIndex.model()->flags(dropIndex) & Qt::ItemIsEnabled)) { event->setAccepted(false); return; } else { const QModelIndex sourceIndex = static_cast(model())->mapToSource(dropIndex); KontactInterface::Plugin *plugin = static_cast(sourceIndex.internalPointer()); if (!plugin->canDecodeMimeData(event->mimeData())) { event->setAccepted(false); return; } } event->acceptProposedAction(); } void Navigator::dropEvent(QDropEvent *event) { if (event->proposedAction() == Qt::IgnoreAction) { return; } const QModelIndex dropIndex = indexAt(event->pos()); if (!dropIndex.isValid()) { return; } else { const QModelIndex sourceIndex = static_cast(model())->mapToSource(dropIndex); KontactInterface::Plugin *plugin = static_cast(sourceIndex.internalPointer()); plugin->processDropEvent(event); } } void Navigator::setHelpText(QAction *act, const QString &text) { act->setStatusTip(text); act->setToolTip(text); if (act->whatsThis().isEmpty()) { act->setWhatsThis(text); } } void Navigator::showEvent(QShowEvent *event) { parentWidget()->setMaximumWidth(sizeHint().width()); parentWidget()->setMinimumWidth(sizeHint().width()); QListView::showEvent(event); } void Navigator::slotCurrentChanged(const QModelIndex ¤t) { if (!current.isValid() || !current.internalPointer() || !(current.model()->flags(current) & Qt::ItemIsEnabled)) { return; } QModelIndex source = static_cast(current.model())->mapToSource(current); Q_EMIT pluginActivated(static_cast(source.internalPointer())); } void Navigator::slotActionTriggered(QAction *object) { bool checked = object->isChecked(); if (object == mShowIconsAction) { mShowIcons = checked; mShowText = !checked; } else if (object == mShowTextAction) { mShowIcons = !checked; mShowText = checked; } else if (object == mShowBothAction) { mShowIcons = checked; mShowText = checked; } else if (object == mBigIconsAction) { mIconSize = KIconLoader::SizeLarge; } else if (object == mNormalIconsAction) { mIconSize = KIconLoader::SizeMedium; } else if (object == mSmallIconsAction) { mIconSize = KIconLoader::SizeSmallMedium; } Prefs::self()->setSidePaneIconSize(mIconSize); Prefs::self()->setSidePaneShowIcons(mShowIcons); Prefs::self()->setSidePaneShowText(mShowText); mModel->emitReset(); QTimer::singleShot(0, this, &Navigator::updateNavigatorSize); } +void Navigator::slotHideSideBarTriggered() +{ + if (mainWindow()) { + mainWindow()->showHideSideBar(false); + mHideSideBarAction->setChecked(false); + } +} + void Navigator::updateNavigatorSize() { parentWidget()->setMaximumWidth(sizeHint().width()); parentWidget()->setMinimumWidth(sizeHint().width()); } IconSidePane::IconSidePane(KontactInterface::Core *core, QWidget *parent) : SidePaneBase(core, parent) { mNavigator = new Navigator(this); layout()->addWidget(mNavigator); mNavigator->setFocusPolicy(Qt::NoFocus); + mNavigator->setMainWindow(dynamic_cast(core)); connect(mNavigator, &Navigator::pluginActivated, this, &IconSidePane::pluginSelected); } IconSidePane::~IconSidePane() { } void IconSidePane::setCurrentPlugin(const QString &plugin) { mNavigator->setCurrentPlugin(plugin); } void IconSidePane::updatePlugins() { mNavigator->updatePlugins(core()->pluginList()); } void IconSidePane::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); const int newWidth(mNavigator->sizeHint().width()); setFixedWidth(newWidth); mNavigator->setFixedWidth(newWidth); } #include "iconsidepane.moc" - diff --git a/src/iconsidepane.h b/src/iconsidepane.h index 7e1094c4..546ad11f 100644 --- a/src/iconsidepane.h +++ b/src/iconsidepane.h @@ -1,124 +1,138 @@ /* This file is part of the KDE Kontact. Copyright (C) 2003 Cornelius Schumacher Copyright (C) 2008 Rafael Fernández López 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KONTACT_ICONSIDEPANEBASE_H #define KONTACT_ICONSIDEPANEBASE_H #include "sidepanebase.h" #include namespace KontactInterface { class Core; class Plugin; } class QAction; namespace Kontact { class Model; +class MainWindow; class Navigator; class Navigator : public QListView { Q_OBJECT public: explicit Navigator(SidePaneBase *parent = nullptr); void updatePlugins(const QList &plugins); void setCurrentPlugin(const QString &plugin); int iconSize() const { return mIconSize; } bool showIcons() const { return mShowIcons; } bool showText() const { return mShowText; } + void setMainWindow(MainWindow *mainWindow) + { + mMainWindow = mainWindow; + } + + MainWindow *mainWindow() + { + return mMainWindow; + } + QSize sizeHint() const override; Q_SIGNALS: void pluginActivated(KontactInterface::Plugin *plugin); protected: void dragEnterEvent(QDragEnterEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; void showEvent(QShowEvent *event) override; private Q_SLOTS: void slotCurrentChanged(const QModelIndex ¤t); void slotActionTriggered(QAction *checked); + void slotHideSideBarTriggered(); void updateNavigatorSize(); private: void setHelpText(QAction *act, const QString &text); SidePaneBase *mSidePane; + MainWindow *mMainWindow; Model *mModel; int mIconSize; bool mShowIcons; bool mShowText; QAction *mShowIconsAction; QAction *mShowTextAction; QAction *mShowBothAction; QAction *mBigIconsAction; QAction *mNormalIconsAction; QAction *mSmallIconsAction; + QAction *mHideSideBarAction; }; class IconSidePane : public SidePaneBase { Q_OBJECT public: IconSidePane(KontactInterface::Core *core, QWidget *parent); ~IconSidePane(); void setCurrentPlugin(const QString &plugin) override; public Q_SLOTS: void updatePlugins() override; protected: void resizeEvent(QResizeEvent *event) override; private: Navigator *mNavigator; }; } #endif diff --git a/src/kcmkontact.cpp b/src/kcmkontact.cpp index 5ef1713f..cc573a85 100644 --- a/src/kcmkontact.cpp +++ b/src/kcmkontact.cpp @@ -1,156 +1,158 @@ /* This file is part of KDE Kontact. Copyright (c) 2003 Cornelius Schumacher 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. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "kcmkontact.h" #include "prefs.h" using namespace Kontact; #include #include #include #include #include #include +#include #include -#include extern "C" { Q_DECL_EXPORT KCModule *create_kontactconfig(QWidget *parent, const char *) { return new KcmKontact(parent); } } KcmKontact::KcmKontact(QWidget *parent) : KPrefsModule(Prefs::self(), parent) { - QBoxLayout *topLayout = new QVBoxLayout(this); + QFormLayout *topLayout = new QFormLayout(this); QBoxLayout *pluginStartupLayout = new QHBoxLayout(); topLayout->addItem(pluginStartupLayout); - topLayout->addStretch(); KPrefsWidBool *forceStartupPlugin = addWidBool(Prefs::self()->forceStartupPluginItem(), this); pluginStartupLayout->addWidget(forceStartupPlugin->checkBox()); PluginSelection *selection = new PluginSelection(Prefs::self()->forcedStartupPluginItem(), this); addWid(selection); pluginStartupLayout->addWidget(selection->comboBox()); selection->comboBox()->setEnabled(false); pluginStartupLayout->addStretch(1); connect(forceStartupPlugin->checkBox(), &QAbstractButton::toggled, selection->comboBox(), &QWidget::setEnabled); + + KPrefsWidBool *showSideBar = addWidBool(Prefs::self()->sideBarOpenItem(), this); + topLayout->addWidget(showSideBar->checkBox()); + load(); } const KAboutData *KcmKontact::aboutData() const { KAboutData *about = new KAboutData( QStringLiteral("kontactconfig"), i18nc("@title", "KDE Kontact"), QString(), QString(), KAboutLicense::GPL, i18nc("@info:credit", "(c), 2003 Cornelius Schumacher")); about->addAuthor(i18nc("@info:credit", "Cornelius Schumacher"), i18nc("@info:credit", "Developer"), QStringLiteral("schumacher@kde.org")); about->addAuthor(i18nc("@info:credit", "Tobias Koenig"), i18nc("@info:credit", "Developer"), QStringLiteral("tokoe@kde.org")); return about; } PluginSelection::PluginSelection(KConfigSkeleton::ItemString *item, QWidget *parent) { mItem = item; mPluginCombo = new KComboBox(parent); mPluginCombo->setToolTip( i18nc("@info:tooltip", "Select the initial plugin to use on each start")); mPluginCombo->setWhatsThis( i18nc("@info:whatsthis", "Select the plugin from this drop down list to be used as the " "initial plugin each time Kontact is started. Otherwise, Kontact " "will restore the last active plugin from the previous usage.")); connect(mPluginCombo, static_cast(&KComboBox::currentIndexChanged), this, &PluginSelection::changed); } PluginSelection::~PluginSelection() { } KComboBox *PluginSelection::comboBox() const { return mPluginCombo; } void PluginSelection::readConfig() { const KService::List offers = KServiceTypeTrader::self()->query( QStringLiteral("Kontact/Plugin"), QStringLiteral("[X-KDE-KontactPluginVersion] == %1").arg(KONTACT_PLUGIN_VERSION)); int activeComponent = 0; mPluginCombo->clear(); mPluginList.clear(); KService::List::ConstIterator end(offers.end()); for (KService::List::ConstIterator it = offers.begin(); it != end; ++it) { KService::Ptr service = *it; // skip summary only plugins QVariant var = service->property(QStringLiteral("X-KDE-KontactPluginHasPart")); if (var.isValid() && var.toBool() == false) { continue; } mPluginCombo->addItem(service->name()); mPluginList.append(service); if (service->property(QStringLiteral("X-KDE-PluginInfo-Name")).toString() == mItem->value()) { activeComponent = mPluginList.count() - 1; } } mPluginCombo->setCurrentIndex(activeComponent); } void PluginSelection::writeConfig() { KService::Ptr ptr = mPluginList.at(mPluginCombo->currentIndex()); mItem->setValue(ptr->property(QStringLiteral("X-KDE-PluginInfo-Name")).toString()); } QList PluginSelection::widgets() const { const QList widgets{mPluginCombo}; return widgets; } - diff --git a/src/kontact.kcfg b/src/kontact.kcfg index e9786ca7..34d92822 100644 --- a/src/kontact.kcfg +++ b/src/kontact.kcfg @@ -1,42 +1,49 @@ kontact_summaryplugin The currently active plugin The currently active plugin false Set the initial plugin on each start Usually Kontact will come up with the plugin used before shutdown. Check this box if you would like the specified plugin to come up on start instead. + + true + + Show/Hide the icon sidebar + To save screen real estate you might chose to hide the icon sidebar. Uncheck this box to hide the component sidebar. Note that the F9 key will hide/show the icon sidebar. + + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index defeed78..a418b671 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,1131 +1,1165 @@ /* This file is part of KDE Kontact. Copyright (c) 2001 Matthias Hoelzer-Kluepfel Copyright (c) 2002-2005 Daniel Molkentin Copyright (c) 2003-2005 Cornelius Schumacher 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. */ #include "mainwindow.h" #include "aboutdialog.h" #include "prefs.h" #include "iconsidepane.h" #include "webengine/introductionwebengineview.h" #include "webengine/introductionwebenginepage.h" #include "kontactconfiguredialog.h" using namespace Kontact; #ifdef WIN32 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "kontact_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Define the maximum time Kontact waits for KSycoca to become available. static const int KSYCOCA_WAIT_TIMEOUT = 10; // This class extends the normal KDBusServiceStarter. // // When a service start is requested, it asks all plugins // to create their dbus interfaces in addition to the normal // way of starting a service. class ServiceStarter : public KDBusServiceStarter { public: virtual int startServiceFor(const QString &serviceType, const QString &constraint = QString(), QString *error = nullptr, QString *dbusService = nullptr, int flags = 0) override; // We need to keep track of the plugins which are loaded, so pass a pointer // to the plugin list here. Be sure to reset it back to 0 with // setPluginList() as soon as the list gets destroyed. ServiceStarter(PluginList *pluginList) { mPlugins = pluginList; } static void setPluginList(PluginList *pluginList) { mPlugins = pluginList; } protected: virtual ~ServiceStarter() {} static PluginList *mPlugins; }; PluginList *ServiceStarter::mPlugins = nullptr; int ServiceStarter::startServiceFor(const QString &serviceType, const QString &constraint, QString *error, QString *dbusService, int flags) { if (mPlugins) { const PluginList::ConstIterator end = mPlugins->constEnd(); for (PluginList::ConstIterator it = mPlugins->constBegin(); it != end; ++it) { if ((*it)->createDBUSInterface(serviceType)) { qCDebug(KONTACT_LOG) << "found interface for" << serviceType; if (dbusService) { *dbusService = (*it)->registerClient(); } return 0; } } } qCDebug(KONTACT_LOG) << "Didn't find dbus interface, falling back to external process"; return KDBusServiceStarter::startServiceFor(serviceType, constraint, error, dbusService, flags); } MainWindow::MainWindow() : KontactInterface::Core(), mSplitter(nullptr), mCurrentPlugin(nullptr), mAboutDialog(nullptr), - mReallyClose(false) + mReallyClose(false), mSaveSideBarWidth(10) { // The ServiceStarter created here will be deleted by the KDbusServiceStarter // base class, which is a global static. new ServiceStarter(&mPlugins); QDBusConnection::sessionBus().registerObject( QStringLiteral("/KontactInterface"), this, QDBusConnection::ExportScriptableSlots); // Set this to be the group leader for all subdialogs - this means // modal subdialogs will only affect this dialog, not the other windows setAttribute(Qt::WA_GroupLeader); initGUI(); initObject(); mSidePane->setMaximumWidth(mSidePane->sizeHint().width()); mSidePane->setMinimumWidth(mSidePane->sizeHint().width()); factory()->plugActionList(this, QStringLiteral("navigator_actionlist"), mActionPlugins); KConfigGroup grp(KSharedConfig::openConfig(), "MainWindow"); KWindowConfig::restoreWindowSize(windowHandle(), grp); setAutoSaveSettings(); } void MainWindow::initGUI() { initWidgets(); setupActions(); setHelpMenuEnabled(false); KHelpMenu *helpMenu = new KHelpMenu(this, QString(), true); connect(helpMenu, &KHelpMenu::showAboutApplication, this, &MainWindow::showAboutDialog); KStandardAction::keyBindings(this, &MainWindow::configureShortcuts, actionCollection()); KStandardAction::configureToolbars(this, &MainWindow::configureToolbars, actionCollection()); setXMLFile(QStringLiteral("kontactui.rc")); setStandardToolBarMenuEnabled(true); createGUI(nullptr); KToolBar *navigatorToolBar = findToolBar("navigatorToolBar"); if (navigatorToolBar) { if (layoutDirection() == Qt::LeftToRight) { navigatorToolBar->setLayoutDirection(Qt::RightToLeft); } else { navigatorToolBar->setLayoutDirection(Qt::LeftToRight); } Q_ASSERT(navigatorToolBar->sizeHint().isValid()); navigatorToolBar->setMinimumWidth(navigatorToolBar->sizeHint().width()); } else { qCritical() << "Unable to find navigatorToolBar, probably kontactui.rc is missing"; } } void MainWindow::waitForKSycoca() { int i = 0; while (i < KSYCOCA_WAIT_TIMEOUT) { if (KSycoca::isAvailable()) { return; } // When KSycoca is not availabe that usually means Kontact // was started before kded is done with it's first run // we want to block Kontact execution to // give Kded time to initialize and create the // System Configuration database necessary for further // Kontact startup qCDebug(KONTACT_LOG) << "Waiting for KSycoca"; #ifdef WIN32 Sleep(1000); #else sleep(1); #endif ++i; } // This should only happen if the distribution is broken qFatal("KSycoca unavailable. Kontact will be unable to find plugins."); } void MainWindow::initObject() { if (!KSycoca::isAvailable()) { waitForKSycoca(); } KService::List offers = KServiceTypeTrader::self()->query( QStringLiteral("Kontact/Plugin"), QStringLiteral("[X-KDE-KontactPluginVersion] == %1").arg(KONTACT_PLUGIN_VERSION)); mPluginInfos = KPluginInfo::fromServices( offers, KConfigGroup(Prefs::self()->config(), "Plugins")); KPluginInfo::List::Iterator it; KPluginInfo::List::Iterator end(mPluginInfos.end()); for (it = mPluginInfos.begin(); it != end; ++it) { it->load(); } // prepare the part manager mPartManager = new KParts::PartManager(this); connect(mPartManager, &KParts::PartManager::activePartChanged, this, &MainWindow::slotActivePartChanged); loadPlugins(); if (mSidePane) { mSidePane->updatePlugins(); } KSettings::Dispatcher::registerComponent(QStringLiteral("kontact"), this, "updateConfig"); loadSettings(); statusBar()->show(); // done initializing slotShowStatusMsg(QString()); connect(KPIM::BroadcastStatus::instance(), SIGNAL(statusMsg(QString)), this, SLOT(slotShowStatusMsg(QString))); // launch commandline specified module if any activateInitialPluginModule(); if (Prefs::lastVersionSeen() == KAboutData::applicationData().version()) { selectPlugin(mCurrentPlugin); } paintAboutScreen(QStringLiteral("introduction_kontact.html"), introductionData()); Prefs::setLastVersionSeen(KAboutData::applicationData().version()); } MainWindow::~MainWindow() { if (mCurrentPlugin) { KConfigGroup grp( KSharedConfig::openConfig()->group( QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier()))); saveMainWindowSettings(grp); } createGUI(nullptr); ServiceStarter::setPluginList(nullptr); saveSettings(); Prefs::self()->save(); // During deletion of plugins, we should not access the plugin list (bug #182176) delete mSidePane; const PluginList::ConstIterator end = mPlugins.constEnd(); for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) { delete *it; } } // Called by main(). void MainWindow::setInitialActivePluginModule(const QString &module) { if (mInitialActiveModule != module) { mInitialActiveModule = module; activateInitialPluginModule(); } } bool MainWindow::pluginActionWeightLessThan(const QAction *left, const QAction *right) { // Since this lessThan method is used only for the toolbar (which is on // the inverse layout direction than the rest of the system), we add the // elements on the exactly inverse order. (ereslibre) return !pluginWeightLessThan(left->data().value(), right->data().value()); } bool MainWindow::pluginWeightLessThan(const KontactInterface::Plugin *left, const KontactInterface::Plugin *right) { return left->weight() < right->weight(); } void MainWindow::activateInitialPluginModule() { if (!mInitialActiveModule.isEmpty() && !mPlugins.isEmpty()) { const PluginList::ConstIterator end = mPlugins.constEnd(); for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) { if (!(*it)->identifier().isEmpty() && (*it)->identifier().contains(mInitialActiveModule)) { selectPlugin(*it); return; } } } } void MainWindow::initWidgets() { QWidget *mTopWidget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); layout->setSpacing(0); mTopWidget->setLayout(layout); setCentralWidget(mTopWidget); mSplitter = new QSplitter(mTopWidget); + connect(mSplitter, &QSplitter::splitterMoved, this, &MainWindow::slotSplitterMoved); layout->addWidget(mSplitter); mSidePane = new IconSidePane(this, mSplitter); - /* - // don't occupy screen estate on load - QList sizes; - sizes << 0; - mSplitter->setSizes(sizes); - */ connect(mSidePane, SIGNAL(pluginSelected(KontactInterface::Plugin*)), SLOT(selectPlugin(KontactInterface::Plugin*))); mPartsStack = new QStackedWidget(mSplitter); mPartsStack->layout()->setSpacing(0); initAboutScreen(); paintAboutScreen(QStringLiteral("loading_kontact.html"), QVariantHash()); KPIM::ProgressStatusBarWidget *progressStatusBarWidget = new KPIM::ProgressStatusBarWidget(statusBar(), this); mStatusMsgLabel = new KSqueezedTextLabel(i18nc("@info:status", " Initializing..."), statusBar()); mStatusMsgLabel->setTextElideMode(Qt::ElideRight); mStatusMsgLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); statusBar()->addWidget(mStatusMsgLabel, 10); statusBar()->addPermanentWidget(progressStatusBarWidget->littleProgress(), 0); mSplitter->setCollapsible(1, false); } void MainWindow::paintAboutScreen(const QString &templateName, const QVariantHash &data) { GrantleeTheme::ThemeManager manager(QStringLiteral("splashPage"), QStringLiteral("splash.theme"), nullptr, QStringLiteral("messageviewer/about/")); GrantleeTheme::Theme theme = manager.theme(QStringLiteral("default")); if (!theme.isValid()) { qCDebug(KONTACT_LOG) << "Theme error: failed to find splash theme"; } else { mIntroPart->setHtml(theme.render(templateName, data, QByteArrayLiteral("kontact")), QUrl::fromLocalFile(theme.absolutePath() + QLatin1Char('/'))); } } void MainWindow::initAboutScreen() { QWidget *introbox = new QWidget(mPartsStack); QHBoxLayout *introboxHBoxLayout = new QHBoxLayout(introbox); introboxHBoxLayout->setMargin(0); mPartsStack->addWidget(introbox); mPartsStack->setCurrentWidget(introbox); mIntroPart = new IntroductionWebEngineView(introbox); connect(mIntroPart, &IntroductionWebEngineView::openUrl, this, &MainWindow::slotOpenUrl, Qt::QueuedConnection); introboxHBoxLayout->addWidget(mIntroPart); } void MainWindow::setupActions() { actionCollection()->addAction(KStandardAction::Quit, this, SLOT(slotQuit())); mNewActions = new KActionMenu( i18nc("@title:menu create new pim items (message,calendar,to-do,etc.)", "New"), this); actionCollection()->addAction(QStringLiteral("action_new"), mNewActions); actionCollection()->setDefaultShortcuts(mNewActions, KStandardShortcut::openNew()); connect(mNewActions, &KActionMenu::triggered, this, &MainWindow::slotNewClicked); QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18nc("@action:inmenu", "Configure Kontact..."), this); setHelpText(action, i18nc("@info:status", "Configure Kontact")); action->setWhatsThis( i18nc("@info:whatsthis", "You will be presented with a dialog where you can configure Kontact.")); actionCollection()->addAction(QStringLiteral("settings_configure_kontact"), action); connect(action, &QAction::triggered, this, &MainWindow::slotPreferences); action = new QAction(QIcon::fromTheme(QStringLiteral("kontact")), i18nc("@action:inmenu", "&Kontact Introduction"), this); setHelpText(action, i18nc("@info:status", "Show the Kontact Introduction page")); action->setWhatsThis( i18nc("@info:whatsthis", "Choose this option to see the Kontact Introduction page.")); actionCollection()->addAction(QStringLiteral("help_introduction"), action); connect(action, &QAction::triggered, this, &MainWindow::slotShowIntroduction); - //TODO 4.12: add description - QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_F9), this); - connect(shortcut, &QShortcut::activated, this, &MainWindow::slotShowHideSideBar); + mShowHideAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), + i18nc("@action:inmenu", "Hide/Show the component sidebar"), this); + setHelpText(mShowHideAction, i18nc("@info:status", "Hide/Show the component sidebar")); + mShowHideAction->setWhatsThis( + i18nc("@info:whatsthis", + "Allows you to show or hide the component sidebar as desired.")); + mShowHideAction->setShortcut(QKeySequence(Qt::Key_F9)); + actionCollection()->addAction(QStringLiteral("hide_show_sidebar"), mShowHideAction); + connect(mShowHideAction, &QAction::triggered, this, &MainWindow::slotShowHideSideBar); } bool MainWindow::isPluginLoaded(const KPluginInfo &info) { return (pluginFromInfo(info) != nullptr); } KontactInterface::Plugin *MainWindow::pluginFromInfo(const KPluginInfo &info) { const PluginList::ConstIterator end = mPlugins.constEnd(); for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) { if ((*it)->identifier() == info.pluginName()) { return *it; } } return nullptr; } void MainWindow::loadPlugins() { QList plugins; int i; KPluginInfo::List::ConstIterator it; const KPluginInfo::List::ConstIterator end(mPluginInfos.constEnd()); for (it = mPluginInfos.constBegin(); it != end; ++it) { if (!it->isPluginEnabled()) { continue; } KontactInterface::Plugin *plugin = nullptr; if (isPluginLoaded(*it)) { plugin = pluginFromInfo(*it); if (plugin) { plugin->configUpdated(); } continue; } qCDebug(KONTACT_LOG) << "Loading Plugin:" << it->name(); QString error; plugin = it->service()->createInstance(this, QVariantList(), &error); if (!plugin) { qCDebug(KONTACT_LOG) << "Unable to create plugin for" << it->name() << error; continue; } plugin->setIdentifier(it->pluginName()); plugin->setTitle(it->name()); plugin->setIcon(it->icon()); QVariant libNameProp = it->property(QStringLiteral("X-KDE-KontactPartLibraryName")); QVariant exeNameProp = it->property(QStringLiteral("X-KDE-KontactPartExecutableName")); QVariant loadOnStart = it->property(QStringLiteral("X-KDE-KontactPartLoadOnStart")); QVariant hasPartProp = it->property(QStringLiteral("X-KDE-KontactPluginHasPart")); if (!loadOnStart.isNull() && loadOnStart.toBool()) { mDelayedPreload.append(plugin); } qCDebug(KONTACT_LOG) << "LIBNAMEPART:" << libNameProp.toString(); plugin->setPartLibraryName(libNameProp.toString().toUtf8()); plugin->setExecutableName(exeNameProp.toString()); if (hasPartProp.isValid()) { plugin->setShowInSideBar(hasPartProp.toBool()); } for (i = 0; i < plugins.count(); ++i) { KontactInterface::Plugin *p = plugins.at(i); if (plugin->weight() < p->weight()) { break; } } plugins.insert(i, plugin); } const int numberOfPlugins(plugins.count()); for (i = 0; i < numberOfPlugins; ++i) { KontactInterface::Plugin *plugin = plugins.at(i); const QList actionList = plugin->newActions(); QList::const_iterator listIt; const QList::const_iterator end(actionList.end()); for (listIt = actionList.begin(); listIt != end; ++listIt) { qCDebug(KONTACT_LOG) << QStringLiteral("Plugging New actions") << (*listIt)->objectName(); mNewActions->addAction((*listIt)); } addPlugin(plugin); } const bool state = (!mPlugins.isEmpty()); mNewActions->setEnabled(state); } void MainWindow::unloadPlugins() { const KPluginInfo::List::ConstIterator end = mPluginInfos.constEnd(); KPluginInfo::List::ConstIterator it; for (it = mPluginInfos.constBegin(); it != end; ++it) { if (!it->isPluginEnabled()) { removePlugin(*it); } } } void MainWindow::updateShortcuts() { const ActionPluginList::ConstIterator end = mActionPlugins.constEnd(); ActionPluginList::ConstIterator it; int i = 0; for (it = mActionPlugins.constBegin(); it != end; ++it) { QAction *action = static_cast(*it); const QString shortcut = QStringLiteral("Ctrl+%1").arg(mActionPlugins.count() - i); actionCollection()->setDefaultShortcut(action, QKeySequence(shortcut)); ++i; } factory()->plugActionList(this, QStringLiteral("navigator_actionlist"), mActionPlugins); } bool MainWindow::removePlugin(const KPluginInfo &info) { const PluginList::Iterator end = mPlugins.end(); for (PluginList::Iterator it = mPlugins.begin(); it != end; ++it) { KontactInterface::Plugin *plugin = *it; if ((*it)->identifier() == info.pluginName()) { QList actionList = plugin->newActions(); QList::const_iterator listIt; QList::const_iterator listEnd(actionList.constEnd()); for (listIt = actionList.constBegin(); listIt != listEnd; ++listIt) { qCDebug(KONTACT_LOG) << QStringLiteral("Unplugging New actions") << (*listIt)->objectName(); mNewActions->removeAction(*listIt); } removeChildClient(plugin); if (mCurrentPlugin == plugin) { mCurrentPlugin = nullptr; createGUI(nullptr); } plugin->deleteLater(); // removes the part automatically mPlugins.erase(it); if (plugin->showInSideBar()) { QAction *q = mPluginAction[plugin]; // remove QAction, to free the shortcut for later use mActionPlugins.removeAll(q); mPluginAction.remove(plugin); delete q; } if (mCurrentPlugin == nullptr) { PluginList::Iterator it; const PluginList::Iterator pluginEnd(mPlugins.end()); for (it = mPlugins.begin(); it != pluginEnd; ++it) { if ((*it)->showInSideBar()) { selectPlugin(*it); return true; } } } return true; } } return false; } void MainWindow::addPlugin(KontactInterface::Plugin *plugin) { qCDebug(KONTACT_LOG); mPlugins.append(plugin); if (plugin->showInSideBar()) { QAction *action = new QAction(QIcon::fromTheme(plugin->icon()), plugin->title(), this); //action->setHelpText( // i18nc( "@info:status", "Plugin %1", plugin->title() ) ); action->setWhatsThis( i18nc("@info:whatsthis", "Switch to plugin %1", plugin->title())); action->setCheckable(true); action->setData(QVariant::fromValue(plugin)); // on the slot we can decode // which action was triggered connect(action, &QAction::triggered, this, &MainWindow::slotActionTriggered); actionCollection()->addAction(plugin->title(), action); mActionPlugins.append(action); mPluginAction.insert(plugin, action); } // merge the plugins GUI into the main window insertChildClient(plugin); // sort the action plugins again and reset shortcuts. If we removed and then readded some plugins // we need to take in count their weights for setting shortcuts again std::sort(mActionPlugins.begin(), mActionPlugins.end(), pluginActionWeightLessThan); std::sort(mPlugins.begin(), mPlugins.end(), pluginWeightLessThan); int i = 0; for (QAction *qaction : qAsConst(mActionPlugins)) { QAction *action = static_cast(qaction); QString shortcut = QStringLiteral("Ctrl+%1").arg(mActionPlugins.count() - i); actionCollection()->setDefaultShortcut(action, QKeySequence(shortcut)); ++i; } } void MainWindow::partLoaded(KontactInterface::Plugin *plugin, KParts::ReadOnlyPart *part) { Q_UNUSED(plugin); // See if we have this part already (e.g. due to two plugins sharing it) if (mPartsStack->indexOf(part->widget()) != -1) { return; } mPartsStack->addWidget(part->widget()); mPartManager->addPart(part, false); // Workaround for KParts misbehavior: addPart calls show! part->widget()->hide(); } void MainWindow::slotActivePartChanged(KParts::Part *part) { if (!part) { createGUI(nullptr); return; } qCDebug(KONTACT_LOG) << QStringLiteral("Part activated:") << part << QStringLiteral("with stack id.") << mPartsStack->indexOf(part->widget()); statusBar()->clearMessage(); } void MainWindow::slotNewClicked() { if (!mCurrentPlugin->newActions().isEmpty()) { mCurrentPlugin->newActions().at(0)->trigger(); } else { PluginList::Iterator it; const PluginList::Iterator end(mPlugins.end()); for (it = mPlugins.begin(); it != end; ++it) { if (!(*it)->newActions().isEmpty()) { (*it)->newActions().first()->trigger(); return; } } } } KToolBar *MainWindow::findToolBar(const char *name) { // like KMainWindow::toolBar, but which doesn't create the toolbar if not found return findChild(QLatin1String(name)); } void MainWindow::selectPlugin(KontactInterface::Plugin *plugin) { if (!plugin) { return; } if (plugin->isRunningStandalone()) { statusBar()->showMessage( i18nc("@info:status", "Application is running standalone. Foregrounding..."), 1000); plugin->bringToForeground(); return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); if (mCurrentPlugin) { KConfigGroup grp = KSharedConfig::openConfig()->group( QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier())); saveMainWindowSettings(grp); } KParts::Part *part = plugin->part(); if (!part) { QApplication::restoreOverrideCursor(); KMessageBox::error( this, i18nc("@info", "Cannot load part for %1.", plugin->title()) + QLatin1Char('\n') + lastErrorMessage()); plugin->setDisabled(true); mSidePane->updatePlugins(); return; } if (mCurrentPlugin) { QAction *action = mPluginAction[ mCurrentPlugin ]; if (action) { action->setChecked(false); } } QAction *selectedPluginAction = mPluginAction[ plugin ]; if (selectedPluginAction) { selectedPluginAction->setChecked(true); } // store old focus widget QWidget *focusWidget = qApp->focusWidget(); if (mCurrentPlugin && focusWidget) { // save the focus widget only when it belongs to the activated part QWidget *parent = focusWidget->parentWidget(); while (parent) { if (parent == mCurrentPlugin->part()->widget()) { mFocusWidgets.insert(mCurrentPlugin->identifier(), QPointer(focusWidget)); } parent = parent->parentWidget(); } } if (mSidePane) { mSidePane->setCurrentPlugin(plugin->identifier()); } plugin->aboutToSelect(); mPartManager->setActivePart(part); QWidget *view = part->widget(); Q_ASSERT(view); if (view) { mPartsStack->setCurrentWidget(view); view->show(); if (!mFocusWidgets.isEmpty() && mFocusWidgets.contains(plugin->identifier())) { focusWidget = mFocusWidgets[ plugin->identifier() ]; if (focusWidget) { focusWidget->setFocus(); } } else { view->setFocus(); } mCurrentPlugin = plugin; QAction *newAction = nullptr; if (!plugin->newActions().isEmpty()) { newAction = plugin->newActions().at(0); } createGUI(plugin->part()); plugin->shortcutChanged(); setCaption(i18nc("@title:window Plugin dependent window title", "%1 - Kontact", plugin->title())); if (newAction) { mNewActions->setIcon(newAction->icon()); static_cast(mNewActions)->setText(newAction->text()); mNewActions->setWhatsThis(newAction->whatsThis()); } else { // we'll use the action of the first plugin which offers one PluginList::Iterator it; PluginList::Iterator end(mPlugins.end()); for (it = mPlugins.begin(); it != end; ++it) { if (!(*it)->newActions().isEmpty()) { newAction = (*it)->newActions().first(); } if (newAction) { static_cast(mNewActions)->setIcon(newAction->icon()); mNewActions->setText(newAction->text()); mNewActions->setWhatsThis(newAction->whatsThis()); break; } } } } KToolBar *navigatorToolBar = findToolBar("navigatorToolBar"); if (navigatorToolBar && !navigatorToolBar->isHidden() && (toolBarArea(navigatorToolBar) == Qt::TopToolBarArea || toolBarArea(navigatorToolBar) == Qt::BottomToolBarArea)) { addToolBar(toolBarArea(navigatorToolBar), navigatorToolBar); } applyMainWindowSettings(KSharedConfig::openConfig()->group( QStringLiteral("MainWindow%1").arg(plugin->identifier()))); QApplication::restoreOverrideCursor(); } void MainWindow::slotActionTriggered() { QAction *actionSender = static_cast(sender()); actionSender->setChecked(true); KontactInterface::Plugin *plugin = actionSender->data().value(); if (!plugin) { return; } mSidePane->setCurrentPlugin(plugin->identifier()); } void MainWindow::selectPlugin(const QString &pluginName) { PluginList::ConstIterator end = mPlugins.constEnd(); for (PluginList::ConstIterator it = mPlugins.constBegin(); it != end; ++it) { if ((*it)->identifier() == pluginName) { selectPlugin(*it); return; } } } void MainWindow::loadSettings() { if (mSplitter) { - // if the preferences do not contain useful values, the side pane part of the splitter - // takes up the full width of the window, so leave the splitter sizing at the widget defaults - QList sizes = Prefs::self()->sidePaneSplitter(); - if (sizes.count() == mSplitter->count()) { - mSplitter->setSizes(sizes); - } + showHideSideBar(Prefs::self()->sideBarOpen()); } // Preload Plugins. This _must_ happen before the default part is loaded PluginList::ConstIterator it; PluginList::ConstIterator end(mDelayedPreload.constEnd()); for (it = mDelayedPreload.constBegin(); it != end; ++it) { selectPlugin(*it); } selectPlugin(Prefs::self()->mActivePlugin); } void MainWindow::saveSettings() { if (mSplitter) { Prefs::self()->mSidePaneSplitter = mSplitter->sizes(); } if (mCurrentPlugin) { Prefs::self()->mActivePlugin = mCurrentPlugin->identifier(); } } void MainWindow::slotShowIntroduction() { mPartsStack->setCurrentIndex(0); } void MainWindow::slotQuit() { mReallyClose = true; close(); } void MainWindow::slotPreferences() { static Kontact::KontactConfigureDialog *dlg = nullptr; if (!dlg) { dlg = new Kontact::KontactConfigureDialog(this); dlg->setAllowComponentSelection(true); // do not show settings of components running standalone KPluginInfo::List filteredPlugins = mPluginInfos; PluginList::ConstIterator it; PluginList::ConstIterator end(mPlugins.constEnd()); for (it = mPlugins.constBegin(); it != end; ++it) { if ((*it)->isRunningStandalone()) { KPluginInfo::List::ConstIterator infoIt; KPluginInfo::List::ConstIterator infoEnd(filteredPlugins.constEnd()); for (infoIt = filteredPlugins.constBegin(); infoIt != infoEnd; ++infoIt) { if (infoIt->pluginName() == (*it)->identifier()) { filteredPlugins.removeAll(*infoIt); break; } } } } dlg->addPluginInfos(filteredPlugins); connect(dlg, &Kontact::KontactConfigureDialog::pluginSelectionChanged, this, &MainWindow::pluginsChanged); } dlg->show(); } void MainWindow::pluginsChanged() { unplugActionList(QStringLiteral("navigator_actionlist")); unloadPlugins(); loadPlugins(); mSidePane->updatePlugins(); updateShortcuts(); } void MainWindow::updateConfig() { qCDebug(KONTACT_LOG); saveSettings(); loadSettings(); } void MainWindow::showAboutDialog() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); if (!mAboutDialog) { mAboutDialog = new AboutDialog(this); } mAboutDialog->show(); mAboutDialog->raise(); QApplication::restoreOverrideCursor(); } void MainWindow::configureShortcuts() { KShortcutsDialog dialog( KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); dialog.addCollection(actionCollection()); if (mCurrentPlugin && mCurrentPlugin->part()) { dialog.addCollection(mCurrentPlugin->part()->actionCollection()); } dialog.configure(); if (mCurrentPlugin && mCurrentPlugin->part()) { mCurrentPlugin->shortcutChanged(); } } void MainWindow::configureToolbars() { if (mCurrentPlugin) { KConfigGroup grp(KSharedConfig::openConfig()->group( QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier()))); saveMainWindowSettings(grp); } QPointer edit = new KEditToolBar(factory()); connect(edit.data(), &KEditToolBar::newToolBarConfig, this, &MainWindow::slotNewToolbarConfig); edit->exec(); delete edit; } void MainWindow::slotNewToolbarConfig() { if (mCurrentPlugin && mCurrentPlugin->part()) { createGUI(mCurrentPlugin->part()); } if (mCurrentPlugin) { applyMainWindowSettings( KSharedConfig::openConfig()->group( QStringLiteral("MainWindow%1").arg(mCurrentPlugin->identifier()))); } updateShortcuts(); // for the plugActionList call } void MainWindow::slotOpenUrl(const QUrl &url) { if (url.scheme() == QLatin1String("exec")) { const QString path(url.path()); if (path == QLatin1String("/switch")) { if (mCurrentPlugin) { mPartsStack->setCurrentIndex(mPartsStack->indexOf(mCurrentPlugin->part()->widget())); } } else if (path == QLatin1String("/accountwizard")) { KRun::runCommand(QStringLiteral("accountwizard"), this); slotQuit(); } else if (path.startsWith(QStringLiteral("/help"))) { QString app(QStringLiteral("org.kde.kontact")); if (!url.query().isEmpty()) { app = url.query(); } KHelpClient::invokeHelp(QString(), app); } } else { new KRun(url, this); } } void MainWindow::readProperties(const KConfigGroup &config) { Core::readProperties(config); QSet activePlugins = QSet::fromList(config.readEntry("ActivePlugins", QStringList())); if (!activePlugins.isEmpty()) { for (KontactInterface::Plugin *plugin : qAsConst(mPlugins)) { if (!plugin->isRunningStandalone() && activePlugins.contains(plugin->identifier())) { plugin->readProperties(config); } } } } void MainWindow::saveProperties(KConfigGroup &config) { Core::saveProperties(config); QStringList activePlugins; for (const KPluginInfo &pluginInfo : qAsConst(mPluginInfos)) { if (pluginInfo.isPluginEnabled()) { KontactInterface::Plugin *plugin = pluginFromInfo(pluginInfo); if (plugin) { activePlugins.append(plugin->identifier()); plugin->saveProperties(config); } } } config.writeEntry("ActivePlugins", activePlugins); } bool MainWindow::queryClose() { if (qApp->isSavingSession() || mReallyClose) { return true; } for (KontactInterface::Plugin *plugin : qAsConst(mPlugins)) { if (!plugin->isRunningStandalone()) { if (!plugin->queryClose()) { return false; } } } return true; } void MainWindow::slotShowStatusMsg(const QString &msg) { if (!statusBar() || !mStatusMsgLabel) { return; } mStatusMsgLabel->setText(msg); } QVariantHash MainWindow::introductionData() { QVariantHash data; data[QStringLiteral("icon")] = QStringLiteral("kontact"); data[QStringLiteral("name")] = i18n("Kontact"); data[QStringLiteral("subtitle")] = i18n("The KDE Personal Information Management Suite."); data[QStringLiteral("version")] = KAboutData::applicationData().version(); QVariantList links = { QVariantHash{ { QStringLiteral("url"), QStringLiteral("exec:/help?org.kde.kontact") }, { QStringLiteral("icon"), QStringLiteral("help-contents") }, { QStringLiteral("title"), i18n("Read Manual") }, { QStringLiteral("subtext"), i18n("Learn more about Kontact and its components") } }, QVariantHash{ { QStringLiteral("url"), QStringLiteral("http://kontact.org") }, { QStringLiteral("icon"), QStringLiteral("kontact") }, { QStringLiteral("title"), i18n("Visit Kontact Website") }, { QStringLiteral("subtext"), i18n("Access online resources and tutorials") } }, QVariantHash{ { QStringLiteral("url"), QStringLiteral("exec:/accountwizard") }, { QStringLiteral("icon"), QStringLiteral("tools-wizard") }, { QStringLiteral("title"), i18n("Setup your Accounts") }, { QStringLiteral("subtext"), i18n("Prepare Kontact for use") } } }; data[QStringLiteral("links")] = links; return data; } -void MainWindow::slotShowHideSideBar() +void MainWindow::showHideSideBar(bool show) { QList sizes = mSplitter->sizes(); if (!sizes.isEmpty()) { - if (sizes.at(0) != 0) { - sizes[0] = 0; + if (show) { + sizes[0] = mSaveSideBarWidth; } else { - sizes[0] = 10; + mSaveSideBarWidth = qMax(sizes[0], 10); + sizes[0] = 0; } mSplitter->setSizes(sizes); + Prefs::self()->setSideBarOpen(show); + } +} + +QString MainWindow::showHideSideBarMessage(bool hidden) const +{ + if (hidden) { + return i18nc("@info:status", + "Sidebar is hidden. Show the sidebar again using the %1 key.", + mShowHideAction->shortcut().toString()); + } else { + return QString(); + } +} + +void MainWindow::slotShowHideSideBar() +{ + QList sizes = mSplitter->sizes(); + if (!sizes.isEmpty()) { + bool open = (sizes.at(0) != 0); + showHideSideBar(!open); + if (open) { + statusBar()->showMessage(showHideSideBarMessage(true)); + } else { + statusBar()->showMessage(showHideSideBarMessage(false)); + } + } +} + +void MainWindow::slotSplitterMoved(int pos, int index) +{ + if (index == 1) { + if (pos == 0) { + statusBar()->showMessage(showHideSideBarMessage(true)); + } else { + statusBar()->showMessage(showHideSideBarMessage(false)); + } } } void MainWindow::setHelpText(QAction *action, const QString &text) { action->setStatusTip(text); action->setToolTip(text); if (action->whatsThis().isEmpty()) { action->setWhatsThis(text); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 3b949ef2..eccde812 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -1,151 +1,156 @@ /* This file is part of KDE Kontact. Copyright (c) 2001 Matthias Hoelzer-Kluepfel Copyright (c) 2002-2005 Daniel Molkentin Copyright (c) 2003-2005 Cornelius Schumacher 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 KONTACT_MAINWINDOW_H #define KONTACT_MAINWINDOW_H #include "kontact_export.h" #include #include #include class KActionMenu; class KPluginInfo; class KSqueezedTextLabel; class QFrame; class QSplitter; class QStackedWidget; class IntroductionWebEngineView; typedef QList PluginList; typedef QList ActionPluginList; namespace Kontact { class AboutDialog; class SidePaneBase; class KONTACT_EXPORT MainWindow : public KontactInterface::Core { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kontact.KontactInterface") public: MainWindow(); ~MainWindow(); PluginList pluginList() const override { return mPlugins; } void setInitialActivePluginModule(const QString &); static bool pluginActionWeightLessThan(const QAction *left, const QAction *right); static bool pluginWeightLessThan(const KontactInterface::Plugin *left, const KontactInterface::Plugin *right); + void showHideSideBar(bool show); public Q_SLOTS: void selectPlugin(KontactInterface::Plugin *plugin) override; Q_SCRIPTABLE void selectPlugin(const QString &pluginName) override; void slotActionTriggered(); void updateConfig(); protected Q_SLOTS: void initObject(); void initGUI(); void slotActivePartChanged(KParts::Part *part); void slotPreferences(); void slotNewClicked(); void slotQuit(); void slotNewToolbarConfig(); void slotShowIntroduction(); void showAboutDialog(); void slotShowStatusMsg(const QString &); void activateInitialPluginModule(); void slotOpenUrl(const QUrl &url); private: void initWidgets(); void initAboutScreen(); void loadSettings(); void saveSettings(); void waitForKSycoca(); bool isPluginLoaded(const KPluginInfo &); KontactInterface::Plugin *pluginFromInfo(const KPluginInfo &); void loadPlugins(); void unloadPlugins(); void updateShortcuts(); bool removePlugin(const KPluginInfo &); void addPlugin(KontactInterface::Plugin *plugin); void partLoaded(KontactInterface::Plugin *plugin, KParts::ReadOnlyPart *part) override; void setupActions(); bool queryClose() override; void readProperties(const KConfigGroup &config) override; void saveProperties(KConfigGroup &config) override; void paintAboutScreen(const QString &templateName, const QVariantHash &data); static QVariantHash introductionData(); KToolBar *findToolBar(const char *name); + QString showHideSideBarMessage(bool hidden) const; private Q_SLOTS: void pluginsChanged(); void configureShortcuts(); void configureToolbars() override; void slotShowHideSideBar(); + void slotSplitterMoved(int pos, int index); private: void setHelpText(QAction *action, const QString &text); QFrame *mTopWidget; QSplitter *mSplitter; KActionMenu *mNewActions; SidePaneBase *mSidePane; QStackedWidget *mPartsStack; KontactInterface::Plugin *mCurrentPlugin; KParts::PartManager *mPartManager; PluginList mPlugins; PluginList mDelayedPreload; ActionPluginList mActionPlugins; QList mPluginInfos; IntroductionWebEngineView *mIntroPart; KSqueezedTextLabel *mStatusMsgLabel; QString mInitialActiveModule; QMap > mFocusWidgets; QMap mPluginAction; AboutDialog *mAboutDialog; bool mReallyClose; + int mSaveSideBarWidth; + QAction *mShowHideAction; }; } Q_DECLARE_METATYPE(KontactInterface::Plugin *) #endif