1 /*****************************************************************************
2 * Copyright (C) 2004-2007 Jonas Bähr <jonas.baehr@web.de> *
3 * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] *
4 * *
5 * This file is part of Krusader [https://krusader.org]. *
6 * *
7 * Krusader is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * Krusader is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with Krusader. If not, see [http://www.gnu.org/licenses/]. *
19 *****************************************************************************/
20
21 #include "actionproperty.h"
22 #include "addplaceholderpopup.h"
23
24 #include "../UserAction/useraction.h"
25 #include "../UserAction/kraction.h"
26 #include "../krusader.h"
27 #include "../krglobal.h"
28 #include "../icon.h"
29
30 // QtWidgets
31 #include <QFileDialog>
32 #include <QInputDialog>
33
34 #include <KI18n/KLocalizedString>
35 #include <KWidgetsAddons/KMessageBox>
36 #include <KXmlGui/KActionCollection>
37
38
ActionProperty(QWidget * parent,KrAction * action)39 ActionProperty::ActionProperty(QWidget *parent, KrAction *action)
40 : QWidget(parent), _modified(false)
41 {
42
43 setupUi(this);
44
45 if (action) {
46 _action = action;
47 updateGUI(_action);
48 }
49
50 ButtonAddPlaceholder->setIcon(Icon("list-add"));
51 ButtonAddStartpath->setIcon(Icon("document-open"));
52
53 // fill with all existing categories
54 cbCategory->addItems(krUserAction->allCategories());
55
56 connect(ButtonAddPlaceholder, SIGNAL(clicked()), this, SLOT(addPlaceholder()));
57 connect(ButtonAddStartpath, SIGNAL(clicked()), this, SLOT(addStartpath()));
58 connect(ButtonNewProtocol, SIGNAL(clicked()), this, SLOT(newProtocol()));
59 connect(ButtonEditProtocol, SIGNAL(clicked()), this, SLOT(editProtocol()));
60 connect(ButtonRemoveProtocol, SIGNAL(clicked()), this, SLOT(removeProtocol()));
61 connect(ButtonAddPath, SIGNAL(clicked()), this, SLOT(addPath()));
62 connect(ButtonEditPath, SIGNAL(clicked()), this, SLOT(editPath()));
63 connect(ButtonRemovePath, SIGNAL(clicked()), this, SLOT(removePath()));
64 connect(ButtonAddMime, SIGNAL(clicked()), this, SLOT(addMime()));
65 connect(ButtonEditMime, SIGNAL(clicked()), this, SLOT(editMime()));
66 connect(ButtonRemoveMime, SIGNAL(clicked()), this, SLOT(removeMime()));
67 connect(ButtonNewFile, SIGNAL(clicked()), this, SLOT(newFile()));
68 connect(ButtonEditFile, SIGNAL(clicked()), this, SLOT(editFile()));
69 connect(ButtonRemoveFile, SIGNAL(clicked()), this, SLOT(removeFile()));
70 connect(KeyButtonShortcut, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(changedShortcut(QKeySequence)));
71 // track modifications:
72 connect(leDistinctName, SIGNAL(textChanged(QString)), SLOT(setModified()));
73 connect(leTitle, SIGNAL(textChanged(QString)), SLOT(setModified()));
74 connect(ButtonIcon, SIGNAL(iconChanged(QString)), SLOT(setModified()));
75 connect(cbCategory, SIGNAL(currentTextChanged(QString)), SLOT(setModified()));
76 connect(leTooltip, SIGNAL(textChanged(QString)), SLOT(setModified()));
77 connect(textDescription, SIGNAL(textChanged()), SLOT(setModified()));
78 connect(leCommandline, SIGNAL(textChanged(QString)), SLOT(setModified()));
79 connect(leStartpath, SIGNAL(textChanged(QString)), SLOT(setModified()));
80 connect(chkSeparateStdError, SIGNAL(clicked()), SLOT(setModified()));
81 connect(radioCollectOutput, SIGNAL(clicked()), SLOT(setModified()));
82 connect(radioNormal, SIGNAL(clicked()), SLOT(setModified()));
83 connect(radioTE, SIGNAL(clicked()), SLOT(setModified()));
84 connect(radioTerminal, SIGNAL(clicked()), SLOT(setModified()));
85 connect(radioLocal, SIGNAL(clicked()), SLOT(setModified()));
86 connect(radioUrl, SIGNAL(clicked()), SLOT(setModified()));
87 connect(KeyButtonShortcut, SIGNAL(keySequenceChanged(QKeySequence)), SLOT(setModified()));
88 connect(chkEnabled, SIGNAL(clicked()), SLOT(setModified()));
89 connect(leDifferentUser, SIGNAL(textChanged(QString)), SLOT(setModified()));
90 connect(chkDifferentUser, SIGNAL(clicked()), SLOT(setModified()));
91 connect(chkConfirmExecution, SIGNAL(clicked()), SLOT(setModified()));
92 connect(chkSeparateStdError, SIGNAL(clicked()), SLOT(setModified()));
93 // The modified-state of the ShowOnly-lists is tracked in the access-functions below
94 }
95
~ActionProperty()96 ActionProperty::~ActionProperty()
97 {
98 }
99
changedShortcut(const QKeySequence & shortcut)100 void ActionProperty::changedShortcut(const QKeySequence& shortcut)
101 {
102 KeyButtonShortcut->setKeySequence(shortcut);
103 }
104
105
clear()106 void ActionProperty::clear()
107 {
108 _action = 0;
109
110 // This prevents the changed-signal from being emitted during the GUI-update
111 _modified = true; // The real state is set at the end of this function.
112
113 leDistinctName->clear();
114 cbCategory->clearEditText();
115 leTitle->clear();
116 leTooltip->clear();
117 textDescription->clear();
118 leCommandline->clear();
119 leStartpath->clear();
120 KeyButtonShortcut->clearKeySequence();
121
122 lbShowonlyProtocol->clear();
123 lbShowonlyPath->clear();
124 lbShowonlyMime->clear();
125 lbShowonlyFile->clear();
126
127 chkSeparateStdError->setChecked(false);
128 radioNormal->setChecked(true);
129
130 radioLocal->setChecked(true);
131
132 chkEnabled->setChecked(true);
133
134 chkConfirmExecution->setChecked(false);
135
136 ButtonIcon->resetIcon();
137
138 leDifferentUser->clear();
139 chkDifferentUser->setChecked(false);
140
141 setModified(false);
142 }
143
updateGUI(KrAction * action)144 void ActionProperty::updateGUI(KrAction *action)
145 {
146 if (action)
147 _action = action;
148 if (! _action)
149 return;
150
151 // This prevents the changed-signal from being emitted during the GUI-update.
152 _modified = true; // The real state is set at the end of this function.
153
154 leDistinctName->setText(_action->objectName());
155 cbCategory->lineEdit()->setText(_action->category());
156 leTitle->setText(_action->text());
157 leTooltip->setText(_action->toolTip());
158 textDescription->setText(_action->whatsThis());
159 leCommandline->setText(_action->command());
160 leCommandline->home(false);
161 leStartpath->setText(_action->startpath());
162 KeyButtonShortcut->setKeySequence(_action->shortcut());
163
164 lbShowonlyProtocol->clear();
165 lbShowonlyProtocol->addItems(_action->showonlyProtocol());
166 lbShowonlyPath->clear();
167 lbShowonlyPath->addItems(_action->showonlyPath());
168 lbShowonlyMime->clear();
169 lbShowonlyMime->addItems(_action->showonlyMime());
170 lbShowonlyFile->clear();
171 lbShowonlyFile->addItems(_action->showonlyFile());
172
173 chkSeparateStdError->setChecked(false);
174 switch (_action->execType()) {
175 case KrAction::CollectOutputSeparateStderr:
176 chkSeparateStdError->setChecked(true);
177 radioCollectOutput->setChecked(true);
178 break;
179 case KrAction::CollectOutput:
180 radioCollectOutput->setChecked(true);
181 break;
182 case KrAction::Terminal:
183 radioTerminal->setChecked(true);
184 break;
185 case KrAction::RunInTE:
186 radioTE->setChecked(true);
187 break;
188 default: // case KrAction::Normal:
189 radioNormal->setChecked(true);
190 break;
191 }
192
193 if (_action->acceptURLs())
194 radioUrl->setChecked(true);
195 else
196 radioLocal->setChecked(true);
197
198 chkEnabled->setChecked(_action->isVisible());
199
200 chkConfirmExecution->setChecked(_action->confirmExecution());
201
202 if (! _action->icon().isNull())
203 ButtonIcon->setIcon(_action->icon());
204 else
205 ButtonIcon->resetIcon();
206
207 leDifferentUser->setText(_action->user());
208 if (_action->user().isEmpty())
209 chkDifferentUser->setChecked(false);
210 else
211 chkDifferentUser->setChecked(true);
212
213 setModified(false);
214 }
215
updateAction(KrAction * action)216 void ActionProperty::updateAction(KrAction *action)
217 {
218 if (action)
219 _action = action;
220 if (! _action)
221 return;
222
223 if (_action->category() != cbCategory->currentText()) {
224 _action->setCategory(cbCategory->currentText());
225 // Update the category-list
226 cbCategory->clear();
227 cbCategory->addItems(krUserAction->allCategories());
228 cbCategory->lineEdit()->setText(_action->category());
229 }
230
231 _action->setObjectName(leDistinctName->text());
232 _action->setText(leTitle->text());
233 _action->setToolTip(leTooltip->text());
234 _action->setWhatsThis(textDescription->toPlainText());
235 _action->setCommand(leCommandline->text());
236 _action->setStartpath(leStartpath->text());
237 _action->setShortcut(KeyButtonShortcut->keySequence());
238
239 QStringList list;
240
241 for (int i1 = 0; i1 != lbShowonlyProtocol->count(); i1++) {
242 QListWidgetItem* lbi = lbShowonlyProtocol->item(i1);
243
244 list << lbi->text();
245 }
246 _action->setShowonlyProtocol(list);
247
248 list = QStringList();
249 for (int i1 = 0; i1 != lbShowonlyPath->count(); i1++) {
250 QListWidgetItem* lbi = lbShowonlyPath->item(i1);
251
252 list << lbi->text();
253 }
254 _action->setShowonlyPath(list);
255
256 list = QStringList();
257 for (int i1 = 0; i1 != lbShowonlyMime->count(); i1++) {
258 QListWidgetItem* lbi = lbShowonlyMime->item(i1);
259
260 list << lbi->text();
261 }
262 _action->setShowonlyMime(list);
263
264 list = QStringList();
265 for (int i1 = 0; i1 != lbShowonlyFile->count(); i1++) {
266 QListWidgetItem* lbi = lbShowonlyFile->item(i1);
267
268 list << lbi->text();
269 }
270 _action->setShowonlyFile(list);
271
272 if (radioCollectOutput->isChecked() && chkSeparateStdError->isChecked())
273 _action->setExecType(KrAction::CollectOutputSeparateStderr);
274 else if (radioCollectOutput->isChecked() && ! chkSeparateStdError->isChecked())
275 _action->setExecType(KrAction::CollectOutput);
276 else if (radioTerminal->isChecked())
277 _action->setExecType(KrAction::Terminal);
278 else if (radioTE->isChecked())
279 _action->setExecType(KrAction::RunInTE);
280 else
281 _action->setExecType(KrAction::Normal);
282
283 if (radioUrl->isChecked())
284 _action->setAcceptURLs(true);
285 else
286 _action->setAcceptURLs(false);
287
288 _action->setEnabled(chkEnabled->isChecked());
289 _action->setVisible(chkEnabled->isChecked());
290
291 _action->setConfirmExecution(chkConfirmExecution->isChecked());
292
293 _action->setIcon(Icon(ButtonIcon->icon()));
294 _action->setIconName(ButtonIcon->icon());
295
296 _action->setUser(leDifferentUser->text());
297
298 setModified(false);
299 }
300
addPlaceholder()301 void ActionProperty::addPlaceholder()
302 {
303 AddPlaceholderPopup popup(this);
304 QString exp = popup.getPlaceholder(mapToGlobal(
305 QPoint(
306 ButtonAddPlaceholder->pos().x() + ButtonAddPlaceholder->width() + 6, // 6 is the default margin
307 ButtonAddPlaceholder->pos().y()
308 )
309 ));
310 leCommandline->insert(exp);
311 }
312
313
addStartpath()314 void ActionProperty::addStartpath()
315 {
316 QString folder = QFileDialog::getExistingDirectory(this);
317 if (!folder.isEmpty()) {
318 leStartpath->setText(folder);
319 }
320 }
321
322
newProtocol()323 void ActionProperty::newProtocol()
324 {
325 bool ok;
326
327 QString currentText;
328 if (lbShowonlyProtocol->currentItem())
329 currentText = lbShowonlyProtocol->currentItem()->text();
330
331 QString text = QInputDialog::getText(this, i18n("New protocol"), i18n("Set a protocol:"),
332 QLineEdit::Normal, currentText, &ok);
333 if (ok && !text.isEmpty()) {
334 lbShowonlyProtocol->addItems(text.split(';'));
335 setModified();
336 }
337 }
338
editProtocol()339 void ActionProperty::editProtocol()
340 {
341 if (lbShowonlyProtocol->currentItem() == 0)
342 return;
343
344 bool ok;
345
346 QString currentText = lbShowonlyProtocol->currentItem()->text();
347
348 QString text = QInputDialog::getText(this, i18n("Edit Protocol"), i18n("Set another protocol:"),
349 QLineEdit::Normal, currentText, &ok);
350 if (ok && !text.isEmpty()) {
351 lbShowonlyProtocol->currentItem()->setText(text);
352 setModified();
353 }
354 }
355
removeProtocol()356 void ActionProperty::removeProtocol()
357 {
358 if (lbShowonlyProtocol->currentItem() != 0) {
359 delete lbShowonlyProtocol->currentItem();
360 setModified();
361 }
362 }
363
addPath()364 void ActionProperty::addPath()
365 {
366 QString folder = QFileDialog::getExistingDirectory(this);
367 if (!folder.isEmpty()) {
368 lbShowonlyPath->addItem(folder);
369 setModified();
370 }
371 }
372
editPath()373 void ActionProperty::editPath()
374 {
375 if (lbShowonlyPath->currentItem() == 0)
376 return;
377
378 bool ok;
379
380 QString currentText = lbShowonlyPath->currentItem()->text();
381
382 QString text = QInputDialog::getText(this, i18n("Edit Path"), i18n("Set another path:"),
383 QLineEdit::Normal, currentText, &ok);
384 if (ok && !text.isEmpty()) {
385 lbShowonlyPath->currentItem()->setText(text);
386 setModified();
387 }
388 }
389
removePath()390 void ActionProperty::removePath()
391 {
392 if (lbShowonlyPath->currentItem() != 0) {
393 delete lbShowonlyPath->currentItem();
394 setModified();
395 }
396 }
397
addMime()398 void ActionProperty::addMime()
399 {
400 bool ok;
401
402 QString currentText;
403 if (lbShowonlyMime->currentItem())
404 currentText = lbShowonlyMime->currentItem()->text();
405
406 QString text = QInputDialog::getText(this, i18n("New MIME Type"), i18n("Set a MIME type:"),
407 QLineEdit::Normal, currentText, &ok);
408 if (ok && !text.isEmpty()) {
409 lbShowonlyMime->addItems(text.split(';'));
410 setModified();
411 }
412 }
413
editMime()414 void ActionProperty::editMime()
415 {
416 if (lbShowonlyMime->currentItem() == 0)
417 return;
418
419 bool ok;
420
421 QString currentText = lbShowonlyMime->currentItem()->text();
422
423 QString text = QInputDialog::getText(this, i18n("Edit MIME Type"), i18n("Set another MIME type:"),
424 QLineEdit::Normal, currentText, &ok);
425 if (ok && !text.isEmpty()) {
426 lbShowonlyMime->currentItem()->setText(text);
427 setModified();
428 }
429 }
430
removeMime()431 void ActionProperty::removeMime()
432 {
433 if (lbShowonlyMime->currentItem() != 0) {
434 delete lbShowonlyMime->currentItem();
435 setModified();
436 }
437 }
438
newFile()439 void ActionProperty::newFile()
440 {
441 bool ok;
442
443 QString currentText;
444 if (lbShowonlyFile->currentItem())
445 currentText = lbShowonlyFile->currentItem()->text();
446
447 QString text = QInputDialog::getText(this, i18n("New File Name"), i18n("Set a file name:"),
448 QLineEdit::Normal, currentText, &ok);
449 if (ok && !text.isEmpty()) {
450 lbShowonlyFile->addItems(text.split(';'));
451 setModified();
452 }
453 }
454
editFile()455 void ActionProperty::editFile()
456 {
457 if (lbShowonlyFile->currentItem() == 0)
458 return;
459
460 bool ok;
461
462 QString currentText = lbShowonlyFile->currentItem()->text();
463
464 QString text = QInputDialog::getText(this, i18n("Edit File Name"), i18n("Set another file name:"),
465 QLineEdit::Normal, currentText, &ok);
466 if (ok && !text.isEmpty()) {
467 lbShowonlyFile->currentItem()->setText(text);
468 setModified();
469 }
470 }
471
removeFile()472 void ActionProperty::removeFile()
473 {
474 if (lbShowonlyFile->currentItem() != 0) {
475 delete lbShowonlyFile->currentItem();
476 setModified();
477 }
478 }
479
480
validProperties()481 bool ActionProperty::validProperties()
482 {
483 if (leDistinctName->text().simplified().isEmpty()) {
484 KMessageBox::error(this, i18n("Please set a unique name for the useraction"));
485 leDistinctName->setFocus();
486 return false;
487 }
488 if (leTitle->text().simplified().isEmpty()) {
489 KMessageBox::error(this, i18n("Please set a title for the menu entry"));
490 leTitle->setFocus();
491 return false;
492 }
493 if (leCommandline->text().simplified().isEmpty()) {
494 KMessageBox::error(this, i18n("Command line is empty"));
495 leCommandline->setFocus();
496 return false;
497 }
498 if (leDistinctName->isEnabled())
499 if (krApp->actionCollection()->action(leDistinctName->text())) {
500 KMessageBox::error(this,
501 i18n("There already is an action with this name.\n"
502 "If you do not have such a useraction the name is used by Krusader for an internal action.")
503 );
504 leDistinctName->setFocus();
505 return false;
506 }
507
508 return true;
509 }
510
setModified(bool m)511 void ActionProperty::setModified(bool m)
512 {
513 if (m && !_modified) { // emit only when the state _changes_to_true_,
514 emit changed();
515 }
516 _modified = m;
517 }
518
519