mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-26 13:55:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
871cd51e8a | ||
|
|
fa31a696a7 | ||
|
|
cab21bd6dd | ||
|
|
19c50fdd72 | ||
|
|
57a16f6971 | ||
|
|
79f1c7d5b0 | ||
|
|
a7f0cb78cd | ||
|
|
3642afc46f | ||
|
|
e4c9afeed0 | ||
|
|
06a0a75349 | ||
|
|
c322e96741 | ||
|
|
a6e2dd0cc6 | ||
|
|
6de9bb897d | ||
|
|
ef30450c5f | ||
|
|
083936e99b | ||
|
|
68032c4f3c | ||
|
|
7e626460c0 | ||
|
|
258d06b755 | ||
|
|
525fcfe3af | ||
|
|
f692e2879c | ||
|
|
d110c7abbc | ||
|
|
5343ba9dae | ||
|
|
3d1e962107 | ||
|
|
c14d7a7791 | ||
|
|
4f3745f8ec | ||
|
|
dadfd60ed3 | ||
|
|
6c8fea7309 | ||
|
|
9d9e3b1a4d | ||
|
|
54425a306a | ||
|
|
be601f514d | ||
|
|
a7c7c8190c | ||
|
|
44948de7d0 | ||
|
|
e2efde2d1a | ||
|
|
6630116ba1 | ||
|
|
1f7c2b6a92 | ||
|
|
10ad6b74eb | ||
|
|
f62de567df | ||
|
|
d96dc79943 | ||
|
|
e35c301605 | ||
|
|
7af8dba9f3 | ||
|
|
caa80a30d1 | ||
|
|
db4928e78f | ||
|
|
af96fa2cda | ||
|
|
c08ef9070b | ||
|
|
6fe5b43bc1 | ||
|
|
b3056aedd0 |
@@ -0,0 +1,124 @@
|
||||
# GSmartControl – Copilot Instructions
|
||||
|
||||
GSmartControl is a C++20 GTK3/Gtkmm GUI for inspecting hard-drive and SSD health via SMART data
|
||||
(wrapping `smartctl` from smartmontools). It supports Linux, Windows (MSYS2/MinGW), macOS, and BSDs.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
|
||||
# Standard Linux build (uses dev toolchain with warnings enabled)
|
||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../toolchains/linux-dev.cmake
|
||||
|
||||
# Windows (MSYS2/MinGW64)
|
||||
cmake .. -G "MSYS Makefiles" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../toolchains/win64-mingw-msys2.cmake
|
||||
|
||||
# Build on MSYS2/MinGW64 (from build dir)
|
||||
cmake --build . --target all -j 18
|
||||
```
|
||||
|
||||
The `configure-dev` script is a convenience wrapper: `./configure-dev -t <toolchain> -c <compiler> -b <build_type>`.
|
||||
|
||||
Key CMake options:
|
||||
- `-DAPP_BUILD_TESTS=ON` – enable test targets
|
||||
- `-DAPP_BUILD_EXAMPLES=ON` – enable example targets
|
||||
- `-DAPP_COMPILER_ENABLE_WARNINGS=ON` – strict compiler warnings (set automatically by `linux-dev.cmake`)
|
||||
|
||||
## Tests
|
||||
|
||||
Tests use **Catch2** (vendored in `dependencies/catch2/`). They are only built when `-DAPP_BUILD_TESTS=ON` is passed.
|
||||
|
||||
```bash
|
||||
# Build and run all tests
|
||||
cmake .. -DAPP_BUILD_TESTS=ON
|
||||
cmake --build .
|
||||
ctest -C RelWithDebInfo --rerun-failed --output-on-failure
|
||||
|
||||
# Run a single test executable directly (from build dir)
|
||||
./test_all "[smartctl_parser]" # Catch2 tag filter
|
||||
./test_all "test name substring" # substring match
|
||||
```
|
||||
|
||||
Test sources live in `src/applib/tests/` and `src/hz/tests/`; they are linked into `src/test_all/`.
|
||||
|
||||
## Linting
|
||||
|
||||
Clang-tidy config is at `.clang-tidy` in the repo root. Run it via CMake or directly:
|
||||
|
||||
```bash
|
||||
clang-tidy src/applib/storage_device.cpp -- -std=c++20 $(pkg-config --cflags gtkmm-3.0)
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The codebase is split into libraries and one GUI executable:
|
||||
|
||||
| Component | Location | Purpose |
|
||||
|---|---|---|
|
||||
| `applib` | `src/applib/` | Core logic: SMART parsing, device detection, command execution |
|
||||
| `hz` | `src/hz/` | General-purpose C++ utilities (strings, filesystem, error types) |
|
||||
| `rconfig` | `src/rconfig/` | Runtime config load/save with auto-save support |
|
||||
| `libdebug` | `src/libdebug/` | Debug logging with named channels |
|
||||
| `gui` | `src/gui/` | GTK/Gtkmm UI (windows, dialogs, Glade `.ui` files) |
|
||||
|
||||
### Parsing pipeline
|
||||
|
||||
`smartctl` output is parsed through a strategy pattern:
|
||||
- JSON parsers (`smartctl_json_ata_parser`, `smartctl_json_nvme_parser`, `smartctl_json_basic_parser`) target smartctl ≥ 7.3.
|
||||
- Text parsers (`smartctl_text_ata_parser`, `smartctl_text_basic_parser`) handle legacy output.
|
||||
- Parsed results are stored as `StorageProperty` objects in a `StoragePropertyRepository` on `StorageDevice`.
|
||||
|
||||
### Device detection
|
||||
|
||||
`StorageDetector` is an abstract interface; platform implementations are:
|
||||
- `storage_detector_linux.cpp` – scans `/dev/sd*`, `/dev/nvme*`, etc.
|
||||
- `storage_detector_win32.cpp` – WMI/registry enumeration
|
||||
- `storage_detector_other.cpp` – generic `/dev` scanning (BSD/macOS)
|
||||
|
||||
### Async execution
|
||||
|
||||
`AsyncCommandExecutor` wraps `CommandExecutor` (which shells out to `smartctl`) in a background thread.
|
||||
Completion is signalled via **libsigc++** signals back to the GTK main loop. Never call GTK APIs from the worker thread;
|
||||
queue them through signals or `Glib::signal_idle()`.
|
||||
|
||||
### Error handling
|
||||
|
||||
Use `hz::ExpectedValue<T, E>` (wrapping `tl::expected`) for recoverable errors instead of exceptions.
|
||||
Enum-based error types (`StorageDeviceError`, `SmartctlParserError`) are preferred over string errors.
|
||||
|
||||
## Key Conventions
|
||||
|
||||
**Naming:**
|
||||
- Classes: `PascalCase` (e.g., `StorageDevice`, `SmartctlParser`)
|
||||
- Methods and members: `snake_case`; private/member fields have a trailing `_` (e.g., `device_list_`)
|
||||
- GUI classes in `src/gui/` are prefixed `Gsc` (e.g., `GscMainWindow`)
|
||||
- Filenames: `snake_case.cpp` / `snake_case.h`
|
||||
|
||||
**Headers:**
|
||||
- Use `#ifndef FILENAME_H` / `#define FILENAME_H` guards (not `#pragma once`)
|
||||
- Include order: standard library → third-party (GTK/Gtkmm) → project headers
|
||||
- All project headers are included relative to `src/` (the include root)
|
||||
|
||||
**Modern C++20/23 patterns used throughout:**
|
||||
- `std::optional<T>` for nullable values
|
||||
- `hz::ExpectedValue<T, E>` for error-returning functions
|
||||
- `std::string_view` for non-owning string parameters
|
||||
- `fmt::format()` (vendored `fmt` library) for string formatting
|
||||
- Smart pointers exclusively; no raw owning pointers
|
||||
|
||||
**Vendored dependencies** are in `dependencies/` and added via `add_subdirectory`.
|
||||
Do not modify them; prefer upgrading the whole vendored copy.
|
||||
|
||||
**Translations:** UI strings are wrapped with `_()` (gettext). `.po` files live in `po/`.
|
||||
Translations are not yet supported.
|
||||
|
||||
**Platform guards:** Use `#ifdef CONFIG_KERNEL_FAMILY_WINDOWS`,
|
||||
`#ifdef CONFIG_KERNEL_LINUX`, etc.
|
||||
|
||||
## Agentic Development
|
||||
|
||||
Place all plans and temporary files in `agent_workspace/`. This directory is ignored by Git.
|
||||
@@ -122,7 +122,8 @@ jobs:
|
||||
matrix:
|
||||
include: [
|
||||
{ msystem: MINGW64, arch: x86_64, platform: win64, urlpath: mingw64 },
|
||||
{ msystem: MINGW32, arch: i686, platform: win32, urlpath: mingw32 }
|
||||
# MSYS2 no longer supports for 32-bit Windows.
|
||||
# { msystem: MINGW32, arch: i686, platform: win32, urlpath: mingw32 }
|
||||
]
|
||||
|
||||
steps:
|
||||
@@ -140,6 +141,7 @@ jobs:
|
||||
mingw-w64-${{ matrix.arch }}-cmake
|
||||
mingw-w64-${{ matrix.arch }}-gcc
|
||||
mingw-w64-${{ matrix.arch }}-fontconfig
|
||||
mingw-w64-${{ matrix.arch }}-nsis
|
||||
make
|
||||
p7zip
|
||||
|
||||
|
||||
@@ -56,6 +56,6 @@ jobs:
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
env:
|
||||
CC: gcc-13
|
||||
CXX: g++-13
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -82,4 +82,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
/win32*
|
||||
/win64*
|
||||
/cmake-build-*
|
||||
/build*
|
||||
/_codeql_build_dir
|
||||
|
||||
# Non-project files
|
||||
/TODO
|
||||
@@ -27,6 +29,9 @@ Thumbs.db
|
||||
/.idea
|
||||
/CMakeLists.txt.user
|
||||
|
||||
# Agents
|
||||
/.codebuddy
|
||||
|
||||
# Translations
|
||||
/po/boldquot.sed
|
||||
/po/en@boldquot.header
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
|
||||
---
|
||||
|
||||
**SCAM ALERT:** gsmartcontrol[.]com is a phishing site! Please don't download anything from there!
|
||||
|
||||
Please always download GSmartControl from its official website [gsmartcontrol.shaduri.dev](https://gsmartcontrol.shaduri.dev) or the official GitHub repository [github.com/ashaduri/gsmartcontrol](https://github.com/ashaduri/gsmartcontrol).
|
||||
|
||||
---
|
||||
|
||||
[GSmartControl](https://gsmartcontrol.shaduri.dev)
|
||||
is a graphical user interface for smartctl (from [smartmontools](https://www.smartmontools.org/)
|
||||
package), which is a tool for
|
||||
@@ -59,7 +65,7 @@ for more information.
|
||||
|
||||
## Copyright and Licensing
|
||||
|
||||
GSmartControl is Copyright (C) 2008 - 2024 Alexander Shaduri [ashaduri@gmail.com](mailto:ashaduri@gmail.com) and contributors.
|
||||
GSmartControl is Copyright (C) 2008 - 2025 Alexander Shaduri [ashaduri@gmail.com](mailto:ashaduri@gmail.com) and contributors.
|
||||
|
||||
GSmartControl is licensed under the terms of
|
||||
[GNU General Public License Version 3](https://www.gnu.org/licenses/gpl-3.0.en.html).
|
||||
@@ -73,4 +79,4 @@ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public Licenses for more details.
|
||||
|
||||
This product includes icons from Oxygen Icons copyright [KDE](https://kde.org)
|
||||
and licenced under the [GNU LGPL version 3](https://www.gnu.org/licenses/lgpl-3.0.en.html) or later.
|
||||
and licensed under the [GNU LGPL version 3](https://www.gnu.org/licenses/lgpl-3.0.en.html) or later.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
*
|
||||
@@ -13,13 +13,44 @@ Type=Application
|
||||
Categories=System;Monitor;
|
||||
|
||||
Name=GSmartControl
|
||||
Name[ca]=GSControlIntel
|
||||
Name[cs]=GSmartControl
|
||||
Name[es]=GSmartControl
|
||||
Name[id]=GSmartControl
|
||||
Name[it]=GSmartControl
|
||||
Name[ja]=GSmartControl
|
||||
Name[pt_BR]=GSmartControl
|
||||
Name[ru]=GSmartControl
|
||||
Name[sk]=GSmartControl
|
||||
Name[sv]=GSmartControl
|
||||
Name[zh_CN]=GSmartControl
|
||||
# short description, usually shown in parentheses after Name
|
||||
GenericName=Hard Disk and SSD Health Inspection
|
||||
GenericName[ca]=Inspecció de salut del disc dur i de l'SSD
|
||||
GenericName[cs]=Prohlídka zdraví pevných a SSD disků
|
||||
GenericName[es]=Inspección de la salud de discos duros y SSD
|
||||
GenericName[fr]=Inspecteur de santé de disque dur et de SSD
|
||||
GenericName[id]=Pemeriksaan Kesehatan Cakram Keras dan SSD
|
||||
GenericName[it]=Ispezione della salute del disco rigido e dell'SSD
|
||||
GenericName[ja]=ハードディスクと SSD の健全状態の調査
|
||||
GenericName[pt_BR]=Inspeção da integridade de disco rígido e SSD
|
||||
GenericName[ru]=Проверка состояния жёстких и твердотельных дисков
|
||||
GenericName[sk]=Zdravotná inšpekcia pevného disku a SSD
|
||||
GenericName[zh_CN]=机械及固态硬盘健康检测
|
||||
|
||||
# tooltip
|
||||
Comment=Monitor and control SMART data on hard disk and solid-state drives
|
||||
Comment[ca]=Monitoreu i controleu les dades SMART dels discs durs i dels discs sòlids.
|
||||
Comment[cs]=Sleduje a nastavuje SMART data na pevném disku a na polovodičových discích
|
||||
Comment[es]=Monitorice y controle datos de SMART sobre discos duros y unidades de estado sólido
|
||||
Comment[fr]=Surveille et contrôle les données SMART des disques durs et des SSD
|
||||
Comment[id]=Pantau dan kendalikan data cerdas pada cakram keras dan SSD
|
||||
Comment[it]=Monitora e controlla i dati SMART sui dischi rigidi e a stato solido
|
||||
Comment[ja]=ハードディスクや SSD 内の SMART データの監視と制御
|
||||
Comment[pt_BR]=Monitorar e controlar dados do SMART em discos rígidos e unidades de estado sólido
|
||||
Comment[ru]=Мониторинг и контроль данных SMART на жёстких и твердотельных дисках
|
||||
Comment[sk]=Monitorovanie a kontrola údajov SMART na pevných diskoch a diskoch SSD
|
||||
Comment[zh_CN]=监测及控制机械硬盘和固态硬盘上的SMART数据
|
||||
|
||||
# If it's a name only, it looks for "name.[png|xpm]" file in
|
||||
# $XDG_DATA_DIRS/icons.
|
||||
|
||||
@@ -58,6 +58,9 @@ can specify this option multiple times. Example:\fR
|
||||
\-\-add\-device /dev/sda \-\-add\-device /dev/twa0::3ware,2 \-\-add\-device '/dev/sdb::::-T permissive'
|
||||
.fi
|
||||
.TP
|
||||
\fB\-\-forget\-devices\fP
|
||||
Forget all previously manually added devices
|
||||
.TP
|
||||
\fB\-\-display\fP=\fIDISPLAY\fP
|
||||
X display to use
|
||||
.TP
|
||||
|
||||
+8590
-8511
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,3 +1,3 @@
|
||||
JSON for Modern C++
|
||||
version 3.9.1
|
||||
version 3.12.0
|
||||
https://github.com/nlohmann/json
|
||||
|
||||
+9
-9
@@ -26,19 +26,19 @@ to install these packages.
|
||||
|
||||
### Windows
|
||||
|
||||
#### Windows Vista or Later
|
||||
#### Windows 7 or Later
|
||||
|
||||
- 64-bit installer **(use this if unsure)**: [gsmartcontrol-2.0.1-win64.exe](https://github.com/ashaduri/gsmartcontrol/releases/download/v2.0.1/gsmartcontrol-2.0.1-win64.exe).
|
||||
- 64-bit installer **(use this if unsure)**: [gsmartcontrol-2.0.2-win64.exe](https://github.com/ashaduri/gsmartcontrol/releases/download/v2.0.2/gsmartcontrol-2.0.2-win64.exe).
|
||||
|
||||
- 64-bit zip (portable): [gsmartcontrol-2.0.1.win64.zip](https://github.com/ashaduri/gsmartcontrol/releases/download/v2.0.1/gsmartcontrol-2.0.1-win64.zip).
|
||||
- 64-bit zip (portable): [gsmartcontrol-2.0.2.win64.zip](https://github.com/ashaduri/gsmartcontrol/releases/download/v2.0.2/gsmartcontrol-2.0.2-win64.zip).
|
||||
|
||||
- 32-bit installer: [gsmartcontrol-2.0.1-win32.exe](https://github.com/ashaduri/gsmartcontrol/releases/download/v2.0.1/gsmartcontrol-2.0.1-win32.exe).
|
||||
- 32-bit installer: [gsmartcontrol-2.0.2-win32.exe](https://github.com/ashaduri/gsmartcontrol/releases/download/v2.0.2/gsmartcontrol-2.0.2-win32.exe).
|
||||
|
||||
- 32-bit zip (portable): [gsmartcontrol-2.0.1-win32.zip](https://github.com/ashaduri/gsmartcontrol/releases/download/v2.0.1/gsmartcontrol-2.0.1-win32.zip).
|
||||
- 32-bit zip (portable): [gsmartcontrol-2.0.2-win32.zip](https://github.com/ashaduri/gsmartcontrol/releases/download/v2.0.2/gsmartcontrol-2.0.2-win32.zip).
|
||||
|
||||
#### Outdated: Windows XP, 2000 and 2003
|
||||
#### Outdated: Windows XP, Vista, 2000, 2003
|
||||
|
||||
The last version of GSmartControl that supports Windows XP, 2000 and 2003
|
||||
The last version of GSmartControl that supports Windows XP, Vista, 2000, and 2003
|
||||
is **0.9.0**:
|
||||
- Outdated 32-bit installer: [gsmartcontrol-0.9.0.exe](https://github.com/ashaduri/gsmartcontrol/releases/download/v0.9.0/gsmartcontrol-0.9.0.exe).
|
||||
- Outdated 32-bit zip (portable): [gsmartcontrol-0.9.0-win32.zip](https://github.com/ashaduri/gsmartcontrol/releases/download/v0.9.0/gsmartcontrol-0.9.0-win32.zip).
|
||||
@@ -69,8 +69,8 @@ which includes GSmartControl.
|
||||
## Source Code
|
||||
|
||||
The latest source package:
|
||||
[gsmartcontrol-2.0.1.tar.gz](https://github.com/ashaduri/gsmartcontrol/archive/refs/tags/v2.0.1.tar.gz) \
|
||||
SHA1 sum: 5bdc1cdd9d47c31ffa2ff3162ca7291d8ae953b4
|
||||
[gsmartcontrol-2.0.2.tar.gz](https://github.com/ashaduri/gsmartcontrol/archive/refs/tags/v2.0.2.tar.gz) \
|
||||
SHA1 sum: 3976f591d17c2007587b9bda27c33d0362721db3
|
||||
|
||||
If you're interested in development, you can check the
|
||||
[GitHub Project](https://github.com/ashaduri/gsmartcontrol) page.
|
||||
|
||||
+8
-2
@@ -13,6 +13,12 @@ nav_order: 1
|
||||
|
||||
---
|
||||
|
||||
**SCAM ALERT:** gsmartcontrol[.]com is a phishing site! Please don't download anything from there!
|
||||
|
||||
Please always download GSmartControl from its official website [gsmartcontrol.shaduri.dev](https://gsmartcontrol.shaduri.dev) or the official GitHub repository [github.com/ashaduri/gsmartcontrol](https://github.com/ashaduri/gsmartcontrol).
|
||||
|
||||
---
|
||||
|
||||
[GSmartControl](https://gsmartcontrol.shaduri.dev)
|
||||
is a graphical user interface for smartctl (from [smartmontools](https://www.smartmontools.org/)
|
||||
package), which is a tool for
|
||||
@@ -60,7 +66,7 @@ for more information.
|
||||
|
||||
## Copyright and Licensing
|
||||
|
||||
GSmartControl is Copyright (C) 2008 - 2024 Alexander Shaduri [ashaduri@gmail.com](mailto:ashaduri@gmail.com) and contributors.
|
||||
GSmartControl is Copyright (C) 2008 - 2025 Alexander Shaduri [ashaduri@gmail.com](mailto:ashaduri@gmail.com) and contributors.
|
||||
|
||||
GSmartControl is licensed under the terms of
|
||||
[GNU General Public License Version 3](https://www.gnu.org/licenses/gpl-3.0.en.html).
|
||||
@@ -74,4 +80,4 @@ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public Licenses for more details.
|
||||
|
||||
This product includes icons from Oxygen Icons copyright [KDE](https://kde.org)
|
||||
and licenced under the [GNU LGPL version 3](https://www.gnu.org/licenses/lgpl-3.0.en.html) or later.
|
||||
and licensed under the [GNU LGPL version 3](https://www.gnu.org/licenses/lgpl-3.0.en.html) or later.
|
||||
|
||||
@@ -43,6 +43,8 @@ Example:
|
||||
`--add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device
|
||||
'/dev/sdb::::-T permissive'`.
|
||||
|
||||
`--forget-devices` - Forget all previously manually added devices.
|
||||
|
||||
`-v`, `--verbose` - Enable verbose logging; same as `--verbosity-level 5`.
|
||||
|
||||
`-q`, `--quiet` - Disable logging; same as `--verbosity-level 0`.
|
||||
|
||||
@@ -18,7 +18,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licenses for more details.
|
||||
|
||||
This product includes icons from Oxygen Icons copyright KDE (https://kde.org)
|
||||
and licenced under the GNU LGPL version 3 or later.
|
||||
and licensed under the GNU LGPL version 3 or later.
|
||||
|
||||
You should have received copies of these licenses along with this program.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
@@ -72,6 +72,7 @@ target_sources(applib PRIVATE
|
||||
storage_property_repository.cpp
|
||||
storage_property_repository.h
|
||||
storage_settings.h
|
||||
warning_colors.cpp
|
||||
warning_colors.h
|
||||
warning_level.h
|
||||
window_instance_manager.h
|
||||
|
||||
@@ -48,6 +48,7 @@ inline void init_default_settings()
|
||||
|
||||
rconfig::set_default_data("system/smartctl_options", ""); // default options on ALL commands
|
||||
rconfig::set_default_data("system/smartctl_device_options", ""); // dev1:val1;dev2:val2;... format, each bin2ascii-encoded.
|
||||
rconfig::set_default_data("system/startup_manual_devices", ""); // Auto-add devices on startup
|
||||
|
||||
rconfig::set_default_data("system/linux_udev_byid_path", "/dev/disk/by-id"); // linux hard disk device links here
|
||||
rconfig::set_default_data("system/linux_proc_partitions_path", "/proc/partitions"); // file in linux /proc/partitions format
|
||||
|
||||
@@ -165,6 +165,29 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes
|
||||
|
||||
|
||||
|
||||
bool gui_is_dark_theme_active()
|
||||
{
|
||||
// Try to get the GTK settings to check for dark theme preference.
|
||||
// If GTK is not available or not initialized, get_default() will return null.
|
||||
const Glib::RefPtr<Gtk::Settings> settings = Gtk::Settings::get_default();
|
||||
if (settings) {
|
||||
// Check if the application prefers dark theme
|
||||
if (settings->property_gtk_application_prefer_dark_theme().get_value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check theme name for common dark theme identifiers
|
||||
Glib::ustring theme_name;
|
||||
settings->get_property("gtk-theme-name", theme_name);
|
||||
const std::string theme_str = theme_name.lowercase();
|
||||
if (theme_str.find("dark") != std::string::npos ||
|
||||
theme_str.find("black") != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes
|
||||
std::string& result, const std::string& default_str, Gtk::Window* parent = nullptr, bool sec_msg_markup = false);
|
||||
|
||||
|
||||
|
||||
|
||||
/// Check if a dark GTK theme is currently active
|
||||
bool gui_is_dark_theme_active();
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -83,7 +83,7 @@ hz::ExpectedVoid<StorageDetectorError> StorageDetector::detect(std::vector<Stora
|
||||
}
|
||||
|
||||
// Sort the drives, because their order is not quite defined.
|
||||
// TODO Sort using natural sort
|
||||
// Natural sort is implemented in the StorageDevicePtr comparison operator.
|
||||
std::sort(drives.begin(), drives.end());
|
||||
|
||||
debug_out_info("app", DBG_FUNC_MSG << "Drive detection finished.\n");
|
||||
|
||||
@@ -35,6 +35,7 @@ Copyright:
|
||||
#include "smartctl_version_parser.h"
|
||||
#include "storage_property_descr.h"
|
||||
#include "build_config.h"
|
||||
#include "smartctl_parser.h"
|
||||
//#include "smartctl_text_parser_helper.h"
|
||||
//#include "ata_storage_property_descr.h"
|
||||
|
||||
@@ -327,71 +328,6 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::fetch_full_data_and_parse(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
hz::ExpectedVoid<StorageDeviceError> StorageDevice::try_parse_data()
|
||||
{
|
||||
this->clear_fetched(false); // clear everything fetched before, except outputs and disk type
|
||||
|
||||
auto parser_format = SmartctlParser::detect_output_format(this->full_output_);
|
||||
if (!parser_format.has_value()) {
|
||||
return hz::Unexpected(StorageDeviceError::ParseError, parser_format.error().message());
|
||||
}
|
||||
|
||||
// auto basic_parser = SmartctlParser::create(SmartctlParserType::Basic, parser_format.value());
|
||||
// if (!basic_parser) {
|
||||
// return hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser"));
|
||||
// }
|
||||
|
||||
// TODO Choose format according to device type
|
||||
SmartctlParserType parser_type = SmartctlParserType::Ata;
|
||||
|
||||
auto parser = SmartctlParser::create(parser_type, parser_format.value());
|
||||
DBG_ASSERT_RETURN(parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser")));
|
||||
|
||||
// Try to parse it (parse only, set the properties after basic parsing).
|
||||
const auto parse_status = parser->parse(this->full_output_);
|
||||
if (parse_status.has_value()) {
|
||||
|
||||
// refresh basic info too
|
||||
this->basic_output_ = this->full_output_; // put data including version information
|
||||
|
||||
// note: this will clear the non-basic properties!
|
||||
// this will parse some info that is already parsed by SmartctlAtaTextParser::parse(),
|
||||
// but this one sets the StorageDevice class members, not properties.
|
||||
static_cast<void>(this->parse_basic_data(false, false)); // don't emit signal, we're not complete yet.
|
||||
|
||||
// Call this after parse_basic_data(), since it sets parse status to "info".
|
||||
this->set_parse_status(StorageDevice::ParseStatus::Full);
|
||||
|
||||
// set the full properties.
|
||||
// copy to our drive, overwriting old data.
|
||||
this->set_property_repository(StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type));
|
||||
|
||||
signal_changed().emit(this); // notify listeners
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Don't show any GUI warnings on parse failure - it may just be an unsupported
|
||||
// drive (e.g. usb flash disk). Plus, it may flood the string. The data will be
|
||||
// parsed again in Info window, and we show the warnings there.
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse smartctl output.\n");
|
||||
|
||||
// proper parsing failed. try to at least extract info section
|
||||
this->basic_output_ = this->full_output_; // complete output here. sometimes it's only the info section
|
||||
auto basic_parse_status = this->parse_basic_data(true); // will add some properties too. this will emit signal_changed().
|
||||
if (!basic_parse_status) {
|
||||
// return full parser's error messages - they are more detailed.
|
||||
std::string message = basic_parse_status.error().message();
|
||||
return hz::Unexpected(StorageDeviceError::ParseError,
|
||||
fmt::format(fmt::runtime(_("Cannot parse smartctl output: {}")), message));
|
||||
}
|
||||
|
||||
return {}; // return ok if at least the info was ok.
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<StorageDeviceError> StorageDevice::parse_full_data(SmartctlParserType parser_type, SmartctlOutputFormat format)
|
||||
{
|
||||
|
||||
@@ -19,8 +19,9 @@ Copyright:
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
#include "hz/fs_ns.h"
|
||||
#include "hz/string_algo.h"
|
||||
#include "storage_property.h"
|
||||
#include "smartctl_text_ata_parser.h" // prop_list_t
|
||||
#include "smartctl_parser_types.h"
|
||||
#include "smartctl_executor.h"
|
||||
#include "storage_property_repository.h"
|
||||
#include "storage_device_detected_type.h"
|
||||
@@ -106,9 +107,6 @@ class StorageDevice {
|
||||
/// Execute smartctl --all / -x (all sections), get output, parse it (basic data too), fill properties.
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> fetch_full_data_and_parse(const std::shared_ptr<CommandExecutor>& smartctl_ex);
|
||||
|
||||
/// Parse full info. If failed, try to parse it as basic info.
|
||||
// [[nodiscard]] hz::ExpectedVoid<StorageDeviceError> try_parse_data();
|
||||
|
||||
/// Parse full info.
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> parse_full_data(SmartctlParserType parser_type, SmartctlOutputFormat format);
|
||||
|
||||
@@ -325,12 +323,12 @@ inline bool operator< (const StorageDevicePtr& a, const StorageDevicePtr& b)
|
||||
return int(a->get_is_virtual()) < int(b->get_is_virtual());
|
||||
}
|
||||
if (a->get_is_virtual()) {
|
||||
return a->get_virtual_file() < b->get_virtual_file();
|
||||
return hz::string_natural_compare(a->get_virtual_filename(), b->get_virtual_filename()) < 0;
|
||||
}
|
||||
if (a->get_device_base() != b->get_device_base()) {
|
||||
return a->get_device_base() < b->get_device_base();
|
||||
return hz::string_natural_compare(a->get_device_base(), b->get_device_base()) < 0;
|
||||
}
|
||||
return a->get_type_argument() < b->get_type_argument();
|
||||
return hz::string_natural_compare(a->get_type_argument(), b->get_type_argument()) < 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,8 +15,10 @@ Copyright:
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <set>
|
||||
|
||||
#include "rconfig/rconfig.h"
|
||||
#include "hz/debug.h"
|
||||
|
||||
|
||||
/// Device name, device type (optional)
|
||||
@@ -75,7 +77,7 @@ inline void from_json(const rconfig::json& j, AppDeviceOptionMap& devmap)
|
||||
|
||||
|
||||
|
||||
/// Get device option map from config
|
||||
/// Get per-device options from config
|
||||
inline AppDeviceOptionMap app_config_get_device_option_map()
|
||||
{
|
||||
return rconfig::get_data<AppDeviceOptionMap>("system/smartctl_device_options");
|
||||
@@ -99,7 +101,8 @@ inline std::vector<std::string> app_get_device_options(const std::string& dev, c
|
||||
}
|
||||
catch(Glib::ShellError& e)
|
||||
{
|
||||
// TODO report error
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse device options for \""
|
||||
<< dev << "\": " << e.what() << "\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -108,6 +111,99 @@ inline std::vector<std::string> app_get_device_options(const std::string& dev, c
|
||||
|
||||
|
||||
|
||||
/// Device information used when manually adding devices
|
||||
struct AppAddDeviceOption {
|
||||
AppAddDeviceOption() = default;
|
||||
|
||||
AppAddDeviceOption(std::string par_device, std::string par_type, std::string par_options)
|
||||
: device(std::move(par_device)), type(std::move(par_type)), options(std::move(par_options))
|
||||
{ }
|
||||
|
||||
std::string device; ///< Device name, e.g. /dev/sda
|
||||
std::string type; ///< Smartctl type
|
||||
std::string options; ///< Additional smartctl options
|
||||
|
||||
|
||||
/// Compare two AppAddDeviceOption objects
|
||||
bool operator==(const AppAddDeviceOption& other) const
|
||||
{
|
||||
return device == other.device && type == other.type && options == other.options;
|
||||
}
|
||||
|
||||
/// Compare two AppAddDeviceOption objects
|
||||
bool operator!=(const AppAddDeviceOption& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
/// Compare two AppAddDeviceOption objects
|
||||
bool operator<(const AppAddDeviceOption& other) const
|
||||
{
|
||||
if (device != other.device)
|
||||
return device < other.device;
|
||||
if (type != other.type)
|
||||
return type < other.type;
|
||||
return options < other.options;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// json serializer for rconfig
|
||||
inline void to_json(rconfig::json& j, const std::set<AppAddDeviceOption>& devices)
|
||||
{
|
||||
for (const auto& dev : devices) {
|
||||
if (!dev.device.empty()) {
|
||||
j.push_back(rconfig::json {
|
||||
{"device", dev.device},
|
||||
{"type", dev.type},
|
||||
{"options", dev.options}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// json deserializer for rconfig
|
||||
inline void from_json(const rconfig::json& j, std::set<AppAddDeviceOption>& devices)
|
||||
{
|
||||
if (!j.is_array()) {
|
||||
return;
|
||||
}
|
||||
for (const auto& obj : j) {
|
||||
try {
|
||||
if (obj.contains("device") && obj.contains("type") && obj.contains("options")) {
|
||||
devices.emplace(
|
||||
obj.at("device").get<std::string>(),
|
||||
obj.at("type").get<std::string>(),
|
||||
obj.at("options").get<std::string>()
|
||||
);
|
||||
}
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
// ignore "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get devices to manually add on startup
|
||||
inline std::set<AppAddDeviceOption> app_get_startup_manual_devices()
|
||||
{
|
||||
return rconfig::get_data<std::set<AppAddDeviceOption>>("system/startup_manual_devices");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Set devices to manually add on startup
|
||||
inline void app_set_startup_manual_devices(std::set<AppAddDeviceOption> devices)
|
||||
{
|
||||
rconfig::set_data("system/startup_manual_devices", std::move(devices));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,10 +9,6 @@ Copyright:
|
||||
/// \weakgroup applib_tests
|
||||
/// @{
|
||||
|
||||
// Catch2 v3
|
||||
//#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
// Catch2 v2
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "applib/app_regex.h"
|
||||
|
||||
@@ -9,10 +9,6 @@ Copyright:
|
||||
/// \weakgroup applib_tests
|
||||
/// @{
|
||||
|
||||
// Catch2 v3
|
||||
//#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
// Catch2 v2
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "applib/smartctl_parser.h"
|
||||
|
||||
@@ -9,10 +9,6 @@ Copyright:
|
||||
/// \weakgroup applib_tests
|
||||
/// @{
|
||||
|
||||
// Catch2 v3
|
||||
//#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
// Catch2 v2
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "applib/smartctl_version_parser.h"
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2008 - 2026 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup applib
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#include <glibmm.h>
|
||||
|
||||
#include "warning_colors.h"
|
||||
#include "gui_utils.h"
|
||||
|
||||
|
||||
bool app_property_get_row_highlight_colors(bool dark_mode, WarningLevel warning, std::string& fg, std::string& bg)
|
||||
{
|
||||
// Note: we're setting both fg and bg, to avoid theme conflicts.
|
||||
if (warning == WarningLevel::Notice) {
|
||||
fg = dark_mode ? "#FFFFFF" : "#000000"; // white for dark themes, black for light themes
|
||||
bg = dark_mode ? "#6B2050" : "#FFD5EE"; // dark pinkish for dark themes, pinkish for light themes
|
||||
|
||||
} else if (warning == WarningLevel::Warning) {
|
||||
fg = dark_mode ? "#FFFFFF" : "#000000"; // white for dark themes, black for light themes
|
||||
bg = dark_mode ? "#802020" : "#FFA0A0"; // dark red for dark themes, light red for light themes
|
||||
|
||||
} else if (warning == WarningLevel::Alert) {
|
||||
fg = dark_mode ? "#FFFFFF" : "#000000"; // white for dark themes, black for light themes
|
||||
bg = dark_mode ? "#AA0000" : "#FF0000"; // darker red for dark themes, bright red for light themes
|
||||
}
|
||||
|
||||
return !(fg.empty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool app_property_get_label_highlight_color(bool dark_mode, WarningLevel warning, std::string& fg)
|
||||
{
|
||||
if (warning == WarningLevel::None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (warning == WarningLevel::Notice) {
|
||||
fg = dark_mode ? "#FF9999" : "#770000"; // lighter red for dark themes, very dark red for light themes
|
||||
|
||||
} else if (warning == WarningLevel::Warning) {
|
||||
fg = dark_mode ? "#FF6666" : "#C00000"; // lighter red for dark themes, dark red for light themes
|
||||
|
||||
} else if (warning == WarningLevel::Alert) {
|
||||
fg = dark_mode ? "#FF4444" : "#FF0000"; // lighter/pink red for dark themes, bright red for light themes
|
||||
}
|
||||
|
||||
return !(fg.empty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string storage_property_get_warning_reason(const StorageProperty& p)
|
||||
{
|
||||
std::string fg, start = "<b>", stop = "</b>";
|
||||
if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), p.warning_level, fg)) {
|
||||
start += "<span color=\"" + fg + "\">";
|
||||
stop = "</span>" + stop;
|
||||
}
|
||||
|
||||
switch (p.warning_level) {
|
||||
case WarningLevel::None:
|
||||
// nothing
|
||||
break;
|
||||
case WarningLevel::Notice:
|
||||
/// Translators: %1 and %2 are HTML tags, %3 is a message.
|
||||
return Glib::ustring::compose(_("%1Notice:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason));
|
||||
case WarningLevel::Warning:
|
||||
/// Translators: %1 and %2 are HTML tags, %3 is a message.
|
||||
return Glib::ustring::compose(_("%1Warning:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason));
|
||||
case WarningLevel::Alert:
|
||||
/// Translators: %1 and %2 are HTML tags, %3 is a message.
|
||||
return Glib::ustring::compose(_("%1ALERT:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
@@ -1,7 +1,7 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com>
|
||||
(C) 2008 - 2026 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
@@ -12,83 +12,24 @@ Copyright:
|
||||
#ifndef WARNING_COLORS_H
|
||||
#define WARNING_COLORS_H
|
||||
|
||||
#include <glibmm.h>
|
||||
#include <string>
|
||||
|
||||
#include "storage_property.h"
|
||||
|
||||
#include "warning_level.h"
|
||||
|
||||
|
||||
/// Get colors for tree rows according to warning severity.
|
||||
/// \return true if the colors were changed.
|
||||
inline bool app_property_get_row_highlight_colors(WarningLevel warning, std::string& fg, std::string& bg)
|
||||
{
|
||||
// Note: we're setting both fg and bg, to avoid theme conflicts.
|
||||
if (warning == WarningLevel::Notice) {
|
||||
fg = "#000000"; // black
|
||||
bg = "#FFD5EE"; // pinkish
|
||||
|
||||
} else if (warning == WarningLevel::Warning) {
|
||||
fg = "#000000"; // black
|
||||
bg = "#FFA0A0"; // even more pinkish
|
||||
|
||||
} else if (warning == WarningLevel::Alert) {
|
||||
fg = "#000000"; // black
|
||||
bg = "#FF0000"; // red
|
||||
}
|
||||
|
||||
return !(fg.empty());
|
||||
}
|
||||
|
||||
bool app_property_get_row_highlight_colors(bool dark_mode, WarningLevel warning, std::string& fg, std::string& bg);
|
||||
|
||||
|
||||
/// Get color for labels according to warning severity.
|
||||
/// \return true if the color was changed.
|
||||
inline bool app_property_get_label_highlight_color(WarningLevel warning, std::string& fg)
|
||||
{
|
||||
if (warning == WarningLevel::Notice) {
|
||||
fg = "#770000"; // very dark red
|
||||
|
||||
} else if (warning == WarningLevel::Warning) {
|
||||
fg = "#C00000"; // dark red
|
||||
|
||||
} else if (warning == WarningLevel::Alert) {
|
||||
fg = "#FF0000"; // red
|
||||
}
|
||||
|
||||
return !(fg.empty());
|
||||
}
|
||||
|
||||
|
||||
bool app_property_get_label_highlight_color(bool dark_mode, WarningLevel warning, std::string& fg);
|
||||
|
||||
|
||||
/// Format warning text, but without description
|
||||
inline std::string storage_property_get_warning_reason(const StorageProperty& p)
|
||||
{
|
||||
std::string fg, start = "<b>", stop = "</b>";
|
||||
if (app_property_get_label_highlight_color(p.warning_level, fg)) {
|
||||
start += "<span color=\"" + fg + "\">";
|
||||
stop = "</span>" + stop;
|
||||
}
|
||||
|
||||
switch (p.warning_level) {
|
||||
case WarningLevel::None:
|
||||
// nothing
|
||||
break;
|
||||
case WarningLevel::Notice:
|
||||
/// Translators: %1 and %2 are HTML tags, %3 is a message.
|
||||
return Glib::ustring::compose(_("%1Notice:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason));
|
||||
case WarningLevel::Warning:
|
||||
/// Translators: %1 and %2 are HTML tags, %3 is a message.
|
||||
return Glib::ustring::compose(_("%1Warning:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason));
|
||||
case WarningLevel::Alert:
|
||||
/// Translators: %1 and %2 are HTML tags, %3 is a message.
|
||||
return Glib::ustring::compose(_("%1ALERT:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
std::string storage_property_get_warning_reason(const StorageProperty& p);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com>
|
||||
(C) 2008 - 2025 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
@@ -40,7 +40,7 @@ GscAboutDialog::GscAboutDialog(BaseObjectType* gtkcobj, Glib::RefPtr<Gtk::Builde
|
||||
|
||||
// spammers go away
|
||||
set_copyright(Glib::ustring::compose("Copyright (C) %1",
|
||||
"2008 - 2024 Alexander Shaduri <ashaduri@gmail.com>"));
|
||||
"2008 - 2025 Alexander Shaduri <ashaduri@gmail.com>"));
|
||||
|
||||
// set_authors({"Alexander Shaduri <ashaduri@gmail.com>"});
|
||||
// set_documenters({"Alexander Shaduri <ashaduri@gmail.com>"});
|
||||
|
||||
@@ -20,7 +20,7 @@ Copyright:
|
||||
#include "gsc_add_device_window.h"
|
||||
#include "gsc_main_window.h"
|
||||
#include "build_config.h"
|
||||
|
||||
#include "applib/storage_settings.h"
|
||||
|
||||
|
||||
|
||||
@@ -148,6 +148,7 @@ void GscAddDeviceWindow::on_window_ok_button_clicked()
|
||||
{
|
||||
std::string dev, type;
|
||||
std::vector<std::string> params;
|
||||
std::string params_str;
|
||||
if (auto* entry = lookup_widget<Gtk::Entry*>("device_name_entry")) {
|
||||
dev = entry->get_text();
|
||||
}
|
||||
@@ -155,7 +156,7 @@ void GscAddDeviceWindow::on_window_ok_button_clicked()
|
||||
type = type_combo->get_entry_text();
|
||||
}
|
||||
if (auto* entry = lookup_widget<Gtk::Entry*>("smartctl_params_entry")) {
|
||||
auto params_str = entry->get_text();
|
||||
params_str = entry->get_text();
|
||||
if (!params_str.empty()) {
|
||||
try {
|
||||
params = Glib::shell_parse_argv(params_str);
|
||||
@@ -166,8 +167,29 @@ void GscAddDeviceWindow::on_window_ok_button_clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool added = false;
|
||||
if (main_window_ && !dev.empty()) {
|
||||
main_window_->add_device(dev, type, params);
|
||||
added = main_window_->add_device_interactive(dev, type, params);
|
||||
}
|
||||
|
||||
if (added) {
|
||||
bool auto_add = false;
|
||||
if (auto* check = lookup_widget<Gtk::CheckButton*>("auto_add_device_check")) {
|
||||
auto_add = check->get_active();
|
||||
}
|
||||
|
||||
// If auto-add is checked, save the device info to config
|
||||
if (auto_add && !dev.empty()) {
|
||||
// Get existing auto-add devices
|
||||
auto devices = app_get_startup_manual_devices();
|
||||
|
||||
// Add or update this device in the map
|
||||
devices.emplace(dev, type, params_str);
|
||||
|
||||
// Save the updated map back to config
|
||||
app_set_startup_manual_devices(devices);
|
||||
}
|
||||
}
|
||||
|
||||
destroy_instance();
|
||||
|
||||
@@ -81,6 +81,7 @@ namespace {
|
||||
// vbox->pack_start(*label, false, false);
|
||||
|
||||
} else {
|
||||
const bool dark_mode = gui_is_dark_theme_active();
|
||||
|
||||
// add one label per element
|
||||
for (const auto& label_string : label_strings) {
|
||||
@@ -95,7 +96,7 @@ namespace {
|
||||
label->set_can_focus(false);
|
||||
|
||||
std::string fg;
|
||||
if (app_property_get_label_highlight_color(label_string.property->warning_level, fg)) {
|
||||
if (app_property_get_label_highlight_color(dark_mode, label_string.property->warning_level, fg)) {
|
||||
label->set_markup(
|
||||
std::string("<span color=\"").append(fg).append("\">")
|
||||
.append(label_text).append("</span>") );
|
||||
@@ -129,7 +130,7 @@ namespace {
|
||||
}
|
||||
|
||||
std::string fg;
|
||||
if (app_property_get_label_highlight_color(warning, fg))
|
||||
if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), warning, fg))
|
||||
label->set_markup_with_mnemonic("<span color=\"" + fg + "\">" + original_label + "</span>");
|
||||
}
|
||||
|
||||
@@ -1038,6 +1039,7 @@ void GscInfoWindow::fill_ui_general(const StoragePropertyRepository& property_re
|
||||
identity_table->hide();
|
||||
|
||||
WarningLevel max_tab_warning = WarningLevel::None;
|
||||
const bool dark_mode = gui_is_dark_theme_active();
|
||||
int row = 0;
|
||||
|
||||
for (auto&& p : general_props) {
|
||||
@@ -1070,7 +1072,7 @@ void GscInfoWindow::fill_ui_general(const StoragePropertyRepository& property_re
|
||||
value->set_markup(Glib::Markup::escape_text(p.format_value()));
|
||||
|
||||
std::string fg;
|
||||
if (app_property_get_label_highlight_color(p.warning_level, fg)) {
|
||||
if (app_property_get_label_highlight_color(dark_mode, p.warning_level, fg)) {
|
||||
name->set_markup("<span color=\"" + fg + "\">" + name->get_label() + "</span>");
|
||||
value->set_markup("<span color=\"" + fg + "\">" + value->get_label() + "</span>");
|
||||
}
|
||||
@@ -1490,7 +1492,7 @@ void GscInfoWindow::fill_ui_self_test_log(const StoragePropertyRepository& prope
|
||||
|
||||
model_columns.add(columns_->self_test_log_table_columns.hours);
|
||||
num_tree_col = app_gtkmm_create_tree_view_column(columns_->self_test_log_table_columns.hours, *treeview,
|
||||
_("Lifetime hours"), _("Hour of the drive's powered-on lifetime when the test completed or aborted.\nThe value wraps after 36535 hours."), true);
|
||||
_("Lifetime hours"), _("Hour of the drive's powered-on lifetime when the test completed or aborted.\nThe value wraps after 65535 hours."), true);
|
||||
|
||||
model_columns.add(columns_->self_test_log_table_columns.lba);
|
||||
num_tree_col = app_gtkmm_create_tree_view_column(columns_->self_test_log_table_columns.lba, *treeview,
|
||||
@@ -2067,7 +2069,7 @@ WarningLevel GscInfoWindow::fill_ui_directory(const StoragePropertyRepository& p
|
||||
inline void cell_renderer_set_warning_fg_bg(Gtk::CellRendererText* crt, const StorageProperty& p)
|
||||
{
|
||||
std::string fg, bg;
|
||||
if (app_property_get_row_highlight_colors(p.warning_level, fg, bg)) {
|
||||
if (app_property_get_row_highlight_colors(gui_is_dark_theme_active(), p.warning_level, fg, bg)) {
|
||||
// Note: property_cell_background makes horizontal tree lines disappear around it,
|
||||
// but property_background doesn't play nice with sorted column color.
|
||||
crt->property_cell_background() = bg;
|
||||
@@ -2444,7 +2446,11 @@ gboolean GscInfoWindow::test_idle_callback(void* data)
|
||||
break;
|
||||
case SelfTestStatusSeverity::Error:
|
||||
if (!result_main_msg.empty()) { // Highlight in red
|
||||
result_main_msg = "<span color=\"#FF0000\">"s + result_main_msg + "</span>";
|
||||
std::string alert_color;
|
||||
// Use the same color as Alert level warnings for consistency
|
||||
if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), WarningLevel::Alert, alert_color) && !alert_color.empty()) {
|
||||
result_main_msg = "<span color=\"" + alert_color + "\">"s + result_main_msg + "</span>";
|
||||
}
|
||||
}
|
||||
result_details_msg += "\n"s + _("Check the Self-Test Log for more information.");
|
||||
break;
|
||||
|
||||
+34
-2
@@ -2,7 +2,7 @@
|
||||
*****************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2024 Alexander Shaduri <ashaduri@gmail.com>
|
||||
(C) 2008 - 2025 Alexander Shaduri <ashaduri@gmail.com>
|
||||
*****************************************************************************
|
||||
*/
|
||||
/// \file
|
||||
@@ -206,6 +206,7 @@ namespace {
|
||||
gboolean arg_locale = TRUE; ///< if false, disable using system locale
|
||||
gboolean arg_version = FALSE; ///< if true, show version and exit
|
||||
gboolean arg_scan = TRUE; ///< if false, don't scan the system for drives on startup
|
||||
gboolean arg_forget_manual_devices = FALSE; ///< if true, forget all manually added devices
|
||||
gchar** arg_add_virtual = nullptr; ///< load smartctl data from these files as virtual drives
|
||||
gchar** arg_add_device = nullptr; ///< add these device files manually
|
||||
double arg_gdk_scale = std::numeric_limits<double>::quiet_NaN(); ///< The value of GDK_SCALE environment variable
|
||||
@@ -230,6 +231,8 @@ namespace {
|
||||
N_("Add this device to device list. The format of the device is \"<device>::<type>::<extra_args>\", where type and extra_args are optional."
|
||||
" This option is useful with --no-scan to list certain drives only. You can specify this option multiple times."
|
||||
" Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device '/dev/sdb::::-T permissive'"), nullptr },
|
||||
{ "forget-devices", '\0', 0, G_OPTION_ARG_NONE, &(args.arg_forget_manual_devices),
|
||||
N_("Forget all previously manually added devices."), nullptr },
|
||||
#ifndef _WIN32
|
||||
// X11-specific
|
||||
{ "gdk-scale", 'l', 0, G_OPTION_ARG_DOUBLE, &(args.arg_gdk_scale),
|
||||
@@ -284,7 +287,7 @@ namespace {
|
||||
std::string warningtext = std::string("\n") + _("Warning: GSmartControl comes with ABSOLUTELY NO WARRANTY.\n"
|
||||
"See LICENSE.txt file for details.") + "\n\n";
|
||||
/// %1 is years, %2 is email address
|
||||
warningtext += Glib::ustring::compose(_("Copyright (C) %1 Alexander Shaduri %2"), "2008 - 2024",
|
||||
warningtext += Glib::ustring::compose(_("Copyright (C) %1 Alexander Shaduri %2"), "2008 - 2025",
|
||||
"<ashaduri@gmail.com>") + "\n\n";
|
||||
|
||||
std::cout << versiontext << warningtext;
|
||||
@@ -502,6 +505,32 @@ bool app_init_and_loop(int& argc, char**& argv)
|
||||
}
|
||||
*/
|
||||
|
||||
// Detect Windows dark mode and set GTK theme preference accordingly
|
||||
if constexpr(BuildEnv::is_kernel_family_windows()) {
|
||||
Glib::RefPtr<Gtk::Settings> gtk_settings = Gtk::Settings::get_default();
|
||||
if (gtk_settings) {
|
||||
bool use_dark_theme = false;
|
||||
#ifdef _WIN32
|
||||
// Check Windows registry for dark mode preference
|
||||
// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize
|
||||
// AppsUseLightTheme = 0 means dark mode, 1 means light mode
|
||||
DWORD apps_use_light_theme = 1; // Default to light mode
|
||||
if (hz::win32_get_registry_value_dword(HKEY_CURRENT_USER,
|
||||
R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)",
|
||||
"AppsUseLightTheme", apps_use_light_theme)) {
|
||||
use_dark_theme = (apps_use_light_theme == 0);
|
||||
debug_out_dump("app", "Windows theme detected: " << (use_dark_theme ? "dark" : "light") << "\n");
|
||||
} else {
|
||||
debug_out_dump("app", "Could not read Windows theme preference, defaulting to light mode.\n");
|
||||
}
|
||||
#endif
|
||||
// Apply the dark theme preference to GTK
|
||||
gtk_settings->property_gtk_application_prefer_dark_theme().set_value(use_dark_theme);
|
||||
debug_out_dump("app", "GTK dark theme preference set to: " << (use_dark_theme ? "dark" : "light") << "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The application is dpi-aware in Windows.
|
||||
// However, Gtk3 does not support fractional scaling, so at 250% scaling in system settings, the UI will use 200%.
|
||||
//
|
||||
@@ -557,6 +586,9 @@ bool app_init_and_loop(int& argc, char**& argv)
|
||||
// add devices to the list on startup if specified.
|
||||
get_startup_settings().add_devices = load_devices;
|
||||
|
||||
// forget all manually added devices on startup if specified.
|
||||
get_startup_settings().forget_manual_devices = bool(args.arg_forget_manual_devices);
|
||||
|
||||
|
||||
// Create executor log window, but don't show it.
|
||||
// It will track all command executor outputs.
|
||||
|
||||
@@ -44,7 +44,7 @@ Copyright:
|
||||
#include "applib/command_executor_factory.h"
|
||||
#include "gsc_startup_settings.h"
|
||||
#include "build_config.h"
|
||||
|
||||
#include "applib/storage_settings.h"
|
||||
|
||||
|
||||
using namespace std::literals;
|
||||
@@ -142,10 +142,18 @@ void GscMainWindow::populate_iconview_on_startup(bool smartctl_valid)
|
||||
}
|
||||
}
|
||||
if (!file.empty()) {
|
||||
add_device(file, type_arg, extra_args);
|
||||
// Report any smartctl-related issues
|
||||
add_device_interactive(file, type_arg, extra_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add auto-add devices stored in configuration
|
||||
if (get_startup_settings().forget_manual_devices) {
|
||||
app_set_startup_manual_devices({}); // clear the list
|
||||
} else {
|
||||
add_startup_manual_devices();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto&& virt : get_startup_settings().load_virtuals) {
|
||||
@@ -483,8 +491,16 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type)
|
||||
case action_remove_device:
|
||||
if (iconview_) {
|
||||
StorageDevicePtr drive = iconview_->get_selected_drive();
|
||||
if (drive && drive->get_is_manually_added() && !drive->get_test_is_active())
|
||||
if (drive && drive->get_is_manually_added() && !drive->get_test_is_active()) {
|
||||
iconview_->remove_selected_drive();
|
||||
|
||||
auto devices = app_get_startup_manual_devices();
|
||||
std::erase_if(devices,
|
||||
[drive](const AppAddDeviceOption& dev) {
|
||||
return (dev.device == drive->get_device() && dev.type == drive->get_type_argument());
|
||||
});
|
||||
app_set_startup_manual_devices(devices);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -507,6 +523,7 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type)
|
||||
|
||||
case action_rescan_devices:
|
||||
rescan_devices(false);
|
||||
add_startup_manual_devices();
|
||||
break;
|
||||
|
||||
case action_executor_log:
|
||||
@@ -757,7 +774,7 @@ void GscMainWindow::update_status_widgets()
|
||||
if (health_prop.generic_name == "smart_status/passed") {
|
||||
health_label_->set_text(health_prop.format_value());
|
||||
std::string fg;
|
||||
if (app_property_get_label_highlight_color(health_prop.warning_level, fg)) {
|
||||
if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), health_prop.warning_level, fg)) {
|
||||
health_label_->set_markup("<span color=\"" + fg + "\">"+ Glib::Markup::escape_text(health_label_->get_text()) + "</span>");
|
||||
}
|
||||
// don't set description tooltip - we already have the basic one.
|
||||
@@ -893,6 +910,29 @@ void GscMainWindow::rescan_devices(bool startup)
|
||||
|
||||
|
||||
|
||||
void GscMainWindow::add_startup_manual_devices()
|
||||
{
|
||||
auto auto_add_devices = app_get_startup_manual_devices();
|
||||
for (const auto& dev : auto_add_devices) {
|
||||
if (!dev.device.empty()) {
|
||||
std::vector<std::string> params;
|
||||
if (!dev.options.empty()) {
|
||||
try {
|
||||
params = Glib::shell_parse_argv(dev.options);
|
||||
}
|
||||
catch(Glib::ShellError& e) {
|
||||
// Skip this entry if we can't parse params
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Ignore errors here so that the devices can be removed manually
|
||||
add_device_silent(dev.device, dev.type, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GscMainWindow::run_update_drivedb()
|
||||
{
|
||||
auto smartctl_binary = get_smartctl_binary();
|
||||
@@ -930,18 +970,34 @@ void GscMainWindow::run_update_drivedb()
|
||||
|
||||
|
||||
|
||||
bool GscMainWindow::add_device(const std::string& file, const std::string& type_arg, const std::vector<std::string>& extra_args)
|
||||
bool GscMainWindow::add_device_interactive(const std::string& file, const std::string& type_arg, const std::vector<std::string>& extra_args)
|
||||
{
|
||||
// win32 doesn't have device files, so skip the check in Windows.
|
||||
if constexpr(!BuildEnv::is_kernel_family_windows()) {
|
||||
std::error_code ec;
|
||||
if (!hz::fs::exists(hz::fs_path_from_string(file), ec)) {
|
||||
gui_show_error_dialog(_("Cannot add device"),
|
||||
(ec.message().empty() ? Glib::ustring::compose(_("Device \"%1\" doesn't exist."), file).raw() : ec.message()), this);
|
||||
(!ec ? Glib::ustring::compose(_("Device \"%1\" doesn't exist."), file).raw() : ec.message()), this);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return add_device(false, file, type_arg, extra_args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GscMainWindow::add_device_silent(const std::string& file, const std::string& type_arg, const std::vector<std::string>& extra_args)
|
||||
{
|
||||
// We want the manual add to always succeed even if there are errors,
|
||||
// so that it can be removed as well.
|
||||
return add_device(true, file, type_arg, extra_args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GscMainWindow::add_device(bool silent, const std::string& file, const std::string& type_arg, const std::vector<std::string>& extra_args)
|
||||
{
|
||||
auto drive = std::make_shared<StorageDevice>(file);
|
||||
drive->set_type_argument(type_arg);
|
||||
drive->set_extra_arguments(extra_args);
|
||||
@@ -952,9 +1008,10 @@ bool GscMainWindow::add_device(const std::string& file, const std::string& type_
|
||||
std::vector<StorageDevicePtr> tmp_drives;
|
||||
tmp_drives.push_back(drive);
|
||||
|
||||
// Don't report errors here, just add the drive to the list.
|
||||
StorageDetector sd;
|
||||
auto fetch_error = sd.fetch_basic_data(tmp_drives, ex_factory, true); // return its first error
|
||||
if (!fetch_error) {
|
||||
if (!silent && !fetch_error) {
|
||||
gsc_executor_error_dialog_show(_("An error occurred while adding the device"), fetch_error.error().message(), this);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -48,13 +48,28 @@ class GscMainWindow : public AppBuilderWidget<GscMainWindow, false> {
|
||||
void rescan_devices(bool startup);
|
||||
|
||||
|
||||
/// Add manually added devices to the icon view.
|
||||
void add_startup_manual_devices();
|
||||
|
||||
|
||||
/// Execute update-smart-drivedb
|
||||
void run_update_drivedb();
|
||||
|
||||
|
||||
/// Manually add device file to icon list
|
||||
bool add_device(const std::string& file, const std::string& type_arg, const std::vector<std::string>& extra_args);
|
||||
/// Add device file to icon list, interactively showing errors if any.
|
||||
bool add_device_interactive(const std::string& file, const std::string& type_arg, const std::vector<std::string>& extra_args);
|
||||
|
||||
/// Add device file to icon list silently, ignoring errors.
|
||||
bool add_device_silent(const std::string& file, const std::string& type_arg, const std::vector<std::string>& extra_args);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/// Add device file to icon list
|
||||
bool add_device(bool silent, const std::string& file, const std::string& type_arg, const std::vector<std::string>& extra_args);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// Read smartctl data from file, add it as a virtual drive to icon list
|
||||
bool add_virtual_drive(const std::string& file);
|
||||
|
||||
@@ -120,7 +120,7 @@ bool GscMainWindowIconView::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
|
||||
return true;
|
||||
}
|
||||
if (empty_view_message_ != Message::None && this->num_icons_ == 0) { // no icons
|
||||
Glib::RefPtr<Pango::Layout> layout = this->create_pango_layout("");
|
||||
const Glib::RefPtr<Pango::Layout> layout = this->create_pango_layout("");
|
||||
layout->set_alignment(Pango::ALIGN_CENTER);
|
||||
layout->set_markup(get_message_string(empty_view_message_));
|
||||
|
||||
@@ -131,6 +131,12 @@ bool GscMainWindowIconView::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
|
||||
const int pos_y = (get_allocation().get_height() - layout_h) / 2;
|
||||
cr->move_to(pos_x, pos_y);
|
||||
|
||||
// Use the foreground color from the widget's style context so
|
||||
// the text is visible in both light and dark themes.
|
||||
const auto style_context = get_style_context();
|
||||
const Gdk::RGBA fg_color = style_context->get_color(style_context->get_state());
|
||||
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), fg_color.get_alpha());
|
||||
|
||||
layout->show_in_cairo_context(cr);
|
||||
|
||||
return true;
|
||||
@@ -548,10 +554,18 @@ Glib::RefPtr<Gdk::Pixbuf> GscMainWindowIconView::load_icon_pixbuf(Glib::RefPtr<G
|
||||
{
|
||||
Glib::RefPtr<Gdk::Pixbuf> icon;
|
||||
|
||||
// Try XDG version first
|
||||
// Try the icon theme first; fall back to the bundled icon if needed.
|
||||
try {
|
||||
if (default_icon_theme && !xdg_icon_name.empty()) {
|
||||
icon = default_icon_theme->load_icon(xdg_icon_name, icon_size_, get_scale_factor(), Gtk::IconLookupFlags(0));
|
||||
|
||||
// Some icon themes may return an icon smaller than requested.
|
||||
// In that case, fall back to the bundled icon.
|
||||
if (icon &&
|
||||
(icon->get_width() < icon_size_ ||
|
||||
icon->get_height() < icon_size_)) {
|
||||
icon.reset();
|
||||
}
|
||||
}
|
||||
} catch (...) { } // ignore exceptions
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ struct GscStartupSettings {
|
||||
bool no_scan = false; ///< No scanning on startup
|
||||
std::vector<std::string> load_virtuals; ///< Virtual files to load
|
||||
std::vector<std::string> add_devices; ///< Devices to add (with options)
|
||||
bool forget_manual_devices = false; ///< Forget all manually added devices
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2008 - 2024 Alexander Shaduri <ashaduri@gmail.com>
|
||||
(C) 2008 - 2025 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
|
||||
gsmartcontrol ICON "gsmartcontrol.ico"
|
||||
@@ -22,7 +22,7 @@ BEGIN
|
||||
VALUE "FileDescription", "GSmartControl - Hard disk drive and SSD health inspection tool"
|
||||
VALUE "FileVersion", "@CMAKE_PROJECT_VERSION@"
|
||||
VALUE "InternalName", "gsmartcontrol.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2008 - 2024 Alexander Shaduri"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2008 - 2025 Alexander Shaduri"
|
||||
VALUE "OriginalFilename", "gsmartcontrol.exe"
|
||||
VALUE "ProductName", "GSmartControl"
|
||||
VALUE "ProductVersion", "@CMAKE_PROJECT_VERSION@"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1
|
||||
<!-- Generated with glade 3.40.0
|
||||
|
||||
Copyright (C) 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com>
|
||||
|
||||
@@ -25,31 +25,28 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<!-- interface-name GSmartControl -->
|
||||
<!-- interface-copyright 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com> -->
|
||||
<object class="GtkWindow" id="gsc_add_device_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">Add Device - GSmartControl</property>
|
||||
<property name="modal">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">12</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="top_info_link_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">The <a href="%1">smartctl man page</a> contains information on what you can enter here.</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="use-markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -59,24 +56,25 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<!-- n-columns=3 n-rows=3 -->
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="row-spacing">6</property>
|
||||
<property name="column-spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="device_name_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="activates_default">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="invisible-char">●</property>
|
||||
<property name="activates-default">True</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -88,9 +86,9 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkButton" id="device_name_browse_button">
|
||||
<property name="label" translatable="yes">Browse...</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -100,80 +98,89 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="device_type_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Device type:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Additional smartctl parameters</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Additional smartctl parameters</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Smartctl parameters:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="device_type_combo">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="has_entry">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="has-entry">True</property>
|
||||
<child internal-child="entry">
|
||||
<object class="GtkEntry">
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="smartctl_params_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Additional smartctl parameters</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="activates_default">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Additional smartctl parameters</property>
|
||||
<property name="invisible-char">●</property>
|
||||
<property name="activates-default">True</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="device_name_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Device name:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -182,19 +189,34 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="auto_add_device_check">
|
||||
<property name="label" translatable="yes">Automatically add this device on startup</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Note: To make this change permanent, you'll have to add the gsmartcontrol --add-device command line option to its shortcut.</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">6</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -207,16 +229,16 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="hbuttonbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<property name="layout-style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="window_cancel_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -228,11 +250,11 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkButton" id="window_ok_button">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="has_default">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="can-default">True</property>
|
||||
<property name="has-default">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -244,7 +266,7 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1
|
||||
<!-- Generated with glade 3.40.0
|
||||
|
||||
Copyright (C) 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com>
|
||||
|
||||
@@ -25,65 +25,62 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<!-- interface-name GSmartControl -->
|
||||
<!-- interface-copyright 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com> -->
|
||||
<object class="GtkWindow" id="gsc_preferences_window">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">12</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="notebook1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="has-focus">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="left-padding">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="scan_on_startup_check">
|
||||
<property name="label" translatable="yes">Scan system for drives on startup</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -95,11 +92,11 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkCheckButton" id="show_smart_capable_only_check">
|
||||
<property name="label" translatable="yes">Show SMART-capable drives only</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -111,11 +108,11 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkCheckButton" id="show_device_name_under_icon_check">
|
||||
<property name="label" translatable="yes">Show device name under drive icon</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -127,11 +124,11 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkCheckButton" id="show_serial_number_under_icon_check">
|
||||
<property name="label" translatable="yes">Show serial number under drive icon</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -154,9 +151,9 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes"><b>Program Settings</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="use-markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -170,35 +167,35 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="left-padding">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox12">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">7</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="search_in_smartmontools_first_check">
|
||||
<property name="label" translatable="yes">Look for smartctl in smartmontools installation directory first</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">If smartmontools is installed, use its smartctl by default</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="tooltip-text" translatable="yes">If smartmontools is installed, use its smartctl by default</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -207,23 +204,24 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<!-- n-columns=3 n-rows=3 -->
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="row-spacing">6</property>
|
||||
<property name="column-spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="hbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="smartctl_binary_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="activates_default">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="activates-default">True</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -235,9 +233,9 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkButton" id="smartctl_binary_browse_button">
|
||||
<property name="label" translatable="yes">Browse...</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -247,51 +245,66 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="smartctl_options_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Global parameters for smartctl. These parameters will be used every time the program invokes smartctl. Must be shell-escaped.</property>
|
||||
<property name="activates_default">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Global parameters for smartctl. These parameters will be used every time the program invokes smartctl. Must be shell-escaped.</property>
|
||||
<property name="activates-default">True</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Global parameters for smartctl. These parameters will be used every time the program invokes smartctl. Must be shell-escaped.</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Global parameters for smartctl. These parameters will be used every time the program invokes smartctl. Must be shell-escaped.</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Smartctl parameters:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="smartctl_binary_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Smartctl binary:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -314,9 +327,9 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes"><b>Smartctl Invocation</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="use-markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -332,53 +345,53 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">3</property>
|
||||
<property name="margin_bottom">3</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">3</property>
|
||||
<property name="margin-bottom">3</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">_General</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab_fill">False</property>
|
||||
<property name="tab-fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="left-padding">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox9">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="hbox6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Semicolon-separated blacklist regular expression patterns for device search. For example, to blacklist all "/dev/sd*" devices, use "sd.{1}$". Simply listing them as "sda;sdb" will also work.</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Semicolon-separated blacklist regular expression patterns for device search. For example, to blacklist all "/dev/sd*" devices, use "sd.{1}$". Simply listing them as "sda;sdb" will also work.</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Device blacklist patterns:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -389,11 +402,11 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkEntry" id="device_blacklist_patterns_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Semicolon-separated blacklist regular expression patterns for device search. For example, to blacklist all "/dev/sd*" devices, use "sd.$". Simply listing them as "sda;sdb" or "sd[ab]" will also work.</property>
|
||||
<property name="activates_default">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Semicolon-separated blacklist regular expression patterns for device search. For example, to blacklist all "/dev/sd*" devices, use "sd.$". Simply listing them as "sda;sdb" or "sd[ab]" will also work.</property>
|
||||
<property name="activates-default">True</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -416,7 +429,7 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Drive Search</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
@@ -434,28 +447,28 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="left-padding">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox8">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="top_padding">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="top-padding">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Adding a drive here will make it use the specified parameters.</property>
|
||||
</object>
|
||||
@@ -471,25 +484,25 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkBox" id="hbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="device_options_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Device list</property>
|
||||
<property name="headers_clickable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Device list</property>
|
||||
<property name="headers-clickable">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
@@ -505,17 +518,17 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkBox" id="hbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="device_options_remove_device_button">
|
||||
<property name="label">gtk-remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Remove selected entry from device list</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Remove selected entry from device list</property>
|
||||
<property name="use-stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -527,10 +540,10 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkButton" id="device_options_add_device_button">
|
||||
<property name="label">gtk-add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Add a new entry to device list</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Add a new entry to device list</property>
|
||||
<property name="use-stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -555,97 +568,98 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox11">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<!-- n-columns=3 n-rows=4 -->
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="row-spacing">6</property>
|
||||
<property name="column-spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label67">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus")</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus")</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Parameters:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="label" translatable="yes">Add parameters:</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="device_options_type_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Match only this type of device (as used by the -d smartctl parameter). Leave empty for all types. This can be used to match a drive behind a RAID device, e.g. "areca,2".</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Match only this type of device (as used by the -d smartctl parameter). Leave empty for all types. This can be used to match a drive behind a RAID device, e.g. "areca,2".</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Type:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="label" translatable="yes">Match type:</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="device_options_device_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Device:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="label" translatable="yes">Match device:</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="device_options_parameter_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus")</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus")</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="device_options_type_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Match only this type of device (as used by the -d smartctl parameter). Leave empty for all types. This can be used to match a drive behind a RAID device, e.g. "areca,2".</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Match only this type of device (as used by the -d smartctl parameter). Leave empty for all types. This can be used to match a drive behind a RAID device, e.g. "areca,2".</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="device_options_device_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label9">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Drive Properties:</property>
|
||||
<attributes>
|
||||
@@ -653,11 +667,23 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -687,8 +713,8 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label11">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Per-drive Smartctl Parameters</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Per-Drive Smartctl Parameters</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
@@ -710,17 +736,17 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">6</property>
|
||||
<property name="margin-right">6</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">_Drives</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="width_chars">10</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="width-chars">10</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</property>
|
||||
<property name="tab-fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -740,17 +766,17 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="window_reset_all_button">
|
||||
<property name="label" translatable="yes">_Reset all</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Reset all program settings to their defaults</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Reset all program settings to their defaults</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -761,7 +787,7 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -773,9 +799,9 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkButton" id="window_cancel_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -787,12 +813,12 @@ along with GSmartControl. If not, see <http://www.gnu.org/licenses/>.
|
||||
<object class="GtkButton" id="window_ok_button">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="is_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="has_default">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="is-focus">True</property>
|
||||
<property name="can-default">True</property>
|
||||
<property name="has-default">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
||||
@@ -884,6 +884,76 @@ inline std::string string_to_upper_copy(std::string_view s)
|
||||
|
||||
|
||||
|
||||
// --------------------------------------------- Natural Sort
|
||||
|
||||
|
||||
/// Compare two strings using natural (alphanumeric) sort order.
|
||||
/// Natural sort order treats consecutive digits as numbers, so "file2.txt" comes before "file10.txt".
|
||||
/// This is useful for sorting filenames, device names, etc.
|
||||
/// Returns: < 0 if a < b, 0 if a == b, > 0 if a > b
|
||||
inline int string_natural_compare(std::string_view a, std::string_view b)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
std::size_t j = 0;
|
||||
const std::size_t a_size = a.size();
|
||||
const std::size_t b_size = b.size();
|
||||
|
||||
while (i < a_size && j < b_size) {
|
||||
const bool a_is_digit = std::isdigit(static_cast<unsigned char>(a[i]));
|
||||
const bool b_is_digit = std::isdigit(static_cast<unsigned char>(b[j]));
|
||||
|
||||
if (a_is_digit && b_is_digit) {
|
||||
// Both are digits, compare as numbers
|
||||
// Skip leading zeros
|
||||
while (i < a_size && a[i] == '0') {
|
||||
++i;
|
||||
}
|
||||
while (j < b_size && b[j] == '0') {
|
||||
++j;
|
||||
}
|
||||
|
||||
// Count the number of digits
|
||||
std::size_t a_digit_start = i;
|
||||
std::size_t b_digit_start = j;
|
||||
while (i < a_size && std::isdigit(static_cast<unsigned char>(a[i]))) {
|
||||
++i;
|
||||
}
|
||||
while (j < b_size && std::isdigit(static_cast<unsigned char>(b[j]))) {
|
||||
++j;
|
||||
}
|
||||
|
||||
const std::size_t a_digit_count = i - a_digit_start;
|
||||
const std::size_t b_digit_count = j - b_digit_start;
|
||||
|
||||
// Compare by length first (longer number is greater)
|
||||
if (a_digit_count != b_digit_count) {
|
||||
return static_cast<int>(a_digit_count) - static_cast<int>(b_digit_count);
|
||||
}
|
||||
|
||||
// Same length, compare digit by digit
|
||||
for (std::size_t k = 0; k < a_digit_count; ++k) {
|
||||
if (a[a_digit_start + k] != b[b_digit_start + k]) {
|
||||
return static_cast<int>(a[a_digit_start + k]) - static_cast<int>(b[b_digit_start + k]);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (a_is_digit != b_is_digit) {
|
||||
// One is digit, one is not - digit comes before non-digit
|
||||
return a_is_digit ? -1 : 1;
|
||||
|
||||
} else {
|
||||
// Both are non-digits, compare as characters
|
||||
if (a[i] != b[j]) {
|
||||
return static_cast<int>(static_cast<unsigned char>(a[i])) - static_cast<int>(static_cast<unsigned char>(b[j]));
|
||||
}
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
// If one string is a prefix of the other, the shorter one comes first
|
||||
return static_cast<int>(a_size) - static_cast<int>(b_size);
|
||||
}
|
||||
|
||||
|
||||
} // ns
|
||||
|
||||
@@ -9,10 +9,6 @@ Copyright:
|
||||
/// \weakgroup hz_tests
|
||||
/// @{
|
||||
|
||||
// Catch2 v3
|
||||
//#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
// Catch2 v2
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
// disable libdebug, we don't link to it
|
||||
|
||||
@@ -9,10 +9,6 @@ Copyright:
|
||||
/// \weakgroup hz_tests
|
||||
/// @{
|
||||
|
||||
// Catch2 v3
|
||||
//#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
// Catch2 v2
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
// disable libdebug, we don't link to it
|
||||
@@ -124,11 +120,61 @@ TEST_CASE("StringAlgorithms", "[hz][string]")
|
||||
string_replace_array(s, from, ":");
|
||||
REQUIRE(s == ":345678:defg : ab");
|
||||
}
|
||||
|
||||
SECTION("string_natural_compare") {
|
||||
using namespace hz;
|
||||
|
||||
// Test basic number comparison
|
||||
REQUIRE(string_natural_compare("file1.txt", "file2.txt") < 0);
|
||||
REQUIRE(string_natural_compare("file2.txt", "file10.txt") < 0);
|
||||
REQUIRE(string_natural_compare("file10.txt", "file2.txt") > 0);
|
||||
REQUIRE(string_natural_compare("file9.txt", "file10.txt") < 0);
|
||||
|
||||
// Test device names (the actual use case)
|
||||
REQUIRE(string_natural_compare("pd0", "pd1") < 0);
|
||||
REQUIRE(string_natural_compare("pd1", "pd2") < 0);
|
||||
REQUIRE(string_natural_compare("pd2", "pd10") < 0);
|
||||
REQUIRE(string_natural_compare("pd9", "pd10") < 0);
|
||||
REQUIRE(string_natural_compare("pd10", "pd11") < 0);
|
||||
REQUIRE(string_natural_compare("pd10", "pd9") > 0);
|
||||
|
||||
// Test equality
|
||||
REQUIRE(string_natural_compare("pd5", "pd5") == 0);
|
||||
REQUIRE(string_natural_compare("test", "test") == 0);
|
||||
|
||||
// Test prefix
|
||||
REQUIRE(string_natural_compare("pd", "pd1") < 0);
|
||||
REQUIRE(string_natural_compare("pd1", "pd") > 0);
|
||||
|
||||
// Test mixed content
|
||||
REQUIRE(string_natural_compare("a1b2c3", "a1b2c10") < 0);
|
||||
REQUIRE(string_natural_compare("a10b2", "a2b10") > 0);
|
||||
|
||||
// Test non-numeric strings
|
||||
REQUIRE(string_natural_compare("abc", "def") < 0);
|
||||
REQUIRE(string_natural_compare("xyz", "abc") > 0);
|
||||
|
||||
// Test numbers vs letters (digits come before non-digits)
|
||||
REQUIRE(string_natural_compare("1test", "atest") < 0);
|
||||
REQUIRE(string_natural_compare("test1", "testa") < 0);
|
||||
|
||||
// Test long digit sequences (well beyond a few digits)
|
||||
REQUIRE(string_natural_compare("file12345678901234567890.txt", "file12345678901234567891.txt") < 0);
|
||||
REQUIRE(string_natural_compare("file12345678901234567891.txt", "file12345678901234567890.txt") > 0);
|
||||
REQUIRE(string_natural_compare("file12345678901234567890.txt", "file12345678901234567890.txt") == 0);
|
||||
|
||||
// Compare short vs very long numeric substrings
|
||||
REQUIRE(string_natural_compare("file1.txt", "file12345678901234567890.txt") < 0);
|
||||
REQUIRE(string_natural_compare("file12345678901234567890.txt", "file99999999999.txt") > 0);
|
||||
|
||||
// Mixed with long numbers inside other text
|
||||
REQUIRE(string_natural_compare("a1b2c123456789012345", "a1b2c123456789012346") < 0);
|
||||
REQUIRE(string_natural_compare("a1b2c123456789012345", "a1b2c123456789012345") == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -9,10 +9,6 @@ Copyright:
|
||||
/// \weakgroup hz_tests
|
||||
/// @{
|
||||
|
||||
// Catch2 v3
|
||||
//#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
// Catch2 v2
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
// disable libdebug, we don't link to it
|
||||
|
||||
@@ -79,6 +79,14 @@ inline bool win32_set_registry_value_string(HKEY base,
|
||||
const std::string& keydir, const std::string& key, const std::string& value);
|
||||
|
||||
|
||||
/// Get registry value as a DWORD.
|
||||
/// Base may be e.g. HKEY_CURRENT_USER.
|
||||
/// Note that this works only with REG_DWORD types.
|
||||
/// False is returned for all other types.
|
||||
inline bool win32_get_registry_value_dword(HKEY base,
|
||||
const std::string& keydir, const std::string& key, DWORD& put_here);
|
||||
|
||||
|
||||
/// Redirect stdout and stderr to console window (if open). Requires winxp (at compile-time).
|
||||
/// \param create_if_none if true, create a new console if none was found and attach to it.
|
||||
/// \return false if failed or unsupported.
|
||||
@@ -340,6 +348,49 @@ inline bool win32_set_registry_value_string(HKEY base,
|
||||
|
||||
|
||||
|
||||
// Get registry value as a DWORD.
|
||||
// Note that this works only with REG_DWORD types.
|
||||
inline bool win32_get_registry_value_dword(HKEY base,
|
||||
const std::string& keydir, const std::string& key, DWORD& put_here)
|
||||
{
|
||||
std::wstring wkeydir = win32_utf8_to_utf16(keydir);
|
||||
if (wkeydir.empty())
|
||||
return false;
|
||||
|
||||
HKEY reg_key = nullptr;
|
||||
bool open_status = (RegOpenKeyExW(base, wkeydir.c_str(), 0, KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS);
|
||||
|
||||
if (!open_status)
|
||||
return false;
|
||||
|
||||
bool ok = false;
|
||||
std::wstring wkey = win32_utf8_to_utf16(key, &ok);
|
||||
if (!ok) { // conversion error. Note that an empty string is not an error.
|
||||
if (reg_key)
|
||||
RegCloseKey(reg_key);
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD type = 0;
|
||||
DWORD value = 0;
|
||||
DWORD nbytes = sizeof(DWORD);
|
||||
bool status = (RegQueryValueExW(reg_key, wkey.c_str(), nullptr, &type,
|
||||
reinterpret_cast<BYTE*>(&value), &nbytes) == ERROR_SUCCESS);
|
||||
|
||||
if (status && type == REG_DWORD) {
|
||||
put_here = value;
|
||||
} else {
|
||||
status = false;
|
||||
}
|
||||
|
||||
if (reg_key)
|
||||
RegCloseKey(reg_key);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Redirect stdout and stderr to console window (if open).
|
||||
inline bool win32_redirect_stdio_to_console(bool create_if_none)
|
||||
{
|
||||
|
||||
@@ -10,10 +10,6 @@ Copyright:
|
||||
/// @{
|
||||
|
||||
|
||||
// Catch2 v3
|
||||
// #include "catch2/catch_session.hpp"
|
||||
|
||||
// Catch2 v2
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user