File indexing completed on 2024-05-12 11:32:45

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2010 Alexander Rieder <alexanderrieder@gmail.com>
0004     SPDX-FileCopyrightText: 2018-2022 Alexander Semke <alexander.semke@web.de>
0005 */
0006 
0007 #include "variablemanagerwidget.h"
0008 #include "backend.h"
0009 #include "extension.h"
0010 #include "session.h"
0011 
0012 #include "ui_newvardlg.h"
0013 
0014 #include <QAction>
0015 #include <QClipboard>
0016 #include <QContextMenuEvent>
0017 #include <QDialog>
0018 #include <QFileDialog>
0019 #include <QMenu>
0020 #include <QPushButton>
0021 #include <QTimer>
0022 #include <QToolButton>
0023 #include <QTreeView>
0024 
0025 #include <KIconLoader>
0026 #include <KMessageBox>
0027 
0028 VariableManagerWidget::VariableManagerWidget(Cantor::Session* session, QWidget* parent) : QWidget(parent),
0029     m_treeView(new QTreeView(this))
0030 {
0031     auto* layout = new QVBoxLayout(this);
0032     layout->addWidget(m_treeView, 1);
0033 
0034     m_treeView->setRootIsDecorated(false);
0035     m_treeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
0036     m_treeView->setAlternatingRowColors(true);
0037 
0038     auto* btnLayout = new QHBoxLayout();
0039     btnLayout->setSpacing(0);
0040     btnLayout->setMargin(0);
0041 
0042     //Buttons to save/load the variables
0043     int size = KIconLoader::global()->currentSize(KIconLoader::MainToolbar);
0044 
0045     m_newBtn = new QToolButton(this);
0046     m_newBtn->setIcon(QIcon::fromTheme(QLatin1String("document-new")));
0047     m_newBtn->setToolTip(i18n("Add New Variable"));
0048     m_newBtn->setIconSize(QSize(size, size));
0049     btnLayout->addWidget(m_newBtn);
0050 
0051     m_loadBtn=new QToolButton(this);
0052     m_loadBtn->setIcon(QIcon::fromTheme(QLatin1String("document-open")));
0053     m_loadBtn->setToolTip(i18n("Load Variables"));
0054     m_loadBtn->setIconSize(QSize(size, size));
0055     btnLayout->addWidget(m_loadBtn);
0056 
0057     m_saveBtn = new QToolButton(this);
0058     m_saveBtn->setIcon(QIcon::fromTheme(QLatin1String("document-save")));
0059     m_saveBtn->setToolTip(i18n("Save Variables"));
0060     m_saveBtn->setIconSize(QSize(size, size));
0061     btnLayout->addWidget(m_saveBtn);
0062 
0063     m_clearBtn = new QToolButton(this);
0064     m_clearBtn->setIcon(QIcon::fromTheme(QLatin1String("edit-delete")));
0065     m_clearBtn->setToolTip(i18n("Remove Variables"));
0066     m_clearBtn->setIconSize(QSize(size, size));
0067     btnLayout->addWidget(m_clearBtn);
0068 
0069     auto* spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
0070     btnLayout->addItem(spacer);
0071 
0072     //Filter
0073     m_leFilter = new QLineEdit(this);
0074     m_leFilter->setClearButtonEnabled(true);
0075     m_leFilter->setPlaceholderText(i18n("Search/Filter"));
0076     btnLayout->addWidget(m_leFilter);
0077 
0078     m_bFilterOptions = new QToolButton(this);
0079     m_bFilterOptions->setIcon(QIcon::fromTheme(QLatin1String("configure")));
0080     m_bFilterOptions->setCheckable(true);
0081     btnLayout->addWidget(m_bFilterOptions);
0082 
0083     layout->addLayout(btnLayout);
0084 
0085     //actions
0086     m_caseSensitiveAction = new QAction(i18n("Case Sensitive"), this);
0087     m_caseSensitiveAction->setCheckable(true);
0088     m_caseSensitiveAction->setChecked(false);
0089 
0090     m_matchCompleteWordAction = new QAction(i18n("Match Complete Word"), this);
0091     m_matchCompleteWordAction->setCheckable(true);
0092     m_matchCompleteWordAction->setChecked(false);
0093 
0094     //signal-slot connections
0095     connect(m_leFilter, &QLineEdit::textChanged, this, &VariableManagerWidget::filterTextChanged);
0096     connect(m_bFilterOptions, &QPushButton::toggled, this, &VariableManagerWidget::toggleFilterOptionsMenu);
0097     connect(m_caseSensitiveAction, &QAction::triggered, this, [=]() {filterTextChanged(m_leFilter->text());} );
0098     connect(m_matchCompleteWordAction, &QAction::triggered, this, [=]() {filterTextChanged(m_leFilter->text());});
0099     connect(m_newBtn, &QToolButton::clicked, this, &VariableManagerWidget::newVariable);
0100     connect(m_loadBtn, &QToolButton::clicked, this, &VariableManagerWidget::load);
0101     connect(m_saveBtn, &QToolButton::clicked, this, &VariableManagerWidget::save);
0102     connect(m_clearBtn, &QToolButton::clicked, this, &VariableManagerWidget::clearVariables);
0103 
0104     setSession(session);
0105 }
0106 
0107 void VariableManagerWidget::setSession(Cantor::Session* session)
0108 {
0109     m_session = session;
0110     if (session)
0111     {
0112         m_model = session->variableDataModel();
0113         if (m_treeView)
0114             m_treeView->setModel(m_model);
0115 
0116         connect(m_model, &QAbstractItemModel::rowsInserted, this, &VariableManagerWidget::updateButtons);
0117         connect(m_model, &QAbstractItemModel::rowsRemoved, this, &VariableManagerWidget::updateButtons);
0118         updateButtons();
0119 
0120         //check for the methods the backend actually supports, and disable the buttons accordingly
0121         auto* ext = dynamic_cast<Cantor::VariableManagementExtension*>(
0122             m_session->backend()->extension(QLatin1String("VariableManagementExtension"))
0123         );
0124 
0125         if (ext)
0126         {
0127             if(ext->loadVariables(QString()).isNull())
0128                 m_loadBtn->setDisabled(true);
0129             if(ext->saveVariables(QString()).isNull())
0130                 m_saveBtn->setDisabled(true);
0131             if(ext->addVariable(QString(), QString()).isNull())
0132                 m_newBtn->setDisabled(true);
0133             if(ext->clearVariables().isNull())
0134                 m_clearBtn->setDisabled(true);
0135         }
0136     }
0137 }
0138 
0139 void VariableManagerWidget::clearVariables()
0140 {
0141     int btn = KMessageBox::questionYesNo(this,
0142                                          i18n("Are you sure you want to remove all variables?"),
0143                                          i18n("Remove Variables"));
0144     if (btn == KMessageBox::Yes)
0145     {
0146         m_model->removeRows(0, m_model->rowCount());
0147 
0148         //evaluate the "clear" command
0149         auto* ext = dynamic_cast<Cantor::VariableManagementExtension*>(m_session->backend()->extension(QLatin1String("VariableManagementExtension")));
0150         if (ext)
0151         {
0152             const QString& cmd = ext->clearVariables();
0153             emit runCommand(cmd);
0154         }
0155 
0156         //HACK? should the model detect that this happened on its own?
0157         //inform the model that all variables have been removed.
0158         //Do so by trying to evaluate the clearVariables slot of
0159         //DefaultVariableModel. If our model isn't one of those,
0160         //this call will just do nothing.
0161         QMetaObject::invokeMethod(m_model,  "clearVariables", Qt::QueuedConnection);
0162 
0163         //QAbstractItemModel::rowsRemoved() doesn't seem to be sent in this case,
0164         //call updateButtons explicitly
0165         QTimer::singleShot(0, this, [=] () { updateButtons(); });
0166     }
0167 }
0168 
0169 void VariableManagerWidget::save()
0170 {
0171     const QString& file = QFileDialog::getSaveFileName(this, i18n("Save"), QString(),  QString());
0172     if (file.trimmed().isEmpty())
0173         return;
0174 
0175     auto* ext = dynamic_cast<Cantor::VariableManagementExtension*>(m_session->backend()->extension(QLatin1String("VariableManagementExtension")));
0176     if (ext)
0177     {
0178         const QString& cmd = ext->saveVariables(file);
0179         emit runCommand(cmd);
0180     }
0181 }
0182 
0183 void VariableManagerWidget::load()
0184 {
0185     const QString& file = QFileDialog::getOpenFileName(this, i18n("Load file"), QString(),  QString());
0186     if (file.trimmed().isEmpty())
0187         return;
0188 
0189     auto* ext = dynamic_cast<Cantor::VariableManagementExtension*>(m_session->backend()->extension(QLatin1String("VariableManagementExtension")));
0190     if (ext)
0191     {
0192         const QString& cmd = ext->loadVariables(file);
0193         emit runCommand(cmd);
0194     }
0195 }
0196 
0197 void VariableManagerWidget::newVariable()
0198 {
0199     QPointer<QDialog> dlg = new QDialog(this);
0200     dlg->setWindowTitle(i18n("Add New Variable"));
0201     dlg->setWindowIcon(QIcon::fromTheme(QLatin1String("document-new")));
0202 
0203     QWidget* widget = new QWidget(dlg);
0204     Ui::NewVariableDialogBase base;
0205     base.setupUi(widget);
0206 
0207     auto* mainLayout = new QVBoxLayout;
0208     dlg->setLayout(mainLayout);
0209 
0210     base.buttonBox->button(QDialogButtonBox::Ok)->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogOkButton));
0211     base.buttonBox->button(QDialogButtonBox::Cancel)->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogCancelButton));
0212 
0213     connect(base.buttonBox, SIGNAL(accepted()), dlg, SLOT(accept()) );
0214     connect(base.buttonBox, SIGNAL(rejected()), dlg, SLOT(reject()) );
0215 
0216     mainLayout->addWidget(widget);
0217 
0218     if (dlg->exec())
0219     {
0220         const QString& name = base.name->text();
0221         const QString& val = base.value->text();
0222 
0223         auto* ext = dynamic_cast<Cantor::VariableManagementExtension*>(m_session->backend()->extension(QLatin1String("VariableManagementExtension")));
0224         if (ext)
0225         {
0226             const QString& cmd = ext->addVariable(name, val);
0227             emit runCommand(cmd);
0228         }
0229     }
0230 
0231     delete dlg;
0232 }
0233 
0234 /*!
0235   toggles the menu for the filter/search options
0236 */
0237 void VariableManagerWidget::toggleFilterOptionsMenu(bool checked) {
0238     if (checked) {
0239         QMenu menu;
0240         menu.addAction(m_caseSensitiveAction);
0241         menu.addAction(m_matchCompleteWordAction);
0242         connect(&menu, &QMenu::aboutToHide, m_bFilterOptions, &QPushButton::toggle);
0243         menu.exec(m_bFilterOptions->mapToGlobal(QPoint(0, m_bFilterOptions->height())));
0244     }
0245 }
0246 
0247 /*!
0248   called when the filter/search text was changed.
0249 */
0250 void VariableManagerWidget::filterTextChanged(const QString& text) {
0251     auto sensitivity = m_caseSensitiveAction->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive;
0252     bool matchCompleteWord = m_matchCompleteWordAction->isChecked();
0253     const auto* model = m_treeView->model();
0254 
0255     for (int i = 0; i < model->rowCount(); i++) {
0256         const auto& child = model->index(i, 0);
0257         const auto& name = model->data(child).toString();
0258         bool visible = true;
0259         if (text.isEmpty())
0260             visible = true;
0261         else if (matchCompleteWord)
0262             visible = name.startsWith(text, sensitivity);
0263         else
0264             visible = name.contains(text, sensitivity);
0265 
0266         m_treeView->setRowHidden(i, QModelIndex(), !visible);
0267     }
0268 }
0269 
0270 void VariableManagerWidget::updateButtons()
0271 {
0272     bool enabled = (m_treeView->model()->rowCount() != 0);
0273     m_saveBtn->setEnabled(enabled);
0274     m_clearBtn->setEnabled(enabled);
0275 }
0276 
0277 void VariableManagerWidget::contextMenuEvent(QContextMenuEvent* event) {
0278     const auto& index  = m_treeView->currentIndex();
0279     if (!index.isValid())
0280         return;
0281 
0282     // initialize the actions if not done yet
0283     if (!m_copyNameAction)
0284     {
0285         auto* group = new QActionGroup(this);
0286         m_copyNameAction = new QAction(QIcon::fromTheme(QLatin1String("edit-copy")), i18n("Copy Name"), group);
0287         m_copyValueAction = new QAction(QIcon::fromTheme(QLatin1String("edit-copy")), i18n("Copy Value"), group);
0288         m_copyNameValueAction = new QAction(QIcon::fromTheme(QLatin1String("edit-copy")), i18n("Copy Name and Value"), group);
0289         connect(group, &QActionGroup::triggered, this, &VariableManagerWidget::copy);
0290 
0291         //m_dataViewerAction = new QAction(QIcon::fromTheme(QLatin1String("document-preview")), i18n("Data Viewer"), this);
0292     }
0293 
0294     auto* menu = new QMenu(this);
0295     menu->addAction(m_copyNameAction);
0296     menu->addAction(m_copyValueAction);
0297     menu->addAction(m_copyNameValueAction);
0298     //menu->addSeparator();
0299     //menu->addAction(m_dataViewerAction);
0300 
0301     menu->exec(event->globalPos());
0302     delete menu;
0303 }
0304 
0305 void VariableManagerWidget::copy(const QAction* action) const
0306 {
0307     const auto& items = m_treeView->selectionModel()->selectedIndexes();
0308     QString text;
0309     if (action == m_copyNameAction)
0310         text = items.at(0).data().toString();
0311     else if (action == m_copyValueAction)
0312     {
0313         text = items.at(1).data().toString();
0314         text = text.replace(QStringLiteral("; "), QStringLiteral("\n"));
0315     }
0316     else if (action == m_copyNameValueAction)
0317     {
0318         text = items.at(0).data().toString();
0319         text += QLatin1Char('\n') + items.at(1).data().toString();
0320         text = text.replace(QStringLiteral("; "), QStringLiteral("\n"));
0321     }
0322 
0323     QApplication::clipboard()->setText(text);
0324 }