# qwfassoc (suite) A Qt Widgets based GUI for the [wfassoc](../../wfassoc) library, split into a reusable shared library and a small standalone executable that exercises it. The project is organized as two CMake subprojects: ``` qwfassoc/ Parent directory (this README) ├── CMakeLists.txt Top-level CMake; add_subdirectory's both │ subprojects and finds Qt, wfassoc, toml11 ├── cmake/ │ ├── Findwfassoc.cmake Verbatim copy of wfassoc's Find module │ └── README.md Provenance notes for the copy ├── qwfassoc/ Shared library subproject │ ├── CMakeLists.txt │ ├── i18n/ │ │ └── qwfassoc_zh_CN.ts Empty placeholder translation file │ └── src/ │ ├── qwfassoc_global.h QWFASSOC_EXPORT macro │ ├── scope.h Shared TargetScope enum │ ├── manifest.h Manifest data struct (no TOML dependency) │ ├── icon_utils.h/.cpp wfassocpp::HICON -> QPixmap conversion │ ├── application_widget.h/.cpp/.ui │ │ Install / uninstall widget │ └── association_widget.h/.cpp/.ui │ File associations widget └── qwfassoc-standalone/ Executable subproject ├── CMakeLists.txt ├── i18n/ │ └── qwfassoc-standalone_zh_CN.ts │ Empty placeholder translation file └── src/ ├── main.cpp Entry point, CLI parsing, translator loading ├── main_window.h/.cpp/.ui │ QDialog hosting the two widgets in a tab widget └── manifest_parser.h/.cpp TOML -> Manifest, Manifest -> Schema ``` ## Subprojects at a glance ### `qwfassoc` (shared library) Exports two reusable widgets that wrap wfassoc: * `qwfassoc::ApplicationWidget` — install / uninstall the program in the configured scope. * `qwfassoc::AssociationWidget` — stage and apply per-extension link / unlink operations. Both widgets follow the **two-phase initialization** pattern expected by Qt Designer promoted widgets: the constructor only takes a `QWidget*` parent and leaves the widget disabled. A `setConfig(Config)` method injects the `wfassocpp::Program` pointer and `TargetScope` (and, for the association widget, whether the OK/Cancel buttons are visible). Each widget also exposes: * a `refresh()` slot that re-queries the live wfassoc state, intended to be called by the host when another component has mutated the registry; * a `changed()` signal emitted whenever the widget itself mutates the registry (install / uninstall / apply); * (`AssociationWidget` only) a `finished(bool accepted)` signal emitted when the user clicks OK (after `changed()`) or Cancel, so the host can close the dialog. The library also exposes the plain `qwfassoc::Manifest` data struct and a `qwfassoc::icon_utils::fromHicon()` helper, but the TOML parsing logic (which depends on toml11) lives in the standalone executable. ### `qwfassoc-standalone` (executable) Reproduces the original tabbed wfassoc configurator by: 1. parsing `-c/--manifest ` and `-f/--for ` from the command line; 2. building a `wfassocpp::Program` via `parseManifestFile` + `buildSchema`; 3. hosting `ApplicationWidget` and `AssociationWidget` inside a `QTabWidget` in a `MainWindow` dialog; 4. wiring the widgets' `changed()` and `finished()` signals so that any registry mutation refreshes both pages and OK/Cancel drive the dialog's acceptance. ## Requirements * **CMake** 3.20 or newer (3.21+ recommended for `qt6_add_translations`). * A C++17 compiler. * **Qt 6** with the `Widgets` and `LinguistTools` components. * **wfassoc**, with `wfassoc_ROOT` pointing at an installed tree (see [`cmake/Findwfassoc.cmake`](cmake/Findwfassoc.cmake) for the expected directory layout). * **toml11** — only required when building the standalone executable. ## Building ```bat cmake -S . -B build ^ -DCMAKE_PREFIX_PATH=C:\Qt\6.x.x\msvc2022_64 ^ -Dwfassoc_ROOT=C:\path\to\wfassoc\install ^ -Dtoml11_DIR=C:\path\to\toml11\share\toml11\cmake cmake --build build --config Release ``` To skip the standalone executable (and the toml11 dependency): ```bat cmake -S . -B build -DQWFASSOC_BUILD_STANDALONE=OFF ... ``` The standalone executable is `build/qwfassoc-standalone/Release/qwfassoc-standalone.exe` (or similar, depending on the generator); the library is `build/qwfassoc/Release/qwfassoc.dll`. ## Running the standalone executable | Short | Long | Meaning | | ----- | ------------ | ------------------------------------------------------------------------ | | `-c` | `--manifest` | Path to the application manifest TOML file (see [`example/manifest/ppic.toml`](../manifest/ppic.toml)). | | `-f` | `--for` | Target scope: `user` or `system`. | ```bat qwfassoc-standalone -c C:\path\to\ppic.toml -f user ``` ## Internationalization Source strings are English and every user-facing string is wrapped in `tr()` (in code) or is a plain `` element in the `.ui` file (which `uic` wraps in `QCoreApplication::translate`). Each subproject ships its own empty placeholder `.ts` file under its `i18n/` directory and registers it with `qt6_add_translations()`: * `qwfassoc/i18n/qwfassoc_zh_CN.ts` — covers the library widgets. * `qwfassoc-standalone/i18n/qwfassoc-standalone_zh_CN.ts` — covers the executable-specific messages (CLI errors, tab titles, dialog window title, etc.). At runtime, `installTranslators()` in `qwfassoc-standalone/src/main.cpp` loads both `.qm` files for the user's preferred UI language from the `:/i18n/` resource prefix. Translators are expected to fill in the `.ts` files; no actual translation work is performed by the build on its own. ## Notes and Limitations * "Self" detection in the file-association table is based on comparing the display name returned by wfassoc with the display name this program would use. Two programs sharing the exact same display name could therefore be confused. * The system column in `AssociationWidget` is rendered disabled (using `Qt::ItemIsSelectable` without `Qt::ItemIsEnabled`) when `TargetScope` is `User`; the cells stay visible but cannot be clicked. * All errors originating from wfassoc are surfaced through `QMessageBox` dialogs; fatal errors during startup cause the process to exit with a non-zero status code.