Line data Source code
1 : // SPDX-FileCopyrightText: 2014 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : /**
4 : * @file
5 : * @brief MainWindow: the main application window (tree, toolbar, search,
6 : * status bar, process output dock) and the glue between the password
7 : * store backends and the widgets.
8 : */
9 : #ifndef SRC_MAINWINDOW_H_
10 : #define SRC_MAINWINDOW_H_
11 :
12 : #include "appsettings.h"
13 : #include "enums.h"
14 : #include "grepsearchcontroller.h"
15 : #include "storetree.h"
16 :
17 : #include <QDialog>
18 : #include <QMainWindow>
19 : #include <QPointer>
20 : #include <QProcess>
21 : #include <QTimer>
22 :
23 : #ifdef __APPLE__
24 : /**
25 : * @brief Qt-internal switch, declared here because Qt does not export it in
26 : * a header: enables the automatic `&` mnemonics in menu titles that
27 : * are off by default on macOS. Called once in the constructor.
28 : * @param b true to enable auto-mnemonics.
29 : * @see https://doc.qt.io/qt-6/qkeysequence.html#qt_set_sequence_auto_mnemonic
30 : */
31 : void qt_set_sequence_auto_mnemonic(bool b);
32 : #endif
33 :
34 : namespace Ui {
35 : class MainWindow;
36 : class Pass;
37 : } // namespace Ui
38 :
39 : class Pass;
40 : class ProcessOutputPanel;
41 : class QProgressDialog;
42 : class QTreeWidgetItem;
43 : class QtPass;
44 : class PasswordDisplayPanel;
45 : class TrayIcon;
46 :
47 : /**
48 : * @class MainWindow
49 : * @ingroup ui
50 : * @brief Main application window orchestrating UI, user interactions, and
51 : * external process handlers.
52 : *
53 : * Provides the central interface for managing items, folders, passwords, and
54 : * OTPs; coordinates UI components (toolbars, panels, dialogs, status/tray),
55 : * selection and navigation in the underlying file/store models, and lifecycle
56 : * interactions with external handlers (e.g., pass, Git, GPG key generation,
57 : * OTP).
58 : */
59 : class MainWindow : public QMainWindow {
60 341 : Q_OBJECT
61 :
62 : public:
63 : /**
64 : * @brief Construct the main window.
65 : * @param searchText Initial search text to populate the search field.
66 : * @param parent Optional parent widget.
67 : */
68 : explicit MainWindow(const QString &searchText = QString(),
69 : QWidget *parent = nullptr);
70 : /**
71 : * @brief Save the window state (a quit from the tray menu or a SIGTERM
72 : * never delivers closeEvent()) and delete the QtPass glue.
73 : */
74 : ~MainWindow() override;
75 :
76 : /**
77 : * @brief Restore window geometry and state from saved settings.
78 : */
79 : void restoreWindow();
80 :
81 : /**
82 : * @brief Open the configuration dialog.
83 : *
84 : * On a fresh start the first-run wizard runs before the dialog is shown.
85 : * Accepting the dialog does not guarantee a usable configuration: the OK
86 : * button is not gated on Util::configIsValid(), so QtPass::init() uses the
87 : * return value to decide whether to ask again or to give up.
88 : * @return true when the dialog was accepted, false when it was cancelled.
89 : */
90 : auto config() -> bool;
91 :
92 : /**
93 : * @brief Enable or disable the main UI elements.
94 : * @param state true to enable, false to disable.
95 : */
96 : void setUiElementsEnabled(bool state);
97 :
98 : /**
99 : * @brief Display a transient message in the text panel.
100 : * @param text Message text to display.
101 : * @param isError true to style the message as an error.
102 : * @param isHtml true if text contains HTML markup.
103 : */
104 : void flashText(const QString &text, const bool isError,
105 : const bool isHtml = false);
106 :
107 : /**
108 : * @brief Return the currently selected index in the tree view.
109 : * @return Current QModelIndex.
110 : */
111 : auto getCurrentTreeViewIndex() -> QModelIndex;
112 :
113 : /**
114 : * @brief Return whether startup configuration succeeded.
115 : *
116 : * Reflects the result of the initial configuration check performed in the
117 : * constructor (QtPass::init()). When it is false the application cannot be
118 : * used (e.g. the user cancelled the first-run wizard), and main() should
119 : * exit before ever showing the window.
120 : * @return true when QtPass::init() reported a working configuration.
121 : */
122 : auto initSucceeded() const -> bool { return m_initSucceeded; }
123 :
124 : protected:
125 : /**
126 : * @brief Save window state and geometry on close.
127 : * @param event The close event.
128 : */
129 : void closeEvent(QCloseEvent *event) override;
130 : /**
131 : * @brief Handle keyboard shortcuts.
132 : * @param event The key press event.
133 : */
134 : void keyPressEvent(QKeyEvent *event) override;
135 : /**
136 : * @brief React to language or window state changes.
137 : * @param event The change event.
138 : */
139 : void changeEvent(QEvent *event) override;
140 : /**
141 : * @brief Notice an application palette change, the theme switch that
142 : * makes a style-imposed bar palette suspect; see
143 : * dropStaleToolsAreaPalettes().
144 : * @param event The event.
145 : * @return true if the event was consumed.
146 : */
147 : auto event(QEvent *event) -> bool override;
148 : /**
149 : * @brief First-show hook used to focus the search input once the window
150 : * is actually mapped (avoids races with timers fired before show).
151 : * @param event The show event.
152 : */
153 : void showEvent(QShowEvent *event) override;
154 : /**
155 : * @brief Filter events from watched objects.
156 : * @param obj The object that received the event.
157 : * @param event The event to filter.
158 : * @return true if the event was consumed.
159 : */
160 : auto eventFilter(QObject *obj, QEvent *event) -> bool override;
161 :
162 : public slots:
163 : /**
164 : * @brief Clear the current tree view selection.
165 : */
166 : void deselect();
167 :
168 : /**
169 : * @brief Handle an incoming inter-process message (single-instance mode).
170 : * @param message Message string received from another instance.
171 : */
172 : void messageAvailable(const QString &message);
173 :
174 : /**
175 : * @brief Handle generic process output for display in output panel.
176 : * @param output Process output text.
177 : * @param isError true if output should be styled as error (red).
178 : * @param pid Identifier of the originating subprocess (for the label
179 : * prefix); defaults to Enums::INVALID for unlabelled output.
180 : */
181 : void onProcessOutput(const QString &output, bool isError,
182 : Enums::PROCESS pid = Enums::INVALID);
183 :
184 : /**
185 : * @brief Display a critical error dialog.
186 : * @param title Dialog title.
187 : * @param msg Error message body.
188 : */
189 : void critical(const QString &title, const QString &msg);
190 :
191 : /**
192 : * @brief Slot called when an external process wrapper has started.
193 : */
194 : void executeWrapperStarted();
195 :
196 : /**
197 : * @brief Show a message in the status bar for the given duration.
198 : * @param msg Message to display.
199 : * @param timeout Duration in milliseconds (default 2000).
200 : */
201 : void showStatusMessage(const QString &msg, int timeout = 2000);
202 :
203 : /**
204 : * @brief Render a decrypted entry: fields into the display panel, the rest
205 : * into the text browser, and the password onto the clipboard when
206 : * "Always copy" is set. Decrypts of any entry but the one the panel
207 : * last asked to show (m_shownFile) are dropped.
208 : * @param output Decrypted password file content.
209 : * @param file Entry the content belongs to; empty for legacy callers that
210 : * do not name it, which always repaints.
211 : */
212 : void passShowHandler(const QString &output, const QString &file = QString());
213 :
214 : /**
215 : * @brief Generate a one-time password from decrypted content and copy it.
216 : * Ignores decrypts of anything but the entry onOtp() asked for.
217 : * @param output Decrypted entry content.
218 : * @param file Entry the content belongs to.
219 : */
220 : void otpFromFileToClipboard(const QString &output, const QString &file);
221 :
222 : /**
223 : * @brief Copy the first line of a decrypted entry (Ctrl+C). Ignores
224 : * decrypts of anything but the entry copyPasswordFromTreeview()
225 : * asked for; an otpauth:// first line is refused.
226 : * @param text Decrypted entry content.
227 : * @param file Entry the content belongs to.
228 : */
229 : void passwordFromFileToClipboard(const QString &text, const QString &file);
230 :
231 : /**
232 : * @brief Forget the pending OTP and copy requests.
233 : *
234 : * A failed decrypt never emits Pass::finishedShow; without this a later
235 : * decrypt of the same entry (the user retrying) would be taken as the
236 : * answer to a request the user has long given up on.
237 : */
238 : void cancelOtpRequest();
239 : /**
240 : * @brief An operation ended in an error before or instead of a result:
241 : * forget the pending requests and what the panel was about to show.
242 : *
243 : * Connected to Pass::processErrorExit. A Show that never ran (a linked
244 : * entry refused at the store boundary) leaves the previous entry's fields
245 : * on the panel under the new name otherwise, and onOtp()'s fast path would
246 : * take that entry's code for the refused one.
247 : */
248 : void onOperationError();
249 :
250 : /**
251 : * @brief Drop a style-imposed menu bar or toolbar palette that belongs to
252 : * the other theme (Breeze header palette after a light/dark switch).
253 : */
254 : void dropStaleToolsAreaPalettes();
255 :
256 : /**
257 : * @brief Handle results from a completed grep search.
258 : * @param results List of file/match pairs from the grep operation.
259 : */
260 : void onGrepFinished(const QList<QPair<QString, QStringList>> &results);
261 :
262 : /**
263 : * @brief Trigger a git push operation.
264 : */
265 : void onPush();
266 :
267 : /**
268 : * @brief Handle a click on an item in the tree view.
269 : * @param index The model index that was clicked.
270 : */
271 : void on_treeView_clicked(const QModelIndex &index);
272 :
273 : /**
274 : * @brief Begin a re-encryption pass on the current path.
275 : *
276 : * Disables the UI, shows a cancellable progress dialog and keeps both in
277 : * place until endReencryptPath(): while the worker runs, unrelated
278 : * completions must not re-enable the interface (see setUiElementsEnabled).
279 : */
280 : void startReencryptPath();
281 :
282 : /**
283 : * @brief Update the re-encryption progress dialog.
284 : * @param current Files checked so far.
285 : * @param total Files found under the re-encrypted folder.
286 : */
287 : void reencryptProgress(int current, int total);
288 :
289 : /**
290 : * @brief Finish a re-encryption pass on the current path.
291 : */
292 : void endReencryptPath();
293 :
294 : private slots:
295 : /**
296 : * @brief Fill the profile box from the settings, the active profile
297 : * selected, without switching profiles while doing so.
298 : */
299 : void updateProfileBox();
300 : /**
301 : * @brief Enable the profile box only when there is more than one profile,
302 : * the interface is enabled and the backend is idle.
303 : */
304 : void updateProfileBoxEnabled();
305 : /**
306 : * @brief The content-search toggle changed: switch the search box between
307 : * name filtering and regex content search, clearing the query, the
308 : * tree filter and any results; leaving while a search runs cancels
309 : * it.
310 : * @param checked true when content search was switched on.
311 : */
312 : void on_grepButton_toggled(bool checked);
313 : /**
314 : * @brief A content-search result was clicked: select the entry in the
315 : * tree, show it, and go back to the tree (clearing the results when
316 : * content is hidden or the panel autoclears).
317 : * @param item The clicked entry or match line; its UserRole holds the
318 : * entry name.
319 : * @param column Unused.
320 : */
321 : void on_grepResultsList_itemClicked(QTreeWidgetItem *item, int column);
322 : /** @brief Toolbar/context-menu "Add password": open PasswordDialog for a
323 : * new entry in the current folder. */
324 : void addPassword();
325 : /**
326 : * @brief Toolbar/context-menu "Add folder": ask for a name, create the
327 : * folder under the current one (refusing paths that escape the
328 : * store) and seed its `.gpg-id` from the parent when configured.
329 : */
330 : void addFolder();
331 : /** @brief Toolbar "Edit" or a double-click: editPassword() on the current
332 : * entry. */
333 : void onEdit();
334 : /**
335 : * @brief Toolbar "Delete" or the Delete key: confirm, then remove the
336 : * current entry or folder. Nothing selected is a no-op (#556); a
337 : * linked folder only loses its link, a folder behind a link is
338 : * refused.
339 : */
340 : void onDelete();
341 : /**
342 : * @brief Toolbar "OTP": copy the current entry's one-time code. Uses the
343 : * code the panel already shows for this entry; otherwise decrypts
344 : * once and lets otpFromFileToClipboard() finish.
345 : */
346 : void onOtp();
347 : /**
348 : * @brief Toolbar "Update": git pull.
349 : * @param block true to pull synchronously (before opening an edit dialog
350 : * with auto-pull on).
351 : */
352 : void onUpdate(bool block = false);
353 : /**
354 : * @brief Toolbar/context-menu "Users": open UsersDialog for the current
355 : * folder, unless it lies behind a link.
356 : */
357 : void onUsers();
358 : /** @brief Toolbar "Config": config(), ignoring its result. */
359 : void onConfig();
360 : /**
361 : * @brief Show the About box: version, project link, licence.
362 : */
363 : void showAbout();
364 : /**
365 : * @brief A double-click on a tree entry opens it for editing; folders are
366 : * left to the view.
367 : * @param index The double-clicked proxy index.
368 : */
369 : void on_treeView_doubleClicked(const QModelIndex &index);
370 : /**
371 : * @brief Empty the display panel and the text browser, and leave content
372 : * search if its results were showing. Runs on the autoclear timer.
373 : * @param notify true to show "Password and content hidden" in the browser,
374 : * false to leave it blank.
375 : */
376 : void clearPanel(bool notify = true);
377 : /**
378 : * @brief The search box changed (name filtering only): drop the pending
379 : * decrypt and the panel, expand the tree and restart searchTimer.
380 : * @param arg1 The new text.
381 : */
382 : void on_lineEdit_textChanged(const QString &arg1);
383 : /**
384 : * @brief Enter in the search box: in content-search mode start (or, for an
385 : * empty query, cancel) the grep; otherwise select and show the first
386 : * matching entry.
387 : */
388 : void on_lineEdit_returnPressed();
389 : /**
390 : * @brief The user picked another profile: save its store, key and Git
391 : * flags as the active settings, re-root the tree and clear the
392 : * selection. Ignored during the fresh start and reverted while an
393 : * operation runs.
394 : * @param name The chosen profile name.
395 : */
396 : void on_profileBox_currentTextChanged(const QString &name);
397 : /**
398 : * @brief Build and show the tree's context menu for the item under the
399 : * cursor: folder actions (open, add, users, rename, Share) or entry
400 : * actions (edit, rename), plus Delete.
401 : * @param pos Viewport position of the right-click.
402 : */
403 : void showContextMenu(const QPoint &pos);
404 : /**
405 : * @brief Add the Share submenu for the folder under the cursor: the
406 : * entries are enabled only where a recipient list and a usable gpg or
407 : * pass make them work.
408 : * @param contextMenu The menu being built, which owns the submenu.
409 : * @param s The settings the executables and the store come from.
410 : */
411 : void addShareMenu(QMenu &contextMenu, const AppSettings &s);
412 : /**
413 : * @brief Ask before deleting an entry or a folder, naming what goes and
414 : * warning when the folder holds anything but entries.
415 : * @param file Store-relative path being deleted.
416 : * @param isDir Whether it is a folder.
417 : * @return Whether the user said yes.
418 : */
419 : auto confirmDeletion(const QString &file, bool isDir) -> bool;
420 : /**
421 : * @brief Ask before removing a folder that is a link, which takes the link
422 : * and leaves what it points to.
423 : * @param file Store-relative path of the link.
424 : * @return Whether the user said yes.
425 : */
426 : auto confirmLinkRemoval(const QString &file) -> bool;
427 : /**
428 : * @brief Show the text browser's standard context menu, reparented to the
429 : * window so the browser's stylesheet does not bleed into it.
430 : * @param pos Viewport position of the right-click.
431 : */
432 : void showBrowserContextMenu(const QPoint &pos);
433 : /** @brief Context menu "Open folder with file manager" on the current
434 : * folder. */
435 : void openFolder();
436 : /**
437 : * @brief Context menu "Rename folder": ask for a new name and Move the
438 : * current folder, refusing a target outside the store.
439 : */
440 : void renameFolder();
441 : /**
442 : * @brief Open PasswordDialog on an existing entry, after a blocking pull
443 : * when auto-pull is on. Releases the panel's claim on the next
444 : * decrypt so the dialog's own Show does not repaint or copy.
445 : * @param file Store-relative entry name; empty is a no-op.
446 : */
447 : void editPassword(const QString &file);
448 : /**
449 : * @brief Context menu "Rename password": ask for a new name and Move the
450 : * current entry within its folder, refusing a target outside the
451 : * store.
452 : */
453 : void renamePassword();
454 : /**
455 : * @brief Select-all and focus the search box once the window is visible;
456 : * sets m_firstShowCompleted only on success so showEvent() retries.
457 : * Looks the widget up by name because setWindowFlags() can rebuild
458 : * it under `ui->lineEdit` (Qt 6.11).
459 : */
460 : void focusInput();
461 : /**
462 : * @brief Ctrl+C on the tree: decrypt the current entry and let
463 : * passwordFromFileToClipboard() copy its first line. The newest
464 : * request wins (m_copyRequestFile).
465 : */
466 : void copyPasswordFromTreeview();
467 : /**
468 : * @brief searchTimer fired: filter the tree on the typed words (matched
469 : * literally, in order, case-insensitively) and select the first
470 : * match; an empty query collapses the tree and deselects.
471 : */
472 : void onTimeoutSearch();
473 :
474 : private:
475 : /**
476 : * @brief The UI-neutral glue to the backends (output, status, clipboard,
477 : * QR codes); created in the constructor, deleted in the destructor.
478 : */
479 : QtPass *m_qtPass;
480 : /** @brief The widgets generated from mainwindow.ui. */
481 : QScopedPointer<Ui::MainWindow> ui;
482 : /** @brief Mode/busy/cancelled state of the content search. */
483 : GrepSearchController m_grep;
484 : /**
485 : * @brief Renders decrypted fields into the grid; created in
486 : * initDisplayPanel(), parented to this window.
487 : */
488 : PasswordDisplayPanel *m_displayPanel = nullptr;
489 : /**
490 : * @brief Set once focusInput() has actually focused the search box, so
491 : * later show events leave the focus alone.
492 : */
493 : bool m_firstShowCompleted = false;
494 : /// Whether the application palette changed in this window's lifetime. A
495 : /// header palette a style stamps on the bars is only suspect after that:
496 : /// at startup the style's colours are fresh, however far from the window
497 : /// colour a scheme puts its header.
498 : bool m_themeSwitched = false;
499 : /// Entry the display panel is currently rendering. The tree's currentIndex
500 : /// can move without a repaint (arrow keys and right-click do not emit
501 : /// QTreeView::clicked), so onOtp() must not assume the visible code belongs
502 : /// to the selected entry.
503 : QString m_shownFile;
504 : /// Entry the in-flight OTP request asked for; empty when none. finishedShow
505 : /// names its file, so otpFromFileToClipboard() simply ignores everything
506 : /// else and passShowHandler() skips the password copy for this one.
507 : QString m_otpRequestFile;
508 : /// Entry the in-flight Ctrl+C request asked for; empty when none. Each
509 : /// finishedShow is matched to it by file, so a second Ctrl+C on another
510 : /// entry just replaces the request instead of racing it.
511 : QString m_copyRequestFile;
512 : /// The process output console, a QDockWidget at the bottom dock area
513 : /// created in initProcessOutput(). It isn't part of the .ui because uic
514 : /// places its QMainWindow children in centralWidget / statusBar / menuBar /
515 : /// toolBars / dock-widget slots only — a sibling widget at the QMainWindow
516 : /// level isn't laid out and ends up obscuring the centralWidget. See #1192
517 : /// for the symptom.
518 : ProcessOutputPanel *m_processOutput = nullptr;
519 : /// The store as shown in the tree: models, view setup, path mapping.
520 : StoreTree *m_tree = nullptr;
521 : /// Single-shot timer that runs clearPanel() "Autoclear panel" seconds
522 : /// after an entry was shown.
523 : QTimer clearPanelTimer, searchTimer; ///< searchTimer: single-shot 350 ms
524 : ///< debounce between typing in the
525 : ///< search box and onTimeoutSearch().
526 : /// Re-enables the UI if a backend operation disables it but never reports
527 : /// completion (see setUiElementsEnabled); armed whenever the UI is
528 : /// disabled, stopped when it is enabled again.
529 : QTimer m_uiWatchdog;
530 : /// Interval of m_uiWatchdog in milliseconds.
531 : static constexpr int UiWatchdogMs = 30000;
532 : /// Progress/cancel dialog shown between startReencryptPath() and
533 : /// endReencryptPath(); QPointer so a closed dialog reads back as null.
534 : QPointer<QProgressDialog> m_reencryptProgress;
535 : /// True between startReencryptPath() and endReencryptPath(). While set,
536 : /// setUiElementsEnabled(true) is ignored so the git completions queued by
537 : /// Init/Move/Copy (or the UI watchdog) cannot re-enable the interface
538 : /// while the re-encryption worker is still rewriting files.
539 : bool m_reencryptRunning = false;
540 : /// What setUiElementsEnabled() last set: updateProfileBox() keeps the
541 : /// profile box disabled while an operation runs.
542 : bool m_uiEnabled = true;
543 : /// The system tray icon; null until initTrayIcon() creates it when the
544 : /// setting is on, deleted again by destroyTrayIcon() when it is turned off
545 : /// or no tray is available.
546 : TrayIcon *m_tray{};
547 : /// Whether the constructor ended with a usable configuration; main()
548 : /// consults it via initSucceeded() to decide whether the application should
549 : /// start at all.
550 : bool m_initSucceeded = false;
551 : /// No usable configuration has been accepted yet: config() runs the
552 : /// first-run wizard and defaults to pass when it is installed.
553 : bool m_freshStart = true;
554 :
555 : /// Wire the toolbar and menu actions to their slots, set their icons, and
556 : /// set up the "Show menu bar" toggle with its MenuBarPeek (not on macOS).
557 : void initToolBarButtons();
558 : /// Welcome message and the permanent logo in the status bar.
559 : void initStatusBar();
560 : /// Connect QtPass's and both backends' signals (the active backend can
561 : /// change through the configuration dialog without a restart).
562 : void connectBackends();
563 : /// Font, wrapping and link handling of the text browser, per the settings.
564 : void initTextBrowser();
565 : /// The grid of decrypted fields and what its buttons ask for.
566 : void initDisplayPanel();
567 : /// The panel-clear and search timers, and the watchdog that re-enables
568 : /// the UI when an operation never signals its end.
569 : void initTimers();
570 : /// The Process Output dock, its Settings-menu toggle, and the feed of
571 : /// non-secret process output into it.
572 : void initProcessOutput();
573 : /**
574 : * @brief Connect the signals of one backend that the window answers
575 : * itself: errors, critical messages, wrapper start, idle, status,
576 : * finishedShow (panel, OTP and Ctrl+C handlers) and finishedGrep.
577 : * @param pass The backend; both are connected, see connectBackends().
578 : */
579 : void connectPassSignals(Pass *pass);
580 :
581 : /// Make the first entry (depth first) under the store root current.
582 : void selectFirstFile();
583 : /**
584 : * @brief Depth-first search for the first entry under a folder.
585 : * @param parentIndex Proxy index of the folder to search.
586 : * @return Proxy index of the first file, or invalid when there is none.
587 : */
588 : auto firstFile(QModelIndex parentIndex) -> QModelIndex;
589 : /**
590 : * @brief StoreTree::fileFor() on the tree.
591 : * @param index Proxy index.
592 : * @param forPass true for the store-relative entry name, false for the
593 : * absolute .gpg path.
594 : * @return Entry name or path; empty for a folder or invalid index.
595 : */
596 : auto getFile(const QModelIndex &index, bool forPass) -> QString;
597 : /**
598 : * @brief Run PasswordDialog for a new or existing entry. For a new one the
599 : * dialog gets the store's real folders (current one preselected) and
600 : * the `.templates`, if any.
601 : * @param file Store-relative entry name; empty when creating.
602 : * @param isNew true to create, false to edit @p file.
603 : */
604 : void setPassword(const QString &file, bool isNew = true);
605 : /**
606 : * @brief Guard the input dialogs (create, move, rename) against names like
607 : * `../../etc/passwd` or absolute paths escaping the store root; warns
608 : * the user when refusing.
609 : * @param candidate Path the user asked for.
610 : * @return true when it resolves inside the store.
611 : */
612 : auto confirmPathInStore(const QString &candidate) -> bool;
613 :
614 : /**
615 : * @brief Create m_tray and add the menu bar toggle to its menu; gives up
616 : * (m_tray stays null) when no system tray is available.
617 : */
618 : void initTrayIcon();
619 : /// Delete m_tray and reset it to null.
620 : void destroyTrayIcon();
621 : /**
622 : * @brief Share menu "Re-encrypt all passwords": confirm, lock the UI via
623 : * startReencryptPath() and have ImitatePass rewrite every entry
624 : * under @p dir for the recipients in its `.gpg-id`.
625 : * @param dir Absolute folder; refused when missing or behind a link.
626 : */
627 : void reencryptPath(const QString &dir);
628 : /**
629 : * @brief Share menu "Export my public key": run `gpg --armor --export` for
630 : * the configured signing key and show the result in
631 : * ExportPublicKeyDialog; explains what to do when no key is set.
632 : */
633 : void exportPublicKey();
634 : /**
635 : * @brief Share menu "Add recipient": open UsersDialog for @p dir (keys
636 : * already in the keyring only).
637 : * @param dir Absolute folder; refused when behind a link.
638 : */
639 : void addRecipient(const QString &dir);
640 : /**
641 : * @brief Refuse, with a message, a tree-picked folder that is or lies
642 : * behind a symlink or junction: what it points to is not part of the
643 : * store. The store root itself may be a link.
644 : * @param dir Absolute folder path.
645 : * @return true when the folder was refused and the caller must stop.
646 : */
647 : auto refuseLinkedFolder(const QString &dir) -> bool;
648 : /// Share menu "What is this?": the explanation of sharing with GPG.
649 : void showShareHelp();
650 :
651 : /// Re-apply the monospace and line-wrapping settings to the text browser
652 : /// after the configuration dialog.
653 : void applyTextBrowserSettings();
654 : /// Toggle Qt::WindowStaysOnTopHint to match the "always on top" setting,
655 : /// skipping the native-window rebuild when nothing changes.
656 : void applyWindowFlagsSettings();
657 : /// Store geometry and dock/toolbar state in the settings; idempotent,
658 : /// called from closeEvent() and the destructor.
659 : void saveWindowState();
660 :
661 : /**
662 : * @brief Show Push and Pull only when Git is in use; enable them only while
663 : * the interface is.
664 : * @param uiEnabled false while an operation runs
665 : */
666 : void updateGitButtonVisibility(bool uiEnabled = true);
667 : /**
668 : * @brief Refresh the OTP toolbar action from settings.
669 : * @param uiEnabled false while a backend operation is in flight, so the
670 : * action is disabled along with the rest of the UI.
671 : */
672 : void updateOtpButtonVisibility(bool uiEnabled = true);
673 : /// Show the content-search buttons only when the setting is on; turning
674 : /// it off while in content-search mode leaves that mode.
675 : void updateGrepButtonVisibility();
676 : /**
677 : * @brief Enable or disable the Push and Pull actions together.
678 : * @param state true to enable.
679 : */
680 : void enableGitButtons(const bool &state);
681 :
682 : /// Sync the Process Output dock and its menu toggle with the setting
683 : /// after the configuration dialog.
684 : void updateProcessOutputVisibility();
685 : };
686 :
687 : #endif // SRC_MAINWINDOW_H_
|