File indexing completed on 2024-05-12 07:41:28

0001 /*
0002     File                 : TemplateHandler.cpp
0003     Project              : LabPlot
0004     Description          : Widget for handling saving and loading of templates
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2012 Stefan Gerlach <stefan.gerlach@uni.kn>
0007     SPDX-FileCopyrightText: 2012-2020 Alexander Semke <alexander.semke@web.de>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "TemplateHandler.h"
0012 
0013 #include <QDir>
0014 #include <QFileInfo>
0015 #include <QHBoxLayout>
0016 #include <QLabel>
0017 #include <QLineEdit>
0018 #include <QMenu>
0019 #include <QSpacerItem>
0020 #include <QToolButton>
0021 #include <QWidgetAction>
0022 
0023 #include <QActionGroup>
0024 #include <QApplication>
0025 #include <QMouseEvent>
0026 #include <QTimer>
0027 
0028 #include <KConfig>
0029 #include <KConfigGroup>
0030 #include <KIconLoader>
0031 #include <KLocalizedString>
0032 
0033 static QVector<TemplateHandler*> templateHandlers;
0034 
0035 /*!
0036  \class TemplateHandler
0037  \brief Provides a widget with buttons for saving and loading of templates.
0038 
0039  Emits \c loadConfig() and \c saveConfig() signals that have to be connected
0040  to the appropriate slots in the ui (mostly in the dock widgets)
0041 
0042  \ingroup kdefrontend
0043 */
0044 
0045 TemplateHandler::TemplateHandler(QWidget* parent, const QString& className, bool alignRight)
0046     : QWidget(parent) {
0047     auto* horizontalLayout = new QHBoxLayout(this);
0048     horizontalLayout->setSpacing(0);
0049     horizontalLayout->setContentsMargins(0, 0, 0, 0);
0050 
0051     if (alignRight) {
0052         auto* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
0053         horizontalLayout->addItem(horizontalSpacer);
0054     }
0055 
0056     int size = KIconLoader::global()->currentSize(KIconLoader::MainToolbar);
0057 
0058     m_tbLoad = new QToolButton(this);
0059     m_tbLoad->setIconSize(QSize(size, size));
0060     horizontalLayout->addWidget(m_tbLoad);
0061 
0062     m_tbSave = new QToolButton(this);
0063     m_tbSave->setIconSize(QSize(size, size));
0064     horizontalLayout->addWidget(m_tbSave);
0065 
0066     m_tbSaveDefault = new QToolButton(this);
0067     m_tbSaveDefault->setIconSize(QSize(size, size));
0068     horizontalLayout->addWidget(m_tbSaveDefault);
0069 
0070     //  QSpacerItem* horizontalSpacer2 = new QSpacerItem(10, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);
0071     //  horizontalLayout->addItem(horizontalSpacer2);
0072 
0073     //  m_tbCopy = new QToolButton(this);
0074     //  m_tbCopy->setIconSize(QSize(size, size));
0075     //  m_tbCopy->setEnabled(false);
0076     //  horizontalLayout->addWidget(m_tbCopy);
0077     //
0078     //  m_tbPaste = new QToolButton(this);
0079     //  m_tbPaste->setIconSize(QSize(size, size));
0080     //  m_tbPaste->setEnabled(false);
0081     //  horizontalLayout->addWidget(m_tbPaste);
0082 
0083     m_tbLoad->setIcon(QIcon::fromTheme(QLatin1String("document-new-from-template")));
0084     m_tbSave->setIcon(QIcon::fromTheme(QLatin1String("document-save-as-template")));
0085     m_tbSaveDefault->setIcon(QIcon::fromTheme(QLatin1String("document-save-as")));
0086     //  m_tbCopy->setIcon(QIcon::fromTheme(QLatin1String("edit-copy")));
0087     //  m_tbPaste->setIcon(QIcon::fromTheme(QLatin1String("edit-paste")));
0088 
0089     if (!alignRight) {
0090         auto* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
0091         horizontalLayout->addItem(horizontalSpacer);
0092     }
0093 
0094     connect(m_tbLoad, &QToolButton::clicked, this, &TemplateHandler::loadMenu);
0095     connect(m_tbSave, &QToolButton::clicked, this, &TemplateHandler::saveMenu);
0096     connect(m_tbSaveDefault, &QToolButton::clicked, this, &TemplateHandler::saveDefaults);
0097 
0098     KConfig config;
0099     KConfigGroup group = config.group(QLatin1String("TemplateHandler"));
0100     auto style = (Qt::ToolButtonStyle)group.readEntry(QLatin1String("TextPosition"), (int)Qt::ToolButtonTextBesideIcon);
0101     m_tbLoad->setToolButtonStyle(style);
0102     m_tbSave->setToolButtonStyle(style);
0103     m_tbSaveDefault->setToolButtonStyle(style);
0104 
0105     m_tbLoad->installEventFilter(this);
0106     m_tbSave->installEventFilter(this);
0107     m_tbSaveDefault->installEventFilter(this);
0108 
0109     m_className = className.toLower();
0110 
0111     // folder where config files will be stored in object specific sub-folders:
0112     // Linux    - ~/.local/share/labplot2/templates/
0113     // Mac      - //TODO
0114     // Windows  - C:/Users/<USER>/AppData/Roaming/labplot2/templates/
0115     m_dirName = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/templates/");
0116 
0117     this->retranslateUi();
0118 
0119     // disable the load-button if no templates are available yet
0120     QStringList list = QDir(m_dirName + m_className).entryList();
0121     list.removeAll(QLatin1String("."));
0122     list.removeAll(QLatin1String(".."));
0123     m_tbLoad->setEnabled(list.size());
0124 
0125     // TODO: implement copy&paste of properties and activate copy- and paste-buttons again
0126     //  m_tbCopy->hide();
0127     //  m_tbPaste->hide();
0128 
0129     templateHandlers << this;
0130 }
0131 
0132 void TemplateHandler::setClassName(const QString& className) {
0133     m_className = className.toLower();
0134 }
0135 
0136 void TemplateHandler::setSaveDefaultAvailable(bool on) {
0137     m_tbSaveDefault->setVisible(on);
0138 }
0139 
0140 void TemplateHandler::setLoadAvailable(bool on) {
0141     m_tbLoad->setVisible(on);
0142 }
0143 
0144 /*!
0145  * returns the config object for the given template name \c name.
0146  */
0147 KConfig TemplateHandler::config(const QString& name) {
0148     QString configFile = m_dirName + m_className + QLatin1Char('/') + name;
0149     return KConfig(configFile, KConfig::SimpleConfig);
0150 }
0151 
0152 /*!
0153  * returns the name of the template for the config object \c config.
0154  */
0155 QString TemplateHandler::templateName(const KConfig& config) {
0156     // extract the name of the template from the file name
0157     QString name;
0158     int index = config.name().lastIndexOf(QLatin1Char('/'));
0159     if (index != -1)
0160         name = config.name().right(config.name().size() - index - 1);
0161     else
0162         name = config.name();
0163 
0164     return name;
0165 }
0166 
0167 QStringList TemplateHandler::templateNames() const {
0168     QStringList names;
0169     auto list = QDir(m_dirName + m_className).entryList();
0170     list.removeAll(QLatin1String("."));
0171     list.removeAll(QLatin1String(".."));
0172     for (int i = 0; i < list.size(); ++i) {
0173         QFileInfo fileinfo(list.at(i));
0174         names << fileinfo.baseName();
0175     }
0176 
0177     return names;
0178 }
0179 
0180 void TemplateHandler::retranslateUi() {
0181     m_tbLoad->setText(i18n("Load"));
0182     m_tbLoad->setToolTip(i18n("Load properties from a template"));
0183     m_tbSave->setText(i18n("Save"));
0184     m_tbSave->setToolTip(i18n("Save current properties as a template"));
0185     m_tbSaveDefault->setText(i18n("Save Default"));
0186     m_tbSaveDefault->setToolTip(i18n("Save current properties as default"));
0187     //  m_tbCopy->setToolTip(i18n("Copy properties"));
0188     //  m_tbPaste->setToolTip(i18n("Paste properties"));
0189 }
0190 
0191 bool TemplateHandler::eventFilter(QObject* obj, QEvent* event) {
0192     if (event->type() == QEvent::MouseButtonPress) {
0193         auto* mouseEvent = static_cast<QMouseEvent*>(event);
0194         if (mouseEvent->button() == Qt::RightButton) {
0195             if (!m_textPositionMenu) {
0196                 auto style = m_tbLoad->toolButtonStyle();
0197 
0198                 m_textPositionMenu = new QMenu(this);
0199                 m_textPositionMenu->addSection(i18n("Toolbar Settings"));
0200 
0201                 auto* actionGroup = new QActionGroup(this);
0202                 actionGroup->setExclusive(true);
0203                 connect(actionGroup, &QActionGroup::triggered, this, &TemplateHandler::updateTextPosition);
0204 
0205                 auto* subMenu = new QMenu(i18n("Text position"), m_textPositionMenu);
0206 
0207                 QAction* action = new QAction(i18n("Icons only"), actionGroup);
0208                 action->setCheckable(true);
0209                 action->setData((int)Qt::ToolButtonIconOnly);
0210                 if (style == Qt::ToolButtonIconOnly)
0211                     action->setChecked(true);
0212                 subMenu->addAction(action);
0213 
0214                 action = new QAction(i18n("Text only"), actionGroup);
0215                 action->setCheckable(true);
0216                 action->setData((int)Qt::ToolButtonTextOnly);
0217                 if (style == Qt::ToolButtonTextOnly)
0218                     action->setChecked(true);
0219                 subMenu->addAction(action);
0220 
0221                 action = new QAction(i18n("Text Alongside Icons"), actionGroup);
0222                 action->setCheckable(true);
0223                 action->setData((int)Qt::ToolButtonTextBesideIcon);
0224                 if (style == Qt::ToolButtonTextBesideIcon)
0225                     action->setChecked(true);
0226                 subMenu->addAction(action);
0227 
0228                 action = new QAction(i18n("Text Under Icons"), actionGroup);
0229                 action->setCheckable(true);
0230                 action->setData((int)Qt::ToolButtonTextUnderIcon);
0231                 if (style == Qt::ToolButtonTextUnderIcon)
0232                     action->setChecked(true);
0233                 subMenu->addAction(action);
0234                 m_textPositionMenu->addMenu(subMenu);
0235             }
0236 
0237             auto* widget = static_cast<QWidget*>(obj);
0238             m_textPositionMenu->exec(widget->mapToGlobal(mouseEvent->pos()));
0239         }
0240     }
0241     return QWidget::eventFilter(obj, event);
0242 }
0243 
0244 void TemplateHandler::updateTextPosition(QAction* action) {
0245     auto style = static_cast<Qt::ToolButtonStyle>(action->data().toInt());
0246 
0247     // save the current style
0248     KConfig config;
0249     KConfigGroup group = config.group(QLatin1String("TemplateHandler"));
0250     group.writeEntry(QLatin1String("TextPosition"), static_cast<int>(style));
0251 
0252     // update all available template handlers
0253     for (auto* handler : templateHandlers)
0254         handler->setToolButtonStyle(style);
0255 }
0256 
0257 void TemplateHandler::setToolButtonStyle(Qt::ToolButtonStyle style) {
0258     m_tbLoad->setToolButtonStyle(style);
0259     m_tbSave->setToolButtonStyle(style);
0260     m_tbSaveDefault->setToolButtonStyle(style);
0261 }
0262 
0263 // ##############################################################################
0264 // ##################################  Slots ####################################
0265 // ##############################################################################
0266 void TemplateHandler::loadMenu() {
0267     QMenu menu(this);
0268     menu.addSection(i18n("Load From Template"));
0269 
0270     auto list = QDir(m_dirName + m_className).entryList();
0271     list.removeAll(QLatin1String("."));
0272     list.removeAll(QLatin1String(".."));
0273     for (int i = 0; i < list.size(); ++i) {
0274         QFileInfo fileinfo(list.at(i));
0275         auto* action = menu.addAction(QIcon::fromTheme(QLatin1String("document-edit")), fileinfo.fileName());
0276         action->setData(fileinfo.fileName());
0277     }
0278     connect(&menu, &QMenu::triggered, this, &TemplateHandler::loadMenuSelected);
0279 
0280     QPoint pos(-menu.sizeHint().width() + m_tbLoad->width(), -menu.sizeHint().height());
0281     menu.exec(m_tbLoad->mapToGlobal(pos));
0282 }
0283 
0284 void TemplateHandler::loadMenuSelected(QAction* action) {
0285     QString configFile = m_dirName + m_className + QLatin1Char('/') + action->data().toString();
0286     KConfig config(configFile, KConfig::SimpleConfig);
0287     Q_EMIT loadConfigRequested(config);
0288     Q_EMIT info(i18n("Template \"%1\" was loaded.", action->text().remove(QLatin1Char('&'))));
0289 }
0290 
0291 void TemplateHandler::saveMenu() {
0292     QMenu menu(this);
0293     menu.addSection(i18n("Save As Template"));
0294 
0295     auto list = QDir(m_dirName + m_className).entryList();
0296     list.removeAll(QLatin1String("."));
0297     list.removeAll(QLatin1String(".."));
0298     for (int i = 0; i < list.size(); ++i) {
0299         QFileInfo fileinfo(list.at(i));
0300         auto* action = menu.addAction(QIcon::fromTheme(QLatin1String("document-edit")), fileinfo.fileName());
0301         action->setData(fileinfo.fileName());
0302     }
0303     connect(&menu, &QMenu::triggered, this, &TemplateHandler::saveMenuSelected);
0304 
0305     // add editable action
0306     auto* widgetAction = new QWidgetAction(this);
0307     auto* frame = new QFrame(this);
0308     auto* layout = new QHBoxLayout(frame);
0309 
0310     auto* label = new QLabel(i18n("New:"), frame);
0311     layout->addWidget(label);
0312 
0313     auto* leFilename = new QLineEdit(QString(), frame);
0314     layout->addWidget(leFilename);
0315     connect(leFilename, &QLineEdit::returnPressed, this, [=]() {
0316         saveNewSelected(leFilename->text());
0317     });
0318     connect(leFilename, &QLineEdit::returnPressed, &menu, &QMenu::close);
0319 
0320     widgetAction->setDefaultWidget(frame);
0321     if (menu.actions().size() > 1)
0322         menu.addSeparator();
0323     menu.addAction(widgetAction);
0324     leFilename->setFocus();
0325 
0326     QPoint pos(-menu.sizeHint().width() + m_tbSave->width(), -menu.sizeHint().height());
0327     menu.exec(m_tbSave->mapToGlobal(pos));
0328 }
0329 
0330 /*!
0331  * Is called when the current properties are going to be saved as a new template.
0332  * Emits \c saveConfigRequested, the receiver of the signal has to config.sync().
0333  */
0334 void TemplateHandler::saveNewSelected(const QString& filename) {
0335     QString path = m_dirName + m_className + QLatin1Char('/') + filename;
0336     KConfig config(path, KConfig::SimpleConfig);
0337     Q_EMIT saveConfigRequested(config);
0338     Q_EMIT info(i18n("New template \"%1\" was saved.", filename));
0339 
0340     // we have at least one saved template now -> enable the load button
0341     m_tbLoad->setEnabled(true);
0342 }
0343 
0344 /*!
0345  * Is called when the current properties are going to be saved in an already available template.
0346  * Emits \c saveConfigRequested, the receiver of the signal has to config.sync().
0347  */
0348 void TemplateHandler::saveMenuSelected(QAction* action) {
0349     QString path = m_dirName + m_className + QLatin1Char('/') + action->data().toString();
0350     KConfig config(path, KConfig::SimpleConfig);
0351     Q_EMIT saveConfigRequested(config);
0352     Q_EMIT info(i18n("Template \"%1\" was saved.", action->text()));
0353 }
0354 
0355 /*!
0356  * Is called when the current properties are going to be saved as new default properties.
0357  * Emits \c saveConfigRequested, the receiver of the signal has to config.sync().
0358  */
0359 void TemplateHandler::saveDefaults() {
0360     KConfig config;
0361     Q_EMIT saveConfigRequested(config);
0362     Q_EMIT info(i18n("New default template was saved."));
0363 }