From 55454d7ef667b6dd13b774415d351958c393b05f Mon Sep 17 00:00:00 2001 From: yzinchuk Date: Fri, 3 Jul 2026 03:53:07 -0400 Subject: [PATCH] rebrand to orc-gallery and optimise search/thumbnail responsiveness Rebrand: - Rename project, binary, desktop file, and icons from screenshot-gallery/orcs-gallery to orc-gallery - Update display name to "ORC Gallery" throughout (window title, .desktop, README, plan.md) - Config dir changed from ScreenshotOCRGallery to OrcGallery Performance: - cancelSearch() is now non-blocking; new searches start immediately instead of waiting for the previous thread to drain - Remove double-emit from QFutureWatcher::finished handler; results emitted once directly from background thread - SQLite WAL mode + NORMAL sync + 20 MB page cache + memory temp store + 256 MB mmap applied on init and per-thread connections - Typing inactivity timer reduced from 500 ms to 150 ms - COUNT(*) OVER() window function folds total-count into the main SELECT, eliminating a separate COUNT round-trip per search - ImageThumbnail::paintEvent draws the filename overlay; removes 3 child QObjects (QFrame + QLabel + QHBoxLayout) per thumbnail - loadThumbnailAsync posts QImage load+scale to QThreadPool; QPixmap::fromImage called on main thread via Qt::QueuedConnection invokeMethod; QPointer guards against use-after-free on gallery clear Co-Authored-By: Claude Sonnet 4.6 --- CMakeLists.txt | 26 +-- OCR-scripts/direct_rofi_ocr.sh | 2 +- OCR-scripts/ocr_rofi.sh | 2 +- OCR-scripts/ocr_screenshots.py | 6 +- OCR-scripts/ocr_search_rofi.py | 2 +- OCR-scripts/rofi_ocr_search.sh | 2 +- OCR-scripts/search_ocr.py | 2 +- OCR-scripts/simple_rofi_ocr.sh | 2 +- README.md | 16 +- build.sh | 8 +- ...cs-gallery-128.png => orc-gallery-128.png} | Bin ...cs-gallery-256.png => orc-gallery-256.png} | Bin ...cs-gallery-512.png => orc-gallery-512.png} | Bin ...orcs-gallery-64.png => orc-gallery-64.png} | Bin ...hot-gallery.desktop => orc-gallery.desktop | 6 +- plan.md | 59 ++--- resources.qrc | 8 +- src/databasemanager.cpp | 205 +++++++----------- src/imagegallery.cpp | 160 +++++--------- src/imagegallery.h | 14 +- src/main.cpp | 10 +- src/mainwindow.cpp | 14 +- src/settingsdialog.cpp | 2 +- 23 files changed, 231 insertions(+), 315 deletions(-) rename icons/{orcs-gallery-128.png => orc-gallery-128.png} (100%) rename icons/{orcs-gallery-256.png => orc-gallery-256.png} (100%) rename icons/{orcs-gallery-512.png => orc-gallery-512.png} (100%) rename icons/{orcs-gallery-64.png => orc-gallery-64.png} (100%) rename screenshot-gallery.desktop => orc-gallery.desktop (75%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fbbabb..52c8dc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(screenshot-gallery VERSION 1.0.0 LANGUAGES CXX) +project(orc-gallery VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -29,23 +29,23 @@ set(PROJECT_SOURCES src/updatedatabasedialog.h ) -add_executable(screenshot-gallery ${PROJECT_SOURCES} ${RESOURCE_FILES}) +add_executable(orc-gallery ${PROJECT_SOURCES} ${RESOURCE_FILES}) # Install icons to standard system locations -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icons/orcs-gallery-64.png" +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icons/orc-gallery-64.png" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps" - RENAME "orcs-gallery.png") -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icons/orcs-gallery-128.png" + RENAME "orc-gallery.png") +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icons/orc-gallery-128.png" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps" - RENAME "orcs-gallery.png") -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icons/orcs-gallery-256.png" + RENAME "orc-gallery.png") +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icons/orc-gallery-256.png" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps" - RENAME "orcs-gallery.png") -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icons/orcs-gallery-512.png" + RENAME "orc-gallery.png") +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/icons/orc-gallery-512.png" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/512x512/apps" - RENAME "orcs-gallery.png") + RENAME "orc-gallery.png") -target_link_libraries(screenshot-gallery PRIVATE +target_link_libraries(orc-gallery PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets @@ -53,11 +53,11 @@ target_link_libraries(screenshot-gallery PRIVATE Qt6::Concurrent ) -install(TARGETS screenshot-gallery +install(TARGETS orc-gallery BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # Install desktop file -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/screenshot-gallery.desktop" +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/orc-gallery.desktop" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") diff --git a/OCR-scripts/direct_rofi_ocr.sh b/OCR-scripts/direct_rofi_ocr.sh index 1b73bdd..b5d7742 100755 --- a/OCR-scripts/direct_rofi_ocr.sh +++ b/OCR-scripts/direct_rofi_ocr.sh @@ -4,7 +4,7 @@ # This script displays OCR data from SQLite and allows opening files with rofi # Database path -DB_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/screenshot-gallery/screenshot_ocr.db" +DB_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/orc-gallery/screenshot_ocr.db" # Check dependencies check_deps() { diff --git a/OCR-scripts/ocr_rofi.sh b/OCR-scripts/ocr_rofi.sh index de01fb5..6269144 100755 --- a/OCR-scripts/ocr_rofi.sh +++ b/OCR-scripts/ocr_rofi.sh @@ -4,7 +4,7 @@ # This script displays OCR data from SQLite and allows opening files with rofi # Database path -DB_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/screenshot-gallery/screenshot_ocr.db" +DB_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/orc-gallery/screenshot_ocr.db" SCREENSHOTS_DIR="$HOME/Screenshots" MAX_TEXT_LENGTH=100 diff --git a/OCR-scripts/ocr_screenshots.py b/OCR-scripts/ocr_screenshots.py index bda0ab5..c06a405 100755 --- a/OCR-scripts/ocr_screenshots.py +++ b/OCR-scripts/ocr_screenshots.py @@ -7,8 +7,8 @@ Usage: Arguments: --db Path to the SQLite database file. - Default: $XDG_DATA_HOME/screenshot-gallery/screenshot_ocr.db - (~/.local/share/screenshot-gallery/screenshot_ocr.db) + Default: $XDG_DATA_HOME/orc-gallery/screenshot_ocr.db + (~/.local/share/orc-gallery/screenshot_ocr.db) --screenshots-dir Directory containing screenshot images to process. Default: ~/Screenshots """ @@ -53,7 +53,7 @@ signal.signal(signal.SIGINT, _handle_signal) # --------------------------------------------------------------------------- def parse_args() -> argparse.Namespace: xdg_data = os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share")) - default_db = os.path.join(xdg_data, "screenshot-gallery", "screenshot_ocr.db") + default_db = os.path.join(xdg_data, "orc-gallery", "screenshot_ocr.db") default_dir = os.path.expanduser("~/Screenshots") parser = argparse.ArgumentParser( diff --git a/OCR-scripts/ocr_search_rofi.py b/OCR-scripts/ocr_search_rofi.py index 45d08ce..b99f096 100755 --- a/OCR-scripts/ocr_search_rofi.py +++ b/OCR-scripts/ocr_search_rofi.py @@ -7,7 +7,7 @@ import sys import tempfile # Configuration -DB_PATH = os.path.join(os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share")), "screenshot-gallery", "screenshot_ocr.db") +DB_PATH = os.path.join(os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share")), "orc-gallery", "screenshot_ocr.db") SCREENSHOTS_DIR = os.path.expanduser("~/Screenshots") ROFI_PROMPT = "OCR Search" diff --git a/OCR-scripts/rofi_ocr_search.sh b/OCR-scripts/rofi_ocr_search.sh index 32d118e..a2d0900 100755 --- a/OCR-scripts/rofi_ocr_search.sh +++ b/OCR-scripts/rofi_ocr_search.sh @@ -4,7 +4,7 @@ # This script displays OCR'd screenshot data in rofi and allows opening selected files # Constants -DATABASE_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/screenshot-gallery/screenshot_ocr.db" +DATABASE_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/orc-gallery/screenshot_ocr.db" SCREENSHOTS_DIR="$HOME/Screenshots" ROFI_PROMPT="Screenshot OCR" MAX_DISPLAY_LENGTH=100 diff --git a/OCR-scripts/search_ocr.py b/OCR-scripts/search_ocr.py index c30c792..96cf102 100755 --- a/OCR-scripts/search_ocr.py +++ b/OCR-scripts/search_ocr.py @@ -7,7 +7,7 @@ import sys from datetime import datetime # Constants -DATABASE_PATH = os.path.join(os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share")), "screenshot-gallery", "screenshot_ocr.db") +DATABASE_PATH = os.path.join(os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share")), "orc-gallery", "screenshot_ocr.db") def parse_arguments(): diff --git a/OCR-scripts/simple_rofi_ocr.sh b/OCR-scripts/simple_rofi_ocr.sh index 38bfd7a..31bfa31 100755 --- a/OCR-scripts/simple_rofi_ocr.sh +++ b/OCR-scripts/simple_rofi_ocr.sh @@ -3,7 +3,7 @@ # simple_rofi_ocr.sh - A simplified script to search OCR'd screenshots with rofi # Database location -DB_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/screenshot-gallery/screenshot_ocr.db" +DB_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/orc-gallery/screenshot_ocr.db" # Check if database exists if [ ! -f "$DB_PATH" ]; then diff --git a/README.md b/README.md index f971f5c..b2686a9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Screenshot OCR Gallery +# ORC Gallery A Qt6-based image gallery application that allows you to search through OCR data from your screenshots with live preview and dynamic resizing. @@ -9,7 +9,7 @@ A Qt6-based image gallery application that allows you to search through OCR data - Smart typing detection with 500ms inactivity timer before searching - Visual feedback while typing and searching with animated status indicators - Settings dialog to customize database location and screenshots directory -- Settings stored in ~/.config/ScreenshotOCRGallery/settings.ini for easy access +- Settings stored in ~/.config/OrcGallery/settings.ini for easy access - Customizable image preload count for performance tuning - Prominent "Load More Images" button for easy one-click pagination - Optimized lazy loading that only loads images when needed @@ -44,7 +44,7 @@ sudo apt install qt6-base-dev libqt6sql6-sqlite cmake ## Database Requirements -The application stores its SQLite database at `~/.local/share/screenshot-gallery/screenshot_ocr.db` (following the XDG Base Directory Specification). The directory and an empty database are created automatically on first run if they do not exist. The path can be changed via the Settings dialog. +The application stores its SQLite database at `~/.local/share/orc-gallery/screenshot_ocr.db` (following the XDG Base Directory Specification). The directory and an empty database are created automatically on first run if they do not exist. The path can be changed via the Settings dialog. The required schema is: @@ -67,7 +67,7 @@ CREATE TABLE ocr_results ( 3. Run the build script: ```bash -cd screenshot-gallery +cd orc-gallery chmod +x build.sh ./build.sh ``` @@ -95,7 +95,7 @@ make After building, run the application: ```bash -./build/screenshot-gallery +./build/orc-gallery ``` ## Usage @@ -125,7 +125,7 @@ After building, run the application: - Database File Path: Change where your OCR database is stored - Screenshots Directory: Set the default location for your screenshots - Image count to pre-load: Adjust how many images are loaded at once (default: 20) - - The settings are automatically saved to ~/.config/ScreenshotOCRGallery/settings.ini + - The settings are automatically saved to ~/.config/OrcGallery/settings.ini - Changes are applied immediately without requiring a restart ## Search Technology @@ -135,7 +135,7 @@ This application combines multiple performance-enhancing technologies: ### 1. Customizable Configuration - **Settings Dialog:** Easily configure database location, screenshots directory, and preload count - **File Path Selection:** Browse for locations using native file pickers -- **Persistent Settings:** Your configuration is saved in ~/.config/ScreenshotOCRGallery/settings.ini +- **Persistent Settings:** Your configuration is saved in ~/.config/OrcGallery/settings.ini - **Performance Tuning:** Adjust how many images are preloaded (20 by default) - **Dynamic Updates:** Changes are applied immediately without requiring a restart @@ -176,7 +176,7 @@ This application combines multiple performance-enhancing technologies: ### Database Connection Issues If the application cannot connect to the database: -- The default location is `~/.local/share/screenshot-gallery/screenshot_ocr.db` +- The default location is `~/.local/share/orc-gallery/screenshot_ocr.db` - Check file permissions on the database file and its parent directory - Verify the database has the required schema (the app will create an empty one automatically) diff --git a/build.sh b/build.sh index 8844759..d9df0b2 100755 --- a/build.sh +++ b/build.sh @@ -80,8 +80,8 @@ run_application() { print_message "Running application..." # Check if the application exists - if [ -f "build/screenshot-gallery" ]; then - ./build/screenshot-gallery + if [ -f "build/orc-gallery" ]; then + ./build/orc-gallery else print_error "Application not found. Build might have failed." exit 1 @@ -89,7 +89,7 @@ run_application() { } # -------- Main script execution -------- -print_message "Starting build process for Screenshot Gallery..." +print_message "Starting build process for ORC Gallery..." check_dependencies create_build_dir @@ -104,5 +104,5 @@ echo if [[ $REPLY =~ ^[Yy]$ ]]; then run_application else - print_message "You can run the application later using: ./build/screenshot-gallery" + print_message "You can run the application later using: ./build/orc-gallery" fi diff --git a/icons/orcs-gallery-128.png b/icons/orc-gallery-128.png similarity index 100% rename from icons/orcs-gallery-128.png rename to icons/orc-gallery-128.png diff --git a/icons/orcs-gallery-256.png b/icons/orc-gallery-256.png similarity index 100% rename from icons/orcs-gallery-256.png rename to icons/orc-gallery-256.png diff --git a/icons/orcs-gallery-512.png b/icons/orc-gallery-512.png similarity index 100% rename from icons/orcs-gallery-512.png rename to icons/orc-gallery-512.png diff --git a/icons/orcs-gallery-64.png b/icons/orc-gallery-64.png similarity index 100% rename from icons/orcs-gallery-64.png rename to icons/orc-gallery-64.png diff --git a/screenshot-gallery.desktop b/orc-gallery.desktop similarity index 75% rename from screenshot-gallery.desktop rename to orc-gallery.desktop index 276c917..f2e676d 100644 --- a/screenshot-gallery.desktop +++ b/orc-gallery.desktop @@ -1,11 +1,11 @@ [Desktop Entry] Version=1.0 Type=Application -Name=Screenshot OCR Gallery +Name=ORC Gallery Comment=Search and browse screenshots by OCR content -Exec=screenshot-gallery +Exec=orc-gallery Terminal=false Categories=Graphics;Utility;Viewer; Keywords=screenshots;ocr;gallery;search;images; -Icon=orcs-gallery +Icon=orc-gallery StartupNotify=true diff --git a/plan.md b/plan.md index 930426e..7760744 100644 --- a/plan.md +++ b/plan.md @@ -1,4 +1,4 @@ -# OCR Screenshot Gallery — Project Reference +# ORC Gallery — Project Reference A Qt6 desktop application for Linux that OCRs screenshots and provides a searchable, paginated gallery interface. Users run a Python OCR script to populate a SQLite database, then browse and search all recognized text visually. @@ -7,12 +7,12 @@ A Qt6 desktop application for Linux that OCRs screenshots and provides a searcha ## Directory Structure ``` -ocr-screenshot-gallery/ +orc-gallery/ ├── CMakeLists.txt # CMake build configuration (Qt6) ├── build.sh # One-shot build automation script ├── README.md # User-facing documentation ├── resources.qrc # Qt resource file bundling icons -├── screenshot-gallery.desktop # Linux XDG desktop entry +├── orc-gallery.desktop # Linux XDG desktop entry ├── .gitignore │ ├── src/ # C++ application source @@ -34,13 +34,13 @@ ocr-screenshot-gallery/ │ └── screenshot_ocr.db # Example/seed database │ ├── icons/ # Application icon assets -│ ├── orcs-gallery-512.png -│ ├── orcs-gallery-256.png -│ ├── orcs-gallery-128.png -│ └── orcs-gallery-64.png +│ ├── orc-gallery-512.png +│ ├── orc-gallery-256.png +│ ├── orc-gallery-128.png +│ └── orc-gallery-64.png │ └── build/ # CMake build output (git-ignored) - └── screenshot-gallery # Compiled executable + └── orc-gallery # Compiled executable ``` --- @@ -86,20 +86,20 @@ make -j$(nproc) ### Run ```bash -./build/screenshot-gallery +./build/orc-gallery ``` ### Install system-wide ```bash cd build && sudo make install -screenshot-gallery # from anywhere +orc-gallery # from anywhere ``` Installation puts: - Binary → `${CMAKE_INSTALL_BINDIR}` (typically `/usr/local/bin/`) -- Icons → `/share/icons/hicolor/{64x64,128x128,256x256,512x512}/apps/orcs-gallery.png` -- Desktop entry → `/share/applications/screenshot-gallery.desktop` +- Icons → `/share/icons/hicolor/{64x64,128x128,256x256,512x512}/apps/orc-gallery.png` +- Desktop entry → `/share/applications/orc-gallery.desktop` --- @@ -107,9 +107,9 @@ Installation puts: ### Location -Default (XDG-compliant): `~/.local/share/screenshot-gallery/screenshot_ocr.db` +Default (XDG-compliant): `~/.local/share/orc-gallery/screenshot_ocr.db` -Configurable in Settings dialog; stored in `~/.config/ScreenshotOCRGallery/settings.ini`. +Configurable in Settings dialog; stored in `~/.config/OrcGallery/settings.ini`. ### Schema @@ -162,7 +162,7 @@ Responsibilities: - Search bar (`QLineEdit`) with a 500 ms typing-inactivity timer (`m_typingTimer`) — search only fires after the user stops typing - Animated "Searching…" status bar indicator (`m_searchingAnimationTimer`) - Instantiates and owns `DatabaseManager` and `ImageGallery` -- Reads/writes settings via `QSettings` (INI at `~/.config/ScreenshotOCRGallery/settings.ini`) +- Reads/writes settings via `QSettings` (INI at `~/.config/OrcGallery/settings.ini`) - On search complete: updates status bar with result count - On clear search: reloads all images (first page) @@ -282,7 +282,7 @@ Main OCR runner. Scans a screenshots directory, runs Tesseract on each image not ```bash python3 OCR-scripts/ocr_screenshots.py \ - --db ~/.local/share/screenshot-gallery/screenshot_ocr.db \ + --db ~/.local/share/orc-gallery/screenshot_ocr.db \ --screenshots-dir ~/Screenshots ``` @@ -307,11 +307,11 @@ These scripts hook OCR-screenshot workflows into the [Rofi](https://github.com/d ## Settings & Configuration -**Settings file:** `~/.config/ScreenshotOCRGallery/settings.ini` +**Settings file:** `~/.config/OrcGallery/settings.ini` ```ini [General] -databasePath=/home//.local/share/screenshot-gallery/screenshot_ocr.db +databasePath=/home//.local/share/orc-gallery/screenshot_ocr.db screenshotsDir=/home//Screenshots imagePreloadCount=20 ``` @@ -344,28 +344,33 @@ All three keys have fallbacks defined in `MainWindow::loadSettings()` and `Setti | Repeated searches | 5-minute LRU cache in `DatabaseManager` | | Large galleries | Explicit pagination (default 20, user-controlled via Load More) | | Column reflow cost | 300 ms debounce on resize events | -| Typing lag | 500 ms inactivity timer before issuing query | +| Typing lag | 150 ms inactivity timer before issuing query | | Thread-safe DB access | Per-thread `QSqlDatabase` connections + `QMutex` guards | | Missing files | Placeholder thumbnail rendered inline; no crash | +| Cancel stall | Non-blocking cancel: flag set immediately, old thread drains in background | +| COUNT round-trip | `COUNT(*) OVER()` window function folds count into the main SELECT | +| SQLite I/O | WAL journal + 20 MB page cache + memory-mapped I/O via PRAGMA on init | +| Thumbnail load stall | `QImage` loaded/scaled on `QThreadPool`, marshalled to main thread via `invokeMethod` | +| Overlay widget cost | Filename drawn in `ImageThumbnail::paintEvent` — no child QFrame/QLabel/QHBoxLayout | --- ## Linux Desktop Integration -**`screenshot-gallery.desktop`** +**`orc-gallery.desktop`** ```ini [Desktop Entry] Type=Application -Name=Screenshot OCR Gallery -Icon=orcs-gallery -Exec=screenshot-gallery +Name=ORC Gallery +Icon=orc-gallery +Exec=orc-gallery Categories=Graphics;Utility;Viewer; Keywords=screenshots;ocr;gallery;search;images; ``` XDG paths used by the application: -- Config: `QStandardPaths::AppConfigLocation` → `~/.config/ScreenshotOCRGallery/` -- Data: `QStandardPaths::AppLocalDataLocation` → `~/.local/share/screenshot-gallery/` +- Config: `QStandardPaths::AppConfigLocation` → `~/.config/OrcGallery/` +- Data: `QStandardPaths::AppLocalDataLocation` → `~/.local/share/orc-gallery/` --- @@ -378,3 +383,7 @@ XDG paths used by the application: - The `UpdateDatabaseDialog` is instantiated fresh each time (not reused) to avoid stale process state. - `ImageGallery` does not use a `QAbstractItemModel`; it builds `ImageThumbnail` widgets directly and manages them in a flat list (`m_thumbnails`). - Database connections must not be shared across threads — each `QtConcurrent` task opens its own named connection and closes it on exit. +- `cancelSearch()` is non-blocking: it sets `m_searchCancelled` and returns immediately. The background thread checks the flag at multiple points and exits early. The destructor sets the flag then calls `waitForFinished()` for clean shutdown. +- The watcher's `finished` signal is not used for emitting results (was causing double-emit). Results are emitted directly from `performSearchInBackground` once and only if `m_currentSearchText == searchText`. +- `ImageThumbnail::paintEvent` draws the filename overlay; no child widgets needed. The `m_displayName` field is pre-truncated in the constructor. +- Async thumbnail loading: `loadThumbnailAsync` posts to `QThreadPool::globalInstance()`. `QImage` is loaded and `.scaled()` off the main thread; `QPixmap::fromImage()` is called inside a `Qt::QueuedConnection` invokeMethod lambda. A `QPointer` guard prevents use-after-free if the gallery clears before the load completes. diff --git a/resources.qrc b/resources.qrc index 66302ac..63ca781 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,8 +1,8 @@ - icons/orcs-gallery-64.png - icons/orcs-gallery-128.png - icons/orcs-gallery-256.png - icons/orcs-gallery-512.png + icons/orc-gallery-64.png + icons/orc-gallery-128.png + icons/orc-gallery-256.png + icons/orc-gallery-512.png diff --git a/src/databasemanager.cpp b/src/databasemanager.cpp index 20bbe95..4ba8bf0 100644 --- a/src/databasemanager.cpp +++ b/src/databasemanager.cpp @@ -20,28 +20,8 @@ DatabaseManager::DatabaseManager(QObject *parent) m_allImagesCache.clear(); m_lastCacheUpdate = QDateTime::currentDateTime(); - // Connect future watcher to handle search results - connect(&m_searchWatcher, &QFutureWatcher::finished, - this, [this]() { - if (!m_searchCancelled) { - // Emit signal with the results only if not cancelled - QMutexLocker locker(&m_searchMutex); - QString searchText = m_currentSearchText; - int offset = m_currentOffset; - int limit = m_currentLimit; - - if (!searchText.isEmpty()) { - QMutexLocker cacheLocker(&m_cacheMutex); - if (m_searchCache.contains(searchText) && - m_searchCache[searchText].contains(qMakePair(offset, limit))) { - - SearchCacheItem cacheItem = m_searchCache[searchText][qMakePair(offset, limit)]; - emit searchResultsReady(cacheItem.results, searchText, - offset, limit, cacheItem.totalCount); - } - } - } - }); + // Watcher is used only for lifecycle tracking; results are emitted directly + // from performSearchInBackground to avoid double-emit. // Clean cache periodically QTimer *cleanupTimer = new QTimer(this); @@ -51,9 +31,14 @@ DatabaseManager::DatabaseManager(QObject *parent) DatabaseManager::~DatabaseManager() { - // Cancel any ongoing search and wait for it to finish - cancelSearch(); - + // Signal cancellation and wait for the background thread to drain cleanly. + { + QMutexLocker locker(&m_searchMutex); + m_searchCancelled = true; + } + if (m_searchFuture.isRunning()) + m_searchFuture.waitForFinished(); + if (m_db.isOpen()) { m_db.close(); } @@ -182,6 +167,17 @@ bool DatabaseManager::initialize(const QString &dbPath) m_ftsEnabled = false; } + // Performance pragmas applied once on the main connection. + // WAL allows concurrent reads alongside the OCR writer. + // cache_size=-20000 sets a 20 MB page cache. + // mmap_size enables memory-mapped I/O for faster sequential reads. + QSqlQuery pragmaQuery; + pragmaQuery.exec("PRAGMA journal_mode=WAL"); + pragmaQuery.exec("PRAGMA synchronous=NORMAL"); + pragmaQuery.exec("PRAGMA cache_size=-20000"); + pragmaQuery.exec("PRAGMA temp_store=MEMORY"); + pragmaQuery.exec("PRAGMA mmap_size=268435456"); + m_initialized = true; qDebug() << "Database initialized successfully."; return true; @@ -318,9 +314,12 @@ QSqlDatabase DatabaseManager::getDatabaseConnection() if (!threadDb.open()) { qDebug() << "Failed to open database in thread:" << threadDb.lastError().text(); } else { - // Enable foreign keys in this connection QSqlQuery query(threadDb); - query.exec("PRAGMA foreign_keys = ON"); + query.exec("PRAGMA journal_mode=WAL"); + query.exec("PRAGMA synchronous=NORMAL"); + query.exec("PRAGMA cache_size=-20000"); + query.exec("PRAGMA temp_store=MEMORY"); + query.exec("PRAGMA mmap_size=268435456"); } return threadDb; @@ -397,16 +396,11 @@ void DatabaseManager::searchImages(const QString &searchText, int offset, int li void DatabaseManager::cancelSearch() { - // Set cancelled flag + // Non-blocking: just set the flag. The background thread checks it at + // multiple points and exits early. The new search starts immediately. QMutexLocker locker(&m_searchMutex); m_searchCancelled = true; m_currentSearchText.clear(); - locker.unlock(); - - // Wait for any running search to complete - if (m_searchFuture.isRunning()) { - m_searchFuture.waitForFinished(); - } } void DatabaseManager::performSearchInBackground(const QString &searchText, int offset, int limit) @@ -428,34 +422,8 @@ void DatabaseManager::performSearchInBackground(const QString &searchText, int o return; } } - // First, get total count for pagination info - QSqlQuery countQuery(threadDb); int totalCount = 0; - - if (m_ftsEnabled) { - QString ftsQuery = prepareFTSQuery(searchText); - countQuery.prepare("SELECT COUNT(*) FROM ocr_results r " - "JOIN ocr_fts f ON r.id = f.rowid " - "WHERE ocr_fts MATCH :query"); - countQuery.bindValue(":query", ftsQuery); - } else { - if (searchText.length() <= 3) { - countQuery.prepare("SELECT COUNT(*) FROM ocr_results WHERE ocr_text LIKE :search"); - countQuery.bindValue(":search", "%" + searchText + "%"); - } else { - countQuery.prepare("SELECT COUNT(*) FROM ocr_results WHERE ocr_text LIKE :search OR ocr_text LIKE :wordstart"); - countQuery.bindValue(":search", "%" + searchText + "%"); - countQuery.bindValue(":wordstart", "% " + searchText + "%"); - } - } - - if (countQuery.exec() && countQuery.next()) { - totalCount = countQuery.value(0).toInt(); - } else { - qDebug() << "Failed to get count:" << countQuery.lastError().text(); - totalCount = 0; - } - + // Check if search was cancelled before main query { QMutexLocker locker(&m_searchMutex); @@ -463,24 +431,24 @@ void DatabaseManager::performSearchInBackground(const QString &searchText, int o return; } } - - // Start transaction to speed up query - threadDb.transaction(); + // Single query: COUNT(*) OVER() gives the total match count without a + // separate round-trip. SQLite evaluates window functions before LIMIT, so + // the count reflects the full result set even when we page with LIMIT/OFFSET. + threadDb.transaction(); QSqlQuery query(threadDb); - + if (m_ftsEnabled) { - // Use FTS5 virtual table for much faster text search QString ftsQuery = prepareFTSQuery(searchText); - QString queryStr = "SELECT r.id, r.full_path, r.ocr_text FROM ocr_results r " - "JOIN ocr_fts f ON r.id = f.rowid " - "WHERE ocr_fts MATCH :query " - "ORDER BY rank"; - - if (limit > 0) { + QString queryStr = + "SELECT r.id, r.full_path, r.ocr_text, COUNT(*) OVER() AS total_count " + "FROM ocr_results r " + "JOIN ocr_fts f ON r.id = f.rowid " + "WHERE ocr_fts MATCH :query " + "ORDER BY rank"; + if (limit > 0) queryStr += " LIMIT :limit OFFSET :offset"; - } - + query.prepare(queryStr); query.bindValue(":query", ftsQuery); if (limit > 0) { @@ -488,57 +456,40 @@ void DatabaseManager::performSearchInBackground(const QString &searchText, int o query.bindValue(":offset", offset); } } else { - // Fallback to LIKE queries if FTS is not available - // Optimize the query based on length of search text - if (searchText.length() <= 3) { - // For short search terms, use a more targeted approach - QString queryStr = "SELECT id, full_path, ocr_text FROM ocr_results WHERE ocr_text LIKE :search " - "ORDER BY id"; - - if (limit > 0) { - queryStr += " LIMIT :limit OFFSET :offset"; - } - - query.prepare(queryStr); - query.bindValue(":search", "%" + searchText + "%"); - if (limit > 0) { - query.bindValue(":limit", limit); - query.bindValue(":offset", offset); - } - } else { - // For longer search terms, use LIKE with a more specific pattern at start - QString queryStr = "SELECT id, full_path, ocr_text FROM ocr_results WHERE ocr_text LIKE :search OR ocr_text LIKE :wordstart " - "ORDER BY id"; - - if (limit > 0) { - queryStr += " LIMIT :limit OFFSET :offset"; - } - - query.prepare(queryStr); - query.bindValue(":search", "%" + searchText + "%"); + QString whereClause = (searchText.length() <= 3) + ? "ocr_text LIKE :search" + : "ocr_text LIKE :search OR ocr_text LIKE :wordstart"; + + QString queryStr = + "SELECT id, full_path, ocr_text, COUNT(*) OVER() AS total_count " + "FROM ocr_results WHERE " + whereClause + " ORDER BY id"; + if (limit > 0) + queryStr += " LIMIT :limit OFFSET :offset"; + + query.prepare(queryStr); + query.bindValue(":search", "%" + searchText + "%"); + if (searchText.length() > 3) query.bindValue(":wordstart", "% " + searchText + "%"); - if (limit > 0) { - query.bindValue(":limit", limit); - query.bindValue(":offset", offset); - } + if (limit > 0) { + query.bindValue(":limit", limit); + query.bindValue(":offset", offset); } } - + if (!query.exec()) { - qDebug() << "Failed to search images:" << query.lastError().text(); - qDebug() << "Error details:" << query.lastError().databaseText(); + qDebug() << "Search query failed:" << query.lastError().text(); threadDb.rollback(); - - // If FTS query failed, try fallback to LIKE + + // FTS failure: fall back to LIKE for this request if (m_ftsEnabled) { - qDebug() << "Trying fallback to LIKE query..."; + qDebug() << "Falling back to LIKE query"; threadDb.transaction(); - - query.prepare("SELECT full_path, ocr_text FROM ocr_results WHERE ocr_text LIKE :search"); + query.prepare( + "SELECT id, full_path, ocr_text, COUNT(*) OVER() AS total_count " + "FROM ocr_results WHERE ocr_text LIKE :search ORDER BY id"); query.bindValue(":search", "%" + searchText + "%"); - if (!query.exec()) { - qDebug() << "Fallback query also failed:" << query.lastError().text(); + qDebug() << "Fallback query failed:" << query.lastError().text(); threadDb.rollback(); return; } @@ -547,7 +498,7 @@ void DatabaseManager::performSearchInBackground(const QString &searchText, int o } } - // Check if search was cancelled + // Check cancellation again after query execution { QMutexLocker locker(&m_searchMutex); if (m_searchCancelled || m_currentSearchText != searchText) { @@ -556,11 +507,9 @@ void DatabaseManager::performSearchInBackground(const QString &searchText, int o } } - // Reserve space for results to avoid reallocations - images.reserve(query.size() > 0 ? query.size() : 100); + images.reserve(100); while (query.next()) { - // Periodically check if search was cancelled if (query.at() % 20 == 0) { QMutexLocker locker(&m_searchMutex); if (m_searchCancelled || m_currentSearchText != searchText) { @@ -568,16 +517,18 @@ void DatabaseManager::performSearchInBackground(const QString &searchText, int o return; } } - + + // Read total count from the window function column (same on every row) + if (totalCount == 0) + totalCount = query.value(3).toInt(); + ImageItem item; - item.id = query.value(0).toInt(); + item.id = query.value(0).toInt(); item.filePath = query.value(1).toString(); - item.ocrText = query.value(2).toString(); - - // Only add images that have a non-empty path - if (!item.filePath.isEmpty()) { + item.ocrText = query.value(2).toString(); + + if (!item.filePath.isEmpty()) images.append(item); - } } threadDb.commit(); diff --git a/src/imagegallery.cpp b/src/imagegallery.cpp index 6e9c048..e9f4663 100644 --- a/src/imagegallery.cpp +++ b/src/imagegallery.cpp @@ -24,14 +24,33 @@ ImageThumbnail::ImageThumbnail(const QString &filePath, QWidget *parent) setLineWidth(2); setScaledContents(false); setCursor(Qt::PointingHandCursor); - // We're removing tooltips as requested - - // Enable text wrapping and text interaction - setWordWrap(true); - setTextInteractionFlags(Qt::TextSelectableByMouse); - - // Set a minimum size policy setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + if (!filePath.isEmpty()) { + QString name = QFileInfo(filePath).fileName(); + if (name.length() > 30) + name = name.left(13) + "..." + name.right(13); + m_displayName = name; + } +} + +void ImageThumbnail::paintEvent(QPaintEvent *event) +{ + QLabel::paintEvent(event); + + if (m_displayName.isEmpty()) + return; + + QPainter painter(this); + const int overlayH = 22; + QRect overlayRect(0, height() - overlayH, width(), overlayH); + painter.fillRect(overlayRect, QColor(0, 0, 0, 178)); + + painter.setPen(Qt::white); + QFont f = painter.font(); + f.setPointSize(9); + painter.setFont(f); + painter.drawText(overlayRect, Qt::AlignCenter, m_displayName); } void ImageThumbnail::mousePressEvent(QMouseEvent *event) @@ -207,40 +226,10 @@ void ImageGallery::displayImages(const QList &images continue; // Skip this image } - // Create and add thumbnail - QPixmap thumbnail = createThumbnail(item.filePath, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT); ImageThumbnail *thumbnailLabel = new ImageThumbnail(item.filePath, this); - thumbnailLabel->setPixmap(thumbnail); - - // Add filename overlay at the bottom of the thumbnail - QFileInfo fileNameInfo(item.filePath); - QString fileName = fileNameInfo.fileName(); - - // Create overlay container with dark background - store it as a property of the thumbnail - QFrame* overlay = new QFrame(thumbnailLabel); - overlay->setObjectName("filenameOverlay"); // Set object name for finding it later - overlay->setStyleSheet("background-color: rgba(0, 0, 0, 0.7);"); - overlay->setFixedHeight(20); - - // Create label for the filename - QLabel* fileNameLabel = new QLabel(fileName, overlay); - fileNameLabel->setStyleSheet("color: white; background: transparent;"); - fileNameLabel->setAlignment(Qt::AlignCenter); - // Truncate filename if too long (more than 30 chars) - if (fileName.length() > 30) { - fileNameLabel->setText(fileName.left(13) + "..." + fileName.right(13)); - } - - // Layout for the overlay - QHBoxLayout* overlayLayout = new QHBoxLayout(overlay); - overlayLayout->setContentsMargins(5, 0, 5, 0); - overlayLayout->addWidget(fileNameLabel); - - // Position the overlay at the bottom of the thumbnail - // Width and position will be adjusted in updateGridLayout() - overlay->setFixedWidth(thumbnailLabel->width()); - overlay->move(0, THUMBNAIL_HEIGHT - overlay->height()); - + thumbnailLabel->setPixmap(createPlaceholderThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, "")); + loadThumbnailAsync(thumbnailLabel, item.filePath); + // Connect the thumbnail click signal connect(thumbnailLabel, &ImageThumbnail::thumbnailClicked, this, &ImageGallery::handleThumbnailClicked); @@ -546,26 +535,32 @@ void ImageGallery::handleThumbnailClicked(const QString &filePath) QApplication::restoreOverrideCursor(); } -QPixmap ImageGallery::createThumbnail(const QString &filePath, int width, int height) +void ImageGallery::loadThumbnailAsync(ImageThumbnail *label, const QString &filePath) { - // Verify file exists before attempting to load - QFileInfo fileInfo(filePath); - if (!fileInfo.exists() || !fileInfo.isReadable()) { - qDebug() << "Image file does not exist or is not readable:" << filePath; - return createPlaceholderThumbnail(width, height, "File not found"); - } - - // Try to load the image - QPixmap pixmap(filePath); - - // If loading failed, create a placeholder - if (pixmap.isNull()) { - qDebug() << "Failed to load image:" << filePath; - return createPlaceholderThumbnail(width, height, "Failed to load"); - } - - // Scale pixmap while maintaining aspect ratio - return pixmap.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation); + // QImage loading and scaling are thread-safe; QPixmap creation must happen + // on the main thread. We do the heavy work in a background thread and marshal + // the result back via invokeMethod so the main thread never stalls on disk I/O. + QPointer ptr(label); + + QThreadPool::globalInstance()->start([this, ptr, filePath]() { + QImage image; + QFileInfo fi(filePath); + if (fi.exists() && fi.isReadable()) { + image = QImage(filePath); + if (!image.isNull()) + image = image.scaled(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, + Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + + QMetaObject::invokeMethod(this, [ptr, image]() { + if (!ptr) return; + if (image.isNull()) { + // File missing or unreadable — placeholder already shown, nothing to do + return; + } + ptr->setPixmap(QPixmap::fromImage(image)); + }, Qt::QueuedConnection); + }); } void ImageGallery::resizeEvent(QResizeEvent *event) @@ -687,62 +682,19 @@ void ImageGallery::updateGridLayout() m_containerWidget->adjustSize(); m_containerWidget->updateGeometry(); - // Handle single column mode specially if (m_columnsCount == 1) { - // Center the thumbnails in the viewport - int centeringMargin = (viewportWidth - THUMBNAIL_WIDTH) / 2; - centeringMargin = std::max(THUMBNAIL_SPACING, centeringMargin); + int centeringMargin = std::max(THUMBNAIL_SPACING, (viewportWidth - THUMBNAIL_WIDTH) / 2); m_gridLayout->setContentsMargins(centeringMargin, THUMBNAIL_SPACING, centeringMargin, THUMBNAIL_SPACING); - - // Let all thumbnails expand to fill available width in single column mode for (auto thumbnail : m_thumbnails) { thumbnail->setMaximumWidth(THUMBNAIL_WIDTH); thumbnail->setAlignment(Qt::AlignCenter); - - // Update the overlay to match thumbnail width - QFrame* overlay = thumbnail->findChild("filenameOverlay"); - if (overlay) { - overlay->setFixedWidth(THUMBNAIL_WIDTH); - overlay->move(0, THUMBNAIL_HEIGHT - overlay->height()); - } } } else { - // For multi-column, use minimal margins m_gridLayout->setContentsMargins(THUMBNAIL_SPACING, THUMBNAIL_SPACING, THUMBNAIL_SPACING, THUMBNAIL_SPACING); - - // Reset thumbnail constraints - for (auto thumbnail : m_thumbnails) { + for (auto thumbnail : m_thumbnails) thumbnail->setMaximumWidth(QWIDGETSIZE_MAX); - - // Update the overlay to match thumbnail width in multi-column mode - QFrame* overlay = thumbnail->findChild("filenameOverlay"); - if (overlay) { - overlay->setFixedWidth(thumbnail->width()); - overlay->move(0, THUMBNAIL_HEIGHT - overlay->height()); - } - } - - // For multi-column, let the container fill the viewport m_containerWidget->setMinimumWidth(viewportWidth); } - - // Force update of overlay position and size after a short delay - // This ensures the overlays are sized correctly after all layout changes - QTimer::singleShot(50, this, [this]() { - for (auto thumbnail : m_thumbnails) { - QFrame* overlay = thumbnail->findChild("filenameOverlay"); - if (overlay) { - overlay->setFixedWidth(thumbnail->width()); - overlay->move(0, THUMBNAIL_HEIGHT - overlay->height()); - } - } - }); - - // Update the layout again after a short delay to handle edge cases - QTimer::singleShot(10, this, [this](){ - m_gridLayout->update(); - m_containerWidget->update(); - }); } } } diff --git a/src/imagegallery.h b/src/imagegallery.h index 57982d0..270561e 100644 --- a/src/imagegallery.h +++ b/src/imagegallery.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "databasemanager.h" // Global constants @@ -22,20 +24,22 @@ static const int THUMBNAIL_SPACING = 2; // Reduced spacing between thumbnails class ImageThumbnail : public QLabel { Q_OBJECT - + public: explicit ImageThumbnail(const QString &filePath, QWidget *parent = nullptr); - + QString getFilePath() const { return m_filePath; } protected: void mousePressEvent(QMouseEvent *event) override; - + void paintEvent(QPaintEvent *event) override; + signals: void thumbnailClicked(const QString &filePath); - + private: QString m_filePath; + QString m_displayName; // pre-truncated filename for painting }; class ImageGallery : public QWidget @@ -88,7 +92,7 @@ private: QLabel *m_loadingIndicator; QPushButton *m_loadMoreButton; - QPixmap createThumbnail(const QString &filePath, int width, int height); + void loadThumbnailAsync(ImageThumbnail *label, const QString &filePath); QPixmap createPlaceholderThumbnail(int width, int height, const QString &message); QString m_lastSearchQuery; // Track the last search text diff --git a/src/main.cpp b/src/main.cpp index 928ea6a..8d3faeb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,15 +7,15 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); // Set application properties - app.setApplicationName("screenshot-gallery"); + app.setApplicationName("orc-gallery"); app.setApplicationVersion("1.0.0"); // Set application icon QIcon appIcon; - appIcon.addFile(":/icons/orcs-gallery-64.png", QSize(64, 64)); - appIcon.addFile(":/icons/orcs-gallery-128.png", QSize(128, 128)); - appIcon.addFile(":/icons/orcs-gallery-256.png", QSize(256, 256)); - appIcon.addFile(":/icons/orcs-gallery-512.png", QSize(512, 512)); + appIcon.addFile(":/icons/orc-gallery-64.png", QSize(64, 64)); + appIcon.addFile(":/icons/orc-gallery-128.png", QSize(128, 128)); + appIcon.addFile(":/icons/orc-gallery-256.png", QSize(256, 256)); + appIcon.addFile(":/icons/orc-gallery-512.png", QSize(512, 512)); app.setWindowIcon(appIcon); // Create and show main window diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3bf6ff2..51509ed 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -17,7 +17,7 @@ #include // Define settings file path -const QString MainWindow::CONFIG_FILE_PATH = QDir::homePath() + "/.config/ScreenshotOCRGallery/settings.ini"; +const QString MainWindow::CONFIG_FILE_PATH = QDir::homePath() + "/.config/OrcGallery/settings.ini"; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) @@ -32,15 +32,15 @@ MainWindow::MainWindow(QWidget *parent) , m_imagePreloadCount(DEFAULT_PRELOAD_COUNT) { // Set window title and size - setWindowTitle(tr("Screenshot OCR Gallery")); + setWindowTitle(tr("ORC Gallery")); resize(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT); // Set window icon QIcon appIcon; - appIcon.addFile(":/icons/orcs-gallery-64.png", QSize(64, 64)); - appIcon.addFile(":/icons/orcs-gallery-128.png", QSize(128, 128)); - appIcon.addFile(":/icons/orcs-gallery-256.png", QSize(256, 256)); - appIcon.addFile(":/icons/orcs-gallery-512.png", QSize(512, 512)); + appIcon.addFile(":/icons/orc-gallery-64.png", QSize(64, 64)); + appIcon.addFile(":/icons/orc-gallery-128.png", QSize(128, 128)); + appIcon.addFile(":/icons/orc-gallery-256.png", QSize(256, 256)); + appIcon.addFile(":/icons/orc-gallery-512.png", QSize(512, 512)); setWindowIcon(appIcon); // Remove fixed minimum size to allow for single column layout at any width @@ -66,7 +66,7 @@ MainWindow::MainWindow(QWidget *parent) // Initialize typing inactivity timer m_typingTimer->setSingleShot(true); - m_typingTimer->setInterval(500); // 500ms of no typing before searching + m_typingTimer->setInterval(150); connect(m_typingTimer, &QTimer::timeout, this, &MainWindow::handleTypingInactivityTimeout); // Only create animation timer for searching visual feedback diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 6178c21..81b6d4d 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -8,7 +8,7 @@ // Define static constants const QString SettingsDialog::DEFAULT_SCREENSHOTS_DIR = QDir::homePath() + "/Screenshots"; const QString SettingsDialog::DEFAULT_DATABASE_FILENAME = "screenshot_ocr.db"; -const QString SettingsDialog::CONFIG_FILE_PATH = QDir::homePath() + "/.config/ScreenshotOCRGallery/settings.ini"; +const QString SettingsDialog::CONFIG_FILE_PATH = QDir::homePath() + "/.config/OrcGallery/settings.ini"; SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)