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 }