1 /*
2 Copyright (c) 2020, Lukas Holecek <hluk@email.cz>
3
4 This file is part of CopyQ.
5
6 CopyQ is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 CopyQ is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with CopyQ. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "actiondialog.h"
21 #include "ui_actiondialog.h"
22
23 #include "common/appconfig.h"
24 #include "common/command.h"
25 #include "common/config.h"
26 #include "common/mimetypes.h"
27 #include "common/textdata.h"
28 #include "item/serialize.h"
29
30 #include <QAbstractButton>
31 #include <QFile>
32 #include <QMessageBox>
33 #include <QShortcut>
34
35 #include <memory>
36
37 namespace {
38
initFormatComboBox(QComboBox * combo,const QStringList & additionalFormats=QStringList ())39 void initFormatComboBox(QComboBox *combo, const QStringList &additionalFormats = QStringList())
40 {
41 QStringList formats = QStringList() << QString() << QString(mimeText) << additionalFormats;
42 formats.removeDuplicates();
43 combo->clear();
44 combo->addItems(formats);
45 }
46
wasChangedByUser(QObject * object)47 bool wasChangedByUser(QObject *object)
48 {
49 return object->property("UserChanged").toBool();
50 }
51
setChangedByUser(QWidget * object)52 void setChangedByUser(QWidget *object)
53 {
54 object->setProperty("UserChanged", object->hasFocus());
55 }
56
commandToLabel(const QString & command)57 QString commandToLabel(const QString &command)
58 {
59 QString label = command.size() > 48 ? command.left(48) + "..." : command;
60 label.replace('\n', " ");
61 label.replace(QRegularExpression("\\s\\+"), " ");
62 return label;
63 }
64
65 } // namespace
66
ActionDialog(QWidget * parent)67 ActionDialog::ActionDialog(QWidget *parent)
68 : QDialog(parent)
69 , ui(new Ui::ActionDialog)
70 , m_data()
71 , m_currentCommandIndex(-1)
72 {
73 ui->setupUi(this);
74
75 auto shortcut = new QShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_P), this);
76 connect(shortcut, &QShortcut::activated, this, &ActionDialog::previousCommand);
77 shortcut = new QShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_N), this);
78 connect(shortcut, &QShortcut::activated, this, &ActionDialog::nextCommand);
79
80 connect(ui->buttonBox, &QDialogButtonBox::clicked,
81 this, &ActionDialog::onButtonBoxClicked);
82 connect(ui->comboBoxCommands, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
83 this, &ActionDialog::onComboBoxCommandsCurrentIndexChanged);
84 connect(ui->comboBoxInputFormat, &QComboBox::currentTextChanged,
85 this, &ActionDialog::onComboBoxInputFormatCurrentTextChanged);
86 connect(ui->comboBoxOutputFormat, &QComboBox::editTextChanged,
87 this, &ActionDialog::onComboBoxOutputFormatEditTextchanged);
88 connect(ui->comboBoxOutputTab, &QComboBox::editTextChanged,
89 this, &ActionDialog::onComboBoxOutputTabEditTextChanged);
90 connect(ui->separatorEdit, &QLineEdit::textEdited,
91 this, &ActionDialog::onSeparatorEditTextEdited);
92
93 onComboBoxInputFormatCurrentTextChanged(QString());
94 onComboBoxOutputFormatEditTextchanged(QString());
95 loadSettings();
96 }
97
~ActionDialog()98 ActionDialog::~ActionDialog()
99 {
100 delete ui;
101 }
102
setInputData(const QVariantMap & data)103 void ActionDialog::setInputData(const QVariantMap &data)
104 {
105 m_data = data;
106
107 QString defaultFormat = ui->comboBoxInputFormat->currentText();
108 initFormatComboBox(ui->comboBoxInputFormat, data.keys());
109 const int index = qMax(0, ui->comboBoxInputFormat->findText(defaultFormat));
110 ui->comboBoxInputFormat->setCurrentIndex(index);
111 }
112
restoreHistory()113 void ActionDialog::restoreHistory()
114 {
115 const int maxCount = AppConfig().option<Config::command_history_size>();
116 ui->comboBoxCommands->setMaxCount(maxCount + 1);
117
118 QFile file( dataFilename() );
119 file.open(QIODevice::ReadOnly);
120 QDataStream in(&file);
121 QVariant v;
122
123 ui->comboBoxCommands->clear();
124 ui->comboBoxCommands->addItem(QString());
125 while( !in.atEnd() && ui->comboBoxCommands->count() <= maxCount ) {
126 in >> v;
127 const QVariantMap values = v.value<QVariantMap>();
128 const QString cmd = values.value("cmd").toString();
129 ui->comboBoxCommands->addItem( commandToLabel(cmd), v );
130 }
131 ui->comboBoxCommands->setCurrentIndex(0);
132 }
133
dataFilename() const134 const QString ActionDialog::dataFilename() const
135 {
136 return getConfigurationFilePath("_cmds.dat");
137 }
138
saveHistory()139 void ActionDialog::saveHistory()
140 {
141 QFile file( dataFilename() );
142 file.open(QIODevice::WriteOnly);
143 QDataStream out(&file);
144
145 for (int i = 1; i < ui->comboBoxCommands->count(); ++i) {
146 const QVariant itemData = ui->comboBoxCommands->itemData(i);
147 out << itemData;
148 }
149 }
150
setCommand(const Command & cmd)151 void ActionDialog::setCommand(const Command &cmd)
152 {
153 ui->comboBoxCommands->setCurrentIndex(0);
154 ui->commandEdit->setCommand(cmd.cmd);
155 ui->separatorEdit->setText(cmd.sep);
156
157 int index = ui->comboBoxInputFormat->findText(cmd.input);
158 if (index == -1) {
159 ui->comboBoxInputFormat->insertItem(0, cmd.input);
160 index = 0;
161 }
162 ui->comboBoxInputFormat->setCurrentIndex(index);
163
164 ui->comboBoxOutputFormat->setEditText(cmd.output);
165 }
166
setOutputTabs(const QStringList & tabs)167 void ActionDialog::setOutputTabs(const QStringList &tabs)
168 {
169 QComboBox *w = ui->comboBoxOutputTab;
170 w->clear();
171 w->addItem("");
172 w->addItems(tabs);
173 }
174
setCurrentTab(const QString & currentTabName)175 void ActionDialog::setCurrentTab(const QString ¤tTabName)
176 {
177 ui->comboBoxOutputTab->setEditText(currentTabName);
178 }
179
loadSettings()180 void ActionDialog::loadSettings()
181 {
182 initFormatComboBox(ui->comboBoxInputFormat);
183 initFormatComboBox(ui->comboBoxOutputFormat);
184 restoreHistory();
185 }
186
command() const187 Command ActionDialog::command() const
188 {
189 Command cmd;
190
191 cmd.cmd = ui->commandEdit->command();
192 cmd.name = commandToLabel(cmd.cmd);
193 cmd.input = ui->comboBoxInputFormat->currentText();
194 cmd.output = ui->comboBoxOutputFormat->currentText();
195 cmd.sep = ui->separatorEdit->text();
196 cmd.outputTab = ui->comboBoxOutputTab->currentText();
197
198 return cmd;
199 }
200
onButtonBoxClicked(QAbstractButton * button)201 void ActionDialog::onButtonBoxClicked(QAbstractButton* button)
202 {
203 switch ( ui->buttonBox->standardButton(button) ) {
204 case QDialogButtonBox::Ok:
205 acceptCommand();
206 saveCurrentCommandToHistory();
207 close();
208 break;
209
210 case QDialogButtonBox::Apply:
211 acceptCommand();
212 saveCurrentCommandToHistory();
213 break;
214
215 case QDialogButtonBox::Save:
216 emit saveCommand(command());
217 QMessageBox::information(
218 this, tr("Command saved"),
219 tr("Command was saved and can be accessed from item menu.\n"
220 "You can set up the command in preferences.") );
221 break;
222
223 case QDialogButtonBox::Cancel:
224 close();
225 break;
226
227 default:
228 break;
229 }
230 }
231
onComboBoxCommandsCurrentIndexChanged(int index)232 void ActionDialog::onComboBoxCommandsCurrentIndexChanged(int index)
233 {
234 if ( m_currentCommandIndex >= 0 && m_currentCommandIndex < ui->comboBoxCommands->count() ) {
235 QVariant itemData = createCurrentItemData();
236 if (itemData != ui->comboBoxCommands->itemData(m_currentCommandIndex))
237 ui->comboBoxCommands->setItemData(m_currentCommandIndex, itemData);
238 }
239
240 m_currentCommandIndex = index;
241
242 // Restore values from history.
243 QVariant v = ui->comboBoxCommands->itemData(index);
244 QVariantMap values = v.value<QVariantMap>();
245
246 ui->commandEdit->setCommand(values.value("cmd").toString());
247
248 // Don't automatically change values if they were edited by user.
249 if ( !wasChangedByUser(ui->comboBoxInputFormat) ) {
250 int i = ui->comboBoxInputFormat->findText(values.value("input").toString());
251 if (i != -1)
252 ui->comboBoxInputFormat->setCurrentIndex(i);
253 }
254
255 if ( !wasChangedByUser(ui->comboBoxOutputFormat) )
256 ui->comboBoxOutputFormat->setEditText(values.value("output").toString());
257
258 if ( !wasChangedByUser(ui->separatorEdit) )
259 ui->separatorEdit->setText(values.value("sep").toString());
260
261 const auto outputTab = values.value("outputTab").toString();
262 if ( !wasChangedByUser(ui->comboBoxOutputTab) && !outputTab.isEmpty() )
263 ui->comboBoxOutputTab->setEditText(values.value(outputTab).toString());
264 }
265
onComboBoxInputFormatCurrentTextChanged(const QString & format)266 void ActionDialog::onComboBoxInputFormatCurrentTextChanged(const QString &format)
267 {
268 setChangedByUser(ui->comboBoxInputFormat);
269
270 const bool show = format.startsWith("text", Qt::CaseInsensitive);
271 ui->inputText->setVisible(show);
272
273 QString text;
274 if ((show || format.isEmpty()) && !m_data.isEmpty() )
275 text = getTextData( m_data, format.isEmpty() ? mimeText : format );
276 ui->inputText->setPlainText(text);
277 }
278
onComboBoxOutputFormatEditTextchanged(const QString & text)279 void ActionDialog::onComboBoxOutputFormatEditTextchanged(const QString &text)
280 {
281 setChangedByUser(ui->comboBoxOutputFormat);
282
283 const bool showSeparator = text.startsWith("text", Qt::CaseInsensitive);
284 ui->separatorLabel->setVisible(showSeparator);
285 ui->separatorEdit->setVisible(showSeparator);
286
287 const bool showOutputTab = !text.isEmpty();
288 ui->labelOutputTab->setVisible(showOutputTab);
289 ui->comboBoxOutputTab->setVisible(showOutputTab);
290 }
291
onComboBoxOutputTabEditTextChanged(const QString &)292 void ActionDialog::onComboBoxOutputTabEditTextChanged(const QString &)
293 {
294 setChangedByUser(ui->comboBoxOutputTab);
295 }
296
onSeparatorEditTextEdited(const QString &)297 void ActionDialog::onSeparatorEditTextEdited(const QString &)
298 {
299 setChangedByUser(ui->separatorEdit);
300 }
301
nextCommand()302 void ActionDialog::nextCommand()
303 {
304 const int index = ui->comboBoxCommands->currentIndex();
305 ui->comboBoxCommands->setCurrentIndex(index - 1);
306 }
307
previousCommand()308 void ActionDialog::previousCommand()
309 {
310 const int index = ui->comboBoxCommands->currentIndex();
311 ui->comboBoxCommands->setCurrentIndex(index + 1);
312 }
313
acceptCommand()314 void ActionDialog::acceptCommand()
315 {
316 const auto command = this->command();
317
318 auto re = command.re;
319 const QString text = getTextData(m_data);
320 const auto m = re.match(text);
321 auto capturedTexts = m.capturedTexts();
322 if ( capturedTexts.isEmpty() )
323 capturedTexts.append(QString());
324 capturedTexts[0] = text;
325
326 if ( ui->inputText->isVisible() )
327 m_data[mimeText] = ui->inputText->toPlainText();
328
329 emit commandAccepted(command, capturedTexts, m_data);
330 }
331
createCurrentItemData()332 QVariant ActionDialog::createCurrentItemData()
333 {
334 QVariantMap values;
335 values["cmd"] = ui->commandEdit->command();
336 values["input"] = ui->comboBoxInputFormat->currentText();
337 values["output"] = ui->comboBoxOutputFormat->currentText();
338 values["sep"] = ui->separatorEdit->text();
339 values["outputTab"] = ui->comboBoxOutputTab->currentText();
340
341 return values;
342 }
343
saveCurrentCommandToHistory()344 void ActionDialog::saveCurrentCommandToHistory()
345 {
346 const QString cmd = ui->commandEdit->command();
347 const QVariant itemData = createCurrentItemData();
348 ui->comboBoxCommands->setCurrentIndex(0);
349
350 for (int i = ui->comboBoxCommands->count() - 1; i >= 1; --i) {
351 const QVariant itemData2 = ui->comboBoxCommands->itemData(i);
352 const QString cmd2 = itemData2.toMap().value("cmd").toString();
353 if (cmd2.isEmpty() || cmd == cmd2)
354 ui->comboBoxCommands->removeItem(i);
355 }
356
357 if ( cmd.isEmpty() ) {
358 ui->comboBoxCommands->setItemData(0, itemData);
359 } else {
360 ui->comboBoxCommands->insertItem(1, commandToLabel(cmd), itemData);
361 ui->comboBoxCommands->setCurrentIndex(1);
362 }
363
364 saveHistory();
365 }
366