Add help text for attribute S/R.
[qpwmc.git] / pwmdMainWindow.cpp
blobbb5bbcf1d01f3ffd1498d30195fc5f8f6777ac9c
1 /*
2 Copyright (C) 2010-2024 Ben Kibbey <bjk@luxsci.net>
4 This file is part of qpwmc.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library 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 GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA
21 #include <QStatusBar>
22 #include <QStatusTipEvent>
23 #include <QColorDialog>
24 #include <QClipboard>
25 #include <QTimer>
26 #include <QCursor>
27 #include <QDialogButtonBox>
28 #include <QPushButton>
29 #include <QMessageBox>
30 #include <QMenu>
31 #include <QContextMenuEvent>
32 #include <QSettings>
33 #include <QDateTime>
34 #include <QCommonStyle>
35 #include <QScroller>
36 #include <time.h>
38 #include "pwmdMainWindow.h"
39 #include "pwmdTreeWidget.h"
40 #include "pwmdGenPassDialog.h"
41 #include "applicationForm.h"
42 #include "pwmdSocketDialog.h"
43 #include "pwmdOptionsDialog.h"
44 #include "pwmdClientDialog.h"
45 #include "pwmdOpenDialog.h"
46 #include "pwmdSaveDialog.h"
47 #include "pwmdFileOptionsDialog.h"
48 #include "pwmdSaveDialog.h"
49 #include "main.h"
50 #include "cmdIds.h"
51 #include "pwmdExpireDialog.h"
52 #include "pwmdFileDialog.h"
53 #include "pwmdLineEdit.h"
54 #include "pwmdSearchDialog.h"
55 #ifdef Q_OS_ANDROID
56 #include "pwmdAndroidJNI.h"
57 #endif
59 /* Custom QEvents. */
60 #define PwmdEventTargetAttr QEvent::User+1
61 #define PwmdEventNewElementCancel QEvent::User+2
62 #define PwmdEventNewAttributeCancel QEvent::User+3
63 #define PwmdEventDnDTargetAttr QEvent::User+4
64 #define PwmdEventRefreshElementTree QEvent::User+5
65 #define PwmdEventReloadConfig QEvent::User+6
66 #define PwmdEventOpenFileClose QEvent::User+7
67 #define PwmdEventOpenFileConfirm QEvent::User+8
68 #define PwmdEventClientListClose QEvent::User+9
69 #define PwmdEventFileOptionsClose QEvent::User+10
70 #define PwmdEventOptionsClose QEvent::User+11
71 #define PwmdEventAttrsRetrieved QEvent::User+12
72 #define PwmdEventBusy QEvent::User+13
74 /* The current tab in the content splitter. */
75 #define TabContent 0
76 #define TabAttributes 1
77 #define TabCommand 2
79 /* Menu items and need to be in positional order. */
80 #define FileMenuOpen 0
81 #define FileMenuOpenForm 1
82 #define FileMenuClose 2
83 #define FileMenuSave 4
84 #define FileMenuSaveExt 5
85 #define ElementMenuNewRoot 0
86 #define ElementMenuNew 1
87 #define ElementMenuNewSibling 2
88 #define ElementMenuDelete 3
89 #define ElementMenuRename 4
90 #define ElementMenuFind 6
91 #define ElementMenuAdvancedFind 7
92 #define ElementMenuResolveTargets 8
93 #define ElementMenuEditContent 10
94 #define ElementMenuRefreshTree 11
95 #define ElementMenuMulti 12
96 #define ContentMenuInsert 0
97 #define ContentMenuSave 1
98 #define ContentMenuClipboard 2
99 #define ContentMenuPassword 4
100 #define ContentMenuExpiry 5
101 #define ContentMenuHidden 6
102 #define ContentMenuBase64 7
103 #define ContentMenuUpdate 9
104 #define ContentMenuRefresh 10
105 #define AttributeMenuNew 0
106 #define AttributeMenuDelete 1
107 #define AttributeMenuTarget 2
108 #define AttributeMenuUpdate 4
109 #define AttributeMenuRefresh 5
110 #define ViewMenuButtonLabels 0
111 #define ViewMenuExpandAll 2
112 #define ViewMenuClientList 4
114 /* A bitmask for enabled menu/toolbar items. Needed for a multi-selection to
115 * determine which items out of the selected items are able to perform which
116 * actions. */
117 #define ElementNewMask 0x01
118 #define ElementDeleteMask 0x02
119 #define ContentPasswordMask 0x04
120 #define ContentExpiryMask 0x08
121 #define AttributeNewMask 0x10
122 #define AttributeDeleteMask 0x20
124 static PwmdInquireData *openInquireData;
125 static bool reconnectSocket;
126 static bool disconnectSocket;
127 static bool updateOnEnter;
128 static QLabel *statusBarLabel;
129 static bool incrementalSearch;
130 static QStringList expandedItemsList;
131 static QTreeWidgetItem *nextElement;
132 static QWidget *nextTabWidget;
133 static int hiddenContent;
134 #ifdef Q_OS_ANDROID
135 static bool findingElement;
136 PwmdAndroidJNI *androidJni;
137 #endif
138 static QList <SearchResult *>searchResults;
139 static SearchResult *currentSearchResult;
140 static bool prevSearchResult;
141 static int releaseTimeout;
142 static int origReleaseTimeout;
144 static void freeSearchResults ();
146 enum { Content, Password, Attributes };
148 PwmdMainWindow::PwmdMainWindow (const QString & f, const QString & name,
149 bool lock, const QString & path,
150 const QString &sock, QWidget * p) :
151 QMainWindow (p)
153 qRegisterMetaType <Pwmd::ConnectionState> ("ConnectionState");
154 qRegisterMetaType <gpg_error_t>("gpg_error_t");
155 ui.setupUi (this);
157 searchResultStatusBarLabel = nullptr;
158 advancedSearchDialog = nullptr;
159 connectedUser = QString ();
160 dndOperation = DndNone;
161 newElement = nullptr;
162 nextElement = nullptr;
163 newAttribute = nullptr;
164 previousElement = nullptr;
165 oldSelectedItem = nullptr;
166 previousRightPage = nullptr;
167 selectedElement = attributeElement = nullptr;
168 targetAttributeElement = nullptr;
169 selectedElementPath = path;
170 selectedAttribute = nullptr;
171 selectedFilename = QString ();
172 nSelectedElements = 0;
173 previousAttributeText = QString ();
174 copyElement = nullptr;
175 newFilename = nullptr;
176 iterationsOrig = 0;
177 clientDialog = nullptr;
178 optionsDialog = nullptr;
179 fileOptionsDialog = nullptr;
180 openDialog = nullptr;
181 _filename = QString ();
182 cancelButton = nullptr;
183 isBusy = false;
184 features = pwmd_features ();
185 state = Pwmd::Init;
186 commandInquireFilename = QString ();
187 lockTimeout = 0;
188 hiddenContent = -1;
189 searchAndReplace = false;
191 setupToolbars ();
192 readConfigSettings (_filename, _socket);
193 if (incrementalSearch)
194 connect (elementToolBar->widgetForAction (findElementAction),
195 SIGNAL (textChanged (const QString &)), this,
196 SLOT (slotFindTextChanged (const QString &)));
198 statusBarLabel = new QLabel ();
199 statusBar()->addWidget (statusBarLabel);
201 if (!f.isEmpty ())
202 _filename = f;
204 if (!sock.isEmpty ())
205 _socket = sock;
207 connect (ui.tabWidget, SIGNAL (currentChanged (int)), this, SLOT (slotTabChanged (int)));
208 QScroller::grabGesture(ui.contentScrollArea, QScroller::TouchGesture);
209 QScroller::grabGesture(ui.attributeScrollArea, QScroller::TouchGesture);
210 QScroller::grabGesture(ui.commandScrollArea, QScroller::TouchGesture);
212 // File signals
213 connect (ui.actionOpen, SIGNAL (triggered ()), this, SLOT (slotOpenFile ()));
214 connect (ui.actionOpenForm, SIGNAL (triggered ()), this, SLOT (slotOpenForm ()));
215 connect (ui.actionClose, SIGNAL (triggered ()), this, SLOT (slotCloseFile()));
216 connect (ui.actionSave, SIGNAL (triggered ()), this, SLOT (slotSaveDocument()));
217 connect (ui.actionExtendedSave, SIGNAL (triggered ()), this, SLOT (slotSaveExtended ()));
218 connect (ui.actionFileOptions, SIGNAL (triggered ()), this, SLOT (slotFileOptions()));
219 connect (ui.actionGeneralOptions, SIGNAL (triggered()), this, SLOT (slotGeneralOptions ()));
220 connect (ui.actionReload, SIGNAL (triggered ()), this, SLOT (slotReload()));
221 connect (ui.actionQuit, SIGNAL (triggered()), this, SLOT (close()));
223 // View signals
224 connect (ui.actionButtonLabels, SIGNAL (triggered(bool)), this, SLOT (slotButtonLabels(bool)));
225 connect (ui.actionClientList, SIGNAL (triggered()), this, SLOT (slotClientList()));
226 connect (ui.actionExpandAll, SIGNAL (triggered(bool)), this, SLOT (slotExpandAll(bool)));
228 // Socket signals
229 connect (ui.actionSocketOptions, SIGNAL (triggered()), this, SLOT (slotSocketOptions ()));
231 // Element signals
232 connect (ui.actionNewRootElement, SIGNAL (triggered()), this, SLOT (slotNewRootElement()));
233 connect (ui.actionNewElement, SIGNAL (triggered()), this, SLOT (slotNewElement()));
234 connect (ui.actionNewSiblingElement, SIGNAL (triggered()), this, SLOT (slotNewSiblingElement ()));
235 connect (ui.actionDeleteElement, SIGNAL (triggered()), this, SLOT (slotDeleteElement()));
236 connect (ui.actionRenameElement, SIGNAL (triggered()), this, SLOT (slotRenameElement()));
237 connect (ui.actionFindElement, SIGNAL (triggered()), this, SLOT (slotFindElement()));
238 connect (ui.actionEditContentOrAttributes, SIGNAL (triggered()), this, SLOT (slotEditContent ()));
239 fixupEditContentOrAttributes (true);
240 connect (ui.actionResolveTargets, SIGNAL (triggered ()), this, SLOT (slotResolveTargets ()));
241 connect (ui.actionRefreshTree, SIGNAL (triggered ()), this, SLOT (slotRefreshElementTree ()));
242 #ifdef Q_OS_ANDROID
243 connect (ui.actionToggleMulti, SIGNAL (toggled(bool)), this, SLOT (slotToggleMultiMode(bool)));
244 #endif
246 // Content signals
247 ui.textEdit->setPwmdParent (this);
248 connect (ui.actionInsertFile, SIGNAL (triggered()), this, SLOT (slotInsertContent()));
249 connect (ui.actionSaveToFile, SIGNAL (triggered()), this, SLOT (slotSaveToFile()));
250 connect (ui.actionCopyToClipboard, SIGNAL (triggered ()), this, SLOT (slotCopyContentToClipboard ()));
251 connect (ui.actionCreatePasswordAttribute, SIGNAL (triggered()), this, SLOT (slotCreatePasswordAttribute()));
252 connect (ui.actionChangeExpire, SIGNAL (triggered()), this, SLOT (slotChangeExpire()));
253 connect (ui.actionHidden, SIGNAL (toggled(bool)), this, SLOT (slotToggleHiddenContent(bool)));
254 connect (ui.actionBase64, SIGNAL (triggered(bool)), this, SLOT (slotToggleBase64Content(bool)));
255 connect (ui.actionUpdateContent, SIGNAL (triggered()), this, SLOT (slotSaveContent ()));
256 connect (ui.actionRefreshContent, SIGNAL (triggered()), this, SLOT (slotRefreshContent ()));
257 connect (ui.passwordWidget, SIGNAL (modified ()), this, SLOT (slotPasswordContentChanged ()));
259 // Attribute signals
260 ui.attributeList->setPwmdParent (this);
261 ui.attributeContent->setPwmdParent (this);
262 ui.attributeList->setItemDelegate (new PwmdListWidgetItemDelegate (ui.attributeList));
263 connect (ui.attributeList, SIGNAL (currentItemChanged (QListWidgetItem *, QListWidgetItem *)), this, SLOT (slotAttributeSelected (QListWidgetItem *, QListWidgetItem *)));
264 connect (ui.attributeList->itemDelegate (), SIGNAL (commitData (QWidget *)), this, SLOT (slotConfirmNewAttribute (QWidget *)));
265 connect (ui.attributeList->itemDelegate (), SIGNAL (closeEditor (QWidget *, QAbstractItemDelegate::EndEditHint)), this, SLOT (slotAttributeEditorClosed (QWidget *, QAbstractItemDelegate::EndEditHint)));
266 connect (ui.actionNewAttribute, SIGNAL (triggered ()), this, SLOT (slotNewAttribute()));
267 connect (ui.actionDeleteAttribute, SIGNAL (triggered ()), this, SLOT (slotDeleteAttribute ()));
268 connect (ui.actionToggleTargetAttribute, SIGNAL (triggered (bool)), this, SLOT (slotEditTargetAttributes(bool)));
269 connect (ui.actionUpdateAttribute, SIGNAL (triggered ()), this, SLOT (slotSaveContent()));
270 connect (ui.actionRefreshAttributes, SIGNAL (triggered ()), this, SLOT (slotRefreshAttributes ()));
271 QRegularExpression rx ("^/[^/]+/(.*)$");
272 ui.cb_searchAndReplace->setValidator (new QRegularExpressionValidator (rx,
273 ui.cb_searchAndReplace));
275 ui.cb_searchAndReplace->lineEdit ()->setClearButtonEnabled (true);
276 ui.cb_searchAndReplace->lineEdit ()->setPlaceholderText (tr ("/pattern/replace"));
277 connect (ui.cb_searchAndReplace->lineEdit (), SIGNAL (textChanged (const QString &)), this,
278 SLOT (slotAttrSearchAndReplaceChanged (const QString &)));
279 connect (ui.cb_searchAndReplace->lineEdit (), SIGNAL (returnPressed ()), this,
280 SLOT (slotAttrSearchAndReplace ()));
281 ui.pb_searchAndReplace->setIcon (QIcon (":icons/system-search-replace.svg"));
282 connect (ui.pb_searchAndReplace, SIGNAL (clicked ()), this,
283 SLOT (slotAttrSearchAndReplace ()));
285 // Command signals
286 connect (ui.commandTextEdit, SIGNAL (textChanged ()),
287 SLOT (slotCommandContentChanged ()));
288 connect (ui.commandResultTextEdit, SIGNAL (anchorClicked (const QUrl &)),
289 this, SLOT (slotCommandAnchorClicked (const QUrl &)));
290 connect (ui.commandResultTextEdit, SIGNAL (textChanged ()),
291 SLOT (slotCommandResultContentChanged ()));
292 connect (ui.pb_commandSearch, SIGNAL (clicked()), this, SLOT (slotSearchCommandResults ()));
293 connect (ui.ck_commandInquire, SIGNAL (stateChanged (int)), this,
294 SLOT (slotCommandInquireStateChanged (int)));
295 connect (ui.cb_commandHistory, SIGNAL (activated (int)), this,
296 SLOT (slotCommandHistoryChanged (int)));
297 connect (ui.cb_commandHistory, SIGNAL (currentIndexChanged (int)), this,
298 SLOT (slotCommandHistoryChanged (int)));
299 connect (ui.tb_commandInquireFilename, SIGNAL (clicked ()), this,
300 SLOT (slotCommandInquireFilename ()));
301 ui.commandTextEdit->setPwmdParent (this);
302 slotClearCommand();
304 // About signals
305 connect (ui.actionVersion, SIGNAL (triggered()), this, SLOT (slotVersion ()));
306 connect (ui.actionAboutQt, SIGNAL (triggered()), qApp, SLOT (aboutQt ()));
308 progressBar = new QProgressBar (ui.statusbar);
309 ui.statusbar->addPermanentWidget (progressBar);
310 progressBar->setHidden (true);
311 progressBar->setAlignment (Qt::AlignHCenter);
313 ui.elementTree->setPwmdParent (this);
314 ui.elementTree->header ()->setSectionResizeMode (0, QHeaderView::ResizeToContents);
315 ui.elementTree->header ()->setStretchLastSection (false);
316 ui.elementTree->setItemDelegate (new PwmdTreeWidgetItemDelegate (ui.elementTree));
318 QAction *act = new QAction (ui.elementTree);
319 connect (act, SIGNAL (triggered ()), this, SLOT (slotDeleteElement ()));
320 ui.elementTree->addAction (act);
321 act = new QAction (ui.elementTree);
322 connect (act, SIGNAL (triggered ()), this, SLOT (slotNewElement ()));
323 ui.elementTree->addAction (act);
324 act = new QAction (ui.elementTree);
325 connect (act, SIGNAL (triggered ()), this, SLOT (slotRenameElement ()));
326 ui.elementTree->addAction (act);
327 act = new QAction (ui.elementTree);
328 connect (act, SIGNAL (triggered ()), this, SLOT (slotEditAttributes ()));
329 ui.elementTree->addAction (act);
330 act = new QAction (ui.elementTree);
331 connect (act, SIGNAL (triggered ()), this, SLOT (slotResolveTargets ()));
332 ui.elementTree->addAction (act);
333 act = new QAction (ui.elementTree);
334 connect (act, SIGNAL (triggered ()), this, SLOT (slotExecuteCommand ()));
335 ui.elementTree->addAction (act);
336 act = new QAction (ui.elementTree);
337 connect (act, SIGNAL (triggered ()), this, SLOT (slotExpandElement ()));
338 ui.elementTree->addAction (act);
339 act = new QAction (ui.elementTree);
340 connect (act, SIGNAL (triggered ()), this, SLOT (slotCollapseElement ()));
341 ui.elementTree->addAction (act);
342 act = new QAction (ui.elementTree);
343 connect (act, SIGNAL (triggered ()), this,
344 SLOT (slotClipboardElementPath ()));
345 ui.elementTree->addAction (act);
347 connect (ui.elementTree->itemDelegate (), SIGNAL (commitData (QWidget *)),
348 this, SLOT (slotRenameElementUpdate (QWidget *)));
349 connect (ui.elementTree->itemDelegate (),
350 SIGNAL (closeEditor
351 (QWidget *, QAbstractItemDelegate::EndEditHint)), this,
352 SLOT (slotElementEditorClosed
353 (QWidget *, QAbstractItemDelegate::EndEditHint)));
354 connect (ui.elementTree,
355 SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)),
356 this,
357 SLOT (slotElementSelected (QTreeWidgetItem *, QTreeWidgetItem *)));
358 connect (ui.elementTree,
359 SIGNAL (itemEntered (QTreeWidgetItem *, int)),
360 this,
361 SLOT (slotElementEntered(QTreeWidgetItem *, int)));
362 connect (ui.elementTree,
363 SIGNAL (itemSelectionChanged ()),
364 this,
365 SLOT (slotItemSelectionChanged ()));
367 connect (ui.textEdit, SIGNAL (textChanged ()),
368 SLOT (slotContentChanged ()));
369 connect (ui.textEdit, SIGNAL (modificationChanged (bool)),
370 SLOT (slotContentModified (bool)));
372 setContentItemsEnabled (true);
373 clipboardTimer = new QTimer (this);
374 clipboardTimer->setSingleShot (true);
375 connect (clipboardTimer, SIGNAL (timeout ()), SLOT (slotClearClipboard ()));
376 connect (ui.passwordWidget, SIGNAL (clipboardTimer ()), this, SLOT (slotClipboardTimer ()));
378 releaseTimer = new QTimer(this);
379 connect(releaseTimer, SIGNAL(timeout()), this, SLOT(slotReleaseTimer()));
380 newFile = false;
381 editTargetAttributes = false;
382 ui.hiddenContentFrame->setHidden (true);
384 pwm = new Pwmd (filename (), name, 0, this);
385 ui.elementTree->setPwmdHandle (pwm);
386 pwm->setBadPassphraseCallback (PwmdMainWindow::badPassphraseCallback);
387 pwm->setBadPassphraseData (this);
388 connect (pwm, SIGNAL (busy (int, bool)), this, SLOT (slotBusy (int, bool)));
389 if (PwmdRemoteHost::fillRemoteHost (_socket, currentHostData))
391 pwm->setSocket (PwmdRemoteHost::socketUrl (currentHostData));
392 pwm->setConnectParameters (currentHostData.socketArgs ());
394 else
395 pwm->setSocket (_socket);
397 pwm->setLockOnOpen (lock);
398 connect (pwm, SIGNAL (stateChanged (Pwmd::ConnectionState)), this,
399 SLOT (slotConnectionStateChanged (Pwmd::ConnectionState)));
400 #ifdef Q_OS_ANDROID
401 ui.menubar->setNativeMenuBar (false);
402 connect (qApp, SIGNAL (applicationStateChanged (Qt::ApplicationState)),
403 this, SLOT (slotApplicationStateChanged (Qt::ApplicationState)));
404 androidAuthenticating = false;
405 androidJni = new PwmdAndroidJNI ();
406 androidJni->init ();
407 qRegisterMetaType <PwmdRemoteHost>("PwmdRemoteHost");
408 connect (androidJni, SIGNAL (jniAuthenticated ()), this, SLOT (slotAndroidAuthenticated ()));
409 connect (androidJni, SIGNAL (jniConnectionResult (PwmdRemoteHost, int)), this, SLOT (slotAndroidConnectResult (PwmdRemoteHost, int)));
410 connect (androidJni, SIGNAL (jniConnectionError (gpg_error_t)), this,
411 SLOT (slotAndroidConnectionError (gpg_error_t)));
412 connect (pwm, SIGNAL (connectReady (PwmdRemoteHost)), this, SLOT (slotAndroidConnectReady (PwmdRemoteHost)));
413 #endif
414 connect (pwm, SIGNAL (statusMessage (QString, void *)), this,
415 SLOT (slotStatusMessage (QString, void *)));
416 connect (pwm, SIGNAL (commandResult (PwmdCommandQueueItem *, QString, gpg_error_t, bool)), this, SLOT (slotCommandResult (PwmdCommandQueueItem *, QString, gpg_error_t, bool)));
417 connect (pwm, SIGNAL (knownHost (void *, const char *, const char *, size_t)), this, SLOT (slotKnownHostCallback (void *, const char *, const char *, size_t)));
419 setConnected (false);
421 if (!filename().isEmpty ())
422 QTimer::singleShot (1, this, SLOT (slotConnectSocket()));
425 PwmdMainWindow::~PwmdMainWindow ()
427 disconnect (ui.elementTree,
428 SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)),
429 this,
430 SLOT (slotElementSelected (QTreeWidgetItem *, QTreeWidgetItem *)));
431 disconnect (ui.elementTree, SIGNAL (itemEntered (QTreeWidgetItem *, int)),
432 this, SLOT (slotElementEntered(QTreeWidgetItem *, int)));
433 disconnect (ui.elementTree->itemDelegate (), SIGNAL (commitData (QWidget *)),
434 this, SLOT (slotRenameElementUpdate (QWidget *)));
435 disconnect (ui.elementTree->itemDelegate (),
436 SIGNAL (closeEditor
437 (QWidget *, QAbstractItemDelegate::EndEditHint)), this,
438 SLOT (slotElementEditorClosed
439 (QWidget *, QAbstractItemDelegate::EndEditHint)));
440 disconnect (ui.elementTree,
441 SIGNAL (itemSelectionChanged ()),
442 this,
443 SLOT (slotItemSelectionChanged ()));
444 clearElementTree (ui.elementTree);
445 delete advancedSearchDialog;
446 delete pwm;
447 delete clientDialog;
448 delete openDialog;
449 delete fileOptionsDialog;
450 delete optionsDialog;
451 delete statusBarLabel;
452 #ifdef Q_OS_ANDROID
453 delete androidJni;
454 #endif
455 QSettings cfg ("qpwmc");
457 if (state == Pwmd::Opened)
458 cfg.setValue ("lastOpenedFile", filename ());
460 cfg.setValue ("lastSocket", _socket);
461 cfg.setValue ("geometry", saveGeometry ());
462 cfg.setValue ("windowState", saveState ());
463 cfg.setValue ("tabGeometry", ui.tabSplitter->saveState ());
464 cfg.setValue ("attributeGeometry", ui.attributeSplitter->saveState ());
465 cfg.setValue ("commandGeometry", ui.commandSplitter->saveState ());
467 Qt::ToolButtonStyle s = fileToolBar->toolButtonStyle ();
468 bool b = s != Qt::ToolButtonIconOnly;
469 cfg.setValue ("buttonLabels", b);
470 freeSearchResults ();
473 void
474 PwmdMainWindow::ignoreErrors (PwmdCommandQueueItem *cmd, bool dnd)
476 if (!hasMultiSelection ())
477 return;
479 cmd->addError (536903681); // Permission denied
480 cmd->addError (536871048); // Element not found
482 if (dnd)
484 cmd->addError (536903743); // ELOOP
485 return;
488 cmd->addError (536870939); // Not found
491 void
492 PwmdMainWindow::slotFindFinished ()
494 #ifdef Q_OS_ANDROID
495 findingElement = false;
496 #endif
497 if (searchResultStatusBarLabel)
498 searchResultStatusBarLabel->hide ();
500 fixupToolBar (elementToolBar, ElementToolBarFind, true);
501 findElementAction->setVisible (false);
502 ui.tabWidget->setEnabled (true);
503 disconnect (ui.elementTree,
504 SIGNAL (itemClicked (QTreeWidgetItem *, int)),
505 this,
506 SLOT (slotElementClicked (QTreeWidgetItem *, int)));
507 connect (ui.elementTree,
508 SIGNAL (itemSelectionChanged ()),
509 this,
510 SLOT (slotItemSelectionChanged ()));
512 if (doSaveContent (true))
514 nextElement = ui.elementTree->currentItem ();
515 pwm->runQueue ();
516 return;
519 ui.elementTree->setFocus ();
520 elementSelected (ui.elementTree->currentItem ());
523 static void
524 freeSearchResults ()
526 while (!searchResults.isEmpty ())
528 SearchResult *s = searchResults.takeFirst ();
529 delete s;
532 currentSearchResult = nullptr;
535 static int
536 searchResultTotal (bool index)
538 int n = 0;
540 foreach (SearchResult *r, searchResults)
542 if (r->resultType () & SearchResult::Element)
543 n++;
545 if (index && r == currentSearchResult)
547 if (currentSearchResult->curAttr () != -1)
548 n += currentSearchResult->attrs ().indexOf (currentSearchResult->curAttr ())+1;
549 break;
552 n += r->attrs ().count ();
555 return n;
558 QTreeWidgetItem *
559 PwmdMainWindow::findElementText (const QString &str, bool elements,
560 bool attrs, bool attrValues)
562 bool cs = false;
563 QTreeWidgetItem *currentItem = firstSelectedItem ();
564 SearchResult *result = nullptr;
566 if (!searchResultStatusBarLabel)
568 searchResultStatusBarLabel = new QLabel;
569 searchResultStatusBarLabel->setAlignment (Qt::AlignRight|Qt::AlignVCenter);
570 searchResultStatusBarLabel->setIndent (15);
571 statusBar()->addPermanentWidget (searchResultStatusBarLabel);
574 foreach (QChar c, str)
576 if (c.isUpper ())
578 cs = true;
579 break;
583 QRegularExpression rx (str, cs ? QRegularExpression::NoPatternOption :
584 QRegularExpression::CaseInsensitiveOption);
585 QList < QTreeWidgetItem * >items = ui.elementTree->findItems ("*", Qt::MatchWildcard|Qt::MatchRecursive);
586 if (items.isEmpty () || !rx.isValid ())
587 return nullptr;
589 foreach (QTreeWidgetItem *item, items)
591 bool match = false;
592 QList <int>attrItems;
593 int type = 0;
595 if (elements)
597 if (rx.match (item->text (0)).hasMatch ())
599 match = true;
600 type |= SearchResult::Element;
604 if (attrs || attrValues)
606 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
607 PwmdAttributeList *alist = data->attributes ();
609 for (int i = 0, t = alist->count (); i < t; i++)
611 PwmdElementAttr *attr = alist->at (i);
612 if (attrs && rx.match (attr->name ()).hasMatch ())
614 match = true;
615 type |= SearchResult::Attr;
616 if (!attrItems.contains (i))
617 attrItems.append (i);
620 if (attrValues && attr->name() != "_name"
621 && rx.match (attr->value ()).hasMatch ())
623 type |= SearchResult::AttrValue;
624 match = true;
625 if (!attrItems.contains (i))
626 attrItems.append (i);
631 if (match)
633 SearchResult *r = new SearchResult (item, type, attrItems);
635 searchResults.append (r);
636 if (item == currentItem)
637 currentSearchResult = result = r;
640 item->setSelected (false);
643 searchResultStatusBarLabel->setText (QString (tr ("Result %1 of %2")).arg (searchResultTotal (true)).arg (searchResultTotal (false)));
644 int n = searchResults.indexOf (result);
645 n = n == -1 ? 0 : n;
646 return !searchResults.isEmpty () ? searchResults.at (n)->element () : nullptr;
649 void
650 PwmdMainWindow::slotFindTextChanged (const QString &str)
652 if (str.isEmpty())
653 return;
655 freeSearchResults ();
657 QTreeWidgetItem *item = findElementText (str);
658 if (!item)
660 QEvent *ev = new QStatusTipEvent (tr ("Element text not found."));
661 QCoreApplication::postEvent (statusBar (), ev);
662 searchResultStatusBarLabel->setText (QString (tr ("Result %1 of %2")).arg ("0", "0"));
663 return;
666 if (searchResults.contains (currentSearchResult))
667 setCurrentElement (item);
668 else
669 findElementCommon (false);
672 void
673 PwmdMainWindow::findElementCommon (bool previous)
675 searchResultStatusBarLabel->show ();
677 if (searchResults.isEmpty ())
679 searchResultStatusBarLabel->setText (QString (tr ("Result %1 of %2")).arg ("0").arg (0));
680 QEvent *ev = new QStatusTipEvent (tr ("Element text not found."));
681 QCoreApplication::postEvent (statusBar (), ev);
682 return;
685 int n = searchResults.indexOf (currentSearchResult);
686 if (n == -1)
687 currentSearchResult = nullptr;
689 if (currentSearchResult && !currentSearchResult->attrs ().isEmpty ())
691 int a = currentSearchResult->curAttr ();
693 if (a != -1)
695 if (!previous)
696 currentSearchResult->nextAttr ();
697 else
698 currentSearchResult->prevAttr ();
700 a = currentSearchResult->curAttr ();
701 if (a == -1)
702 currentSearchResult->reset ();
703 else
704 goto done;
708 if (!previous && n+1 >= searchResults.count ())
709 currentSearchResult = searchResults.first ();
710 else if (previous && n-1 < 0)
711 currentSearchResult = searchResults.last ();
712 else if (previous)
713 currentSearchResult = searchResults.at (n-1);
714 else
715 currentSearchResult = searchResults.at (n+1);
717 if (!currentSearchResult)
718 currentSearchResult = !searchResults.isEmpty () ? searchResults.at (0) : nullptr;
720 if (!currentSearchResult)
722 setCurrentElement (nullptr);
723 return;
726 if (previous && !currentSearchResult->attrs ().isEmpty ())
727 currentSearchResult->lastAttr ();
729 done:
730 searchResultStatusBarLabel->setText (QString (tr ("Result %1 of %2")).arg (searchResultTotal (true)).arg (searchResultTotal (false)));
731 setCurrentElement (currentSearchResult->element ());
732 QEvent *ev = new QStatusTipEvent (tr (""));
733 QCoreApplication::postEvent (statusBar (), ev);
736 void
737 PwmdMainWindow::slotAdvancedSearchNext (const QString &text, bool elements,
738 bool attrs, bool attrValues,
739 bool select, bool previousResult)
741 static QString previous;
742 // Toggle of search types
743 static bool prevElements, prevAttrs, prevAttrValues, prevSelect;
744 bool b = false;
746 b |= prevElements != elements, prevElements = elements;
747 b |= prevAttrs != attrs, prevAttrs = attrs;
748 b |= prevAttrValues != attrValues, prevAttrValues = attrValues;
749 b |= prevSelect != select, prevSelect = select;
750 b |= previous != text, previous = text;
752 if (b)
753 freeSearchResults ();
755 if (text.isEmpty ())
756 return;
758 if (searchResults.isEmpty () || b)
760 findElementText (text, elements, attrs, attrValues);
761 foreach (SearchResult *s, searchResults)
762 s->element ()->setSelected (select);
765 if (select)
766 return;
768 findElementCommon (previousResult);
770 if ((attrs || attrValues) && currentSearchResult &&
771 ((currentSearchResult->resultType () & SearchResult::Attr)
772 || currentSearchResult->resultType () & SearchResult::AttrValue))
773 editAttributes (false, currentSearchResult->element ());
774 else
775 slotEditContent ();
778 void
779 PwmdMainWindow::slotAdvancedSearchFinished (int)
781 if (searchResultStatusBarLabel)
782 searchResultStatusBarLabel->hide ();
784 advancedSearchDialog->hide ();
785 ui.tabWidget->setEnabled (true);
786 ui.elementTree->setFocus ();
787 QTreeWidgetItem *item = firstSelectedItem ();
788 elementSelected (item);
789 connect (ui.elementTree,
790 SIGNAL (itemSelectionChanged ()),
791 this,
792 SLOT (slotItemSelectionChanged ()));
793 fixupMultiToolbar ();
794 #ifdef Q_OS_ANDROID
795 ui.actionToggleMulti->setChecked (hasMultiSelection ());
796 #endif
799 void
800 PwmdMainWindow::slotAdvancedSearchAction ()
802 slotAdvancedSearch (QString ());
805 void
806 PwmdMainWindow::slotSearchShiftEnterPressed ()
808 prevSearchResult = true;
809 slotFindElement ();
812 void
813 PwmdMainWindow::slotAdvancedSearch (const QString &)
815 ui.tabWidget->setEnabled (false);
816 if (!advancedSearchDialog)
818 PwmdLineEdit *w = qobject_cast<PwmdLineEdit *> (elementToolBar->widgetForAction (findElementAction));
819 advancedSearchDialog = new PwmdSearchDialog (this);
820 advancedSearchDialog->setSearchText (w->text ());
821 connect (advancedSearchDialog,
822 SIGNAL (findNext (const QString &, bool, bool, bool, bool, bool)),
823 this,
824 SLOT (slotAdvancedSearchNext (const QString &, bool, bool, bool, bool, bool)));
825 connect (advancedSearchDialog, SIGNAL (finished (int)), this,
826 SLOT (slotAdvancedSearchFinished (int)));
829 advancedSearchDialog->setIncremental (incrementalSearch);
831 disconnect (ui.elementTree,
832 SIGNAL (itemClicked (QTreeWidgetItem *, int)),
833 this,
834 SLOT (slotElementClicked (QTreeWidgetItem *, int)));
835 connect (ui.elementTree,
836 SIGNAL (itemClicked (QTreeWidgetItem *, int)),
837 this,
838 SLOT (slotElementClicked (QTreeWidgetItem *, int)));
839 disconnect (ui.elementTree,
840 SIGNAL (itemSelectionChanged ()),
841 this,
842 SLOT (slotItemSelectionChanged ()));
843 resetSelectedItems ();
844 advancedSearchDialog->reset ();
845 advancedSearchDialog->show ();
848 void
849 PwmdMainWindow::slotFindElement ()
851 disconnect (ui.elementTree,
852 SIGNAL (itemClicked (QTreeWidgetItem *, int)),
853 this,
854 SLOT (slotElementClicked (QTreeWidgetItem *, int)));
855 connect (ui.elementTree,
856 SIGNAL (itemClicked (QTreeWidgetItem *, int)),
857 this,
858 SLOT (slotElementClicked (QTreeWidgetItem *, int)));
859 disconnect (ui.elementTree,
860 SIGNAL (itemSelectionChanged ()),
861 this,
862 SLOT (slotItemSelectionChanged ()));
863 ui.tabWidget->setEnabled (false);
864 PwmdLineEdit *w = qobject_cast<PwmdLineEdit *> (elementToolBar->widgetForAction (findElementAction));
866 #ifdef Q_OS_ANDROID
867 if (!findingElement)
869 w->setFocus ();
870 qApp->inputMethod ()->show ();
871 findingElement = true;
873 else
874 qApp->inputMethod ()->hide ();
875 #else
876 w->setFocus ();
877 #endif
879 if (!findElementAction->isVisible ())
881 doSaveContent ();
882 w->setText ("");
883 w->setFocus ();
884 fixupToolBar (elementToolBar, ElementToolBarFind, true);
885 findElementAction->setVisible (true);
886 prevSearchResult = false;
887 return;
890 if (w->text ().isEmpty ())
892 prevSearchResult = false;
893 return;
896 findElementCommon (prevSearchResult);
897 prevSearchResult = false;
900 void
901 PwmdMainWindow::updateElementExpiry (long t)
903 QTreeWidgetItem *item = previousElement ? previousElement : selectedElement;
905 if (!item || !t)
907 if (ui.tabWidget->currentWidget() == ui.contentTab)
908 setContentItemsEnabled ();
910 return;
913 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
914 QDateTime date;
915 date.setSecsSinceEpoch (t);
916 data->addAttribute ("_expire", QString ("%1").arg (t));
917 updateTargetAttributes (item, "_expire", QString ("%1").arg (t));
919 if (ui.tabWidget->currentWidget() == ui.contentTab)
920 setContentItemsEnabled ();
923 void
924 PwmdMainWindow::slotChangeExpire ()
926 doSaveContent (true);
927 if (!selectedElement)
928 return;
930 PwmdTreeWidgetItemData *data = qvariant_cast<PwmdTreeWidgetItemData * >(selectedElement->data (0, Qt::UserRole));
931 QString expire, incr;
932 long e, i;
934 expire = data->attribute ("_expire");
935 incr = data->attribute ("_age");
936 e = expire.toLong ();
937 i = incr.toLong ();
939 PwmdExpireDialog d (e >= time (nullptr) ? e : time (nullptr), i, this);
940 if (d.exec () == QDialog::Rejected)
941 return;
943 newAttribute = nullptr;
944 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
946 if (!isElementOwner (resolveTargets (item)))
947 continue;
949 data = qvariant_cast<PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
950 if (!d.expire ())
952 if (data->hasAttribute ("_expire"))
953 deleteAttribute (item, "_expire", true);
955 if (data->hasAttribute ("_age"))
956 deleteAttribute (item, "_age", true);
958 continue;
961 setNewAttribute (item, "_expire", QString ("%1").arg (d.expire ()),
962 false, true);
963 if (d.increment ())
964 setNewAttribute (item, "_age", QString ("%1").arg (d.increment ()),
965 false, true);
966 else if (data->hasAttribute ("_age"))
967 deleteAttribute (item, "_age", true);
970 pwm->runQueue ();
973 static void
974 showErrorMsg (gpg_error_t rc, const QString &str)
976 QMessageBox m;
978 m.setText (QApplication::
979 tr ("There was an error while communicating with pwmd."));
980 m.setInformativeText (QString ("ERR %1: %2: %3").arg (rc).arg (gpg_strsource(rc), str));
981 m.setIcon (QMessageBox::Critical);
982 m.exec ();
985 void
986 PwmdMainWindow::slotCommandResult (PwmdCommandQueueItem *item,
987 QString result, gpg_error_t rc, bool queued)
989 PwmdInquireData *inq = item ? item->data () : nullptr;
990 QEvent *ev = nullptr;
992 releaseTimer->stop ();
993 if (releaseTimeout > 0)
994 releaseTimer->start (releaseTimeout*1000);
996 // BULK command helper result built from existing queued commands.
997 if (!item)
999 showError (rc);
1000 return;
1003 if (item->id () >= PwmdCmdIdMainMax)
1005 item->setSeen ();
1006 return;
1009 switch (item->id ())
1011 case PwmdCmdIdLock:
1012 case PwmdCmdIdUnLock:
1013 releaseTimer->stop();
1014 releaseTimeout = 0;
1015 emit mainClientLockStateChanged (item->id () == PwmdCmdIdLock ? true : false);
1016 break;
1017 case PwmdCmdIdInternalStatusError:
1018 /* Don't do anything here. Pretend the connection is still valid so the
1019 * user has a chance to recover any cached elements. */
1020 if (state != Pwmd::Opened)
1021 pwm->reset ();
1022 break;
1023 case PwmdCmdIdInternalConnect:
1024 case PwmdCmdIdInternalConnectHost:
1025 if (rc)
1026 pwm->reset ();
1027 break;
1028 case PwmdCmdIdInternalOpen:
1029 openFileFinalize (rc);
1030 break;
1031 case PwmdCmdIdInternalGenKey:
1032 case PwmdCmdIdInternalPassword:
1033 item->setSeen ();
1034 return;
1035 break;
1036 case PwmdCmdIdInternalSave:
1037 pwmd_setopt (pwm->handle (), PWMD_OPTION_OVERRIDE_INQUIRE, 0);
1038 pwmd_setopt (pwm->handle (), PWMD_OPTION_LOCAL_PINENTRY, 0);
1040 if (!rc)
1042 setModified (false);
1043 setWindowModified (false);
1044 newFile = false;
1045 if (reconnectSocket)
1047 pwm->reset ();
1048 QTimer::singleShot (1, this, SLOT (slotConnectSocket()));
1050 else if (disconnectSocket)
1051 pwm->reset ();
1053 break;
1054 case PwmdCmdIdInternalCloseFile:
1055 break;
1056 case PwmdCmdIdDndMoveElement:
1057 dndOperation = DndNone;
1058 if (!rc && item->isFinalItem ())
1059 dndCopyMoveElementFinalize (inq->user ());
1060 else if (item->isFinalItem ())
1062 QEvent *ev = new QEvent (QEvent::Type (PwmdEventRefreshElementTree));
1063 QCoreApplication::postEvent (this, ev);
1065 break;
1066 case PwmdCmdIdDndCopyElement:
1067 if (rc && item->isFinalItem ())
1069 if (copyElement)
1070 selectedElement = attributeElement = copyElement;
1072 ev = new QEvent (QEvent::Type (PwmdEventNewElementCancel));
1073 QCoreApplication::postEvent (this, ev);
1075 if (hasMultiSelection ())
1077 QEvent *ev = new QEvent (QEvent::Type (PwmdEventRefreshElementTree));
1078 QCoreApplication::postEvent (this, ev);
1080 break;
1082 else if (!rc)
1084 newElement = nullptr;
1085 if (item->isFinalItem ())
1086 dndCopyMoveElementFinalize (inq->user ());
1088 break;
1089 case PwmdCmdIdDndCreateTarget:
1090 if (rc)
1092 if (item->isFinalItem ())
1094 dndOperation = DndNone;
1095 QEvent *ev = new QEvent (QEvent::Type (PwmdEventDnDTargetAttr));
1096 QCoreApplication::postEvent (this, ev);
1098 break;
1101 setAttribute (static_cast <QTreeWidgetItem *>(inq->userPtr ()),
1102 "_target", inq->user ());
1103 if (item->isFinalItem ())
1104 dndCreateTargetFinalize ();
1105 break;
1106 case PwmdCmdIdReload:
1107 if (!rc)
1109 reloadFinalize ();
1111 break;
1112 case PwmdCmdIdInvokingUser:
1113 if (!rc)
1115 invokingUserFinalize (result);
1117 break;
1118 case PwmdCmdIdCurrentUser:
1120 if (!rc)
1121 currentUserFinalize (result);
1123 break;
1124 case PwmdCmdIdLockTimeout:
1125 break;
1126 case PwmdCmdIdCommand:
1127 ui.commandResultTextEdit->clear ();
1129 if (rc)
1131 ui.commandResultTextEdit->setPlainText (QString ("ERR %1: %2").arg(rc).arg(gpg_strerror(rc)));
1132 break;
1135 if (inq && inq->userBool ())
1137 ui.commandResultTextEdit->setOpenLinks (true);
1138 ui.commandResultTextEdit->setHtml (result);
1140 else
1141 ui.commandResultTextEdit->setPlainText (result);
1143 setModified ();
1144 if (pwm->state () == Pwmd::Opened && (!inq || !inq->userBool ()))
1145 refreshElementTree ();
1146 break;
1147 case PwmdCmdIdGetContent:
1148 if (rc && item->checkError (rc))
1149 rc = 0;
1151 if (rc && previousElement && previousElement != selectedElement)
1153 if (ui.tabWidget->currentWidget () != ui.commandTab)
1155 setContentItemsEnabled (false);
1156 setContent ("");
1159 else if (!rc)
1160 elementSelectedFinalize (static_cast <QTreeWidgetItem *> (inq->userPtr ()), result);
1161 else
1162 setCurrentElement (selectedElement);
1163 break;
1164 case PwmdCmdIdNewElement:
1165 if (!rc)
1167 newElementFinalize (inq->user());
1168 break;
1170 selectedElement = newElement->parent ();
1171 ev = new QEvent (QEvent::Type (PwmdEventNewElementCancel));
1172 QCoreApplication::postEvent (this, ev);
1173 break;
1174 case PwmdCmdIdDeleteElement:
1175 deleteElementFinalize (item);
1176 if (!rc && item->isFinalItem ())
1177 setCurrentElement (selectedElement);
1178 break;
1179 case PwmdCmdIdRenameElement:
1180 if (!rc)
1182 renameElementFinalize ();
1183 break;
1185 selectedElement->setText (0, inq->user());
1186 ev = new QEvent (QEvent::Type (PwmdEventNewElementCancel));
1187 QCoreApplication::postEvent (this, ev);
1188 break;
1189 case PwmdCmdIdElementTreeForm:
1190 if (rc && item->checkError (rc))
1191 rc = 0;
1193 if (!rc)
1195 QByteArray b = result.toUtf8 ();
1196 openFormFinalize (b, inq->user (), inq->user2 ());
1198 break;
1199 case PwmdCmdIdElementTree:
1200 if (rc && item->checkError (rc))
1201 rc = 0;
1203 if (rc)
1205 if (gpg_err_code (rc) == GPG_ERR_ELOOP && inq->userBool ()) // target
1207 QTreeWidgetItem *user = static_cast<QTreeWidgetItem *> (inq->userPtr ());
1208 rc = deleteAttribute (user ? user : selectedElement, "_target");
1211 break;
1215 QByteArray b = result.toUtf8 ();
1216 refreshElementTreeFinalize (b);
1218 break;
1219 case PwmdCmdIdStoreContent:
1220 if (!rc)
1222 bool e;
1224 saveContentFinalize (static_cast <QTreeWidgetItem *> (inq->userPtr()), inq->user(), inq->userBool());
1225 if (inq->isSaving (e))
1226 saveDocument (e);
1228 else
1229 setCurrentElement (selectedElement);
1230 break;
1231 case PwmdCmdIdPasswordContent:
1232 if (!rc)
1234 savePasswordContentFinalize (static_cast <QTreeWidgetItem *> (inq->userPtr()), inq->user());
1236 else
1237 setCurrentElement (selectedElement);
1238 break;
1239 case PwmdCmdIdAttrContent:
1240 if (!rc)
1242 AttributeFinalizeT *attr = static_cast <AttributeFinalizeT *> (inq->userPtr ());
1243 saveAttrContentFinalize (inq->user(), attr);
1244 fixupToolBar (attributeToolBar, AttributeToolBarOk, false);
1248 AttributeFinalizeT *attr = static_cast <AttributeFinalizeT *> (inq->userPtr ());
1249 delete attr;
1250 if (item->isFinalItem ())
1252 bool e;
1253 if (inq->isSaving (e))
1254 saveDocument (e);
1257 break;
1258 case PwmdCmdIdAttrNew:
1260 AttributeFinalizeT *attr;
1261 if (!rc)
1263 attr = static_cast <AttributeFinalizeT *> (inq->userPtr ());
1264 setNewAttributeFinalize (attr, inq->userBool());
1266 else
1268 attr = static_cast <AttributeFinalizeT *> (inq->userPtr ());
1269 if (attr && item->isFinalItem ())
1270 ui.attributeList->takeItem (ui.attributeList->row (attr->listItem));
1272 delete attr->listItem;
1273 delete attr;
1275 break;
1276 case PwmdCmdIdAttrDelete:
1277 if (!rc)
1279 deleteAttributeFinalize (static_cast <QTreeWidgetItem *>(inq->userPtr ()), inq->user());
1281 break;
1282 case PwmdCmdIdAttrList:
1283 if (!rc)
1285 refreshAttributeListFinalize (static_cast <QTreeWidgetItem *>(inq->userPtr ()), result, true, inq->userBool ());
1287 else
1288 setCurrentElement (selectedElement);
1289 break;
1290 default:
1291 break;
1294 if (rc && item->id () != PwmdCmdIdCommand)
1296 if (!queued)
1298 if ((item->checkError (rc)
1299 && hasMultiSelection ()
1300 && item->isFinalItem ())
1301 || !item->checkError (rc))
1303 if (item->error () && !item->errorString().isEmpty ())
1304 showErrorMsg (item->error (), item->errorString ());
1305 else
1306 showError (rc);
1310 /* The error may have been related to the previously selected element in
1311 * a bulk command. Restore the view to the previous element. */
1312 if (previousElement && previousElement != selectedElement)
1314 ui.elementTree->setCurrentItem (previousElement);
1315 selectedElement = previousElement;
1319 item->setSeen ();
1321 if (hasQueuedRefreshTree ())
1322 nextElement = nullptr;
1324 if (!pwm->queued () && nextElement)
1325 elementSelected (nextElement);
1328 void
1329 PwmdMainWindow::slotVersion ()
1331 QMessageBox::about (this, tr ("About QPwmc"),
1332 tr ("QPwmc version %1\nlibpwmd %2\n\n"
1333 "Copyright 2015-2024 Ben Kibbey <bjk@luxsci.net>\n"
1334 "https://gitlab.com/bjk/qpwmc").arg (QPWMC_VERSION,
1335 pwmd_version ()));
1338 void
1339 PwmdMainWindow::openFormFinalize (QByteArray &result, const QString &file,
1340 const QString &dir)
1342 QSettings cfg ("qpwmc");
1343 cfg.setValue ("lastFormLocation", dir);
1344 cfg.sync ();
1346 QList < QTreeWidgetItem * >items = buildElementTree (result, false);
1347 PwmdTreeWidget *tree = new PwmdTreeWidget ();
1349 tree->header ()->setSectionResizeMode (0, QHeaderView::ResizeToContents);
1350 tree->header ()->setStretchLastSection (false);
1351 tree->insertTopLevelItems (0, items);
1352 tree->sortByColumn (0, Qt::AscendingOrder);
1353 tree->setHeaderHidden (true);
1354 tree->setEditTriggers (QAbstractItemView::NoEditTriggers);
1355 tree->ungrabGesture(Qt::TapAndHoldGesture);
1356 tree->ungrabGesture(Qt::TapGesture);
1357 tree->setCurrentItem (findElement (elementPath (selectedElement), 0, true,
1358 tree));
1360 ApplicationForm *f = new ApplicationForm (pwm, file, tree, this);
1361 if (!f->hasError ())
1363 f->setParent (this, Qt::Dialog);
1364 if (f->exec () || f->modified ())
1366 setModified ();
1367 refreshElementTree ();
1371 clearElementTree (tree);
1372 delete tree;
1373 delete f;
1376 void
1377 PwmdMainWindow::slotOpenForm ()
1379 doSaveContent (true);
1380 PwmdFileDialog d (this, tr ("Open application form"), 0, "*.xml");
1381 QSettings cfg ("qpwmc");
1382 #ifndef Q_OS_ANDROID
1383 d.setDirectory (cfg.value ("lastFormLocation", "/usr/local/share/qpwmc").toString ());
1384 #else
1385 d.setMimeTypeFilters (QStringList ({"text/xml"}));
1386 d.setDirectory (cfg.value ("lastFormLocation", "/sdcard").toString ());
1387 #endif
1389 d.setUpdateLastLocation (false);
1390 d.setFileMode (QFileDialog::ExistingFile);
1391 d.setOption (QFileDialog::ReadOnly);
1393 if (!d.exec () || !d.selectedFiles ().count ())
1394 return;
1396 PwmdInquireData *inq = new PwmdInquireData ();
1397 inq->setUser (d.selectedFiles ().at (0));
1398 inq->setUser2 (d.directory ().absolutePath ());
1399 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdElementTreeForm,
1400 "LIST --recurse --sexp",
1401 Pwmd::inquireCallback, inq));
1404 void
1405 PwmdMainWindow::slotFileOptionsDone (int)
1407 QEvent *ev = new QEvent (QEvent::Type (PwmdEventFileOptionsClose));
1408 QCoreApplication::postEvent (this, ev);
1411 void
1412 PwmdMainWindow::slotClientLockStateChanged (bool b)
1414 if (!b)
1416 releaseTimer->stop();
1417 releaseTimeout = 0;
1420 if (clientDialog)
1421 emit mainClientLockStateChanged (b);
1424 void
1425 PwmdMainWindow::slotFileOptions ()
1427 fixupToolBar (fileToolBar, FileToolBarOpen, false);
1429 if (!fileOptionsDialog)
1431 fileOptionsDialog = new PwmdFileOptionsDialog (this);
1432 connect (fileOptionsDialog, SIGNAL (finished (int)), this, SLOT (slotFileOptionsDone (int)));
1433 connect (fileOptionsDialog, SIGNAL (clientLockStateChanged (bool)), this, SLOT (slotClientLockStateChanged (bool)));
1436 fileOptionsDialog->show ();
1439 void
1440 PwmdMainWindow::slotButtonLabels (bool b)
1442 setupToolBarLabels (b);
1445 void
1446 PwmdMainWindow::slotTabChanged (int n)
1448 static int last;
1449 int which = 0;
1450 bool update;
1452 previousElement = nextElement = nullptr;
1453 disconnect (ui.tabWidget, SIGNAL (currentChanged (int)), this, SLOT (slotTabChanged (int)));
1454 ui.tabWidget->setCurrentWidget (ui.tabWidget->widget (last));
1455 update = doSaveContent (true, false, false, &which);
1457 if (update)
1459 nextTabWidget = ui.tabWidget->widget (n);
1460 switch (which)
1462 case Attributes:
1463 ui.tabWidget->setCurrentWidget (ui.attributeTab);
1464 break;
1465 case Content:
1466 case Password:
1467 ui.tabWidget->setCurrentWidget (ui.contentTab);
1468 break;
1469 default:
1470 break;
1473 connect (ui.tabWidget, SIGNAL (currentChanged (int)), this, SLOT (slotTabChanged (int)));
1474 pwm->runQueue ();
1475 return;
1478 last = n;
1479 ui.tabWidget->setCurrentWidget (ui.tabWidget->widget (n));
1480 connect (ui.tabWidget, SIGNAL (currentChanged (int)), this, SLOT (slotTabChanged (int)));
1481 nextTabWidget = nullptr;
1482 fixupEditContentOrAttributes (ui.tabWidget->currentWidget () != ui.attributeTab);
1484 switch (n)
1486 case TabContent:
1487 slotEditContent ();
1488 break;
1489 case TabAttributes:
1490 editTargetAttributes = false;
1491 ui.actionToggleTargetAttribute->setChecked (false);
1492 setAttributeItemsEnabled ();
1493 editAttributes (false, selectedElement);
1494 break;
1495 case TabCommand:
1496 doSaveContent (false);
1497 setCommandItemsEnabled ();
1498 break;
1499 default:
1500 break;
1503 fixupMultiToolbar ();
1506 void
1507 PwmdMainWindow::slotEditContent ()
1509 fixupEditContentOrAttributes (true);
1510 doSaveContent (false);
1511 setContentItemsEnabled ();
1514 void
1515 PwmdMainWindow::setupToolBarLabels (bool b)
1517 fileToolBar->setToolButtonStyle (b ? Qt::ToolButtonTextUnderIcon : Qt::ToolButtonIconOnly);
1518 elementToolBar->setToolButtonStyle (b ? Qt::ToolButtonTextUnderIcon : Qt::ToolButtonIconOnly);
1519 contentToolBar->setToolButtonStyle (b ? Qt::ToolButtonTextUnderIcon : Qt::ToolButtonIconOnly);
1520 attributeToolBar->setToolButtonStyle (b ? Qt::ToolButtonTextUnderIcon : Qt::ToolButtonIconOnly);
1521 commandToolBar->setToolButtonStyle (b ? Qt::ToolButtonTextUnderIcon : Qt::ToolButtonIconOnly);
1522 passwordToolBar->setToolButtonStyle (b ? Qt::ToolButtonTextUnderIcon : Qt::ToolButtonIconOnly);
1525 /* En/Dis/ables menu items. Returns whether the button is enabled when 'test'
1526 * is true or not. */
1527 bool
1528 PwmdMainWindow::fixupMenu (QMenu *m, int which, bool enable, int checked,
1529 bool test)
1531 QList <QAction *>actions = m->actions();
1533 if (test)
1534 return actions.at (which)->isEnabled ();
1536 if (m == ui.menuFile)
1538 switch (which)
1540 case FileMenuOpen:
1541 case FileMenuOpenForm:
1542 case FileMenuClose:
1543 case FileMenuSave:
1544 case FileMenuSaveExt:
1545 actions.at (which)->setEnabled (enable);
1546 break;
1547 default:
1548 break;
1551 else if (m == ui.menuElement)
1553 switch (which)
1555 case ElementMenuNew:
1556 case ElementMenuDelete:
1557 case ElementMenuRename:
1558 case ElementMenuFind:
1559 case ElementMenuAdvancedFind:
1560 actions.at (which)->setEnabled (enable);
1561 break;
1562 default:
1563 break;
1566 else if (m == ui.menuContent)
1568 switch (which)
1570 case ContentMenuInsert:
1571 case ContentMenuSave:
1572 case ContentMenuClipboard:
1573 case ContentMenuHidden:
1574 case ContentMenuBase64:
1575 case ContentMenuUpdate:
1576 case ContentMenuPassword:
1577 case ContentMenuRefresh:
1578 case ContentMenuExpiry:
1579 actions.at (which)->setEnabled (enable);
1580 break;
1581 default:
1582 break;
1585 else if (m == ui.menuAttributes)
1587 switch (which)
1589 case AttributeMenuTarget:
1590 actions.at (which)->setEnabled (enable);
1592 if (checked != -1)
1593 actions.at (which)->setChecked (checked);
1594 break;
1595 case AttributeMenuNew:
1596 case AttributeMenuDelete:
1597 case AttributeMenuUpdate:
1598 case AttributeMenuRefresh:
1599 actions.at (which)->setEnabled (enable);
1600 break;
1601 default:
1602 break;
1605 else if (m == ui.menuView)
1607 switch (which)
1609 case ViewMenuButtonLabels:
1610 case ViewMenuExpandAll:
1611 case ViewMenuClientList:
1612 actions.at (which)->setEnabled (enable);
1613 break;
1614 default:
1615 break;
1619 return true;
1622 /* En/Dis/ables toolbar buttons. Returns whether the button is enabled when
1623 * 'test' is true or not. */
1624 bool
1625 PwmdMainWindow::fixupToolBar (QToolBar *t, int which, bool enable, bool test,
1626 QWidget **rWidget)
1628 QList <QAction *> list = t->actions ();
1629 QWidget *w = nullptr;
1631 for (int i = 0; i < list.size(); i++)
1633 if (t == fileToolBar && which == i)
1635 w = fileToolBar->widgetForAction (list.at (i));
1636 if (rWidget)
1637 *rWidget = w;
1639 if (test)
1640 return w->isEnabled ();
1642 switch (which)
1644 case FileToolBarOpen:
1645 fixupMenu (ui.menuFile, FileMenuOpen, enable);
1646 w->setEnabled (enable);
1647 break;
1648 case FileToolBarSave:
1649 fixupMenu (ui.menuFile, FileMenuSave, enable);
1650 w->setEnabled (enable);
1651 break;
1652 default:
1653 break;
1656 else if (t == elementToolBar && which == i)
1658 w = elementToolBar->widgetForAction (list.at (i));
1659 if (rWidget)
1660 *rWidget = w;
1662 if (test)
1663 return w->isEnabled ();
1665 switch (which)
1667 case ElementToolBarCreate:
1668 fixupMenu (ui.menuElement, ElementMenuNew, enable);
1669 w->setEnabled (enable);
1670 break;
1671 case ElementToolBarDelete:
1672 fixupMenu (ui.menuElement, ElementMenuDelete, enable);
1673 w->setEnabled (enable);
1674 break;
1675 case ElementToolBarRename:
1676 fixupMenu (ui.menuElement, ElementMenuRename, enable);
1677 w->setEnabled (enable);
1678 break;
1679 case ElementToolBarFind:
1680 fixupMenu (ui.menuElement, ElementMenuFind, enable);
1681 fixupMenu (ui.menuElement, ElementMenuAdvancedFind, enable);
1682 w->setEnabled (enable);
1683 findElementWidget->setEnabled (enable);
1684 default:
1685 break;
1688 else if (t == contentToolBar && which == i)
1690 w = contentToolBar->widgetForAction (list.at (i));
1691 if (rWidget)
1692 *rWidget = w;
1694 if (test)
1695 return w->isEnabled ();
1697 switch (which)
1699 case ContentToolBarOk:
1700 fixupMenu (ui.menuContent, ContentMenuUpdate, enable);
1701 w->setEnabled (enable);
1702 break;
1703 case ContentToolBarPassword:
1704 fixupMenu (ui.menuContent, ContentMenuPassword, enable);
1705 w->setEnabled (enable);
1706 break;
1707 case ContentToolBarExpiry:
1708 fixupMenu (ui.menuContent, ContentMenuExpiry, enable);
1709 w->setEnabled (enable);
1710 break;
1711 case ContentToolBarSave:
1712 fixupMenu (ui.menuContent, ContentMenuSave, enable);
1713 w->setEnabled (enable);
1714 break;
1715 case ContentToolBarInsert:
1716 fixupMenu (ui.menuContent, ContentMenuInsert, enable);
1717 w->setEnabled (enable);
1718 break;
1719 case ContentToolBarRefresh:
1720 fixupMenu (ui.menuContent, ContentMenuRefresh, enable);
1721 w->setEnabled (enable);
1722 break;
1723 default:
1724 break;
1727 else if (t == passwordToolBar && which == i)
1729 w = passwordToolBar->widgetForAction (list.at (i));
1730 if (rWidget)
1731 *rWidget = w;
1733 if (test)
1734 return w->isEnabled ();
1736 switch (which)
1738 case PasswordToolBarOk:
1739 if (!ui.passwordWidget->isHidden ())
1741 fixupMenu (ui.menuContent, ContentMenuUpdate, enable);
1742 w->setEnabled (enable);
1744 break;
1745 case PasswordToolBarInsert:
1746 case PasswordToolBarSave:
1747 case PasswordToolBarExpiry:
1748 case PasswordToolBarRefresh:
1749 w->setEnabled (enable);
1750 break;
1751 default:
1752 break;
1755 else if (t == attributeToolBar && which == i)
1757 w = attributeToolBar->widgetForAction (list.at (i));
1758 if (rWidget)
1759 *rWidget = w;
1761 if (test)
1762 return w->isEnabled ();
1764 switch (which)
1766 case AttributeToolBarNew:
1767 fixupMenu (ui.menuAttributes, AttributeMenuNew, enable);
1768 w->setEnabled (enable);
1769 break;
1770 case AttributeToolBarDelete:
1771 fixupMenu (ui.menuAttributes, AttributeMenuDelete, enable);
1772 w->setEnabled (enable);
1773 break;
1774 case AttributeToolBarOk:
1775 fixupMenu (ui.menuAttributes, AttributeMenuUpdate, enable);
1776 w->setEnabled (enable);
1777 break;
1778 case AttributeToolBarRefresh:
1779 fixupMenu (ui.menuAttributes, AttributeMenuRefresh, enable);
1780 w->setEnabled (enable);
1781 break;
1782 default:
1783 break;
1786 else if (t == commandToolBar && which == i)
1788 w = commandToolBar->widgetForAction (list.at (i));
1789 if (rWidget)
1790 *rWidget = w;
1792 if (test)
1793 return w->isEnabled ();
1795 switch (which)
1797 case CommandToolBarSave:
1798 case CommandToolBarClear:
1799 case CommandToolBarOk:
1800 case CommandToolBarRefresh:
1801 case CommandToolBarHelp:
1802 w->setEnabled (enable);
1803 break;
1804 default:
1805 break;
1810 return true;
1813 void PwmdMainWindow::slotCommandInquireFilename ()
1815 PwmdFileDialog d (this, tr ("Inquire data from file"));
1817 d.setFileMode (QFileDialog::ExistingFile);
1818 d.setOption (QFileDialog::ReadOnly);
1820 if (!d.exec () || !d.selectedFiles ().count ())
1821 return;
1823 setCommandInquireFilename (d.selectedFiles ().at (0));
1826 void PwmdMainWindow::slotCommandInquireStateChanged (int s)
1828 bool b = s == Qt::Unchecked ? false : true;
1830 ui.le_commandInquire->setEnabled (b);
1831 ui.tb_commandInquireFilename->setEnabled (b);
1832 setCommandInquireFilename (QString ());
1835 void
1836 PwmdMainWindow::slotSearchCommandResults ()
1838 if (ui.le_commandSearch->text ().isEmpty ())
1839 return;
1841 ui.commandResultTextEdit->find (ui.le_commandSearch->text ());
1844 void
1845 PwmdMainWindow::slotClearCommand ()
1847 ui.commandTextEdit->clear ();
1848 setCommandInquireFilename (QString ());
1849 ui.commandResultTextEdit->clear ();
1850 ui.ck_commandInquire->setChecked (false);
1851 ui.le_commandInquire->setText ("");
1852 ui.le_commandInquire->setEnabled (false);
1853 ui.le_commandSearch->setText ("");
1854 ui.le_commandSearch->setEnabled (false);
1855 ui.pb_commandSearch->setEnabled (false);
1858 void
1859 PwmdMainWindow::slotSaveCommandResult ()
1861 PwmdFileDialog d (this, tr ("Save command result"));
1863 d.setAcceptMode (QFileDialog::AcceptSave);
1864 d.setFileMode (QFileDialog::AnyFile);
1866 if (!d.exec () || d.selectedFiles ().isEmpty ())
1867 return;
1869 QStringList f = d.selectedFiles ();
1870 QFile file (f.at (0));
1872 if (!file.open (QFile::WriteOnly))
1874 QMessageBox m;
1876 m.setText (file.fileName ());
1877 m.setInformativeText (file.errorString ());
1878 m.setIcon (QMessageBox::Warning);
1879 m.exec ();
1880 return;
1883 if (file.write (ui.commandResultTextEdit->document ()->toPlainText ().toUtf8 ()) ==
1886 QMessageBox m;
1888 m.setText (file.fileName ());
1889 m.setInformativeText (file.errorString ());
1890 m.setIcon (QMessageBox::Warning);
1891 m.exec ();
1895 void
1896 PwmdMainWindow::slotCommandHelp ()
1898 PwmdInquireData *inq = new PwmdInquireData ();
1899 PwmdCommandQueueItem *item = new PwmdCommandQueueItem (PwmdCmdIdCommand,
1900 "HELP --html", 0, inq);
1902 inq->setUserBool (true);
1903 pwm->command (item);
1906 void
1907 PwmdMainWindow::slotCommandAnchorClicked (const QUrl &url)
1909 QString cmd = QString ("HELP --html %1").arg (url.path ());
1910 PwmdInquireData *inq = new PwmdInquireData ();
1911 PwmdCommandQueueItem *item = new PwmdCommandQueueItem (PwmdCmdIdCommand, cmd,
1912 0, inq);
1914 inq->setUserBool (true);
1915 pwm->command (item);
1918 void
1919 PwmdMainWindow::slotCommandResultContentChanged ()
1921 bool b = ui.commandResultTextEdit->document ()->characterCount () > 1;
1923 fixupToolBar (commandToolBar, CommandToolBarSave, b);
1924 ui.commandResultTextEdit->setEnabled (b);
1925 ui.le_commandSearch->setEnabled (b);
1926 ui.pb_commandSearch->setEnabled (b);
1929 void
1930 PwmdMainWindow::slotCommandContentChanged ()
1932 tabify (ui.commandTextEdit);
1933 bool b = ui.commandTextEdit->toPlainText ().length();
1934 fixupToolBar (commandToolBar, CommandToolBarOk, b);
1935 fixupToolBar (commandToolBar, CommandToolBarClear, b);
1938 void
1939 PwmdMainWindow::setCommandInquireFilename (const QString &s)
1941 if (s.isEmpty ())
1942 ui.commandTextEdit->setPlaceholderText ("");
1943 else
1944 ui.commandTextEdit->setPlaceholderText(QString (tr ("Inquire data obtained from file %1")).arg (s));
1946 ui.commandTextEdit->setEnabled (s.isEmpty ());
1947 commandInquireFilename = s;
1948 if (!s.isEmpty ())
1949 ui.commandTextEdit->document ()->setPlainText ("");
1951 ui.tb_commandInquireFilename->setEnabled (ui.ck_commandInquire->isChecked ());
1952 fixupToolBar (commandToolBar, CommandToolBarOk, !s.isEmpty ()
1953 || !ui.commandTextEdit->document ()->toPlainText ().isEmpty ());
1956 void
1957 PwmdMainWindow::slotCommandHistoryChanged (int idx)
1959 if (idx == -1)
1960 return;
1962 PwmdCommandHistoryData *data = qvariant_cast<PwmdCommandHistoryData *> (ui.cb_commandHistory->currentData ());
1963 if (!data)
1964 return;
1966 ui.commandTextEdit->setPlainText (data->args ());
1967 ui.ck_commandInquire->setChecked (data->isInquire ());
1968 ui.le_commandInquire->setText (data->inquireCmd ());
1969 setCommandInquireFilename (data->inquireFilename ());
1972 void
1973 PwmdMainWindow::slotExecuteCommand ()
1975 QString str = ui.commandTextEdit->toPlainText ().replace ("<TAB>", "\t", Qt::CaseInsensitive);
1977 ui.commandResultTextEdit->setPlainText ("");
1979 if (str.isEmpty () &&
1980 (!ui.ck_commandInquire->isChecked () ||
1981 (ui.ck_commandInquire->isChecked () && ui.le_commandInquire->text ().isEmpty ())))
1982 return;
1984 bool match = false;
1985 for (int i = 0; i < ui.cb_commandHistory->count (); i++)
1987 PwmdCommandHistoryData *data = qvariant_cast<PwmdCommandHistoryData *> (ui.cb_commandHistory->itemData (i));
1988 if (data && ui.ck_commandInquire->isChecked () == data->isInquire ()
1989 && ui.le_commandInquire->text () == data->inquireCmd ()
1990 && ui.commandTextEdit->toPlainText () == data->args ())
1992 match = true;
1993 break;
1995 else if (data && !commandInquireFilename.isEmpty ()
1996 && ui.ck_commandInquire->isChecked () == data->isInquire ()
1997 && ui.le_commandInquire->text () == data->inquireCmd ()
1998 && commandInquireFilename == data->inquireFilename ())
2000 match = true;
2001 break;
2005 if (!match)
2007 PwmdCommandHistoryData *data = new PwmdCommandHistoryData (ui.commandTextEdit->toPlainText (), ui.ck_commandInquire->isChecked (), ui.le_commandInquire->text (), commandInquireFilename);
2008 if (!commandInquireFilename.isEmpty ())
2009 ui.cb_commandHistory->addItem (ui.le_commandInquire->text ()+": "+commandInquireFilename, QVariant::fromValue (data));
2010 else
2011 ui.cb_commandHistory->addItem (ui.commandTextEdit->toPlainText (), QVariant::fromValue (data));
2014 ui.cb_commandHistory->setEnabled (true);
2015 saveExpandedItemList ();
2017 if (ui.ck_commandInquire->isChecked ())
2019 PwmdInquireData *inq = new PwmdInquireData ();
2021 if (!commandInquireFilename.isEmpty ())
2023 QFile file (commandInquireFilename);
2025 if (!file.open (QIODevice::ReadOnly))
2027 gpg_err_code_t rc;
2029 switch (file.error ())
2031 case QFile::OpenError:
2032 rc = GPG_ERR_ENOENT;
2033 break;
2034 case QFile::PermissionsError:
2035 rc = GPG_ERR_EPERM;
2036 break;
2037 case QFile::ResourceError:
2038 rc = GPG_ERR_ENOMEM;
2039 break;
2040 default:
2041 rc = GPG_ERR_UNKNOWN_ERRNO;
2042 break;
2045 delete inq;
2046 showError (gpg_error (rc));
2047 return;
2050 inq->setData (file.readAll ());
2051 file.close ();
2053 else
2054 inq->setData (str);
2056 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdCommand, ui.le_commandInquire->text ().replace ("<TAB>", "\t").toUtf8 (), Pwmd::inquireCallback, inq));
2058 else
2060 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdCommand, str));
2064 void
2065 PwmdMainWindow::setupToolbars ()
2067 QCommonStyle style;
2068 QWidget *w;
2069 QAction *a;
2071 fileToolBar = new QToolBar (tr ("File Toolbar"), this);
2072 fileToolBar->addAction (QIcon (":icons/rodentia-icons_folder-open-blue.svg"), tr ("Open"), this, SLOT (slotOpenFile()));
2073 fixupToolBar (fileToolBar, FileToolBarOpen, false, false, &w);
2074 w->setStatusTip (ui.actionOpen->statusTip ());
2075 fileToolBar->addAction (QIcon (":icons/rodentia-icons_media-floppy.svg"), tr ("Save"), this, SLOT (slotSaveDocument()));
2076 fixupToolBar (fileToolBar, FileToolBarSave, false, false, &w);
2077 w->setStatusTip (ui.actionSave->statusTip ());
2078 fileToolBar->setObjectName ("fileToolBar");
2079 addToolBar (fileToolBar);
2081 elementToolBar = new QToolBar (tr ("Element Toolbar"), this);
2082 elementToolBar->setEnabled (false);
2083 elementToolBar->addAction (QIcon (":icons/rodentia-icons_folder-add.svg"), tr ("Create Element"), this, SLOT (slotNewElement()));
2084 fixupToolBar (elementToolBar, ElementToolBarCreate, false, false, &w);
2085 w->setStatusTip (ui.actionNewElement->statusTip ());
2086 elementToolBar->addAction (QIcon (":icons/rodentia-icons_user-trash-full.svg"), tr ("Delete Element"), this, SLOT (slotDeleteElement()));
2087 fixupToolBar (elementToolBar, ElementToolBarDelete, false, false, &w);
2088 w->setStatusTip (ui.actionDeleteElement->statusTip ());
2089 elementToolBar->addAction (QIcon (":icons/matt-icons_font-x-generic.svg"), tr ("Rename Element"), this, SLOT (slotRenameElement()));
2090 fixupToolBar (elementToolBar, ElementToolBarRename, false, false, &w);
2091 w->setStatusTip (ui.actionRenameElement->statusTip ());
2093 findElementAction = elementToolBar->addWidget (new PwmdLineEdit);
2094 findElementAction->setVisible (false);
2095 PwmdLineEdit *le = qobject_cast<PwmdLineEdit *> (elementToolBar->widgetForAction (findElementAction));
2096 le->setInputMethodHints (Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText);
2097 le->setClearButtonEnabled (true);
2098 le->setPlaceholderText (tr ("Find element..."));
2099 QIcon icon = QCommonStyle().standardIcon (QStyle::SP_ArrowUp);
2100 a = le->addAction (icon, QLineEdit::TrailingPosition);
2101 connect (a, SIGNAL (triggered ()), this, SLOT (slotSearchShiftEnterPressed ()));
2102 icon = QCommonStyle().standardIcon (QStyle::SP_ArrowDown);
2103 a = le->addAction (icon, QLineEdit::TrailingPosition);
2104 connect (a, SIGNAL (triggered ()), this, SLOT (slotFindElement ()));
2105 connect (le, SIGNAL (returnPressed ()), this, SLOT (slotFindElement ()));
2106 connect (le, SIGNAL (shiftEnterPressed ()), this, SLOT (slotSearchShiftEnterPressed ()));
2107 connect (le, SIGNAL (finished ()), this, SLOT (slotFindFinished ()));
2109 a = elementToolBar->addAction (QIcon (":icons/system-search.svg"), tr ("Find Element"), this, SLOT (slotFindElement ()));
2110 findElementWidget = elementToolBar->widgetForAction (a);
2111 fixupToolBar (elementToolBar, ElementToolBarFind, false, false, &w);
2112 w->setStatusTip (ui.actionFindElement->statusTip ());
2114 QLabel *l = new QLabel (tr ("<a href=\"advanced\">Advanced</a>"));
2115 connect (l, SIGNAL (linkActivated (const QString &)), this,
2116 SLOT (slotAdvancedSearch (const QString &)));
2117 l->setTextInteractionFlags (Qt::LinksAccessibleByMouse|Qt::LinksAccessibleByKeyboard);
2118 l->setStatusTip (tr ("Advanced element tree search..."));
2119 elementToolBar->addWidget (l);
2120 connect (ui.actionAdvancedFindElement, SIGNAL (triggered ()), this,
2121 SLOT (slotAdvancedSearchAction ()));
2122 elementToolBar->setObjectName ("elementToolBar");
2123 addToolBar (Qt::TopToolBarArea, elementToolBar);
2125 contentToolBar = new QToolBar (tr ("Content Toolbar"), this);
2126 contentToolBar->addAction (QIcon (":icons/matt-icons_document-add.svg"), tr ("Insert Content"), this, SLOT (slotInsertContent()));
2127 fixupToolBar (contentToolBar, ContentToolBarInsert, false, false, &w);
2128 w->setStatusTip (ui.actionInsertFile->statusTip ());
2129 contentToolBar->addAction (QIcon (":icons/rodentia-icons_media-floppy.svg"), tr ("Save Content"), this, SLOT (slotSaveToFile()));
2130 fixupToolBar (contentToolBar, ContentToolBarSave, false, false, &w);
2131 w->setStatusTip (ui.actionSaveToFile->statusTip ());
2132 contentToolBar->addAction (QIcon (":icons/trayicon.svg"), tr ("Set Password"), this, SLOT (slotCreatePasswordAttribute()));
2133 fixupToolBar (contentToolBar, ContentToolBarPassword, true, false, &w);
2134 w->setStatusTip (ui.actionCreatePasswordAttribute->statusTip ());
2135 contentToolBar->addAction (QIcon (":icons/expiry.svg"), tr ("Change expiry"), this, SLOT (slotChangeExpire()));
2136 fixupToolBar (contentToolBar, ContentToolBarExpiry, true, false, &w);
2137 w->setStatusTip (ui.actionChangeExpire->statusTip ());
2138 contentToolBar->addSeparator ();
2139 contentToolBar->addAction (QIcon (":icons/rodentia-icons_ok.svg"), tr ("Update"), this, SLOT (slotSaveContent()));
2140 fixupToolBar (contentToolBar, ContentToolBarOk, false, false, &w);
2141 w->setStatusTip (ui.actionUpdateContent->statusTip ());
2142 contentToolBar->addAction (QIcon (":icons/matt-icons_view-refresh.svg"), tr ("Refresh Content"), this, SLOT (slotRefreshContent()));
2143 fixupToolBar (contentToolBar, ContentToolBarRefresh, false, false, &w);
2144 w->setStatusTip (ui.actionRefreshContent->statusTip ());
2145 contentToolBar->setObjectName ("contentToolBar");
2146 addToolBar (Qt::RightToolBarArea, contentToolBar);
2148 passwordToolBar = new QToolBar (tr ("Password Toolbar"), this);
2149 passwordToolBar->addAction (QIcon (":icons/matt-icons_document-add.svg"), tr ("Insert Content"), this, SLOT (slotInsertContent()));
2150 fixupToolBar (passwordToolBar, PasswordToolBarInsert, false, false, &w);
2151 w->setStatusTip (ui.actionInsertFile->statusTip ());
2152 passwordToolBar->addAction (QIcon (":icons/rodentia-icons_media-floppy.svg"), tr ("Save Content"), this, SLOT (slotSaveToFile()));
2153 fixupToolBar (passwordToolBar, PasswordToolBarSave, false, false, &w);
2154 w->setStatusTip (ui.actionSaveToFile->statusTip ());
2155 passwordToolBar->addAction (QIcon (":icons/expiry.svg"), tr ("Change expiry"), this, SLOT (slotChangeExpire()));
2156 fixupToolBar (passwordToolBar, PasswordToolBarExpiry, false, false, &w);
2157 w->setStatusTip (ui.actionChangeExpire->statusTip ());
2158 passwordToolBar->addSeparator ();
2159 passwordToolBar->addAction (QIcon (":icons/rodentia-icons_ok.svg"), tr ("Update"), this, SLOT (slotSaveContent()));
2160 fixupToolBar (passwordToolBar, PasswordToolBarOk, false, false, &w);
2161 w->setStatusTip (ui.actionUpdateContent->statusTip ());
2162 passwordToolBar->addAction (QIcon (":icons/matt-icons_view-refresh.svg"), tr ("Refresh Content"), this, SLOT (slotRefreshContent()));
2163 fixupToolBar (passwordToolBar, PasswordToolBarRefresh, false, false, &w);
2164 w->setStatusTip (ui.actionRefreshContent->statusTip ());
2165 passwordToolBar->setObjectName ("passwordToolBar");
2166 addToolBar (Qt::RightToolBarArea, passwordToolBar);
2168 attributeToolBar = new QToolBar (tr ("Attribute Toolbar"), this);
2169 attributeToolBar->addAction (QIcon (":icons/matt-icons_document-add.svg"), tr ("New Attribute"), this, SLOT (slotNewAttribute()));
2170 fixupToolBar (attributeToolBar, AttributeToolBarNew, false, false, &w);
2171 w->setStatusTip (ui.actionNewAttribute->statusTip ());
2172 attributeToolBar->addAction (QIcon (":icons/rodentia-icons_user-trash-full.svg"), tr ("Delete Attribute"), this, SLOT (slotDeleteAttribute()));
2173 fixupToolBar (attributeToolBar, AttributeToolBarDelete, false, false, &w);
2174 w->setStatusTip (ui.actionDeleteAttribute->statusTip ());
2175 attributeToolBar->addSeparator ();
2176 attributeToolBar->addAction (QIcon (":icons/rodentia-icons_ok.svg"), tr ("Update"), this, SLOT (slotSaveContent()));
2177 fixupToolBar (attributeToolBar, AttributeToolBarOk, false, false, &w);
2178 w->setStatusTip (ui.actionUpdateAttribute->statusTip ());
2179 attributeToolBar->addAction (QIcon (":icons/matt-icons_view-refresh.svg"), tr ("Refresh List"), this, SLOT (slotRefreshAttributes()));
2180 fixupToolBar (attributeToolBar, AttributeToolBarRefresh, false, false, &w);
2181 w->setStatusTip (ui.actionRefreshAttributes->statusTip ());
2182 attributeToolBar->setObjectName ("attributeToolBar");
2183 addToolBar (Qt::RightToolBarArea, attributeToolBar);
2185 commandToolBar = new QToolBar (tr ("Command Toolbar"), this);
2186 commandToolBar->addAction (QIcon (":icons/rodentia-icons_media-floppy.svg"), tr ("Save Result"), this, SLOT (slotSaveCommandResult ()));
2187 fixupToolBar (commandToolBar, CommandToolBarSave, false, false, &w);
2188 w->setStatusTip (tr ("Save command result to file."));
2189 commandToolBar->addAction (QIcon (":icons/nicubunu-Broom.svg"), tr ("Clear Results"), this, SLOT (slotClearCommand ()));
2190 fixupToolBar (commandToolBar, CommandToolBarClear, false, false, &w);
2191 w->setStatusTip (tr ("Reset command fields."));
2192 commandToolBar->addSeparator ();
2193 commandToolBar->addAction (QIcon (":icons/rodentia-icons_ok.svg"), tr ("Execute"), this, SLOT (slotExecuteCommand ()));
2194 fixupToolBar (commandToolBar, CommandToolBarOk, false, false, &w);
2195 w->setStatusTip (tr ("Send command to the server."));
2196 commandToolBar->addAction (QIcon (":icons/matt-icons_view-refresh.svg"), tr ("Refresh Elements"), this, SLOT (slotRefreshElementTree ()));
2197 fixupToolBar (commandToolBar, CommandToolBarRefresh, false, false, &w);
2198 w->setStatusTip (tr ("Refresh the element tree."));
2199 commandToolBar->setObjectName ("commandToolBar");
2200 addToolBar (Qt::RightToolBarArea, commandToolBar);
2201 commandToolBar->addSeparator ();
2202 commandToolBar->addAction (QIcon (":icons/rodentia-icons_dialog-question.svg"), tr ("Command Help"), this, SLOT (slotCommandHelp ()));
2203 fixupToolBar (commandToolBar, CommandToolBarHelp, false, false, &w);
2204 w->setStatusTip (tr ("List available commands and their syntax."));
2205 ui.tb_commandInquireFilename->setIcon (QIcon (":icons/matt-icons_document-add.svg"));
2208 bool
2209 PwmdMainWindow::confirmQuit ()
2211 QSettings cfg ("qpwmc");
2213 if (cfg.value ("confirmQuit", "true").toBool())
2215 QMessageBox msgBox;
2217 msgBox.setText (tr ("Do you really want to quit?"));
2218 msgBox.setStandardButtons (QMessageBox::Ok | QMessageBox::Cancel);
2219 msgBox.setDefaultButton (QMessageBox::Cancel);
2220 int ret = msgBox.exec ();
2221 if (ret == QMessageBox::Cancel)
2222 return false;
2225 return true;
2228 void
2229 PwmdMainWindow::closeEvent (QCloseEvent *ev)
2231 bool b;
2233 if (!doFinished (b, true))
2234 ev->ignore ();
2235 else if (!confirmQuit ())
2236 ev->ignore ();
2237 else
2239 if (clipboardTimer->isActive ())
2241 clipboardTimer->stop ();
2242 slotClearClipboard ();
2245 ev->accept ();
2249 void
2250 PwmdMainWindow::tabify (QPlainTextEdit * w)
2252 QString s = w->toPlainText ();
2253 int pos = w->textCursor ().position ();
2254 int n = s.count ('\t');
2256 if (!n)
2257 return;
2259 s.replace ("\t", "<TAB>", Qt::CaseInsensitive);
2260 w->document ()->setPlainText (s);
2261 QTextCursor t = w->textCursor ();
2262 pos += n * 4;
2263 t.setPosition (pos, QTextCursor::MoveAnchor);
2264 w->setTextCursor (t);
2267 void
2268 PwmdMainWindow::slotGeneralOptionsDone (int n)
2270 QEvent *ev = new QEvent (QEvent::Type (PwmdEventOptionsClose));
2271 QCoreApplication::postEvent (this, ev);
2273 if (!n)
2274 return;
2276 ev = new QEvent (QEvent::Type (PwmdEventReloadConfig));
2277 QCoreApplication::postEvent (this, ev);
2280 void
2281 PwmdMainWindow::slotGeneralOptions ()
2283 if (!optionsDialog)
2285 optionsDialog = new PwmdOptionsDialog (this);
2286 connect (optionsDialog, SIGNAL (finished (int)), this, SLOT (slotGeneralOptionsDone (int)));
2289 optionsDialog->show ();
2292 void
2293 PwmdMainWindow::slotClientListDone (int)
2295 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdClientState, "OPTION",
2296 Pwmd::inquireCallback,
2297 new PwmdInquireData ("CLIENT-STATE=0")));
2298 QEvent *ev = new QEvent (QEvent::Type (PwmdEventClientListClose));
2299 QCoreApplication::postEvent (this, ev);
2302 void
2303 PwmdMainWindow::slotClientList ()
2305 if (!clientDialog)
2307 clientDialog = new PwmdClientDialog (pwm, invokingUser, connectedUser,
2308 this);
2309 connect (clientDialog, SIGNAL (finished (int)), this, SLOT (slotClientListDone (int)));
2312 clientDialog->show ();
2315 void
2316 PwmdMainWindow::readConfigSettings (QString &lastFile, QString &sock, bool init)
2318 QSettings cfg ("qpwmc");
2320 sortElementTree = cfg.value ("sortElementTree", true).toBool ();
2321 cacheContent = cfg.value ("cacheContent", true).toBool ();
2322 cacheContentToggled ();
2323 clipboardTimeout = cfg.value ("clipboardTimeout", 20).toUInt();
2325 int n = cfg.value ("lockTimeout", 5).toInt ();
2326 if (!init && lockTimeout != n && pwm && pwm->state () >= Pwmd::Connected)
2328 lockTimeout = n;
2329 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdLockTimeout, "OPTION",
2330 Pwmd::inquireCallback,
2331 new PwmdInquireData (QString ("LOCK-TIMEOUT=%1").arg (lockTimeout * 10), pwm->handle ())));
2334 lockTimeout = n;
2335 n = cfg.value ("releaseTimeout", 0).toInt ();
2336 origReleaseTimeout = releaseTimeout = n;
2337 if (!init && releaseTimeout != n && pwm && pwm->state () >= Pwmd::Connected)
2338 releaseTimer->start(releaseTimeout*1000);
2340 updateOnEnter = cfg.value ("updateOnEnter", false).toBool ();
2341 incrementalSearch = cfg.value ("incrementalSearch", true).toBool ();
2343 targetColor = cfg.value ("targetColor", "DarkOrange").value < QColor > ();
2344 permissionsColor = cfg.value ("permissionsColor", "DarkRed").value < QColor > ();
2345 invalidTargetColor = cfg.value ("invalidTargetColor", "Red").value < QColor > ();
2346 hilightColor = cfg.value ("hilightColor", "Green").value < QColor > ();
2347 targetLoopColor = cfg.value ("targetLoopColor", "DarkCyan").value < QColor > ();
2348 targetColorBg = cfg.value ("targetColorBg", "DarkGray").value < QColor > ();
2349 permissionsColorBg = cfg.value ("permissionsColorBg", "DarkGray").value < QColor > ();
2350 invalidTargetColorBg = cfg.value ("invalidTargetColorBg", "DarkGray").value < QColor > ();
2351 hilightColorBg = cfg.value ("hilightColorBg", "DarkGray").value < QColor > ();
2352 targetLoopColorBg = cfg.value ("targetLoopColorBg", "DarkGray").value < QColor > ();
2354 if (init && cfg.value ("rememberGeometry", true).toBool ())
2356 restoreGeometry (cfg.value ("geometry").toByteArray ());
2357 ui.tabSplitter->restoreState (cfg.value ("tabGeometry").toByteArray ());
2358 ui.attributeSplitter->restoreState (cfg.value ("attributeGeometry").toByteArray ());
2359 ui.commandSplitter->restoreState (cfg.value ("commandGeometry").toByteArray());
2362 if (cfg.value ("reopenLastFile").toBool())
2364 lastFile = cfg.value ("lastOpenedFile", "").toString ();
2365 sock = cfg.value ("lastSocket", "").toString ();
2367 else
2369 lastFile = QString ();
2370 sock = QString ();
2373 bool b = cfg.value ("executeOnEnter").toBool ();
2374 disconnect (ui.commandTextEdit, SIGNAL (updateContent ()), this,
2375 SLOT (slotExecuteCommand ()));
2376 ui.commandTextEdit->setConfirmOnEnter (b);
2377 if (b)
2378 connect (ui.commandTextEdit, SIGNAL (updateContent ()), this,
2379 SLOT (slotExecuteCommand ()));
2381 b = cfg.value ("updateOnEnter").toBool ();
2382 disconnect (ui.attributeContent, SIGNAL (updateContent ()), this,
2383 SLOT (slotSaveContent ()));
2384 /* Always enabled for attributes since the list is newline separated. But not
2385 * actually updated unless the content is. */
2386 ui.attributeContent->setConfirmOnEnter (true);
2387 disconnect (ui.textEdit, SIGNAL (updateContent ()), this,
2388 SLOT (slotSaveContent ()));
2389 ui.textEdit->setConfirmOnEnter (b);
2390 if (b)
2392 connect (ui.attributeContent, SIGNAL (updateContent ()), this,
2393 SLOT (slotSaveContent ()));
2394 connect (ui.textEdit, SIGNAL (updateContent ()), this,
2395 SLOT (slotSaveContent ()));
2398 if (init)
2400 ui.actionButtonLabels->setChecked (cfg.value ("buttonLabels", false).toBool ());
2401 setupToolBarLabels (ui.actionButtonLabels->isChecked ());
2402 restoreState (cfg.value ("windowState").toByteArray ());
2406 void
2407 PwmdMainWindow::slotSocketOptions ()
2409 PwmdSocketDialog *w = new PwmdSocketDialog (_socket, this);
2410 bool needToUpdate = needsUpdate ();
2411 bool reconn = false;
2412 bool no = false;
2413 w->setParent (this, Qt::Dialog);
2414 int ret = w->exec ();
2415 QString s = ret ? w->socket () : QString ();
2417 /* Updates the settings database file. */
2418 delete w;
2420 if (ret)
2422 PwmdRemoteHost old, newHost;
2423 bool a = PwmdRemoteHost::fillRemoteHost (_socket, old);
2424 bool b = PwmdRemoteHost::fillRemoteHost (s, newHost);
2425 bool remote = false;
2427 if (a)
2428 old = currentHostData;
2430 if ((a != b && b) || (a && b && old != newHost))
2431 reconn = remote = true;
2432 else if (_socket != s)
2433 reconn = true;
2435 if (reconn)
2437 if (!doFinished (no, true))
2438 return;
2440 if (remote)
2442 pwm->setSocket (PwmdRemoteHost::socketUrl (newHost));
2443 pwm->setConnectParameters (newHost.socketArgs ());
2444 _socket = newHost.name ();
2445 currentHostData = newHost;
2447 else
2449 pwm->setSocket (s);
2450 _socket = s;
2454 else
2455 return;
2457 /* If the socket changed in the dialog and the document has changes then
2458 * slotCommandResult will connect to the new socket after the changes have
2459 * been committed. Otherwise, there are no document changes and we can
2460 * connect here. */
2461 if ((reconn || pwm->state () == Pwmd::Init) && (!needToUpdate || no))
2463 pwm->reset ();
2464 reconnectSocket = true;
2465 QTimer::singleShot (1, this, SLOT (slotConnectSocket()));
2467 else if (reconn && needToUpdate)
2468 reconnectSocket = true;
2471 QString
2472 PwmdMainWindow::socket ()
2474 return _socket;
2477 QString
2478 PwmdMainWindow::filename ()
2480 return _filename;
2483 gpg_error_t
2484 PwmdMainWindow::badPassphraseCallback (void *data, bool isFinal)
2486 PwmdMainWindow *d = static_cast <PwmdMainWindow *>(data);
2487 pwmd_socket_t type;
2488 gpg_error_t rc = GPG_ERR_BAD_PASSPHRASE;
2490 if (isFinal)
2491 return rc; // ignored
2493 rc = pwmd_socket_type (d->pwm->handle (), &type);
2494 if (rc)
2495 return rc;
2497 /* Already handled by libpwmd. */
2498 if (type != PWMD_SOCKET_LOCAL)
2499 return GPG_ERR_BAD_PASSPHRASE;
2501 if (++d->pinentry_try == 3)
2502 return GPG_ERR_BAD_PASSPHRASE;
2504 return 0;
2507 void
2508 PwmdMainWindow::clearElementTree (QTreeWidget *tree)
2510 foreach (QTreeWidgetItem * item, tree->findItems ("*",
2511 Qt::MatchRecursive |
2512 Qt::MatchWildcard))
2514 PwmdTreeWidgetItemData *data = qvariant_cast<PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
2515 delete data;
2518 tree->clear ();
2521 void
2522 PwmdMainWindow::slotToggleHiddenContent (bool b)
2524 if (doSaveContent (true))
2526 disconnect (ui.actionHidden, SIGNAL (toggled(bool)), this, SLOT (slotToggleHiddenContent(bool)));
2527 ui.actionHidden->setChecked (!b);
2528 connect (ui.actionHidden, SIGNAL (toggled(bool)), this, SLOT (slotToggleHiddenContent(bool)));
2529 hiddenContent = b;
2530 pwm->runQueue ();
2531 return;
2534 hiddenContent = -1;
2536 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
2538 PwmdTreeWidgetItemData *data = qvariant_cast<PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
2540 if (b && !data->hasAttribute ("hidden"))
2541 setNewAttribute (item, "hidden", QString (), false, true);
2542 else if (!b && data->hasAttribute ("hidden"))
2543 deleteAttribute (item, "hidden", true);
2546 pwm->runQueue ();
2549 void
2550 PwmdMainWindow::slotSaveDocument ()
2552 doSaveContent (false, true, newFile);
2555 bool
2556 PwmdMainWindow::needsUpdate ()
2558 return fixupToolBar (passwordToolBar, PasswordToolBarOk, false, true)
2559 || fixupToolBar (contentToolBar, ContentToolBarOk, false, true)
2560 || fixupToolBar (attributeToolBar, AttributeToolBarOk, false, true);
2563 void
2564 PwmdMainWindow::saveDocument (bool extended)
2566 PwmdSaveDialog *d = new PwmdSaveDialog (pwm, newFile);
2568 d->setParent (this, Qt::Dialog);
2570 if (extended)
2572 #ifdef Q_OS_ANDROID
2573 d->showMaximized();
2574 #endif
2575 d->exec ();
2577 else
2579 PwmdSaveWidget *w = d->widget ();
2580 w->save (0, true);
2583 delete d;
2586 void
2587 PwmdMainWindow::slotSaveExtended ()
2589 doSaveContent (true, true, true);
2592 void
2593 PwmdMainWindow::savePasswordContentFinalize (QTreeWidgetItem *item,
2594 const QString &type)
2596 fixupToolBar (passwordToolBar, PasswordToolBarOk, false);
2597 setAttribute (item, "password", type, true);
2598 setModifiedContent (false);
2600 if (ui.tabWidget->currentWidget () == ui.contentTab)
2601 setContentItemsEnabled ();
2604 void
2605 PwmdMainWindow::updatePasswordAttribute (QTreeWidgetItem *item, bool queue)
2607 QString type = ui.passwordWidget->type ();
2609 PwmdInquireData *inq = new PwmdInquireData (QString ("SET password %1 %2").arg (elementPath (item), type));
2610 inq->setUser (type);
2611 inq->setUserPtr (item);
2612 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdPasswordContent,
2613 "ATTR",
2614 Pwmd::inquireCallback, inq), queue);
2617 void
2618 PwmdMainWindow::fixupNextTabOrElement ()
2620 if (findElementAction->isVisible ())
2622 QEvent *ev = new QKeyEvent (QEvent::KeyPress, 'F', Qt::ControlModifier);
2623 QCoreApplication::postEvent (this, ev);
2624 return;
2627 if (nextElement)
2628 ui.elementTree->setCurrentItem (nextElement);
2629 else if (nextTabWidget)
2630 ui.tabWidget->setCurrentWidget (nextTabWidget);
2632 QTreeWidgetItem *cur = firstSelectedItem ();
2633 if ((hasTarget (oldSelectedItem, true) || hasTarget (cur, true))
2634 && resolveTargets (oldSelectedItem) == resolveTargets (cur))
2635 elementSelected (cur, false, oldSelectedItem);
2637 nextTabWidget = nullptr;
2638 nextElement = nullptr;
2640 if (editTargetAttributes)
2642 disconnect (ui.actionToggleTargetAttribute, SIGNAL (triggered (bool)), this, SLOT (slotEditTargetAttributes(bool)));
2643 ui.actionToggleTargetAttribute->setChecked (true);
2644 connect (ui.actionToggleTargetAttribute, SIGNAL (triggered (bool)), this, SLOT (slotEditTargetAttributes(bool)));
2645 editAttributes (editTargetAttributes, selectedElement);
2646 return;
2648 else if (targetAttributeElement)
2650 elementSelected (selectedElement);
2651 targetAttributeElement = nullptr;
2652 return;
2654 else if (newAttribute)
2656 delete newAttribute;
2657 newAttribute = nullptr;
2658 slotNewAttribute ();
2659 return;
2661 else if (hiddenContent != -1)
2663 slotToggleHiddenContent (hiddenContent);
2664 return;
2667 switch (dndOperation)
2669 case DndCopy:
2670 slotDndCopyElement ();
2671 break;
2672 case DndMove:
2673 slotDndMoveElement ();
2674 break;
2675 case DndTarget:
2676 slotDndCreateTarget ();
2677 break;
2678 default:
2679 break;
2683 void
2684 PwmdMainWindow::saveContentFinalize (QTreeWidgetItem *item,
2685 const QString &content, bool password)
2687 if (cacheContent)
2688 updateContentPointer (item, new PwmdElementContent (content.toUtf8 ()));
2690 if (password)
2691 return;
2693 QTreeWidgetItem *r = resolveTargets (item);
2694 if (!r || r == item)
2695 setTimeLabels (item, true);
2696 else if (r)
2697 setTimeLabels (r, true);
2699 setModifiedContent (false);
2700 fixupToolBar (contentToolBar, ContentToolBarOk, false);
2701 setContent (content, item);
2702 fixupNextTabOrElement ();
2705 void
2706 PwmdMainWindow::saveAttrContentFinalize (const QString &path,
2707 AttributeFinalizeT *attr)
2709 QString value = attr->content.replace ("<TAB>", "\t", Qt::CaseInsensitive);
2711 if (attr->name == "password")
2712 setContent (attr->password, attr->item);
2714 QTreeWidgetItem *item = !editTargetAttributes ? findElement (path) : attr->item;
2715 setAttribute (item, attr->name, value, true);
2716 setModifiedContent (false);
2718 if (attr->name == "_target" || (attr->name == "_acl" && !isElementOwner (item)))
2720 targetAttributeElement = nullptr;
2721 QEvent *ev = new QEvent (QEvent::Type (PwmdEventRefreshElementTree));
2722 QCoreApplication::postEvent (this, ev);
2723 return;
2726 fixupNextTabOrElement ();
2729 void
2730 PwmdMainWindow::setAttributeContentOnce (QTreeWidgetItem *item,
2731 const QString &path,
2732 const QString &value,
2733 bool save, bool extended, bool queue)
2735 PwmdInquireData *inq = new PwmdInquireData (QString ("SET %1 %2 %3").arg (selectedAttribute->text (), path, value));
2736 AttributeFinalizeT *attr = new AttributeFinalizeT;
2737 attr->item = item;
2738 attr->content = value;
2739 attr->name = selectedAttribute->text ();
2740 attr->password = ui.passwordWidget->password ();
2741 inq->setUser (path);
2742 inq->setUserPtr (attr);
2743 if (save)
2744 inq->setSaving (extended);
2746 PwmdCommandQueueItem *cmd = new PwmdCommandQueueItem (PwmdCmdIdAttrContent, "ATTR", Pwmd::inquireCallback, inq);
2747 ignoreErrors (cmd);
2748 pwm->command (cmd, queue
2749 /* Fixes editing attribute content then selecting
2750 * another attribute before committing. */
2751 && selectedAttribute == ui.attributeList->currentItem ());
2754 bool
2755 PwmdMainWindow::doSaveContent (bool queue, bool save, bool extended,
2756 int *which, bool force)
2758 if (!needsUpdate ())
2760 if (save)
2762 saveDocument (extended);
2763 return false;
2766 if (!force)
2767 return false;
2770 QString path;
2771 QTreeWidgetItem *item = nullptr;
2772 QString content = QString ();
2773 bool passwordMod = fixupToolBar (passwordToolBar, PasswordToolBarOk, false, true);
2774 bool contentMod = ui.passwordFrame->isHidden() && fixupToolBar (contentToolBar, ContentToolBarOk, false, true);
2775 int base64 = 0;
2777 if (passwordMod || contentMod)
2778 item = selectedElement;
2779 else if (ui.tabWidget->currentWidget () == ui.attributeTab)
2780 item = attributeElement;
2781 else if (force)
2783 contentMod = ui.passwordFrame->isHidden ();
2784 passwordMod = !contentMod;
2785 item = selectedElement;
2788 if (!item)
2789 return false;
2791 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
2792 path = elementPath (item);
2794 if (contentMod || passwordMod)
2796 QByteArray b;
2798 path.append ("\t");
2800 if (passwordMod)
2802 b = QByteArray (ui.passwordWidget->password ().toUtf8 ());
2803 updatePasswordAttribute (item, queue);
2805 else
2806 b = QByteArray (ui.textEdit->toPlainText ().toUtf8 ());
2808 if (ui.actionBase64->isChecked ())
2810 content = toBase64 (item, b);
2811 base64 = 1;
2813 else if (!ui.actionBase64->isChecked ())
2815 if (data->attribute ("encoding") == "base64")
2817 content = QByteArray::fromBase64 (b);
2818 base64 = 2;
2820 else
2821 content = b;
2823 else
2824 content = b;
2826 if (base64 == 1)
2827 setNewAttribute (item, "encoding", "base64", false, true);
2828 else if (base64 == 2)
2829 deleteAttribute (item, "encoding", true);
2831 if (which)
2832 *which = passwordMod ? Password : Content;
2834 path.append (content);
2835 PwmdInquireData *inq = new PwmdInquireData (path);
2836 inq->setHandle (pwm->handle ());
2837 inq->setUserBool (passwordMod && !queue);
2838 inq->setUser (content);
2839 inq->setUserPtr (item);
2841 if (save)
2842 inq->setSaving (extended);
2844 PwmdCommandQueueItem *cmd = new PwmdCommandQueueItem (PwmdCmdIdStoreContent, "STORE",
2845 Pwmd::inquireCallback, inq);
2847 /* Normally a STORE may be included in a BULK command but we can't do
2848 * that here because of newline characters being parsed by libassuan. */
2849 if (base64 == 1)
2850 cmd->setNeedsInquire ();
2852 pwm->command (cmd, queue);
2854 else if (fixupToolBar (attributeToolBar, AttributeToolBarOk, false, true))
2856 if (!selectedAttribute
2857 || !(selectedAttribute->flags () & Qt::ItemIsEnabled))
2859 setModifiedContent (false);
2860 return false;
2863 if (which)
2864 *which = Attributes;
2866 QString value = ui.attributeContent->toPlainText ().replace ("<TAB>", "\t", Qt::CaseInsensitive);
2868 foreach (QTreeWidgetItem *cur, ui.elementTree->selectedItems ())
2870 if (selectedAttribute->text () == "_target" || !editTargetAttributes)
2871 path = elementPath (cur);
2873 setAttributeContentOnce (cur, path, value, save, extended, queue);
2874 if (searchAndReplace)
2876 searchAndReplace = false;
2877 break;
2882 return true;
2885 void
2886 PwmdMainWindow::slotSaveContent ()
2888 doSaveContent (true);
2889 pwm->runQueue ();
2892 void
2893 PwmdMainWindow::slotCopyContentToClipboard ()
2895 PwmdTreeWidgetItemData *data = qvariant_cast <PwmdTreeWidgetItemData *>(selectedElement->data (0, Qt::UserRole));
2897 if (data->hasAttribute ("password"))
2899 ui.passwordWidget->clipboardPassword ();
2900 return;
2903 setClipboard (ui.textEdit->toPlainText ().toUtf8 ());
2904 slotClipboardTimer ();
2907 void
2908 PwmdMainWindow::slotSaveToFile ()
2910 PwmdFileDialog d (this, tr ("Save to file"));
2912 d.setAcceptMode (QFileDialog::AcceptSave);
2913 d.setFileMode (QFileDialog::AnyFile);
2915 if (!d.exec () || d.selectedFiles ().isEmpty ())
2916 return;
2918 QStringList f = d.selectedFiles ();
2919 QFile file (f.at (0));
2921 if (!file.open (QFile::WriteOnly))
2923 QMessageBox m;
2925 m.setText (file.fileName ());
2926 m.setInformativeText (file.errorString ());
2927 m.setIcon (QMessageBox::Warning);
2928 m.exec ();
2929 return;
2932 QByteArray data;
2933 PwmdTreeWidgetItemData *idata = qvariant_cast <PwmdTreeWidgetItemData *>(selectedElement->data (0, Qt::UserRole));
2935 if (idata->hasAttribute ("password"))
2937 data.append (ui.passwordWidget->password ().toUtf8 ());
2939 else
2941 if (ui.actionBase64->isChecked () && idata->attribute ("encoding") == "base64")
2942 data = QByteArray::fromBase64 (ui.textEdit->toPlainText ().toUtf8 ());
2943 else
2944 data.append (ui.textEdit->toPlainText ().toUtf8 ());
2947 if (file.write (data) == -1)
2949 QMessageBox m;
2951 m.setText (file.fileName ());
2952 m.setInformativeText (file.errorString ());
2953 m.setIcon (QMessageBox::Warning);
2954 m.exec ();
2958 void
2959 PwmdMainWindow::slotRefreshContent ()
2961 elementSelected (selectedElement, true);
2964 void
2965 PwmdMainWindow::cacheContentToggled ()
2967 if (cacheContent)
2968 return;
2970 foreach (QTreeWidgetItem * item, ui.elementTree->findItems ("*",
2971 Qt::
2972 MatchRecursive |
2973 Qt::
2974 MatchWildcard))
2976 PwmdTreeWidgetItemData *data = qvariant_cast <PwmdTreeWidgetItemData *>(item->data (0, Qt::UserRole));
2977 data->setContent (nullptr);
2981 QString
2982 PwmdMainWindow::toBase64 (QTreeWidgetItem *item, QByteArray &b)
2984 QString content;
2985 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
2987 // Don't re-encode the content.
2988 if (data->attribute ("encoding") == "base64")
2990 return b;
2993 content = b.toBase64 ();
2995 // Do line breaks at 64 characters (OpenSSL style). It is also much
2996 // faster when setting the content for ui.textEdit.
2997 for (int c = 0, n = 0, t = content.length (); n < t; c++, n++)
2999 if (c == 64)
3001 content.insert (n, "\n");
3002 c = -1;
3003 t = content.length ();
3007 if (content.at (content.size ()) != '\n')
3008 content.append ("\n");
3010 return content;
3013 void
3014 PwmdMainWindow::slotCreatePasswordAttribute ()
3016 doSaveContent (true);
3017 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
3019 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
3020 if (!data->hasAttribute ("password")
3021 && isElementOwner (resolveTargets (item)))
3023 setNewAttribute (item, "password", 0, true, true);
3027 pwm->runQueue ();
3030 void
3031 PwmdMainWindow::slotInsertContent ()
3033 bool base64 = false;
3034 PwmdFileDialog d (this, tr ("Insert file"));
3036 d.setFileMode (QFileDialog::ExistingFile);
3037 d.setOption (QFileDialog::ReadOnly);
3039 if (!d.exec () || !d.selectedFiles ().count ())
3040 return;
3042 QFile file (d.selectedFiles ().at (0));
3043 if (!file.open (QIODevice::ReadOnly))
3045 QMessageBox m;
3047 m.setText (file.fileName ());
3048 m.setInformativeText (file.errorString ());
3049 m.setIcon (QMessageBox::Warning);
3050 m.exec ();
3051 return;
3054 QByteArray data = file.readAll ();
3055 file.close ();
3056 for (int i = 0, t = data.size (); i < t; i++)
3058 QChar c = data.at (i);
3060 if ((c.category () == QChar::Other_Control && c.toLatin1 () != '\n')
3061 || c.isNull ())
3063 base64 = true;
3064 break;
3068 PwmdTreeWidgetItemData *idata = qvariant_cast < PwmdTreeWidgetItemData * >(selectedElement->data (0, Qt::UserRole));
3070 if (idata->hasAttribute ("password"))
3072 if (base64)
3074 QMessageBox m;
3076 m.setText (QApplication::
3077 tr ("Binary data in file."));
3078 m.setInformativeText (tr ("Cannot use binary data as a passphrase. Please use only plain text when setting the passphrase from a file."));
3079 m.setIcon (QMessageBox::Critical);
3080 m.exec ();
3081 return;
3084 ui.passwordWidget->setPassword (data);
3085 return;
3088 QMessageBox m (this);
3089 m.setIcon (QMessageBox::Question);
3090 m.setStandardButtons (QMessageBox::Apply|QMessageBox::Discard|QMessageBox::Cancel);
3091 QAbstractButton *pb = m.button (QMessageBox::Apply);
3092 pb->setText (tr ("Append"));
3093 pb = m.button (QMessageBox::Discard);
3094 pb->setText (tr ("Overwrite"));
3095 m.setDefaultButton (QMessageBox::Cancel);
3096 m.setEscapeButton (QMessageBox::Cancel);
3097 m.setText (tr ("Would you like to append the read file to the existing content, or overwrite existing content?"));
3099 int r = m.exec ();
3100 if (r == QMessageBox::Cancel)
3101 return;
3103 QString content = QString ();
3104 if (r == QMessageBox::Apply)
3105 content = ui.textEdit->document ()->toPlainText ();
3107 if (base64)
3109 removeAttribute (selectedElement, "encoding");
3110 content += toBase64 (selectedElement, data);
3111 setAttribute (selectedElement, "encoding", "base64");
3113 else
3114 content += data;
3116 ui.textEdit->document ()->setPlainText (content);
3117 ui.actionBase64->setChecked (base64);
3118 if (base64)
3119 slotToggleBase64Content (true);
3122 void
3123 PwmdMainWindow::slotConnectionStateChanged (Pwmd::ConnectionState s)
3125 Pwmd::ConnectionState old = state;
3126 state = s;
3127 pinentry_try = 0;
3128 reconnectSocket = false;
3129 disconnectSocket = false;
3131 /* Fixes a race condition when Pwmd::connect() fails. The handle may have
3132 * been reset (which clears the error code) before the dialog is shown.
3134 pwm->tlsError = pwmd_gnutls_error(pwm->handle(), nullptr);
3136 switch (s)
3138 case Pwmd::Init:
3139 setConnected (false);
3140 break;
3141 case Pwmd::Connecting:
3142 updateConnectTimer ();
3143 break;
3144 case Pwmd::Connected:
3145 updateConnectTimer (true);
3146 setConnected (true, old);
3147 break;
3148 case Pwmd::Opened:
3149 setOpen (true);
3150 break;
3154 void
3155 PwmdMainWindow::slotDisconnectSocket ()
3157 bool b = false;
3159 if (!doFinished (b, true) && !b)
3160 return;
3162 if (b || (!needsUpdate () && !isWindowModified ()))
3163 pwm->reset ();
3164 else
3165 disconnectSocket = true;
3168 void
3169 PwmdMainWindow::slotKnownHostCallback (void *data, const char *host,
3170 const char *key, size_t len)
3172 gpg_error_t rc = Pwmd::knownHostPrompt (data, host, key, len);
3173 emit knownHostRc (rc);
3176 void
3177 PwmdMainWindow::updateSocketOptions ()
3179 pwmd_setopt (pwm->handle (), PWMD_OPTION_SSH_AGENT,
3180 currentHostData.sshAgent ());
3181 pwmd_setopt (pwm->handle (), PWMD_OPTION_SOCKET_TIMEOUT,
3182 pwm->state() == Pwmd::Init ? currentHostData.connectTimeout ()
3183 : currentHostData.socketTimeout ());
3184 pwmd_setopt (pwm->handle (), PWMD_OPTION_TLS_VERIFY,
3185 currentHostData.tlsVerify ());
3186 if (!currentHostData.tlsPriority ().isEmpty ())
3187 pwmd_setopt (pwm->handle (), PWMD_OPTION_TLS_PRIORITY,
3188 currentHostData.tlsPriority ().toUtf8 ().data ());
3191 void
3192 PwmdMainWindow::slotCancel ()
3194 #ifdef Q_OS_ANDROID
3195 androidJni->cancel ();
3196 #endif
3197 pwm->cancel ();
3200 void
3201 PwmdMainWindow::slotUpdateConnectTimer ()
3203 updateConnectTimer (false, pwm->state () != Pwmd::Connecting);
3206 void
3207 PwmdMainWindow::updateConnectTimer (bool reset, bool cancel)
3209 static int remaining = currentHostData.connectTimeout ();
3211 if (reset || cancel)
3213 if (!cancelButton && pwm->state () == Pwmd::Init && !cancel)
3215 cancelButton = new QPushButton (tr ("Cancel"));
3216 connect (cancelButton, SIGNAL (clicked ()), this,
3217 SLOT (slotCancel ()));
3218 statusBar ()->addPermanentWidget (cancelButton);
3220 else if (cancelButton && pwm->state () == Pwmd::Connected)
3222 delete cancelButton;
3223 cancelButton = nullptr;
3226 remaining = currentHostData.connectTimeout ();
3227 progressBar->setHidden (true);
3228 progressBar->reset ();
3229 progressBar->setRange (0, currentHostData.connectTimeout ());
3230 progressBar->setValue (0);
3231 return;
3234 progressBar->setHidden (false);
3235 progressBar->setTextVisible (true);
3236 progressBar->setMaximum (currentHostData.connectTimeout ());
3237 progressBar->setFormat (QString (tr ("Connecting: timeout in %1s...")).arg (remaining));
3238 progressBar->setValue (remaining--);
3239 QTimer::singleShot (1000, this, SLOT (slotUpdateConnectTimer ()));
3242 #ifdef Q_OS_ANDROID
3243 void
3244 PwmdMainWindow::slotAndroidAuthenticated ()
3246 androidAuthenticating = false;
3247 centralWidget ()->setHidden (false);
3250 /* The state gets messed up when verifying the key guard so we need to keep the
3251 * state of the previous call. */
3252 void
3253 PwmdMainWindow::slotApplicationStateChanged (Qt::ApplicationState s)
3255 static bool prev;
3256 QSettings cfg ("qpwmc");
3258 if (!cfg.value ("requireUnlock", true).toBool())
3259 return;
3261 if (androidAuthenticating)
3262 return;
3264 if (!prev && (s & Qt::ApplicationActive))
3266 androidAuthenticating = true;
3267 if (androidJni->authenticate ())
3268 centralWidget ()->setHidden (true);
3269 else
3270 androidAuthenticating = false;
3273 prev = !!s;
3276 void
3277 PwmdMainWindow::slotAndroidConnectReady (PwmdRemoteHost hostData)
3279 pwmd_setopt(pwm->handle(), PWMD_OPTION_READ_CB_DATA, androidJni);
3280 pwmd_setopt(pwm->handle(), PWMD_OPTION_WRITE_CB_DATA, androidJni);
3281 androidJni->setTimeout (hostData.connectTimeout ());
3282 androidJni->connectSocket (hostData);
3285 void
3286 PwmdMainWindow::slotAndroidConnectResult (PwmdRemoteHost hostData, int fd)
3288 if (hostData.type () != PWMD_SOCKET_SSH)
3290 pwmd_setopt (pwm->handle (), PWMD_OPTION_READ_CB, PwmdAndroidJNI::read);
3291 pwmd_setopt (pwm->handle (), PWMD_OPTION_WRITE_CB, PwmdAndroidJNI::write);
3294 androidJni->setTimeout (hostData.socketTimeout ());
3295 pwm->connectFd (fd);
3298 void
3299 PwmdMainWindow::slotAndroidConnectionError (gpg_error_t rc)
3301 if (!rc)
3302 return;
3304 pwm->cancel ();
3305 showError (rc);
3306 return;
3308 #endif
3310 void
3311 PwmdMainWindow::slotConnectSocket ()
3313 if (reconnectSocket)
3314 _filename = "";
3316 ui.actionDisconnect->disconnect (this);
3317 connect (ui.actionDisconnect, SIGNAL (triggered()), this,
3318 SLOT (slotCancel ()));
3319 ui.actionDisconnect->setStatusTip (tr ("Disconnect from the pwmd server."));
3320 ui.actionDisconnect->setText (tr ("&Disconnect"));
3321 updateConnectTimer (true);
3322 pwm->flushQueue ();
3323 updateSocketOptions ();
3324 pwm->setFilename (filename ());
3326 #ifdef Q_OS_ANDROID
3327 if (currentHostData.type () == PWMD_SOCKET_USER)
3328 pwm->connectHost (currentHostData);
3329 else
3330 pwm->connect ();
3331 #else
3332 pwm->connect ();
3333 #endif
3336 void
3337 PwmdMainWindow::openFileFinalize (gpg_error_t rc)
3339 /* Will be deleted in Pwmd::PwmdCommandCollectorThread. */
3340 openInquireData = nullptr;
3341 pwmd_setopt (pwm->handle (), PWMD_OPTION_OVERRIDE_INQUIRE, 0);
3342 pwmd_setopt (pwm->handle (), PWMD_OPTION_LOCAL_PINENTRY, 0);
3343 setWindowModified (false);
3345 if (rc)
3347 _filename = tr ("No file");
3348 state = Pwmd::Connected;
3349 setOpen (false);
3350 return;
3353 releaseTimeout = origReleaseTimeout;
3354 if (releaseTimeout)
3355 releaseTimer->start(releaseTimeout*1000);
3357 QTimer::singleShot (1, this, SLOT (slotEditElements ()));
3360 void
3361 PwmdMainWindow::slotDoOpenFile ()
3363 pinentry_try = 0;
3364 newFile = false;
3366 if (openInquireData && !openInquireData->keyFile().isEmpty ())
3368 pwmd_socket_t type;
3370 pwmd_socket_type (pwm->handle (), &type);
3371 pwmd_setopt (pwm->handle (), PWMD_OPTION_OVERRIDE_INQUIRE, 1);
3372 pwmd_setopt (pwm->handle (), PWMD_OPTION_LOCAL_PINENTRY, 1);
3373 pwm->open (Pwmd::inquireCallback, openInquireData);
3375 else
3377 if (openInquireData)
3378 delete openInquireData;
3380 openInquireData = new PwmdInquireData (pwm->handle(), pwm->filename());
3381 pwm->open (Pwmd::inquireCallback, openInquireData);
3385 void
3386 PwmdMainWindow::slotOpenFileDone (int r)
3388 if (r)
3390 _filename = openDialog->filename ();
3391 openInquireData = new PwmdInquireData (pwm->handle (),
3392 openDialog->filename ());
3393 pwm->setFilename (openDialog->filename ());
3394 pwm->setLockOnOpen (openDialog->lock ());
3395 openInquireData->setKeyFile (openDialog->keyFile ());
3398 /* Depending on how the file list was scrolled (scrollbar or click-and-drag),
3399 * determines if there are a bunch of commands in the queue to fetch cache
3400 * status for each item. Since the dialog is now closed, we can remove these
3401 * queued commands entirely. */
3402 pwm->flushQueue ();
3404 QEvent *ev = new QEvent (QEvent::Type (PwmdEventOpenFileClose));
3405 QCoreApplication::postEvent (this, ev);
3407 if (r)
3409 ev = new QEvent (QEvent::Type (PwmdEventOpenFileConfirm));
3410 QCoreApplication::postEvent (this, ev);
3414 void
3415 PwmdMainWindow::slotCloseFile ()
3417 bool b;
3419 if (!doFinished (b, true))
3420 return;
3422 pwm->close ();
3425 void
3426 PwmdMainWindow::slotOpenFile ()
3428 bool b;
3430 if (!doFinished (b, true))
3431 return;
3433 if (!openDialog)
3435 openDialog = new PwmdOpenDialog (pwm, this);
3436 connect (openDialog, SIGNAL (finished (int)), this, SLOT (slotOpenFileDone (int)));
3439 openDialog->show ();
3442 void
3443 PwmdMainWindow::setWindowTitle (bool b)
3445 pwmd_socket_t type;
3446 pwmd_socket_type (pwm->handle (), &type);
3447 QString s;
3449 if (type != PWMD_SOCKET_LOCAL)
3450 s = QString ("QPwmc (%1%2)").arg (type == PWMD_SOCKET_SSH ? currentHostData.sshUsername ()+"@" : "", currentHostData.hostname ());
3451 else
3452 s = "QPwmc";
3454 s.append (QString (": %1[*]").arg (!b ? tr ("No file") : filename ()));
3455 QMainWindow::setWindowTitle (s);
3458 void
3459 PwmdMainWindow::setOpen (bool b)
3461 _filename = pwm->filename ();
3462 setWindowTitle (b);
3463 selectedElement = nullptr;
3464 attributeElement = nullptr;
3465 dropElement = nullptr;
3466 targetAttributeElement = nullptr;
3467 selectedAttribute = nullptr;
3468 ui.elementTree->setEnabled(b);
3469 setContent ("");
3470 clearElementTree (ui.elementTree);
3471 clearAttributeTab ();
3472 ui.actionReload->setEnabled(b);
3473 ui.actionCut->setEnabled (b);
3474 ui.actionCopy->setEnabled (b);
3475 ui.actionPaste->setEnabled (b);
3476 fixupMenu (ui.menuFile, FileMenuSave, b);
3477 fixupMenu (ui.menuFile, FileMenuSaveExt, b);
3478 fixupMenu (ui.menuFile, FileMenuOpenForm, b);
3479 fixupMenu (ui.menuFile, FileMenuClose, b);
3480 ui.actionFileOptions->setEnabled (b);
3481 elementToolBar->setEnabled (b);
3482 setElementItemsEnabled (nullptr, false);
3483 fixupMenu (ui.menuView, ViewMenuExpandAll, b);
3484 fixupMenu (ui.menuView, ViewMenuButtonLabels, b);
3487 void
3488 PwmdMainWindow::invokingUserFinalize (const QString &result)
3490 invokingUser = result.split (",");
3493 void
3494 PwmdMainWindow::currentUserFinalize (const QString &result)
3496 connectedUser = result;
3499 void
3500 PwmdMainWindow::setConnected (bool b, Pwmd::ConnectionState old)
3502 QCommonStyle style;
3503 pwmd_socket_t type;
3505 setWindowTitle (false);
3506 pwmd_socket_type (pwm->handle (), &type);
3507 setWindowModified (false);
3508 setModified (false);
3509 setOpen (false);
3510 selectedElement = nullptr;
3511 attributeElement = nullptr;
3512 targetAttributeElement = nullptr;
3513 ui.actionNew->setEnabled(b);
3514 ui.actionOpen->setEnabled(b);
3515 fileToolBar->setEnabled (b);
3516 ui.actionReload->setEnabled(false);
3517 ui.actionOpenForm->setEnabled(false);
3518 ui.tabWidget->setEnabled(false);
3519 ui.tabWidget->setCurrentIndex (TabContent);
3520 setContentItemsEnabled (true);
3521 clearAttributeTab ();
3522 setContent ("");
3523 pinentry_try = 0;
3524 fixupMenu (ui.menuFile, FileMenuOpen, b);
3526 if (b)
3528 pwmd_setopt (pwm->handle (), PWMD_OPTION_LOCAL_PINENTRY,
3529 type != PWMD_SOCKET_LOCAL);
3530 ui.actionDisconnect->disconnect (this);
3531 connect (ui.actionDisconnect, SIGNAL (triggered()), this,
3532 SLOT (slotDisconnectSocket ()));
3533 ui.actionDisconnect->setStatusTip (tr ("Disconnect from the pwmd server."));
3534 ui.actionDisconnect->setText (tr ("&Disconnect"));
3535 if (type != PWMD_SOCKET_LOCAL)
3536 pwmd_setopt (pwm->handle (), PWMD_OPTION_SOCKET_TIMEOUT,
3537 currentHostData.socketTimeout ());
3539 fixupMenu (ui.menuView, ViewMenuClientList, b);
3540 ui.tabWidget->setEnabled (true);
3541 ui.commandTab->setEnabled (true);
3543 if (old == Pwmd::Connecting)
3545 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdInvokingUser,
3546 "GETCONFIG",
3547 Pwmd::inquireCallback,
3548 new PwmdInquireData ("invoking_user", pwm->handle ())),
3549 true);
3551 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdCurrentUser,
3552 "GETINFO",
3553 Pwmd::inquireCallback,
3554 new PwmdInquireData ("user", pwm->handle ())),
3555 true);
3557 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdLockTimeout,
3558 "OPTION",
3559 Pwmd::inquireCallback,
3560 new PwmdInquireData (QString ("LOCK-TIMEOUT=%1").arg (lockTimeout*10), pwm->handle ())),
3561 true);
3564 /* Prevent looping over this when the previous open failed by testing
3565 * 'old'. */
3566 if (!filename().isEmpty () && old == Pwmd::Connecting)
3568 PwmdInquireData *inq = new PwmdInquireData (pwm->handle(), pwm->filename());
3569 newFile = false;
3570 pwm->open (Pwmd::inquireCallback, inq, true);
3573 pwm->runQueue ();
3574 return;
3576 else
3578 updateConnectTimer (true, true);
3579 delete cancelButton;
3580 cancelButton = nullptr;
3583 ui.actionDisconnect->disconnect (this);
3584 connect (ui.actionDisconnect, SIGNAL (triggered ()), this,
3585 SLOT (slotConnectSocket ()));
3586 ui.actionDisconnect->setStatusTip (tr ("Connect to the configured pwmd server."));
3587 ui.actionDisconnect->setText (tr ("&Connect"));
3588 #ifdef Q_OS_ANDROID
3589 ui.actionDisconnect->setEnabled (!currentHostData.name ().isEmpty ());
3590 #endif
3591 pwm->reset (false);
3592 if (type != PWMD_SOCKET_LOCAL)
3593 pwmd_setopt (pwm->handle (), PWMD_OPTION_SOCKET_TIMEOUT,
3594 currentHostData.connectTimeout ());
3597 void
3598 PwmdMainWindow::setElementStatusTip (QTreeWidgetItem *item)
3600 QStatusBar *sb = statusBar();
3602 if (!item)
3604 sb->showMessage (QString ());
3605 return;
3608 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
3609 if (!data)
3610 return;
3612 if (data->badTarget() && !data->targetLoop())
3613 sb->showMessage (tr("This element contains a target attribute that does not resolve."));
3614 else if (data->targetLoop())
3615 sb->showMessage (tr("This element contains a recursion loop. Use the attribute editor to edit the \"target\"."));
3616 else if (data->badPermissions())
3617 sb->showMessage (tr("This element has permissions that deny access to the current user ID."));
3618 else if (!isElementOwner (resolveTargets (item)))
3619 sb->showMessage (tr ("You cannot modify this element but may add child elements."));
3620 else
3621 sb->showMessage (QString ());
3624 void
3625 PwmdMainWindow::slotElementEntered (QTreeWidgetItem * item, int)
3627 QBrush br;
3629 if (item && item == previousElement)
3630 return;
3632 if (previousElement)
3634 setElementColors (previousElement);
3635 br = previousElement->foreground (0);
3636 br.setColor (previousElementColor);
3637 previousElement->setForeground (0, br);
3638 br.setColor(previousElementColorBg);
3639 previousElement->setBackground(0, br);
3640 setElementColors (previousElement);
3643 if (!item || dndOperation == DndNone)
3645 previousElement = nullptr;
3646 return;
3649 br = item->foreground (0);
3650 previousElementColor = br.color ();
3651 br.setColor (hilightColor);
3652 item->setForeground (0, br);
3654 br = item->background (0);
3655 previousElementColorBg = br.color ();
3656 QBrush bg(QPalette::Window);
3657 bg.setColor(hilightColorBg);
3658 item->setBackground(0, bg);
3660 previousElement = item;
3661 setElementStatusTip (item);
3664 void
3665 PwmdMainWindow::slotElementEditorClosed (QWidget * w,
3666 QAbstractItemDelegate::EndEditHint h)
3668 QLineEdit *le = qobject_cast <QLineEdit *> (w);
3670 if (le->text () == tr ("New element") || le->text ().isEmpty ()
3671 || h == QAbstractItemDelegate::RevertModelCache)
3673 QEvent *ev = new QEvent (QEvent::Type (PwmdEventNewElementCancel));
3674 QCoreApplication::postEvent (this, ev);
3675 return;
3678 setElementItemsEnabled (selectedElement, selectedElement);
3681 bool
3682 PwmdMainWindow::event (QEvent * ev)
3684 int type = ev->type ();
3685 AttributesRetrievedEvent *attrsEvent;
3686 PwmdTreeWidgetItemData *data = nullptr;
3688 if (type == QEvent::StatusTip)
3690 QStatusTipEvent *se = static_cast < QStatusTipEvent * >(ev);
3691 QStatusBar *sb = statusBar ();
3693 sb->showMessage (se->tip ());
3694 ev->accept ();
3695 return true;
3698 if (type >= QEvent::User)
3699 dndOperation = DndNone;
3701 switch (type)
3703 default:
3704 break;
3705 case PwmdEventBusy:
3706 if (ui.tabWidget->currentWidget () == ui.attributeTab)
3707 setAttributeItemsEnabled ();
3708 else if (ui.tabWidget->currentWidget () == ui.contentTab)
3709 setContentItemsEnabled ();
3710 else if (ui.tabWidget->currentWidget () == ui.commandTab)
3711 setCommandItemsEnabled ();
3713 fixupMultiToolbar ();
3714 ev->accept ();
3715 return true;
3716 case PwmdEventOptionsClose:
3717 delete optionsDialog;
3718 optionsDialog = nullptr;
3719 ev->accept ();
3720 return true;
3721 case PwmdEventFileOptionsClose:
3722 delete fileOptionsDialog;
3723 fileOptionsDialog = nullptr;
3724 fixupToolBar (fileToolBar, FileToolBarOpen, true);
3725 ev->accept ();
3726 return true;
3727 case PwmdEventClientListClose:
3728 delete clientDialog;
3729 clientDialog = nullptr;
3730 ev->accept ();
3731 return true;
3732 case PwmdEventOpenFileClose:
3733 delete openDialog;
3734 openDialog = nullptr;
3735 ev->accept ();
3736 return true;
3737 case PwmdEventOpenFileConfirm:
3738 slotDoOpenFile ();
3739 ev->accept ();
3740 return true;
3741 case PwmdEventReloadConfig:
3743 QString tmp, tmp2;
3744 readConfigSettings (tmp, tmp2, false);
3746 ev->accept ();
3747 return true;
3748 case PwmdEventRefreshElementTree:
3749 case PwmdEventDnDTargetAttr:
3750 refreshElementTree (ev->type () == PwmdEventDnDTargetAttr);
3751 setModified ();
3752 ev->accept ();
3753 return true;
3754 case PwmdEventTargetAttr:
3755 refreshElementTree ();
3756 ev->accept ();
3757 return true;
3758 case PwmdEventNewElementCancel:
3759 if (newElement)
3760 data = qvariant_cast<PwmdTreeWidgetItemData *> (newElement->data (0, Qt::UserRole));
3761 delete data;
3762 delete newElement;
3763 newElement = nullptr;
3764 attributeElement = selectedElement = previousElement;
3765 dndOperation = DndNone;
3766 setElementItemsEnabled (selectedElement, selectedElement);
3767 ev->accept ();
3768 return true;
3769 case PwmdEventNewAttributeCancel:
3770 delete newAttribute;
3771 newAttribute = nullptr;
3772 selectedAttribute = nullptr;
3773 slotAttributeSelected (ui.attributeList->item (previousAttributeIndex ()),
3774 nullptr);
3775 ev->accept ();
3776 return true;
3777 case PwmdEventAttrsRetrieved:
3778 attrsEvent = static_cast<AttributesRetrievedEvent *> (ev);
3779 emit attributesRetrieved (attrsEvent->item ());
3780 ev->accept ();
3781 return true;
3784 return QMainWindow::event (ev);
3787 void
3788 PwmdMainWindow::newElementFinalize (QString path)
3790 setModified ();
3791 path.chop (1);
3792 selectedElementPath = path;
3793 selectedElement = nullptr;
3794 newElement = nullptr;
3795 // Need to create an event to avoid a segfault in the mouse event
3796 // handler since this function is in an event itself.
3797 QEvent *ev = new QEvent (QEvent::Type (PwmdEventTargetAttr));
3798 QCoreApplication::postEvent (this, ev);
3801 void
3802 PwmdMainWindow::renameElementFinalize ()
3804 setModified ();
3805 refreshElementTree ();
3808 void
3809 PwmdMainWindow::slotRenameElementUpdate (QWidget * w)
3811 QLineEdit *le = qobject_cast<QLineEdit *> (w);
3813 if (newElement)
3815 QString path = QString ();
3817 if (le->text () == tr ("New element") || le->text ().isEmpty ())
3819 QEvent *ev = new QEvent (QEvent::Type (PwmdEventNewElementCancel));
3820 QCoreApplication::postEvent (this, ev);
3821 return;
3824 for (QTreeWidgetItem * item = newElement; item; item = item->parent ())
3826 PwmdTreeWidgetItemData *data = qvariant_cast <PwmdTreeWidgetItemData *>(item->data (0, Qt::UserRole));
3828 path.prepend (QString ("%1%2").arg (data->hasTarget () ? "" : "",
3829 item->text (0)));
3830 if (item->parent ())
3831 path.prepend ('\t');
3834 if (findElement (path, newElement))
3836 selectedElement = attributeElement = previousElement;
3837 QEvent *ev = new QEvent (QEvent::Type (PwmdEventNewElementCancel));
3838 QCoreApplication::postEvent (this, ev);
3839 return;
3842 if (dndOperation != DndNone)
3844 (void)doDndCopyElement (elementPath (copyElement),
3845 elementPath (newElement));
3847 else
3849 path.append ('\t');
3850 PwmdInquireData *inq = new PwmdInquireData (path);
3851 inq->setUser (path);
3852 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdNewElement, "STORE", Pwmd::inquireCallback, inq));
3854 return;
3856 else if (!selectedElement)
3857 return;
3859 int n = selectedElementPath.lastIndexOf ("\t") + 1;
3860 QString origName = selectedElementPath.mid (n);
3862 if (le->text ().isEmpty ())
3864 selectedElement->setText (0, origName);
3865 return;
3868 if (origName == le->text ())
3869 return;
3871 PwmdInquireData *inq = new PwmdInquireData (QString ("%1 %2").arg (selectedElementPath, le->text ()));
3872 inq->setUser (origName);
3873 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdRenameElement, "RENAME",
3874 Pwmd::inquireCallback, inq));
3877 void
3878 PwmdMainWindow::slotContentModified (bool b)
3880 if (ui.tabWidget->currentWidget() == ui.contentTab)
3882 if (ui.passwordFrame->isHidden ())
3883 fixupToolBar (contentToolBar, ContentToolBarOk, b);
3885 else if (ui.tabWidget->currentWidget() == ui.attributeTab)
3886 fixupToolBar (attributeToolBar, AttributeToolBarOk, b);
3888 setFileModified (b);
3891 void
3892 PwmdMainWindow::setFileModified (bool b)
3894 if (!b)
3896 ui.textEdit->document ()->setModified (b);
3897 ui.attributeContent->document ()->setModified (b);
3898 ui.passwordWidget->setModified (b);
3901 if (b || (!isWindowModified () && !b))
3902 setModified (b);
3905 void
3906 PwmdMainWindow::setModified (bool b)
3908 if (b && state != Pwmd::Opened)
3909 return;
3911 fixupToolBar (fileToolBar, FileToolBarSave, b);
3913 if (b)
3914 setWindowModified (true);
3916 if (!b && isWindowModified ())
3918 newFile = false;
3919 setFileModified (b);
3923 void
3924 PwmdMainWindow::slotExpandAll (bool b)
3926 QTreeWidgetItem *item;
3927 unsigned total = 0, n = 0;
3929 ui.elementTree->setHorizontalScrollMode (QAbstractItemView::ScrollPerItem);
3930 progressBar->setHidden (false);
3931 setBusy ();
3933 foreach (item, ui.elementTree->findItems ("*",
3934 Qt::MatchRecursive | Qt::
3935 MatchWildcard)) total++;
3937 if (total > 100)
3939 progressBar->setMaximum (total);
3940 progressBar->setFormat (tr ("Building view %p%"));
3943 foreach (item, ui.elementTree->findItems ("*",
3944 Qt::MatchRecursive | Qt::
3945 MatchWildcard))
3947 item->setExpanded (b);
3949 if (total > 100 && !(++n % 50))
3950 progressBar->setValue (n);
3953 item = selectedElement;
3955 while (item)
3957 item->setExpanded (b);
3958 item = item->parent ();
3961 setBusy (false);
3962 ui.elementTree->scrollToItem (selectedElement,
3963 QAbstractItemView::PositionAtCenter);
3964 ui.elementTree->setHorizontalScrollMode (QAbstractItemView::ScrollPerPixel);
3967 void
3968 PwmdMainWindow::slotClipboardAttribute ()
3970 setClipboard (selectedAttribute->text ());
3973 void
3974 PwmdMainWindow::setClipboard (const QString &s)
3976 QClipboard *c = QApplication::clipboard ();
3978 if (c->supportsSelection ())
3979 c->setText (s, QClipboard::Selection);
3980 c->setText (s);
3983 void
3984 PwmdMainWindow::slotClipboardElementPath ()
3986 setClipboard (elementPath (selectedElement));
3989 bool
3990 PwmdMainWindow::doFinished (bool &no, bool ignore)
3992 QMessageBox msgBox;
3994 no = false;
3995 msgBox.setIcon (QMessageBox::Question);
3997 if (isWindowModified () || needsUpdate ())
3999 QMessageBox m (QMessageBox::Question, tr ("Save to disk?"),
4000 tr ("The current data file has changes that have yet to be written to disk. Would you like to save the changes?"));
4001 m.addButton (QMessageBox::Yes);
4002 m.addButton (QMessageBox::No);
4003 m.addButton (QMessageBox::Cancel);
4004 switch (m.exec ())
4006 case QMessageBox::Yes:
4007 doSaveContent (true, true, newFile);
4008 pwm->runQueue ();
4009 // Prevent closing the window when there are changes since there may
4010 // be an error from somewhere like pinentry, gpg-agent, etc.
4011 return false;
4012 break;
4013 case QMessageBox::Cancel:
4014 doSaveContent (true);
4015 return false;
4016 case QMessageBox::No:
4017 no = ignore == true;
4018 return ignore ? true : false;
4019 default:
4020 break;
4024 return true;
4027 void
4028 PwmdMainWindow::slotClipboardTimer ()
4030 if (clipboardTimeout)
4031 clipboardTimer->start (clipboardTimeout * 1000);
4034 void
4035 PwmdMainWindow::slotClearClipboard ()
4037 QClipboard *c = QApplication::clipboard();
4039 clipboardTimer->stop();
4040 if (c->supportsSelection ())
4041 c->clear (QClipboard::Selection);
4043 c->clear (QClipboard::Clipboard);
4044 #ifdef Q_OS_ANDROID
4045 PwmdAndroidJNI::clearClipboard ();
4046 #endif
4049 void
4050 PwmdMainWindow::slotReleaseTimer ()
4052 PwmdInquireData *inq = new PwmdInquireData();
4053 PwmdCommandQueueItem *cmd = new PwmdCommandQueueItem (PwmdCmdIdUnLock, "UNLOCK", 0, inq);
4054 cmd->addError (GPG_ERR_SOURCE_USER_1, GPG_ERR_NOT_LOCKED);
4055 if (!pwm->command (cmd))
4056 delete inq;
4059 void
4060 PwmdMainWindow::slotPasswordContentChanged ()
4062 fixupToolBar (passwordToolBar, PasswordToolBarOk, true);
4063 setModifiedContent ();
4066 void
4067 PwmdMainWindow::slotContentChanged ()
4069 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(selectedElement->data (0, Qt::UserRole));
4070 QPlainTextEdit *w = ui.tabWidget->currentWidget () == ui.attributeTab
4071 ? ui.attributeContent : ui.textEdit;
4072 bool base64 = false;
4074 // Undo
4075 if (!w->document ()->isModified ())
4077 setFileModified (false);
4078 return;
4081 if (ui.tabWidget->currentWidget () == ui.attributeTab)
4083 if (selectedAttribute && selectedAttribute->text () == "_target")
4084 tabify (ui.attributeContent);
4086 else
4088 QString s = w->toPlainText ();
4090 for (int i = 0, t = s.length (); i < t; i++)
4092 QChar c = s.at (i);
4094 if ((c.category () == QChar::Other_Control && c.toLatin1 () != '\n')
4095 || c.isNull ())
4097 base64 = true;
4098 break;
4103 ui.actionBase64->setChecked (base64
4104 || data->attribute ("encoding") == "base64");
4105 setModifiedContent ();
4108 void
4109 PwmdMainWindow::updateBusyStatusBar (Pwmd::ConnectionState s)
4111 if (s == Pwmd::Init)
4112 statusBarLabel->setText (tr ("Not connected."));
4113 else if (s == Pwmd::Connecting)
4114 statusBarLabel->setText (tr ("Connecting ..."));
4115 else
4116 statusBarLabel->setText (isBusy ? tr ("Busy ...") :
4117 #ifdef Q_OS_ANDROID
4118 tr ("Ready."));
4119 #else
4120 tr ("Ready. Use Shift-click and Ctrl-click for multi-selection."));
4121 #endif
4124 void
4125 PwmdMainWindow::slotBusy (int cmdId, bool b)
4127 setBusy (b, cmdId);
4128 updateBusyStatusBar (pwm->state ());
4131 void
4132 PwmdMainWindow::setBusy (bool b, int cmdId)
4134 static int refcount;
4136 if (b)
4138 if (cancelButton && pwm->state () == Pwmd::Connecting)
4139 cancelButton->setHidden (false);
4141 if (!refcount)
4143 setElementStatusTip (selectedElement);
4144 setCursor (Qt::BusyCursor);
4147 refcount++;
4149 else
4151 refcount--;
4152 if (refcount < 0)
4153 refcount = 0;
4155 if (!refcount)
4157 if (pwm->state () != Pwmd::Connecting)
4159 if (cancelButton)
4160 cancelButton->setHidden (true);
4162 progressBar->setHidden (true);
4163 progressBar->setValue (0);
4164 progressBar->setMaximum (100);
4165 progressBar->setFormat ("");
4166 unsetCursor ();
4171 switch (cmdId)
4173 case PwmdCmdIdInvalid:
4174 case PwmdCmdIdClientList:
4175 case PwmdCmdIdClientKill:
4176 case PwmdCmdIdClientState:
4177 case PwmdCmdIdClientNCache:
4178 case PwmdCmdIdClientNClients:
4179 case PwmdCmdIdFileGetConfig:
4180 case PwmdCmdIdFileIsCached:
4181 case PwmdCmdIdFileClearCache:
4182 case PwmdCmdIdFileCacheTimeout:
4183 case PwmdCmdIdFileKeyInfo:
4184 return;
4185 default:
4186 break;
4189 if (pwm->queued () > 0 && isBusy)
4190 return;
4192 isBusy = b || refcount;
4193 if (!isBusy && pwm->state () == Pwmd::Init)
4194 return;
4196 fixupToolBar (fileToolBar, FileToolBarOpen, !isBusy && state != Pwmd::Init
4197 && !fileOptionsDialog);
4198 fixupToolBar (fileToolBar, FileToolBarSave, !isBusy && isWindowModified ());
4199 fixupMenu (ui.menuFile, FileMenuSaveExt, !isBusy && state == Pwmd::Opened);
4201 QEvent *ev = new QEvent (QEvent::Type (PwmdEventBusy));
4202 QCoreApplication::postEvent (this, ev);
4205 void
4206 PwmdMainWindow::showError (gpg_error_t rc)
4208 Pwmd::showError (rc, pwm, hasMultiSelection ());
4211 void
4212 PwmdMainWindow::reloadFinalize ()
4214 state = Pwmd::Connected;
4215 setConnected ();
4216 QEvent *ev = new QEvent (QEvent::Type (PwmdEventOpenFileConfirm));
4217 QCoreApplication::postEvent (this, ev);
4220 void
4221 PwmdMainWindow::saveExpandedItemList ()
4223 expandedItemsList.clear ();
4224 foreach (QTreeWidgetItem * item,
4225 ui.elementTree->findItems ("*", Qt:: MatchRecursive
4226 | Qt:: MatchWildcard))
4228 if (item->isExpanded ())
4229 expandedItemsList.append (elementPath (item));
4233 void
4234 PwmdMainWindow::slotReload ()
4236 bool b;
4238 if (!doFinished (b, true))
4240 pwm->runQueue ();
4241 return;
4244 fixupToolBar (passwordToolBar, PasswordToolBarOk, false);
4245 fixupToolBar (contentToolBar, ContentToolBarOk, false);
4246 saveExpandedItemList ();
4247 pwm->runQueue ();
4248 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdReload, "RESET"));
4251 void
4252 PwmdMainWindow::resetSelectedItems ()
4254 QTreeWidgetItem *item = firstSelectedItem ();
4255 ui.elementTree->setCurrentItem (item, 0, QItemSelectionModel::ClearAndSelect);
4258 void
4259 PwmdMainWindow::slotToggleMultiMode (bool b)
4261 #ifdef Q_OS_ANDROID
4262 if (b)
4263 ui.elementTree->setSelectionMode (QAbstractItemView::MultiSelection);
4264 else
4265 ui.elementTree->setSelectionMode (QAbstractItemView::SingleSelection);
4266 #else
4267 (void) b;
4268 #endif
4271 void
4272 PwmdMainWindow::slotRefreshElementTree ()
4274 pwm->flushQueue ();
4275 fixupToolBar (passwordToolBar, PasswordToolBarOk, false);
4276 fixupToolBar (contentToolBar, ContentToolBarOk, false);
4277 saveExpandedItemList ();
4278 refreshElementTree ();
4281 bool
4282 PwmdMainWindow::refreshElementTree (bool target)
4284 if (state != Pwmd::Opened)
4285 return false;
4287 nextElement = selectedElement = attributeElement = dropElement = nullptr;
4288 oldSelectedItem = nullptr;
4289 doSaveContent (true);
4290 setContent ("");
4291 clearAttributeTab ();
4292 PwmdInquireData *inq = new PwmdInquireData ("--recurse --sexp");
4293 inq->setUserBool (target);
4294 return pwm->command (new PwmdCommandQueueItem (PwmdCmdIdElementTree,
4295 "LIST",
4296 Pwmd::inquireCallback, inq));
4299 void
4300 PwmdMainWindow::refreshElementTreeFinalize (QByteArray &tmp)
4302 QString path = QString ();
4304 freeSearchResults ();
4306 if (!selectedElementPath.isEmpty ())
4307 path = selectedElementPath;
4309 clearElementTree (ui.elementTree);
4310 previousElement = targetAttributeElement = nullptr;
4311 QList < QTreeWidgetItem * >items = buildElementTree (tmp);
4313 ui.elementTree->insertTopLevelItems (0, items);
4314 ui.elementTree->setSortingEnabled (sortElementTree);
4315 if (sortElementTree)
4316 ui.elementTree->sortByColumn (0, Qt::AscendingOrder);
4318 selectedElement = findElement (path);
4319 if (!selectedElement)
4320 setContent ("");
4322 setCurrentElement (selectedElement);
4324 if (ui.actionExpandAll->isChecked ())
4325 slotExpandAll (ui.actionExpandAll->isChecked ());
4326 else if (!expandedItemsList.isEmpty ())
4328 foreach (QTreeWidgetItem * item,
4329 ui.elementTree->findItems ("*", Qt:: MatchRecursive | Qt:: MatchWildcard))
4331 if (expandedItemsList.contains (elementPath (item)))
4332 item->setExpanded (true);
4336 #ifdef Q_OS_ANDROID
4337 QList <QAction *>actions = ui.menuElement->actions();
4338 actions[ElementMenuMulti]->setChecked (false);
4339 #endif
4340 ui.elementTree->scrollToItem (selectedElement,
4341 QAbstractItemView::PositionAtCenter);
4342 setElementStatusTip (selectedElement);
4343 elementSelected (selectedElement);
4346 void
4347 PwmdMainWindow::slotStatusMessage (QString line, void *)
4349 QStringList l = QString (line).split (" ");
4351 if (l.at (0) == "NEWFILE")
4353 newFile = true;
4355 else if (l.at (0) == "XFER")
4357 progressBar->setHidden (false);
4358 progressBar->setMaximum (l.at (2).toULong (nullptr));
4359 progressBar->setValue (l.at (1).toULong (nullptr));
4360 progressBar->setFormat (tr ("Transferring - %v of %m %p%"));
4362 else if (l.at (0) == "LOCKED")
4364 progressBar->setHidden (false);
4365 progressBar->setMaximum (0);
4366 progressBar->setValue (0);
4367 progressBar->setFormat (tr ("Waiting for lock"));
4369 else if (l.at (0) == "EXPIRE")
4371 if (l.at(2).toUInt() != 0) // STORE status message, not GET
4372 updateElementExpiry (l.at(2).toLong ());
4374 else if (l.at (0) == "MODIFIED")
4376 QString title = QString (tr ("The currently opened data file has been modified by another client."));
4377 QString desc = QString (tr ("You will be unable to commit any current and further changes and will need to find a way to record any changes made then reopen the data file and begin editing again. This can be avoided by locking the data file before editing (File->File Options)"));
4378 QMessageBox m;
4380 m.setText (title);
4381 m.setInformativeText (desc);
4382 m.setIcon (QMessageBox::Information);
4383 m.exec ();
4387 void
4388 PwmdMainWindow::slotToggleBase64Content (bool)
4390 setFileModified ();
4391 doSaveContent (true, false, false, nullptr, true);
4392 pwm->runQueue ();
4395 void
4396 PwmdMainWindow::setCurrentElement (QTreeWidgetItem *item)
4398 disconnect (ui.elementTree,
4399 SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)),
4400 this,
4401 SLOT (slotElementSelected (QTreeWidgetItem *, QTreeWidgetItem *)));
4402 ui.elementTree->setCurrentItem (item, 0, QItemSelectionModel::ClearAndSelect);
4403 connect (ui.elementTree,
4404 SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)),
4405 this,
4406 SLOT (slotElementSelected (QTreeWidgetItem *, QTreeWidgetItem *)));
4409 void
4410 PwmdMainWindow::updateContentPointer (QTreeWidgetItem * i,
4411 PwmdElementContent * newData)
4413 QTreeWidgetItem *r = resolveTargets (i);
4414 if (!r)
4416 delete newData;
4417 return;
4419 PwmdTreeWidgetItemData *data =
4420 qvariant_cast < PwmdTreeWidgetItemData * >(r->data (0, Qt::UserRole));
4421 PwmdElementContent *old = data->content ();
4423 // For elements with a "_target" attribute pointing to this element.
4424 foreach (QTreeWidgetItem * item, ui.elementTree->findItems ("*",
4425 Qt::
4426 MatchRecursive |
4427 Qt::
4428 MatchWildcard))
4430 if (item == r)
4431 continue;
4433 data = qvariant_cast <PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
4434 if ((old && data->content () == old) || (resolveTargets (item) == r))
4435 data->setContent (newData);
4438 data = qvariant_cast < PwmdTreeWidgetItemData * >(r->data (0, Qt::UserRole));
4439 data->setContent (newData);
4442 bool
4443 PwmdMainWindow::createAttributeCache (QTreeWidgetItem *item, const QString &tmp)
4445 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
4447 if (tmp.isEmpty () || !data)
4448 return false;
4450 QStringList attrs = tmp.split ("\n");
4451 PwmdAttributeList *newAttrs = new PwmdAttributeList ();
4453 for (int attr = 0, t = attrs.count (); attr < t; attr++)
4455 int n = attrs.at (attr).indexOf (QChar (' '));
4456 QString s;
4458 if (n == -1)
4459 s.append (attrs.at (attr));
4460 else
4461 s.append (attrs.at (attr).left (n));
4463 data->addAttribute (newAttrs, s, attrs.at (attr).mid (n + 1));
4466 data->setAttributes (newAttrs);
4467 data->setNeedsAttrRefresh (false);
4468 return true;
4472 PwmdMainWindow::previousAttributeIndex ()
4474 if (advancedSearchDialog && advancedSearchDialog->isVisible ())
4476 if (currentSearchResult
4477 && ((currentSearchResult->resultType () & SearchResult::Attr)
4478 || (currentSearchResult->resultType () & SearchResult::AttrValue)))
4479 return currentSearchResult->curAttr ();
4482 for (int i = 0; i < ui.attributeList->count (); i++)
4484 QListWidgetItem *item = ui.attributeList->item (i);
4486 if (item->text () == previousAttributeText)
4487 return i;
4490 return 0;
4493 void
4494 PwmdMainWindow::elementSelectedFinalize (QTreeWidgetItem *item,
4495 const QString &result, bool attrs)
4497 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
4499 previousElement = selectedElement;
4500 selectedElement = attributeElement = item;
4501 ui.elementTree->scrollToItem (selectedElement);
4502 setElementColors (item);
4503 setElementItemsEnabled (item, true);
4505 if (sortElementTree)
4506 item->sortChildren (0, Qt::AscendingOrder);
4508 if (ui.tabWidget->currentWidget () == ui.attributeTab)
4509 refreshAttributeListFinalize (item, QString (), false);
4511 if (ui.tabWidget->currentWidget () == ui.contentTab)
4513 if (data->hasAttribute ("password"))
4514 setPasswordItemsEnabled ();
4515 else
4516 setContentItemsEnabled ();
4518 else if (ui.tabWidget->currentWidget () == ui.attributeTab)
4520 if (attrs)
4521 pwm->runQueue ();
4522 else
4523 slotEditAttributes ();
4526 setContent (result, item);
4527 ui.tabWidget->setEnabled (true);
4528 if (cacheContent)
4529 updateContentPointer (item, new PwmdElementContent (result.toUtf8 ()));
4531 if (item->isDisabled ())
4532 ui.contentTab->setEnabled (false);
4535 void
4536 PwmdMainWindow::elementSelected (QTreeWidgetItem * item, bool refresh,
4537 QTreeWidgetItem *old)
4539 if (!item)
4541 pwm->runQueue ();
4542 return;
4545 bool attrs = refreshAttributeList (item, !cacheContent || refresh, true);
4546 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
4547 QString s = QString ();
4549 if (cacheContent && data->contentCached () && !refresh)
4551 s = QString::fromUtf8 (data->content ()->data(), -1);
4552 elementSelectedFinalize (item, s, attrs);
4554 else if (!data->hasError () && (refresh || item != old))
4556 if (hasMultiSelection ())
4557 return;
4559 PwmdInquireData *inq = new PwmdInquireData (elementPath (item));
4560 inq->setUserPtr (item);
4561 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdGetContent, "GET",
4562 Pwmd::inquireCallback, inq));
4563 return;
4565 else
4567 setContent ("");
4568 if (ui.tabWidget->currentWidget () == ui.contentTab)
4570 ui.contentTab->setEnabled (false);
4571 setContentItemsEnabled (true);
4572 ui.attributeTab->setEnabled (false);
4574 else if (ui.tabWidget->currentWidget () == ui.attributeTab)
4576 if (targetAttributeElement)
4578 setElementColors (targetAttributeElement, true, true);
4579 targetAttributeElement = nullptr;
4580 selectedElement = attributeElement = item;
4582 if (!attrs)
4583 slotEditAttributes ();
4587 pwm->runQueue ();
4590 bool
4591 PwmdMainWindow::hasOwnerOfItemSelected ()
4593 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
4595 if (isElementOwner (item))
4596 return true;
4599 return false;
4602 void
4603 PwmdMainWindow::setAttributeItemsEnabled (bool b, QTreeWidgetItem *multi)
4605 QTreeWidgetItem *element = multi ? multi : selectedElement;
4606 element = !multi && attributeElement ? attributeElement : element;
4608 b = multi || b;
4609 if (b && !multi)
4611 setContentItemsEnabled (false);
4612 setCommandItemsEnabled (false);
4613 setPasswordItemsEnabled (false);
4614 ui.tabWidget->setCurrentWidget (ui.attributeTab);
4617 setElementItemsEnabled (element, !isBusy, multi);
4619 if (!multi)
4620 attributeToolBar->setHidden (!b);
4622 b = b && element && state == Pwmd::Opened && !isBusy;
4623 attributeToolBar->setEnabled (b);
4624 ui.attributeTab->setEnabled (b);
4625 PwmdTreeWidgetItemData *data = nullptr;
4626 if (element)
4627 data = qvariant_cast < PwmdTreeWidgetItemData * >(element->data (0, Qt::UserRole));
4629 b = b && data && !data->hasError ()
4630 && isElementOwner (resolveTargets (element));
4632 fixupMenu (ui.menuAttributes, AttributeMenuTarget, !isBusy && !multi
4633 && (targetAttributeElement
4634 || (hasTarget (element) && !badTarget (element)))
4635 && ui.tabWidget->currentWidget () == ui.attributeTab);
4636 fixupToolBar (attributeToolBar, AttributeToolBarNew, b);
4637 fixupToolBar (attributeToolBar, AttributeToolBarDelete,
4638 b && ui.attributeList->count () > 1
4639 && ui.attributeList->selectedItems ().count ());
4640 fixupToolBar (attributeToolBar, AttributeToolBarOk, !isBusy && data
4641 && !data->badPermissions ()
4642 && fixupToolBar (attributeToolBar, AttributeToolBarOk, false, true));
4643 fixupToolBar (attributeToolBar, AttributeToolBarRefresh, !isBusy && !multi
4644 && element
4645 && ui.tabWidget->currentWidget () == ui.attributeTab
4646 && hasSingleSelection ());
4647 ui.cb_searchAndReplace->setEnabled (ui.attributeList->currentItem ());
4650 void
4651 PwmdMainWindow::setContentItemsEnabled (bool b, bool hide,
4652 QTreeWidgetItem *multi)
4654 QTreeWidgetItem *element = multi ? multi : selectedElement;
4655 PwmdTreeWidgetItemData *data = nullptr;
4657 if (!multi && ui.tabWidget->currentWidget () == ui.contentTab)
4658 setElementColors (selectedElement);
4660 if (element)
4661 data = qvariant_cast < PwmdTreeWidgetItemData * >(element->data (0, Qt::UserRole));
4663 if (b && !multi)
4665 setAttributeItemsEnabled (false);
4666 setCommandItemsEnabled (false);
4667 if (!data || !data->hasAttribute ("password"))
4668 setPasswordItemsEnabled (false);
4669 ui.tabWidget->setCurrentWidget (ui.contentTab);
4672 setElementItemsEnabled (element, b && !isBusy, multi);
4674 if ((multi || element) && b)
4676 if (data && data->hasAttribute ("password"))
4678 setPasswordItemsEnabled (true, multi);
4679 if (!multi)
4680 return;
4684 if (!multi)
4686 contentToolBar->setHidden (!b &&
4687 (hide || ui.tabWidget->currentWidget () != ui.contentTab));
4689 ui.textEdit->setHidden (data && data->hasAttribute ("hidden"));
4690 ui.passwordWidget->setHidden (true);
4693 ui.textEdit->setEnabled (element && hasSingleSelection ());
4694 contentToolBar->setEnabled (element &&
4695 data && !data->hasError () && !isBusy);
4696 b = (multi || b)
4697 && element && state == Pwmd::Opened
4698 && !element->isDisabled ()
4699 && !badTarget (element)
4700 && data && !data->badPermissions ()
4701 && isElementOwner (resolveTargets (element));
4703 if (!multi)
4705 if (data && !ui.f_expire->isHidden ())
4706 setElementExpireLabel (data->attribute ("_expire").toUInt ());
4708 ui.f_expire->setHidden (!b || (data && data->attribute ("_expire").isEmpty ()));
4709 ui.f_expire->setEnabled (b && isElementOwner (element));
4710 ui.contentTab->setEnabled (b && !isBusy);
4713 fixupMenu (ui.menuContent, ContentMenuBase64, b && !isBusy
4714 && hasSingleSelection ());
4715 fixupMenu (ui.menuContent, ContentMenuHidden, b && !isBusy
4716 && hasSingleSelection ());
4717 fixupMenu (ui.menuContent, ContentMenuClipboard, data && !data->hasError ()
4718 && hasSingleSelection ()
4719 && isElementOwner (element, true));
4720 fixupToolBar (contentToolBar, ContentToolBarSave, data && !data->hasError ()
4721 && hasSingleSelection ()
4722 && isElementOwner (element, true));
4723 fixupToolBar (contentToolBar, ContentToolBarInsert, b && !isBusy
4724 && hasSingleSelection ());
4725 fixupToolBar (contentToolBar, ContentToolBarRefresh, b && !isBusy
4726 && hasSingleSelection ());
4727 fixupToolBar (contentToolBar, ContentToolBarPassword, b && !isBusy
4728 && isElementOwner (resolveTargets (element)));
4729 fixupToolBar (contentToolBar, ContentToolBarExpiry, b && !isBusy
4730 && isElementOwner (resolveTargets (element)));
4732 if (!multi)
4733 setElementStatusTip (selectedElement);
4736 void
4737 PwmdMainWindow::setElementExpireLabel (long expire)
4739 QDateTime date;
4741 date.setSecsSinceEpoch (expire);
4742 ui.l_expires->setText (date.toString ());
4743 ui.l_expiresTitle->setText (tr (expire <= time (nullptr) ? "Expired:" : "Expires:"));
4746 void
4747 PwmdMainWindow::setPasswordItemsEnabled (bool b, QTreeWidgetItem *multi)
4749 QTreeWidgetItem *element = multi ? multi : selectedElement;
4751 if (b && !multi)
4753 setContentItemsEnabled (false);
4754 setAttributeItemsEnabled (false);
4755 setCommandItemsEnabled (false);
4756 ui.contentTab->setEnabled (true);
4759 PwmdTreeWidgetItemData *data = nullptr;
4760 if (element)
4761 data = qvariant_cast < PwmdTreeWidgetItemData * >(element->data (0, Qt::UserRole));
4763 if (!multi)
4765 ui.passwordFrame->setHidden (!b);
4766 ui.passwordFrame->setEnabled (element &&
4767 isElementOwner (element, true)
4768 && hasSingleSelection ());
4769 ui.textEdit->setHidden (true);
4770 ui.passwordWidget->setHidden (false);
4771 passwordToolBar->setHidden (!b);
4772 passwordToolBar->setEnabled (element);
4775 b = (multi || b) && element && state == Pwmd::Opened
4776 && !element->isDisabled () && !badTarget (element);
4778 if (!multi)
4780 ui.f_expire->setHidden (!b || (data && data->attribute ("_expire").isEmpty ()));
4781 if (data && !ui.f_expire->isHidden ())
4782 setElementExpireLabel (data->attribute ("_expire").toUInt ());
4785 b = b && isElementOwner (resolveTargets (element));
4786 if (!multi)
4787 ui.f_expire->setEnabled (b);
4789 fixupToolBar (passwordToolBar, PasswordToolBarInsert, b && !isBusy
4790 && hasSingleSelection ());
4791 fixupToolBar (passwordToolBar, PasswordToolBarSave, b && !isBusy
4792 && hasSingleSelection ());
4793 fixupToolBar (passwordToolBar, PasswordToolBarExpiry, b && !isBusy);
4794 fixupToolBar (passwordToolBar, PasswordToolBarRefresh, b && !isBusy
4795 && hasSingleSelection ());
4797 fixupMenu (ui.menuContent, ContentMenuInsert, b && !isBusy
4798 && hasSingleSelection ());
4799 fixupMenu (ui.menuContent, ContentMenuSave, data && !data->hasError ()
4800 && hasSingleSelection ());
4801 fixupMenu (ui.menuContent, ContentMenuClipboard, data && !data->hasError ()
4802 && hasSingleSelection ());
4803 fixupMenu (ui.menuContent, ContentMenuExpiry, b && !isBusy);
4804 fixupMenu (ui.menuContent, ContentMenuPassword, false);
4805 fixupMenu (ui.menuContent, ContentMenuBase64, false);
4806 fixupMenu (ui.menuContent, ContentMenuHidden, false);
4807 fixupMenu (ui.menuContent, ContentMenuRefresh, b && !isBusy
4808 && hasSingleSelection ());
4809 passwordToolBar->setEnabled (b && !isBusy);
4810 ui.passwordWidget->setEditable (b && !isBusy
4811 && hasSingleSelection ()
4812 && isElementOwner (resolveTargets (element)));
4816 void
4817 PwmdMainWindow::setCommandItemsEnabled (bool b, QTreeWidgetItem *multi)
4819 if (b && !multi)
4821 ui.tabWidget->setEnabled (true);
4822 setContentItemsEnabled (false);
4823 setAttributeItemsEnabled (false);
4824 setPasswordItemsEnabled (false);
4827 setElementItemsEnabled (selectedElement, multi || !isBusy, multi);
4828 ui.commandTab->setEnabled (b && !isBusy);
4829 commandToolBar->setHidden (!b);
4830 fixupToolBar (commandToolBar, CommandToolBarOk, state == Pwmd::Opened
4831 && !isBusy && !ui.commandTextEdit->toPlainText ().isEmpty ());
4832 fixupToolBar (commandToolBar, CommandToolBarRefresh, state == Pwmd::Opened
4833 && !isBusy);
4834 fixupToolBar (commandToolBar, CommandToolBarHelp, !isBusy
4835 && (state == Pwmd::Opened || state == Pwmd::Connected));
4838 void
4839 PwmdMainWindow::setElementItemsEnabled (QTreeWidgetItem *item, bool b,
4840 QTreeWidgetItem *multi)
4842 QTreeWidgetItem *element = multi ? multi : item;
4843 PwmdTreeWidgetItemData *data = nullptr;
4845 b = multi || b;
4847 if (element)
4848 data = qvariant_cast < PwmdTreeWidgetItemData * >(element->data (0, Qt::UserRole));
4850 // Toolbar icons
4851 elementToolBar->setEnabled (state == Pwmd::Opened && !isBusy);
4852 fixupToolBar (elementToolBar, ElementToolBarCreate, !newElement
4853 && state == Pwmd::Opened
4854 && !hasMultiSelection ());
4855 fixupToolBar (elementToolBar, ElementToolBarDelete, b &&
4856 ((data && !data->badPermissions() && isElementOwner (element))
4857 || isElementOwner (element)));
4858 fixupToolBar (elementToolBar, ElementToolBarRename, b &&
4859 data && !data->badPermissions() && isElementOwner (element)
4860 && hasSingleSelection ());
4861 fixupToolBar (elementToolBar, ElementToolBarFind, state == Pwmd::Opened
4862 && !isBusy &&ui.elementTree->topLevelItemCount () > 1);
4863 if (!multi)
4864 contentToolBar->setEnabled (element && b);
4866 // Menu items
4867 QList <QAction *>actions = ui.menuElement->actions();
4868 actions[ElementMenuNewRoot]->setEnabled (state == Pwmd::Opened);
4869 actions[ElementMenuNew]->setEnabled (state == Pwmd::Opened && element
4870 && !data->badPermissions()
4871 && !data->badTarget () && b
4872 && hasSingleSelection ());
4873 actions[ElementMenuNewSibling]->setEnabled (state == Pwmd::Opened && element
4874 && element->parent ()
4875 && hasSingleSelection ());
4876 actions[ElementMenuResolveTargets]->setEnabled (element
4877 && hasTarget (element, true)
4878 && !badTarget (element)
4879 && (!data->badPermissions() ||
4880 resolveTargets (element))
4881 && b
4882 && hasSingleSelection ());
4883 actions[ElementMenuEditContent]->setEnabled (element && b &&
4884 ((!data->badPermissions()
4885 && ui.tabWidget->currentWidget() != ui.contentTab)
4887 (ui.tabWidget->currentWidget() != ui.attributeTab)));
4888 actions[ElementMenuRefreshTree]->setEnabled (state == Pwmd::Opened
4889 && !isBusy);
4890 #ifdef Q_OS_ANDROID
4891 actions[ElementMenuMulti]->setEnabled (state == Pwmd::Opened
4892 && !isBusy
4893 && ui.elementTree->findItems ("*", Qt::MatchRecursive|Qt::MatchWildcard).count () > 1);
4894 #else
4895 actions[ElementMenuMulti]->setVisible (false);
4896 #endif
4899 void
4900 PwmdMainWindow::slotElementClicked (QTreeWidgetItem *, int)
4902 slotFindFinished ();
4905 void
4906 PwmdMainWindow::fixupMultiToolbar ()
4908 bool b = false;;
4909 unsigned mask = 0;
4910 int count = ui.elementTree->selectedItems ().count ();
4912 if (count <= 1)
4913 return;
4915 if (ui.tabWidget->currentWidget () == ui.contentTab)
4917 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
4919 setContentItemsEnabled (true, false, item);
4920 b = fixupToolBar (contentToolBar, ContentToolBarPassword, false,
4921 true);
4922 if (b)
4923 mask |= ContentPasswordMask;
4925 b = fixupToolBar (contentToolBar, ContentToolBarExpiry, false,
4926 true);
4927 if (b)
4928 mask |= ContentExpiryMask;
4931 fixupToolBar (contentToolBar, ContentToolBarPassword,
4932 (mask & ContentPasswordMask));
4933 fixupToolBar (contentToolBar, ContentToolBarExpiry,
4934 (mask & ContentExpiryMask));
4935 if (mask)
4936 contentToolBar->setEnabled (true);
4938 else if (ui.tabWidget->currentWidget () == ui.attributeTab)
4940 b = fixupToolBar (attributeToolBar, AttributeToolBarRefresh, false, true);
4941 fixupToolBar (attributeToolBar, AttributeToolBarRefresh, b && count == 1);
4943 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
4945 setContentItemsEnabled (true, false, item);
4946 setAttributeItemsEnabled (true, item);
4947 b = fixupToolBar (attributeToolBar, AttributeToolBarNew, false,
4948 true);
4949 if (b)
4950 mask |= AttributeNewMask;
4952 b = fixupToolBar (attributeToolBar, AttributeToolBarDelete, false,
4953 true);
4954 if (b)
4955 mask |= AttributeDeleteMask;
4958 fixupToolBar (attributeToolBar, AttributeToolBarNew,
4959 (mask & AttributeNewMask));
4960 fixupToolBar (attributeToolBar, AttributeToolBarDelete,
4961 (mask & AttributeDeleteMask));
4962 if (mask)
4963 attributeToolBar->setEnabled (true);
4965 else if (ui.tabWidget->currentWidget () == ui.commandTab)
4967 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
4968 setCommandItemsEnabled (true, item);
4971 mask = 0;
4972 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
4974 setElementItemsEnabled (nullptr, true, item);
4975 b = fixupToolBar (elementToolBar, ElementToolBarDelete, false, true);
4976 mask |= b ? 1 : 0;
4978 fixupToolBar (elementToolBar, ElementToolBarDelete, mask != 0);
4981 /* Test if the command queue contains a command that when completed will
4982 * refresh the element tree. */
4983 bool
4984 PwmdMainWindow::hasQueuedRefreshTree ()
4986 if (pwm->isQueued (PwmdCmdIdElementTree)
4987 || pwm->isQueued (PwmdCmdIdDeleteElement)
4988 || pwm->isQueued (PwmdCmdIdRenameElement)
4989 || pwm->isQueued (PwmdCmdIdNewElement)
4990 || pwm->isQueued (PwmdCmdIdDndMoveElement)
4991 || pwm->isQueued (PwmdCmdIdDndCopyElement)
4992 || pwm->isQueued (PwmdCmdIdDndCreateTarget))
4993 return true;
4995 return false;
4998 /* This slot is called after slotElementSelected(). It seems to be consistant
4999 * in the firing order but may break in the future in which case element
5000 * selection will also break. */
5001 void
5002 PwmdMainWindow::slotItemSelectionChanged ()
5004 QList <QTreeWidgetItem *> items = ui.elementTree->selectedItems ();
5005 static unsigned lastCount;
5006 QTreeWidgetItem *item = firstSelectedItem ();
5008 fixupMultiToolbar ();
5010 /* Prevent fetching content for other selected items. */
5011 if (items.count () > 1)
5013 lastCount = items.count ();
5014 return;
5017 /* The nextElement is set when there are commands queued from
5018 * slotElementSelected() and is the item to be selected when the queue
5019 * becomes empty. The lastCount is the number of selected items during the
5020 * previous call to this function. Here, it means that the selection has
5021 * changed back to only one item and the command queue is to be run. It
5022 * handles the case that the content of an element has been edited but yet to
5023 * be committed while selecting other elements. */
5024 if (nextElement && lastCount > 1)
5026 lastCount = items.count ();
5027 nextElement = nullptr;
5029 /* A different element selected with content of the original element yet to
5030 * be commited . */
5031 else if (nextElement)
5033 lastCount = items.count ();
5034 elementSelected (oldSelectedItem, false, oldSelectedItem);
5037 lastCount = items.count ();
5039 /* Handle the case where an item is selected, then edited, then other items
5040 * are selected and the edited item deselected. Let the edit be queued for
5041 * commit. */
5042 if (!items.contains (oldSelectedItem))
5044 oldSelectedItem = nullptr;
5045 pwm->runQueue ();
5048 if (!hasQueuedRefreshTree ())
5049 elementSelected (item, false, oldSelectedItem);
5051 pwm->runQueue ();
5054 void
5055 PwmdMainWindow::slotElementSelected (QTreeWidgetItem * item,
5056 QTreeWidgetItem * old)
5058 /* If there is content yet to be commited, only test for it once rather than
5059 * messing up a multi-element selection. */
5060 if (!pwm->isQueued (PwmdCmdIdStoreContent)
5061 && !pwm->isQueued (PwmdCmdIdAttrContent)
5062 && doSaveContent (true))
5064 selectedElement = previousElement = old;
5065 nextElement = item;
5068 if (!hasMultiSelection ())
5069 oldSelectedItem = old;
5072 void
5073 PwmdMainWindow::setAttributeContent (const QString & str)
5075 disconnect (ui.attributeContent, SIGNAL (modificationChanged (bool)), this,
5076 SLOT (slotContentModified (bool)));
5077 disconnect (ui.attributeContent, SIGNAL (textChanged ()), this, SLOT (slotContentChanged ()));
5078 ui.attributeContent->document ()->setPlainText (str);
5079 connect (ui.attributeContent, SIGNAL (textChanged ()), SLOT (slotContentChanged ()));
5080 connect (ui.attributeContent, SIGNAL (modificationChanged (bool)), this,
5081 SLOT (slotContentModified (bool)));
5082 ui.attributeContent->document ()->setModified (false);
5084 if (attributeElement)
5086 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(attributeElement->data (0, Qt::UserRole));
5087 ui.attributeContent->setReadOnly (data->badPermissions ());
5091 void
5092 PwmdMainWindow::setContent (const QString & str, QTreeWidgetItem *item)
5094 PwmdTreeWidgetItemData *data = nullptr;
5096 item = item ? item : selectedElement;
5098 if (item)
5099 data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
5100 else
5101 ui.hiddenContentFrame->setHidden (true);
5103 if (data)
5105 bool base64 = data->attribute ("encoding") == "base64";
5107 ui.actionBase64->setChecked (base64);
5109 if (item == selectedElement)
5111 ui.actionHidden->setChecked (data->hasAttribute ("hidden"));
5112 if (!data->hasAttribute ("password"))
5113 ui.hiddenContentFrame->setHidden (!ui.actionHidden->isChecked ());
5115 if (ui.actionHidden->isChecked ())
5116 ui.hiddenContentLabel->setText (QString ("<html><head/><body><p><span style=\" font-style:italic;\">%1 %2.</span></p></body></html>").arg (tr ("The content for this element is hidden. Uncheck the Hidden item in the Content menu to make it visible. The length of the hidden content is "), QString::number (str.length ())));
5120 disconnect (ui.passwordWidget, SIGNAL (modified ()), this,
5121 SLOT (slotPasswordContentChanged ()));
5122 disconnect (ui.textEdit, SIGNAL (modificationChanged (bool)), this,
5123 SLOT (slotContentModified (bool)));
5124 disconnect (ui.textEdit, SIGNAL (textChanged ()), this,
5125 SLOT (slotContentChanged ()));
5126 if (item == selectedElement)
5128 if (data && data->hasAttribute ("password"))
5130 ui.passwordWidget->setPassword (str, !isElementOwner (item));
5131 ui.passwordWidget->setType (data->attribute ("password"));
5132 ui.hiddenContentFrame->setHidden (true);
5133 fixupToolBar (passwordToolBar, PasswordToolBarOk, false);
5134 ui.textEdit->document ()->setPlainText ("");
5136 else
5138 ui.passwordWidget->setPassword ("");
5139 ui.textEdit->document ()->setPlainText (str);
5140 ui.textEdit->document ()->setModified (false);
5142 if (data)
5143 ui.contentTab->setEnabled (!data->hasError ());
5147 connect (ui.passwordWidget, SIGNAL (modified ()), this,
5148 SLOT (slotPasswordContentChanged ()));
5149 connect (ui.textEdit, SIGNAL (textChanged ()), this,
5150 SLOT (slotContentChanged ()));
5151 connect (ui.textEdit, SIGNAL (modificationChanged (bool)), this,
5152 SLOT (slotContentModified (bool)));
5155 static bool
5156 hasFlag (const QString &str, const QString &flags)
5158 if (flags.isEmpty () || str.isEmpty ())
5159 return false;
5161 for (int i = 0; i < flags.length (); i++)
5163 if (str.contains (flags.at (i)))
5164 return true;
5167 return false;
5170 bool
5171 PwmdMainWindow::hasBadTarget (QTreeWidgetItem * item, const QString & path,
5172 const QString & flags)
5174 bool b = false;
5175 bool notAllowed = hasFlag (flags, "P");
5176 PwmdTreeWidgetItemData *data =
5177 qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
5179 if (notAllowed)
5181 Qt::ItemFlags flags = item->flags ();
5183 flags &= ~Qt::ItemIsEditable;
5184 item->setFlags (flags);
5185 data->setBadPermissions ();
5188 if (!data->hasTarget ())
5190 setElementColors (item);
5191 return false;
5194 if (hasFlag (flags, "EO"))
5196 b = true;
5197 data->setBadTarget (b);
5200 data->setTarget (path);
5201 bool loop = hasFlag (flags, "O");
5202 data->setTargetLoop (loop);
5203 setElementColors (item);
5205 if (data->badPermissions())
5206 return false;
5208 return b;
5211 bool
5212 PwmdMainWindow::hasTarget (const QTreeWidgetItem * item, bool parent)
5214 if (!item)
5215 return false;
5217 PwmdTreeWidgetItemData *data =
5218 qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
5220 if (!parent || data->hasTarget ())
5221 return data->hasTarget ();
5223 item = item->parent ();
5224 while (item)
5226 data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
5227 if (data->hasTarget ())
5228 return true;
5229 item = item->parent ();
5232 return false;
5235 bool
5236 PwmdMainWindow::targetLoop (const QTreeWidgetItem * item)
5238 PwmdTreeWidgetItemData *data =
5239 qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
5241 return data->targetLoop ();
5244 bool
5245 PwmdMainWindow::badTarget (const QTreeWidgetItem * item)
5247 PwmdTreeWidgetItemData *data =
5248 qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
5250 return data ? data->badTarget () : false;
5253 QString
5254 PwmdMainWindow::elementName (const QTreeWidgetItem * item)
5256 if (!item)
5257 return QString ();
5259 return item->text (0);
5262 void
5263 PwmdMainWindow::setItemText (QTreeWidgetItem * item, const QString & str,
5264 bool target)
5266 item->setText (0, str);
5267 PwmdTreeWidgetItemData *data = new PwmdTreeWidgetItemData (target, str);
5268 item->setData (0, Qt::UserRole, QVariant::fromValue (data));
5270 if (target)
5272 QBrush br = item->foreground (0);
5274 br.setColor (targetColor);
5275 item->setForeground (0, br);
5278 item->setFlags (item->flags () | Qt::ItemIsEditable);
5281 class ElementPath
5283 public:
5284 ElementPath (QString path, QString flags = 0)
5286 _path = path;
5287 _flags = flags;
5289 ~ElementPath () { };
5290 QString path () { return _path; };
5291 QString flags () { return _flags; };
5293 class ElementAttr
5295 public:
5296 ElementAttr (QString name, QString value = 0)
5298 _name = name;
5299 _value = value;
5301 ~ElementAttr () { };
5302 QString name () { return _name; };
5303 QString value () { return _value; };
5305 private:
5306 QString _name;
5307 QString _value;
5310 QList <ElementAttr *> attrs () { return _attrs; };
5311 void setAttrs (QList <ElementAttr *>list) { _attrs = list; };
5312 private:
5313 QString _path;
5314 QString _flags;
5315 QList <ElementAttr *> _attrs;
5318 #define EXTRACT_TOKEN(ba,offset,ns,n,var) \
5319 offset = 0; \
5320 ns.clear (); \
5321 foreach (QChar c, ba) \
5323 if (c.isDigit ()) \
5324 ns += QString::number (c.digitValue ()); \
5325 else \
5326 break; \
5327 offset++; \
5329 n = ns.toUInt (nullptr); \
5330 var = ba.mid (++offset, n); \
5331 ba = ba.mid (offset+n);
5333 QList <QTreeWidgetItem *>PwmdMainWindow::buildElementTree (QByteArray &sexp,
5334 bool withAttrs)
5336 QList <QTreeWidgetItem *>elementTree;
5337 QList <ElementPath *>elementPaths;
5338 int n;
5340 n = sexp.indexOf ("(4:path");
5341 if (sexp.isEmpty () || !sexp.startsWith ("(11:list-result(") || n != 15)
5342 return elementTree;
5344 sexp = sexp.mid (n);
5345 while (sexp.startsWith ("(4:path"))
5347 QString ns, path, flags;
5348 unsigned offset = 0;
5350 sexp = sexp.mid (7); // "(4:path"
5351 EXTRACT_TOKEN (sexp, offset, ns, n, path);
5352 sexp = sexp.mid (7); // 7 = "5:flags"
5353 EXTRACT_TOKEN (sexp, offset, ns, n, flags);
5354 sexp = sexp.mid (8); // 8 = "(5:attrs"
5355 ElementPath *p = new ElementPath (path, flags);
5356 QList <ElementPath::ElementAttr *> attrs;
5358 do {
5359 QString name, value;
5360 EXTRACT_TOKEN (sexp, offset, ns, n, name);
5361 EXTRACT_TOKEN (sexp, offset, ns, n, value);
5362 attrs.append (new ElementPath::ElementAttr (name, value));
5363 } while (!sexp.isEmpty () && sexp.at (0) != ')');
5365 p->setAttrs (attrs);
5366 elementPaths.append (p);
5367 sexp = sexp.mid (2); // 2 = "))"
5370 ui.elementTree->setSortingEnabled (false);
5372 foreach (ElementPath *path, elementPaths)
5374 QStringList elements = path->path ().split ("\t");
5375 QTreeWidgetItem *parent = nullptr;
5376 bool nomatch = true;
5377 QTreeWidgetItem *item;
5378 int element = 1;
5379 bool isBad = hasFlag (path->flags (), "EO");
5380 QString target;
5382 if (hasFlag (path->flags (), "T"))
5384 foreach (ElementPath::ElementAttr *a, path->attrs ())
5386 if (a->name () == "_target")
5388 target = a->value ();
5389 break;
5394 for (int n = 0; n < elementTree.count (); n++)
5396 QTreeWidgetItem *root = elementTree.at (n);
5398 if (elementName (root) == elements.at (0))
5400 parent = root;
5401 break;
5405 if (!parent)
5407 parent = new QTreeWidgetItem ((QTreeWidget *) 0);
5408 setItemText (parent, elements.at (0), !target.isEmpty ());
5409 elementTree.append (parent);
5410 hasBadTarget (parent, target, path->flags ());
5411 if (isBad)
5412 goto attrs;
5415 while ((item = parent->child (0)))
5417 if (elements.at (element) == elementName (item))
5419 element++;
5420 parent = item;
5421 continue;
5424 for (int i = 0; i < parent->childCount (); i++)
5426 QTreeWidgetItem *
5427 tmp = parent->child (i);
5429 if (elementName (parent->child (i)) == elements.at (element))
5431 parent = tmp;
5432 element++;
5433 nomatch = false;
5434 break;
5438 if (element == elements.count () || nomatch)
5439 break;
5441 nomatch = true;
5444 if (nomatch)
5446 for (; element < elements.count (); element++)
5448 parent = new QTreeWidgetItem (parent);
5449 setItemText (parent, elements.at (element), !target.isEmpty ());
5450 hasBadTarget (parent, target, path->flags ());
5453 else if (parent)
5455 qWarning("%s(%i): %s", __FILE__, __LINE__, __FUNCTION__);
5456 /* A path has been parsed whose current element is the parent of
5457 * previously parsed element. Update the target and flags for the
5458 * previously created item, if any. */
5459 hasBadTarget (parent, target, path->flags ());
5462 attrs:
5463 foreach (ElementPath::ElementAttr *a, path->attrs ())
5465 if (withAttrs)
5466 setAttribute (parent, a->name (), a->value (), true);
5467 delete a;
5470 delete path;
5473 return elementTree;
5476 QString
5477 PwmdMainWindow::elementPath (const QTreeWidgetItem *item, const QString &sep)
5479 QString path = QString ();
5481 if (!item)
5482 return path;
5486 path.prepend (QString ("%1%2").arg (sep, elementName (item)));
5487 item = item->parent ();
5489 while (item);
5491 path.remove (0, sep.length ());
5492 return path;
5495 QString
5496 PwmdMainWindow::elementPath (const QString &sep)
5498 return elementPath (selectedElement, sep);
5501 void
5502 PwmdMainWindow::slotNewRootElement ()
5504 createNewElement (true);
5507 void
5508 PwmdMainWindow::slotNewSiblingElement ()
5510 createNewElement (!selectedElement ? true : false, true);
5513 void
5514 PwmdMainWindow::slotNewElement ()
5516 bool b = false;
5518 if (selectedElement)
5520 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(selectedElement->data (0, Qt::UserRole));
5521 if (data->badTarget () || data->badPermissions ())
5522 b = true;
5525 createNewElement (!selectedElement || b);
5528 void
5529 PwmdMainWindow::slotNewSiblingElementFromPopup ()
5531 QPoint pos = ui.elementTree->mousePosition ();
5532 QTreeWidgetItem *item = ui.elementTree->itemAt (pos.x (), pos.y ());
5534 createNewElement (!item ? true : false, true);
5537 void
5538 PwmdMainWindow::slotNewElementFromPopup ()
5540 QPoint pos = ui.elementTree->mousePosition ();
5541 QTreeWidgetItem *item = ui.elementTree->itemAt (pos.x (), pos.y ());
5543 createNewElement (item == nullptr);
5546 void
5547 PwmdMainWindow::createNewElement (bool root, bool sibling)
5549 QTreeWidgetItem *item = selectedElement;
5550 newElement = nullptr;
5552 doSaveContent (true);
5553 if (!root && !selectedElement)
5555 pwm->runQueue ();
5556 return;
5559 if (!root)
5561 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(selectedElement->data (0, Qt::UserRole));
5562 if (data->badPermissions ())
5564 pwm->runQueue ();
5565 return;
5568 if (sibling && item)
5569 item = item->parent ();
5571 if (!item)
5572 root = true;
5575 newElement = new QTreeWidgetItem ((QTreeWidget *) 0);
5576 setItemText (newElement, tr ("New element"));
5578 if (root)
5580 ui.elementTree->setHorizontalScrollMode (QAbstractItemView::ScrollPerItem);
5581 ui.elementTree->addTopLevelItem (newElement);
5583 else if (dropElement)
5585 dropElement->addChild (newElement);
5586 dropElement->setExpanded (true);
5588 else
5590 item->addChild (newElement);
5591 item->setExpanded (true);
5594 setElementItemsEnabled (newElement, false);
5595 previousElement = item;
5596 attributeElement = selectedElement = newElement;
5597 ui.elementTree->scrollToItem (newElement,
5598 QAbstractItemView::PositionAtCenter);
5599 ui.elementTree->editItem (newElement);
5600 ui.elementTree->setHorizontalScrollMode (QAbstractItemView::ScrollPerPixel);
5603 void
5604 PwmdMainWindow::setModifiedContent (bool b)
5606 setWindowModified (true);
5607 setFileModified (b);
5610 void
5611 PwmdMainWindow::deleteElementFinalize (PwmdCommandQueueItem *cmd)
5613 if (!cmd->isFinalItem ())
5614 return;
5616 if (ui.tabWidget->currentWidget () != ui.commandTab)
5617 ui.tabWidget->setCurrentWidget (ui.contentTab);
5619 if (hasMultiSelection ())
5620 selectedElementPath = QString ();
5622 setModified ();
5623 clearAttributeTab ();
5624 refreshElementTree ();
5627 void
5628 PwmdMainWindow::slotDeleteElement ()
5630 if (!selectedElement)
5631 return;
5633 saveExpandedItemList ();
5634 selectedElementPath = elementPath (ui.elementTree->itemBelow (selectedElement));
5635 if (selectedElementPath.isEmpty ())
5636 selectedElementPath = elementPath (ui.elementTree->itemAbove (selectedElement));
5638 if (selectedElementPath.isEmpty ())
5639 selectedElement = nullptr;
5641 QList <QTreeWidgetItem *> itemsToRemove;
5642 QList <QTreeWidgetItem *> itemsToRemoveWithTarget;
5643 QList <QTreeWidgetItem *> items = ui.elementTree->selectedItems ();
5644 foreach (QTreeWidgetItem *item, items)
5646 if (hasTarget (item))
5648 if (!itemsToRemoveWithTarget.contains (item) && isElementOwner (item))
5649 itemsToRemoveWithTarget.append (item);
5650 continue;
5653 QTreeWidgetItem *r = resolveElementPath (item);
5654 if (!itemsToRemove.contains (r) && isElementOwner (r))
5655 itemsToRemove.append (r);
5658 items = itemsToRemove;
5659 again:
5660 foreach (QTreeWidgetItem *item, items)
5662 PwmdCommandQueueItem *cmd;
5663 PwmdInquireData *inq = new PwmdInquireData (elementPath (item));
5664 cmd = new PwmdCommandQueueItem (PwmdCmdIdDeleteElement, "DELETE",
5665 Pwmd::inquireCallback, inq);
5666 ignoreErrors (cmd);
5667 pwm->command (cmd, true);
5670 if (items != itemsToRemoveWithTarget)
5672 items = itemsToRemoveWithTarget;
5673 goto again;
5676 pwm->runQueue ();
5679 void
5680 PwmdMainWindow::slotRenameElement ()
5682 doSaveContent (true);
5684 if (!selectedElement)
5686 pwm->runQueue ();
5687 return;
5690 setElementItemsEnabled (nullptr, false);
5691 selectedElementPath = elementPath (selectedElement);
5692 ui.elementTree->editItem (selectedElement);
5695 bool
5696 PwmdMainWindow::editElements ()
5698 return refreshElementTree ();
5701 void
5702 PwmdMainWindow::slotEditElements ()
5704 editElements ();
5707 QTreeWidgetItem *
5708 PwmdMainWindow::resolveElementPath (QTreeWidgetItem * item)
5710 QString path = QString ();
5711 QTreeWidgetItem *i;
5713 for (i = item; i; i = i->parent ())
5715 PwmdTreeWidgetItemData *data =
5716 qvariant_cast < PwmdTreeWidgetItemData * >(i->data (0, Qt::UserRole));
5718 if (data->badTarget ())
5719 return nullptr;
5721 if (hasTarget (i))
5723 path.prepend (data->target ());
5724 return resolveElementPath (findElement (path));
5727 path.prepend (elementName (i));
5728 path.prepend ("\t");
5731 if (path.isEmpty ())
5732 return nullptr;
5734 if (path.at (0) == '\t')
5735 path.remove (0, 1);
5737 return findElement (path);
5740 QTreeWidgetItem *
5741 PwmdMainWindow::resolveTargets (QTreeWidgetItem * item)
5743 return resolveElementPath (item);
5746 void
5747 PwmdMainWindow::slotResolveTargets ()
5749 QTreeWidgetItem *i = resolveTargets (selectedElement);
5751 if (i)
5753 ui.elementTree->setHorizontalScrollMode (QAbstractItemView::ScrollPerItem);
5754 ui.elementTree->setCurrentItem (i);
5755 selectedElement = i;
5756 ui.elementTree->scrollToItem (selectedElement,
5757 QAbstractItemView::PositionAtCenter);
5758 ui.elementTree->setHorizontalScrollMode (QAbstractItemView::ScrollPerPixel);
5762 QTreeWidgetItem *
5763 PwmdMainWindow::findElement (const QString &path, QTreeWidgetItem *skip,
5764 bool any, PwmdTreeWidget *tree)
5766 if (path.isEmpty ())
5767 return nullptr;
5769 QStringList elements = path.split ("\t");
5770 int element = 0;
5771 if (!tree)
5772 tree = ui.elementTree;
5773 QTreeWidgetItem *item = tree->invisibleRootItem ();
5774 QTreeWidgetItem *match = nullptr;
5776 while (element < elements.count ())
5778 match = nullptr;
5780 for (int n = 0, t = item->childCount (); n < t; n++)
5782 QTreeWidgetItem *child = item->child (n);
5784 if (skip && child == skip)
5785 continue;
5787 if ((any && child->text (0) == elements.at (element)) ||
5788 elementName (child) == elements.at (element))
5790 match = item = child;
5791 break;
5795 element++;
5798 return match;
5801 gpg_error_t
5802 PwmdMainWindow::doDndCopyElement (const QString & src, const QString & dst,
5803 bool queue)
5805 PwmdInquireData *inq = new PwmdInquireData (QString ("%1 %2").arg (src, dst));
5806 inq->setUser (dst);
5807 PwmdCommandQueueItem *cmd = new PwmdCommandQueueItem (PwmdCmdIdDndCopyElement, "COPY", Pwmd::inquireCallback, inq);
5808 ignoreErrors (cmd, true);
5809 pwm->command (cmd, queue);
5810 return 0;
5813 void
5814 PwmdMainWindow::dndCopyMoveElementFinalize (const QString &path)
5816 saveExpandedItemList ();
5817 selectedElementPath = path;
5818 QEvent *ev = new QEvent (QEvent::Type (PwmdEventRefreshElementTree));
5819 QCoreApplication::postEvent (this, ev);
5822 void
5823 PwmdMainWindow::slotDndCopyElement ()
5825 dndOperation = DndCopy;
5826 if (doSaveContent (true))
5828 nextElement = selectedElement;
5829 pwm->runQueue ();
5830 return;
5833 bool b = false;
5835 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
5837 QString srcPath = elementPath (item);
5838 QString dst = elementPath (dropElement);
5839 int n = srcPath.lastIndexOf ('\t') + 1;
5841 if (dst.isEmpty () || srcPath != dst
5842 || (srcPath == dst && !findElement (dst + '\t' + dst)))
5844 QString e = srcPath.mid (n);
5846 if (dst.isEmpty ())
5847 dst = e;
5848 else
5849 dst.append (QString ("\t%1").arg (e));
5851 if (!findElement (dst))
5853 b = true;
5854 doDndCopyElement (srcPath, dst, true);
5859 copyElement = selectedElement;
5860 if (b)
5862 pwm->runQueue ();
5863 return;
5866 createNewElement (dropElement == nullptr);
5869 void
5870 PwmdMainWindow::slotDndMoveElement ()
5872 dndOperation = DndMove;
5873 if (doSaveContent (true))
5875 nextElement = selectedElement;
5876 pwm->runQueue ();
5877 return;
5880 bool match = false;
5882 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
5884 if (dropElement == item || isParentOf (item, dropElement))
5885 continue;
5887 match = true;
5888 QString args = QString ("%1 ").arg (elementPath (item));
5889 QString s;
5890 if (dropElement)
5892 s = elementPath (dropElement);
5893 args.append (QString ("%1").arg (s));
5896 PwmdInquireData *inq = new PwmdInquireData (args);
5897 if (!dropElement)
5898 inq->setUser (item->text (0));
5899 else
5900 inq->setUser (s + "\t" + item->text (0));
5902 PwmdCommandQueueItem *cmd = new PwmdCommandQueueItem (PwmdCmdIdDndMoveElement, "MOVE", Pwmd::inquireCallback, inq);
5903 ignoreErrors (cmd, true);
5904 pwm->command (cmd, true);
5907 if (!match)
5909 dndOperation = DndNone;
5910 return;
5913 pwm->runQueue ();
5916 void
5917 PwmdMainWindow::dndCreateTargetFinalize ()
5919 selectedElementPath = elementPath (selectedElement);
5920 QEvent *ev = new QEvent (QEvent::Type (PwmdEventDnDTargetAttr));
5921 QCoreApplication::postEvent (this, ev);
5924 void
5925 PwmdMainWindow::slotDndCreateTarget ()
5927 dndOperation = DndTarget;
5928 if (doSaveContent (true))
5930 nextElement = selectedElement;
5931 pwm->runQueue ();
5932 return;
5935 bool b = false;
5937 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
5939 if (!dropElement || dropElement == item)
5940 continue;
5942 b = true;
5943 QString dropPath = elementPath (dropElement);
5944 PwmdInquireData *inq = new PwmdInquireData (QString ("SET _target %1 %2").arg (elementPath (item), dropPath));
5945 inq->setUser (dropPath);
5946 inq->setUserPtr (item);
5947 PwmdCommandQueueItem *cmd = new PwmdCommandQueueItem (PwmdCmdIdDndCreateTarget, "ATTR", Pwmd::inquireCallback, inq);
5948 ignoreErrors (cmd, true);
5949 pwm->command (cmd, true);
5952 if (!b)
5954 dndOperation = DndNone;
5955 return;
5958 pwm->runQueue ();
5961 bool
5962 PwmdMainWindow::isParentOf (QTreeWidgetItem * a, QTreeWidgetItem * b)
5964 if (!a || !b)
5965 return false;
5967 while (b)
5969 b = b->parent ();
5971 if (b == a)
5972 return true;
5975 return false;
5978 static void
5979 recurseExpand (QTreeWidgetItem * item, bool b)
5981 item->setExpanded (b);
5983 for (int i = 0; i < item->childCount (); i++)
5985 QTreeWidgetItem *tmp = item->child (i);
5987 recurseExpand (tmp, b);
5991 void
5992 PwmdMainWindow::slotExpandElement ()
5994 setBusy ();
5995 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
5996 recurseExpand (item, true);
5997 setBusy (false);
6000 void
6001 PwmdMainWindow::slotCollapseElement ()
6003 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
6004 recurseExpand (item, false);
6007 void
6008 PwmdMainWindow::slotMenuItemHovered (QAction * a)
6010 QStatusBar *sb = statusBar ();
6011 sb->showMessage (a->statusTip ());
6014 void
6015 PwmdMainWindow::popupContextMenu ()
6017 QTreeWidgetItem *item = selectedElement;
6019 selectedElement = ui.elementTree->currentItem ();
6020 selectedAttribute = ui.attributeList->currentItem ();
6022 PwmdTreeWidgetItemData *data = nullptr;
6023 if (item)
6024 data = qvariant_cast < PwmdTreeWidgetItemData * >(selectedElement->data (0, Qt::UserRole));
6026 QMenu *m = new QMenu (this);
6027 connect (m, SIGNAL (hovered (QAction *)), this,
6028 SLOT (slotMenuItemHovered (QAction *)));
6029 QAction *a = m->addAction (tr ("New &root element"), this,
6030 SLOT (slotNewRootElement ()));
6031 a->setEnabled (fixupMenu (ui.menuElement, ElementMenuNewRoot, false, false, true));
6032 a = m->addAction (tr ("&New child element"), this,
6033 SLOT (slotNewElementFromPopup ()));
6034 a->setEnabled (fixupMenu (ui.menuElement, ElementMenuNew, false, false, true));
6035 a = m->addAction (tr ("New &sibling element"), this,
6036 SLOT (slotNewSiblingElementFromPopup ()));
6037 a->setEnabled (fixupMenu (ui.menuElement, ElementMenuNewSibling, false, false, true));
6038 a = m->addAction (tr ("Rena&me"), this, SLOT (slotRenameElement ()));
6039 a->setEnabled (fixupMenu (ui.menuElement, ElementMenuRename, false, false, true));
6040 a = m->addAction (tr ("&Delete"), this, SLOT (slotDeleteElement ()));
6041 a->setEnabled (fixupMenu (ui.menuElement, ElementMenuDelete, false, false, true));
6043 m->addSeparator ();
6044 if (ui.tabWidget->currentWidget () != ui.contentTab)
6045 a = m->addAction (tr ("&Edit content"), this,
6046 SLOT (slotEditContent ()));
6047 else
6048 a = m->addAction (tr ("&Edit attributes"), this,
6049 SLOT (slotEditAttributes ()));
6050 a->setEnabled (fixupMenu (ui.menuElement, ElementMenuEditContent, false, false, true));
6051 a = m->addAction (tr ("Resolve &targets"), this,
6052 SLOT (slotResolveTargets ()));
6053 a->setEnabled (fixupMenu (ui.menuElement, ElementMenuResolveTargets, false, false, true));
6055 m->addSeparator ();
6056 a = m->addAction (tr ("E&xpand"), this, SLOT (slotExpandElement ()));
6057 a->setEnabled (fixupMenu (ui.menuView, ViewMenuExpandAll, false, false, true));
6058 a->setEnabled (item && !data->badPermissions () && !data->badTarget ());
6059 a = m->addAction (tr ("Colla&pse"), this, SLOT (slotCollapseElement ()));
6060 a->setEnabled (item && !data->badPermissions () && !data->badTarget ());
6061 a = m->addAction (tr ("Cop&y element path"), this,
6062 SLOT (slotClipboardElementPath ()));
6063 a->setEnabled (item);
6065 m->exec (QCursor::pos ());
6066 delete m;
6069 bool
6070 PwmdMainWindow::isInvoker ()
6072 return !connectedUser.isEmpty() && !invokingUser.isEmpty ()
6073 && invokingUser.indexOf (connectedUser) != -1;
6076 bool
6077 PwmdMainWindow::isElementOwner (QTreeWidgetItem *item, bool secondary)
6079 if (!item)
6080 return false;
6082 if (isInvoker ())
6083 return true;
6085 PwmdTreeWidgetItemData *data = item ? qvariant_cast <PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole)) : nullptr;
6086 if (data)
6088 QString acl = data->attribute("_acl");
6089 QStringList list = acl.split(",");
6091 if (!list.isEmpty ()
6092 && (list.at(0) == connectedUser || list.at(0).isEmpty ()))
6093 return true;
6095 if (secondary)
6097 foreach (QString user, list)
6099 if (user == connectedUser)
6100 return true;
6105 return false;
6108 void
6109 PwmdMainWindow::setTimeLabels (QTreeWidgetItem * item, bool needsRefresh)
6111 if (!item)
6113 ui.l_ctime->setText (tr ("-"));
6114 ui.l_mtime->setText (tr ("-"));
6115 return;
6118 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
6120 if (needsRefresh) {
6121 if (item == attributeElement)
6123 ui.l_ctime->setText (tr("click refresh to update"));
6124 ui.l_mtime->setText (tr("click refresh to update"));
6127 data->setNeedsAttrRefresh ();
6128 return;
6131 QString s = data->attribute ("_ctime");
6132 unsigned t = s.toUInt ();
6133 QDateTime date;
6135 date.setSecsSinceEpoch (t);
6136 ui.l_ctime->setText (!s.isEmpty () ? date.toString () : tr ("-"));
6137 s = data->attribute ("_mtime");
6138 t = s.toUInt ();
6139 date.setSecsSinceEpoch (t);
6140 ui.l_mtime->setText (!s.isEmpty () ? date.toString () : tr ("-"));
6143 void
6144 PwmdMainWindow::slotEditTargetAttributes (bool b)
6146 if (doSaveContent (true))
6148 editTargetAttributes = b;
6149 disconnect (ui.actionToggleTargetAttribute, SIGNAL (triggered (bool)), this, SLOT (slotEditTargetAttributes(bool)));
6150 ui.actionToggleTargetAttribute->setChecked (!b);
6151 connect (ui.actionToggleTargetAttribute, SIGNAL (triggered (bool)), this, SLOT (slotEditTargetAttributes(bool)));
6152 pwm->runQueue ();
6153 return;
6156 editAttributes (b, selectedElement);
6159 void
6160 PwmdMainWindow::fixupEditContentOrAttributes (bool attrs)
6162 if (attrs)
6164 disconnect (ui.actionEditContentOrAttributes, SIGNAL (triggered()), this, SLOT (slotEditContent ()));
6165 ui.actionEditContentOrAttributes->setText (tr ("&Edit attributes"));
6166 connect (ui.actionEditContentOrAttributes, SIGNAL (triggered()), this, SLOT (slotEditAttributes ()));
6167 return;
6170 disconnect (ui.actionEditContentOrAttributes, SIGNAL (triggered()), this, SLOT (slotEditAttributes ()));
6171 ui.actionEditContentOrAttributes->setText (tr ("&Edit content"));
6172 connect (ui.actionEditContentOrAttributes, SIGNAL (triggered()), this, SLOT (slotEditContent ()));
6175 void
6176 PwmdMainWindow::slotEditAttributes ()
6178 fixupEditContentOrAttributes ();
6179 targetAttributeElement = nullptr;
6180 ui.actionToggleTargetAttribute->setChecked (false);
6181 editAttributes (false, selectedElement);
6184 void
6185 PwmdMainWindow::setElementColors (QTreeWidgetItem *item, bool reset,
6186 bool target)
6188 PwmdTreeWidgetItemData *data = nullptr;
6190 if (targetAttributeElement && target)
6192 if (!reset)
6194 QBrush b = targetAttributeElement->foreground (0);
6195 QBrush bg(QPalette::Window);
6197 b.setColor (hilightColor);
6198 targetAttributeElement->setForeground (0, b);
6199 bg.setColor(hilightColorBg);
6200 targetAttributeElement->setBackground(0, bg);
6201 return;
6205 again:
6206 if (item)
6207 data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
6208 else
6209 return;
6211 if (!data)
6213 if (targetAttributeElement)
6215 item = targetAttributeElement;
6216 goto again;
6218 return;
6221 if (data->badPermissions ())
6223 QBrush br = item->foreground (0);
6224 QBrush bg(QPalette::Window);
6226 br.setColor (permissionsColor);
6227 item->setForeground (0, br);
6228 bg.setColor(permissionsColorBg);
6229 item->setBackground(0, bg);
6231 else if (data->targetLoop ())
6233 QBrush br = item->foreground (0);
6234 QBrush bg(QPalette::Window);
6236 bg.setColor(targetLoopColorBg);
6237 item->setBackground(0, bg);
6239 br.setColor (targetLoopColor);
6240 item->setForeground (0, br);
6242 else if (data->badTarget ())
6244 QBrush br = item->foreground (0);
6245 QBrush bg(QPalette::Window);
6247 bg.setColor(invalidTargetColorBg);
6248 item->setBackground(0, bg);
6250 br.setColor (invalidTargetColor);
6251 item->setForeground (0, br);
6253 else if (data->hasTarget ())
6255 QBrush br = item->foreground (0);
6256 QBrush bg(QPalette::Window);
6258 bg.setColor(targetColorBg);
6259 item->setBackground(0, bg);
6261 br.setColor (targetColor);
6262 item->setForeground (0, br);
6264 else
6266 QBrush b (QPalette::Text);
6267 b.setColor ("Black");
6268 item->setForeground (0, b);
6269 b.setColor ("White");
6270 item->setBackground (0, b);
6273 if (item != targetAttributeElement)
6275 item = targetAttributeElement;
6276 targetAttributeElement = nullptr;
6277 ui.actionToggleTargetAttribute->setChecked (false);
6278 editTargetAttributes = false;
6279 goto again;
6283 void
6284 PwmdMainWindow::editAttributes (bool resolve, QTreeWidgetItem *item)
6286 if (doSaveContent (true))
6288 editTargetAttributes = resolve;
6289 pwm->runQueue ();
6290 return;
6293 if (!item)
6295 fixupMenu (ui.menuAttributes, AttributeMenuTarget,
6296 hasTarget (item) && !badTarget (item),
6297 hasTarget (item) && !badTarget (item)
6298 && ui.actionToggleTargetAttribute->isChecked ());
6299 pwm->runQueue ();
6300 return;
6303 editTargetAttributes = resolve;
6304 setElementItemsEnabled (item, item);
6305 QTreeWidgetItem *titem = !resolve ? item : resolveTargets (item);
6306 targetAttributeElement = resolve ? titem : targetAttributeElement;
6307 attributeElement = titem ? titem : item;
6309 PwmdTreeWidgetItemData *data = attributeElement ? qvariant_cast < PwmdTreeWidgetItemData * >(attributeElement->data (0, Qt::UserRole)) : nullptr;
6310 if (!resolve && data)
6312 if (data->badPermissions () && data->hasTarget ())
6313 attributeElement = findElement (elementPath (attributeElement));
6316 refreshAttributeList (attributeElement);
6317 setAttributeItemsEnabled ();
6318 ui.attributeList->setCurrentRow (previousAttributeIndex ());
6319 setElementColors (targetAttributeElement, !resolve, true);
6320 ui.elementTree->scrollToItem (attributeElement);
6323 void
6324 PwmdMainWindow::slotAttrSearchAndReplaceChanged (const QString &)
6326 ui.pb_searchAndReplace->setEnabled (ui.cb_searchAndReplace->lineEdit ()->hasAcceptableInput ());
6329 void
6330 PwmdMainWindow::slotAttrSearchAndReplace ()
6332 QString str = ui.cb_searchAndReplace->lineEdit ()->text ();
6333 bool escaped = false;
6334 bool doingReplace = false;
6335 QString pattern, replace;
6337 foreach (QChar c, str.mid (1))
6339 if (doingReplace || (c == '/' && !escaped))
6341 if (!doingReplace)
6342 doingReplace = true;
6343 else
6344 replace += c;
6345 continue;
6348 if (escaped)
6350 pattern += c;
6351 escaped = false;
6352 continue;
6355 escaped = c == '\\';
6356 if (!escaped)
6357 pattern += c;
6360 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
6362 searchAndReplace = true;
6363 PwmdTreeWidgetItemData *data = qvariant_cast <PwmdTreeWidgetItemData *>(item->data (0, Qt::UserRole));
6364 QString value = data->attribute (selectedAttribute->text ());
6365 QRegularExpression rx (pattern);
6366 QRegularExpressionMatch match = rx.match (value);
6367 if (match.hasMatch ()) {
6368 value.replace (rx, replace);
6369 while (value.endsWith (','))
6370 value.chop (1);
6372 setAttributeContentOnce (item, elementPath (item), value, false,
6373 false, true);
6374 fixupToolBar (attributeToolBar, AttributeToolBarOk, true);
6378 ui.cb_searchAndReplace->lineEdit ()->setText ("");
6379 pwm->runQueue ();
6382 void
6383 PwmdMainWindow::slotRefreshAttributes ()
6385 refreshAttributeList (attributeElement, true);
6388 void
6389 PwmdMainWindow::clearAttributeTab ()
6391 setTimeLabels (nullptr);
6392 disconnect (ui.attributeContent, SIGNAL (modificationChanged (bool)), this,
6393 SLOT (slotContentModified (bool)));
6394 disconnect (ui.attributeContent, SIGNAL (textChanged ()), this, SLOT (slotContentChanged ()));
6395 ui.attributeContent->document()->setPlainText ("");
6396 ui.attributeList->clear ();
6397 connect (ui.attributeContent, SIGNAL (textChanged ()), SLOT (slotContentChanged ()));
6398 connect (ui.attributeContent, SIGNAL (modificationChanged (bool)), this,
6399 SLOT (slotContentModified (bool)));
6402 void
6403 PwmdMainWindow::refreshAttributeListFinalize (QTreeWidgetItem *element,
6404 const QString &result,
6405 bool create, bool fromTree)
6407 if (create && !createAttributeCache (element, result))
6408 return;
6410 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(element->data (0, Qt::UserRole));
6411 PwmdAttributeList *attrs = data->attributes ();
6412 QList<QListWidgetItem *> items;
6414 for (int i = 0, t = attrs->count (); i < t; i++)
6416 PwmdElementAttr *attr = attrs->at (i);
6417 QListWidgetItem *item = new QListWidgetItem (attr->name ());
6419 if (PwmdElementAttr::isProtectedAttribute (attr->name ()))
6420 item->setFlags (~(item->flags () & Qt::ItemIsEnabled));
6422 items.append (item);
6425 if (!editTargetAttributes && !fromTree)
6426 selectedElement = attributeElement = element;
6428 if (ui.tabWidget->currentWidget () == ui.attributeTab
6429 && (element == attributeElement || element == targetAttributeElement
6430 || element == ui.elementTree->currentItem ()))
6432 setAttributeContent ("");
6433 selectedAttribute = nullptr;
6434 clearAttributeTab ();
6435 data = qvariant_cast <PwmdTreeWidgetItemData *>(element->data (0, Qt::UserRole));
6437 foreach (QListWidgetItem *item, items)
6439 if (!PwmdElementAttr::isReservedAttribute (item->text ())
6440 && data->hasTarget ())
6442 QBrush b = item->foreground ();
6443 QBrush bg(QPalette::Window);
6445 b.setColor (hilightColor);
6446 item->setForeground (b);
6447 bg.setColor(hilightColorBg);
6448 item->setBackground(bg);
6451 ui.attributeList->addItem (item);
6454 setTimeLabels (element, data->needsAttrRefresh ());
6456 else if (ui.tabWidget->currentWidget () == ui.contentTab
6457 && element == selectedElement)
6459 foreach (QListWidgetItem *item, items)
6460 delete item;
6462 else
6464 foreach (QListWidgetItem *item, items)
6465 delete item;
6468 if (isElementOwner (element))
6469 element->setFlags (element->flags () | Qt::ItemIsEditable);
6471 fixupToolBar (attributeToolBar, AttributeToolBarOk, false);
6472 ui.attributeList->setCurrentRow (previousAttributeIndex ());
6474 /* The item attributes are wanted during a DnD operation. Emit a signal
6475 to tell the element tree that certain popup context menu items may be
6476 available. */
6477 if (fromTree)
6479 AttributesRetrievedEvent *ev = new AttributesRetrievedEvent
6480 (QEvent::Type (PwmdEventAttrsRetrieved));
6481 ev->setItem (element);
6482 QCoreApplication::postEvent (this, ev);
6486 bool
6487 PwmdMainWindow::refreshAttributeList (QTreeWidgetItem *item, bool force,
6488 bool queue, bool fromTree)
6490 if (!item)
6492 clearAttributeTab ();
6493 return false;
6496 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
6497 if (!data->attributes ()->isEmpty () && !force)
6499 refreshAttributeListFinalize (item, QString (), false, fromTree);
6500 if (!queue)
6501 pwm->runQueue ();
6502 return false;
6505 if (!fromTree && item != selectedElement)
6506 clearAttributeTab ();
6508 PwmdInquireData *inq = new PwmdInquireData (QString ("LIST %1").arg (elementPath (item)));
6509 inq->setUserPtr (item);
6510 inq->setUserBool (fromTree);
6511 pwm->command (new PwmdCommandQueueItem (PwmdCmdIdAttrList, "ATTR",
6512 Pwmd::inquireCallback, inq), queue);
6513 return true;
6516 void
6517 PwmdMainWindow::slotAttributeEditorClosed (QWidget * w,
6518 QAbstractItemDelegate::EndEditHint h)
6520 QLineEdit *le = qobject_cast < QLineEdit * >(w);
6521 QString s = le->text ();
6522 int pos = 0;
6524 if (le->text () == tr ("New attribute") || le->text ().isEmpty () ||
6525 h == QAbstractItemDelegate::RevertModelCache
6526 || (le->validator() && le->validator()->validate (s, pos) == QValidator::Invalid))
6528 QEvent *ev = new QEvent (QEvent::Type (PwmdEventNewAttributeCancel));
6529 QCoreApplication::postEvent (this, ev);
6530 return;
6534 /* Update element attributes for all items that have a target to "item", or
6535 * just the "item" itself. */
6536 void
6537 PwmdMainWindow::updateTargetAttributes (QTreeWidgetItem *item,
6538 const QString &name,
6539 const QString &value, bool remove)
6541 /* Cannot set time attributes and other reserved attributes require a tree
6542 * refresh. */
6543 if (PwmdElementAttr::isReservedAttribute (name))
6544 return;
6546 QTreeWidgetItem *t = resolveTargets (item);
6547 if (!t)
6548 return;
6550 foreach (QTreeWidgetItem *e, ui.elementTree->findItems ("*",
6551 Qt::MatchRecursive
6552 |Qt::MatchWildcard))
6554 if (resolveTargets (e) != t)
6555 continue;
6557 if (remove)
6558 removeAttribute (e, name);
6559 else
6560 setAttribute (e, name, value);
6563 setTimeLabels (t, true);
6566 void
6567 PwmdMainWindow::setNewAttributeFinalize (AttributeFinalizeT *attr,
6568 bool fromSlot)
6570 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(attr->item->data (0, Qt::UserRole));
6571 setAttribute (attr->item, attr->name, attr->content, true);
6572 setModified ();
6574 if (attr->item != selectedElement && attr->item != attributeElement)
6575 return;
6577 if (attr->name == "password"
6578 && ui.tabWidget->currentWidget() == ui.contentTab)
6580 elementSelected (selectedElement);
6581 return;
6583 else if (attr->name == "_expire"
6584 && ui.tabWidget->currentWidget () == ui.contentTab)
6586 setElementExpireLabel (attr->content.toUInt ());
6587 setContentItemsEnabled ();
6589 else if (attr->name == "hidden")
6591 ui.textEdit->setHidden (true);
6592 ui.hiddenContentFrame->setHidden (data->hasAttribute ("password"));
6593 disconnect (ui.actionHidden, SIGNAL (toggled(bool)), this, SLOT (slotToggleHiddenContent(bool)));
6594 ui.actionHidden->setChecked (true);
6595 connect (ui.actionHidden, SIGNAL (toggled(bool)), this, SLOT (slotToggleHiddenContent(bool)));
6597 else if (attr->name == "encoding" && attr->content == "base64")
6599 disconnect (ui.actionBase64, SIGNAL (triggered(bool)), this, SLOT (slotToggleBase64Content(bool)));
6600 ui.actionBase64->setChecked (true);
6601 connect (ui.actionBase64, SIGNAL (triggered(bool)), this, SLOT (slotToggleBase64Content(bool)));
6604 if (!attr->listItem)
6605 return;
6607 Qt::ItemFlags flags = attr->listItem->flags();
6608 flags &= ~Qt::ItemIsEditable;
6609 attr->listItem->setFlags(flags);
6611 if (ui.tabWidget->currentWidget () == ui.attributeTab)
6613 data = qvariant_cast < PwmdTreeWidgetItemData * >(attr->item->data (0, Qt::UserRole));
6614 if (!PwmdElementAttr::isReservedAttribute (attr->listItem->text ())
6615 && data->hasTarget ())
6617 QBrush b = attr->listItem->foreground ();
6618 QBrush bg(QPalette::Window);
6620 b.setColor (hilightColor);
6621 attr->listItem->setForeground (b);
6622 bg.setColor(hilightColorBg);
6623 attr->listItem->setBackground(bg);
6627 if (fromSlot && (attr->item == selectedElement
6628 || attr->item == attributeElement))
6630 slotAttributeSelected (attr->listItem, nullptr);
6631 ui.attributeList->setCurrentItem (attr->listItem);
6633 if (attr->name == "password")
6634 elementSelected (selectedElement);
6638 bool
6639 PwmdMainWindow::setNewAttribute (QTreeWidgetItem *item, QString name,
6640 QString value, bool fromSlot, bool queue)
6642 PwmdInquireData *inq = new PwmdInquireData (QString ("SET %1 %2 %3").arg (name, elementPath (item), value));
6644 AttributeFinalizeT *attr = new AttributeFinalizeT;
6645 attr->item = item;
6646 attr->listItem = newAttribute ? newAttribute->clone () : nullptr;
6647 attr->name = name;
6648 attr->content = value;
6649 inq->setUserPtr (attr);
6650 inq->setUserBool (fromSlot);
6651 PwmdCommandQueueItem *cmd = new PwmdCommandQueueItem (PwmdCmdIdAttrNew,
6652 "ATTR",
6653 Pwmd::inquireCallback,
6654 inq);
6655 ignoreErrors (cmd);
6656 return pwm->command (cmd, queue);
6659 bool
6660 PwmdMainWindow::attributeIsEditable (QTreeWidgetItem *item,
6661 QListWidgetItem *attr)
6663 if (!item || !attr)
6664 return false;
6666 PwmdTreeWidgetItemData *data = nullptr;
6667 data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
6668 if (!data)
6669 return false;
6671 if (attr->text() == "password")
6672 return false;
6674 if (PwmdElementAttr::isReservedAttribute (attr->text ()))
6675 return isElementOwner (item);
6677 return !data->hasError () && isElementOwner (item);
6680 void
6681 PwmdMainWindow::slotAttributeSelected (QListWidgetItem *item, QListWidgetItem *)
6683 if (ui.tabWidget->currentWidget () != ui.attributeTab)
6684 return;
6686 PwmdTreeWidgetItemData *data = nullptr;
6687 if (attributeElement)
6688 data = qvariant_cast < PwmdTreeWidgetItemData * >(attributeElement->data (0, Qt::UserRole));
6690 ui.attributeContent->setEnabled (item && (item->flags() & Qt::ItemIsEnabled));
6691 setAttributeItemsEnabled ();
6693 doSaveContent (true);
6694 if (!item)
6696 pwm->runQueue ();
6697 return;
6700 if (!(item->flags () & Qt::ItemIsEnabled))
6702 ui.attributeList->setCurrentItem (selectedAttribute);
6703 pwm->runQueue ();
6704 return;
6707 selectedAttribute = item;
6708 previousAttributeText = item->text ();
6709 QTreeWidgetItem *i = attributeElement;
6710 data = qvariant_cast < PwmdTreeWidgetItemData * >(i->data (0, Qt::UserRole));
6711 QString s = data->attribute (item->text ());
6712 setAttributeContent (s.isEmpty ()? "" : s.replace ("\t", "<TAB>", Qt::CaseInsensitive));
6714 if (item && item->text () == "_target")
6715 fixupToolBar (attributeToolBar, AttributeToolBarDelete, false);
6716 else if (item && item->text () == "_acl" && isElementOwner (selectedElement))
6717 fixupToolBar (attributeToolBar, AttributeToolBarDelete, true);
6719 ui.attributeContent->setReadOnly (!attributeIsEditable (attributeElement, item));
6722 void
6723 PwmdMainWindow::slotConfirmNewAttribute (QWidget * w)
6725 QLineEdit *le = qobject_cast < QLineEdit * >(w);
6727 if (!newAttribute)
6728 return;
6730 PwmdTreeWidgetItemData *data = qvariant_cast <PwmdTreeWidgetItemData *>(attributeElement->data (0, Qt::UserRole));
6732 if (le->text () == tr ("New attribute")
6733 || le->text ().isEmpty ()
6734 || PwmdElementAttr::isProtectedAttribute (le->text ()))
6736 QEvent *ev = new QEvent (QEvent::Type (PwmdEventNewAttributeCancel));
6737 QCoreApplication::postEvent (this, ev);
6738 return;
6741 bool match = false;
6742 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
6744 QTreeWidgetItem *ritem = resolveTargets (item);
6745 data = qvariant_cast <PwmdTreeWidgetItemData *>(item->data (0, Qt::UserRole));
6746 if (!data->hasAttribute (le->text ())
6747 && attributeIsEditable (item, newAttribute))
6749 data = qvariant_cast <PwmdTreeWidgetItemData *>(ritem->data (0, Qt::UserRole));
6750 if (ritem == item
6751 || (attributeIsEditable (ritem, newAttribute)
6752 && !data->hasAttribute (le->text ())))
6754 match = true;
6755 setNewAttribute (item, le->text (), QString (), true, true);
6760 delete newAttribute;
6761 newAttribute = nullptr;
6762 if (!match)
6764 QEvent *ev = new QEvent (QEvent::Type (PwmdEventNewAttributeCancel));
6765 QCoreApplication::postEvent (this, ev);
6768 pwm->runQueue ();
6771 void
6772 PwmdMainWindow::slotNewAttribute ()
6774 if (doSaveContent (true))
6776 newAttribute = new QListWidgetItem (tr ("New attribute"));
6777 pwm->runQueue ();
6778 return;
6781 setAttributeContent ("");
6782 ui.attributeContent->setEnabled (false);
6783 fixupToolBar (attributeToolBar, AttributeToolBarNew, false);
6784 fixupToolBar (attributeToolBar, AttributeToolBarDelete, false);
6785 delete newAttribute;
6786 newAttribute = new QListWidgetItem (tr ("New attribute"));
6787 newAttribute->setFlags (newAttribute->flags () | Qt::ItemIsEditable);
6788 ui.attributeList->addItem (newAttribute);
6789 ui.attributeList->scrollToItem (newAttribute);
6790 ui.attributeList->editItem (newAttribute);
6793 void
6794 PwmdMainWindow::slotDeleteAttribute ()
6796 if (!selectedAttribute
6797 || !(selectedAttribute->flags () & Qt::ItemIsEnabled))
6798 return;
6801 doSaveContent (true);
6802 foreach (QTreeWidgetItem *item, ui.elementTree->selectedItems ())
6804 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
6805 QString attr = selectedAttribute->text ();
6806 QTreeWidgetItem *ritem = resolveTargets (item);
6808 if (data->hasAttribute (attr) &&
6809 ((!PwmdElementAttr::isReservedAttribute (attr) || attr == "_acl")
6810 && isElementOwner (item)))
6812 if (ritem == item || isElementOwner (ritem))
6813 deleteAttribute (item, attr, true);
6817 pwm->runQueue ();
6820 void
6821 PwmdMainWindow::setAttribute (QTreeWidgetItem *item, const QString &name,
6822 const QString &value, bool update)
6824 if (!item)
6825 return;
6827 PwmdTreeWidgetItemData *data =
6828 qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
6830 if (!data)
6831 return;
6833 data->addAttribute (name, value);
6834 if (update)
6835 updateTargetAttributes (item, name, value);
6837 refreshAttributeListFinalize (item, QString (), false);
6840 void
6841 PwmdMainWindow::removeAttribute (QTreeWidgetItem * item, const QString & name,
6842 bool update)
6844 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
6846 data->removeAttribute (name);
6847 data->setNeedsAttrRefresh ();
6848 if (update)
6849 updateTargetAttributes (item, name, QString (), true);
6852 void
6853 PwmdMainWindow::deleteAttributeFinalize (QTreeWidgetItem *item,
6854 const QString &name)
6856 removeAttribute (item, name, true);
6857 setModified ();
6858 if (item != selectedElement)
6859 return;
6861 PwmdTreeWidgetItemData *data = qvariant_cast < PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
6863 if (name == "_expire" && ui.tabWidget->currentWidget () == ui.contentTab)
6864 setContentItemsEnabled ();
6865 else if (name == "hidden")
6867 ui.textEdit->setHidden (data->hasAttribute ("password"));
6868 ui.hiddenContentFrame->setHidden (true);
6869 disconnect (ui.actionHidden, SIGNAL (toggled(bool)), this, SLOT (slotToggleHiddenContent(bool)));
6870 ui.actionHidden->setChecked (false);
6871 connect (ui.actionHidden, SIGNAL (toggled(bool)), this, SLOT (slotToggleHiddenContent(bool)));
6873 else if (name == "encoding" && data->attribute ("encoding") != "base64")
6875 disconnect (ui.actionBase64, SIGNAL (triggered(bool)), this, SLOT (slotToggleBase64Content(bool)));
6876 ui.actionBase64->setChecked (false);
6877 connect (ui.actionBase64, SIGNAL (triggered(bool)), this, SLOT (slotToggleBase64Content(bool)));
6880 if (item == selectedElement || item == attributeElement)
6882 if (selectedAttribute)
6883 delete selectedAttribute;
6885 selectedAttribute = ui.attributeList->currentItem ();
6886 setAttributeContent ("");
6888 if (name == "_target")
6890 selectedElementPath = elementPath (attributeElement);
6891 QEvent *ev = new QEvent (QEvent::Type (PwmdEventTargetAttr));
6892 QCoreApplication::postEvent (this, ev);
6894 else
6895 elementSelected (selectedElement);
6899 bool
6900 PwmdMainWindow::deleteAttribute (QTreeWidgetItem *item, const QString &name,
6901 bool queue)
6903 PwmdInquireData *inq = new PwmdInquireData (QString ("DELETE %1 %2").arg (name, elementPath (item)));
6905 inq->setUserPtr (item);
6906 inq->setUser (name);
6907 PwmdCommandQueueItem *cmd = new PwmdCommandQueueItem (PwmdCmdIdAttrDelete,
6908 "ATTR",
6909 Pwmd::inquireCallback,
6910 inq);
6911 ignoreErrors (cmd);
6912 return pwm->command (cmd, queue);
6915 bool
6916 PwmdMainWindow::hasMultiSelection ()
6918 return ui.elementTree->selectedItems ().count () > 1;
6921 bool
6922 PwmdMainWindow::hasSingleSelection ()
6924 return !hasMultiSelection ();
6927 QTreeWidgetItem *
6928 PwmdMainWindow::firstSelectedItem ()
6930 return ui.elementTree->selectedItems ().count ()
6931 ? ui.elementTree->selectedItems ().at (0) : nullptr;