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 }