Add ability to save the key to avoid being prompted for it

This commit is contained in:
Miguel Jacq 2025-11-02 11:44:22 +11:00
parent 4f773e1c1b
commit 43bbe971eb
Signed by: mig5
GPG key ID: 59B3F0C24135C6A9
7 changed files with 98 additions and 46 deletions

View file

@ -1,7 +1,6 @@
from __future__ import annotations
from PySide6.QtGui import (
QBrush,
QColor,
QDesktopServices,
QFont,
@ -14,7 +13,6 @@ from PySide6.QtGui import (
from PySide6.QtCore import Qt, QUrl, Signal, Slot, QRegularExpression
from PySide6.QtWidgets import QTextEdit
from .url_highlighter import UrlHighlighter
class Editor(QTextEdit):
linkActivated = Signal(str)
@ -55,13 +53,13 @@ class Editor(QTextEdit):
while it.hasNext():
m = it.next()
start = block.position() + m.capturedStart()
end = start + m.capturedLength()
end = start + m.capturedLength()
cur.setPosition(start)
cur.setPosition(end, QTextCursor.KeepAnchor)
fmt = cur.charFormat()
if fmt.isAnchor(): # already linkified; skip
if fmt.isAnchor(): # already linkified; skip
continue
href = m.captured(0)
@ -110,7 +108,7 @@ class Editor(QTextEdit):
# When pressing Enter/return key, insert first, then neutralise the empty blocks inline format
if key in (Qt.Key_Return, Qt.Key_Enter):
super().keyPressEvent(e) # create the new (possibly empty) paragraph
super().keyPressEvent(e) # create the new (possibly empty) paragraph
# If we're on an empty block, clear the insertion char format so the
# *next* Enter will create another new line (not consume the press to reset formatting).
@ -156,7 +154,11 @@ class Editor(QTextEdit):
def apply_weight(self):
cur = self.currentCharFormat()
fmt = QTextCharFormat()
weight = QFont.Weight.Normal if cur.fontWeight() == QFont.Weight.Bold else QFont.Weight.Bold
weight = (
QFont.Weight.Normal
if cur.fontWeight() == QFont.Weight.Bold
else QFont.Weight.Bold
)
fmt.setFontWeight(weight)
self.merge_on_sel(fmt)

View file

@ -26,7 +26,7 @@ from .db import DBManager
from .editor import Editor
from .key_prompt import KeyPrompt
from .search import Search
from .settings import APP_NAME, load_db_config, save_db_config
from .settings import APP_ORG, APP_NAME, load_db_config, save_db_config
from .settings_dialog import SettingsDialog
from .toolbar import ToolBar
@ -44,9 +44,12 @@ class MainWindow(QMainWindow):
else:
first_time = False
# Always prompt for the key (we never store it)
if not self._prompt_for_key_until_valid(first_time):
sys.exit(1)
# Prompt for the key unless it is found in config
if not self.cfg.key:
if not self._prompt_for_key_until_valid(first_time):
sys.exit(1)
else:
self._try_connect()
# ---- UI: Left fixed panel (calendar) + right editor -----------------
self.calendar = QCalendarWidget()
@ -149,7 +152,7 @@ class MainWindow(QMainWindow):
self._refresh_calendar_marks()
# Restore window position from settings
self.settings = QSettings(APP_NAME)
self.settings = QSettings(APP_ORG, APP_NAME)
self._restore_window_position()
def _try_connect(self) -> bool:
@ -328,12 +331,13 @@ class MainWindow(QMainWindow):
return False
def _move_to_cursor_screen_center(self):
screen = QGuiApplication.screenAt(QCursor.pos()) or QGuiApplication.primaryScreen()
screen = (
QGuiApplication.screenAt(QCursor.pos()) or QGuiApplication.primaryScreen()
)
r = screen.availableGeometry()
# Center the window in that screens available area
self.move(r.center() - self.rect().center())
def closeEvent(self, event):
try:
# Save window position

View file

@ -21,9 +21,11 @@ def get_settings() -> QSettings:
def load_db_config() -> DBConfig:
s = get_settings()
path = Path(s.value("db/path", str(default_db_path())))
return DBConfig(path=path, key="")
key = s.value("db/key", "")
return DBConfig(path=path, key=key)
def save_db_config(cfg: DBConfig) -> None:
s = get_settings()
s.setValue("db/path", str(cfg.path))
s.setValue("db/key", str(cfg.key))

View file

@ -3,8 +3,12 @@ from __future__ import annotations
from pathlib import Path
from PySide6.QtWidgets import (
QCheckBox,
QDialog,
QFormLayout,
QFrame,
QGroupBox,
QLabel,
QHBoxLayout,
QVBoxLayout,
QWidget,
@ -15,9 +19,12 @@ from PySide6.QtWidgets import (
QSizePolicy,
QMessageBox,
)
from PySide6.QtCore import Qt, Slot
from PySide6.QtGui import QPalette
from .db import DBConfig, DBManager
from .settings import save_db_config
from .settings import load_db_config, save_db_config
from .key_prompt import KeyPrompt
@ -27,10 +34,11 @@ class SettingsDialog(QDialog):
self.setWindowTitle("Settings")
self._cfg = DBConfig(path=cfg.path, key="")
self._db = db
self.key = ""
form = QFormLayout()
form.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
self.setMinimumWidth(520)
self.setMinimumWidth(560)
self.setSizeGripEnabled(True)
self.path_edit = QLineEdit(str(self._cfg.path))
@ -47,18 +55,65 @@ class SettingsDialog(QDialog):
h.setStretch(1, 0)
form.addRow("Database path", path_row)
# Encryption settings
enc_group = QGroupBox("Encryption")
enc = QVBoxLayout(enc_group)
enc.setContentsMargins(12, 8, 12, 12)
enc.setSpacing(6)
# Checkbox to remember key
self.save_key_btn = QCheckBox("Remember key")
current_settings = load_db_config()
if current_settings.key:
self.save_key_btn.setChecked(True)
else:
self.save_key_btn.setChecked(False)
self.save_key_btn.setCursor(Qt.PointingHandCursor)
self.save_key_btn.toggled.connect(self.save_key_btn_clicked)
enc.addWidget(self.save_key_btn, 0, Qt.AlignLeft)
# Explanation for remembering key
self.save_key_label = QLabel(
"If you don't want to be prompted for your encryption key, check this to remember it. "
"WARNING: the key is saved to disk and could be recoverable if your disk is compromised."
)
self.save_key_label.setWordWrap(True)
self.save_key_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
# make it look secondary
pal = self.save_key_label.palette()
pal.setColor(self.save_key_label.foregroundRole(), pal.color(QPalette.Mid))
self.save_key_label.setPalette(pal)
exp_row = QHBoxLayout()
exp_row.setContentsMargins(24, 0, 0, 0) # indent to line up under the checkbox
exp_row.addWidget(self.save_key_label)
enc.addLayout(exp_row)
line = QFrame()
line.setFrameShape(QFrame.HLine)
line.setFrameShadow(QFrame.Sunken)
enc.addWidget(line)
# Change key button
self.rekey_btn = QPushButton("Change key")
self.rekey_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.rekey_btn.clicked.connect(self._change_key)
enc.addWidget(self.rekey_btn, 0, Qt.AlignLeft)
# Put the group into the form so it spans the full width nicely
form.addRow(enc_group)
# Buttons
bb = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
bb.accepted.connect(self._save)
bb.rejected.connect(self.reject)
# Root layout (adjust margins/spacing a bit)
v = QVBoxLayout(self)
v.setContentsMargins(12, 12, 12, 12)
v.setSpacing(10)
v.addLayout(form)
v.addWidget(self.rekey_btn)
v.addWidget(bb)
v.addWidget(bb, 0, Qt.AlignRight)
def _browse(self):
p, _ = QFileDialog.getSaveFileName(
@ -71,7 +126,7 @@ class SettingsDialog(QDialog):
self.path_edit.setText(p)
def _save(self):
self._cfg = DBConfig(path=Path(self.path_edit.text()), key="")
self._cfg = DBConfig(path=Path(self.path_edit.text()), key=self.key)
save_db_config(self._cfg)
self.accept()
@ -97,6 +152,18 @@ class SettingsDialog(QDialog):
except Exception as e:
QMessageBox.critical(self, "Error", f"Could not change key:\n{e}")
@Slot(bool)
def save_key_btn_clicked(self, checked: bool):
if checked:
p1 = KeyPrompt(
self, title="Enter your key", message="Enter the encryption key"
)
if p1.exec() != QDialog.Accepted:
return
self.key = p1.key()
self._cfg = DBConfig(path=Path(self.path_edit.text()), key=self.key)
save_db_config(self._cfg)
@property
def config(self) -> DBConfig:
return self._cfg

View file

@ -1,7 +1,7 @@
from __future__ import annotations
from PySide6.QtCore import Signal, Qt
from PySide6.QtGui import QFont, QAction
from PySide6.QtGui import QAction
from PySide6.QtWidgets import QToolBar

View file

@ -1,25 +0,0 @@
from __future__ import annotations
import re
from PySide6.QtGui import QSyntaxHighlighter, QTextCharFormat
from PySide6.QtCore import Qt, QRegularExpression
class UrlHighlighter(QSyntaxHighlighter):
def __init__(self, doc):
super().__init__(doc)
self.rx = QRegularExpression(r"(https?://[^\s<>\"]+|www\.[^\s<>\"]+)")
def highlightBlock(self, text: str):
it = self.rx.globalMatch(text)
while it.hasNext():
m = it.next()
href = m.captured(0)
if href.startswith("www."):
href = "https://" + href
fmt = QTextCharFormat()
fmt.setAnchor(True)
fmt.setAnchorHref(href)
fmt.setFontUnderline(True)
fmt.setForeground(Qt.blue)
self.setFormat(m.capturedStart(0), m.capturedLength(0), fmt)