46 Commits

Author SHA1 Message Date
69985913b8 win32 argv: stop trusting GLib, add “zoitechat” lifesaver 2026-02-26 13:35:09 -07:00
df45cc996d Fix Windows theme imports with uppercase .TAR.GZ/etc (case-insensitive archive check) 2026-02-26 13:15:49 -07:00
5763653672 I fixed a Windows theming regression path in fe_apply_windows_theme() by changing the fallback-disable condition from “a GTK3 theme name is configured” to “a GTK3 theme provider is actually active.” This ensures fallback dark/light palette CSS is still applied when a configured imported GTK3 theme fails to load, preserving Windows release theming behavior instead of leaving mismatched visuals.
I added an in-code explanation documenting this Windows-release safety behavior so the intent is explicit for future maintenance.
2026-02-26 12:10:59 -07:00
2ece544792 Updated the Windows Inno Setup installer template to explicitly show the install-directory step by setting DisableDirPage=no, so users are asked where to install ZoiteChat.
Disabled automatic reuse of a previously selected install path by setting UsePreviousAppDir=no, ensuring the prompt appears each installer run.
2026-02-26 11:56:01 -07:00
8e0958fd19 Added a Windows-specific startup path in main() to reconstruct argc/argv from g_win32_get_command_line() before any argument parsing, so GLib option parsing no longer depends on potentially malformed CRT argv in subsystem/protocol-handler launches (a likely source of your launch crash stack through glib-2.0-0.dll).
Ensured the allocated Windows command-line vector is freed on all early-return and normal-exit paths in main() to avoid leaks/regressions from the new startup handling.
2026-02-26 11:54:42 -07:00
324aeab8c9 Fixed the Win32 startup path initialization in fe_args to avoid blindly calling g_path_get_dirname(argv[0]) when argv may be missing/invalid in subsystem:windows launch contexts (like URL handler launches).
Updated logic to prefer g_win32_get_package_installation_directory_of_module(NULL) first, and only fall back to argv[0] when argc > 0, argv != NULL, and argv[0] != NULL, preventing null/invalid pointer access during startup.
2026-02-26 11:38:08 -07:00
e8dbe06f01 Added a Win32-only forward declaration for fe_apply_windows_theme(gboolean dark) before its first use so MSVC sees the correct signature and no longer infers an int return type. This addresses the C4013 + C2371 sequence from your build log. 2026-02-26 11:10:33 -07:00
f3213c56eb I implemented a broader, platform-agnostic theme application path so chat-area surfaces and controls actually follow the selected ZoiteChat dark/light mode, instead of relying on a narrow fallback path. This adds a dedicated app CSS provider (theme_surface_provider) and applies palette-based foreground/background rules for core widgets (box, grid, frame, viewport, paned, notebook, eventbox, scrolledwindow, treeview, button, entry) scoped by .zoitechat-light / .zoitechat-dark.
I wired that CSS application into the theme switch flow (fe_apply_theme_for_mode) so changing theme mode now reapplies those surface colors immediately during runtime mode changes.
2026-02-26 11:03:18 -07:00
9ef607f44a Fixed GTK3 theme switching on Win32 so fallback window CSS is synchronized immediately when switching back to the system theme (theme_name == NULL) by calling fe_apply_windows_theme(dark) in that path before provider cleanup.
Fixed GTK3 theme activation on Win32 so fallback window CSS is disabled immediately after loading an imported GTK3 theme, preventing mixed styling (like unthemed buttons/chat areas) during runtime theme changes
2026-02-26 08:58:38 -07:00
a93bed2c41 Adjusted the Windows theme fallback path so it does not apply ZoiteChat’s fallback window-color CSS when a GTK3 theme is selected, preventing the mixed-theme effect you reported (e.g., mismatched button/chat window colors).
Added cleanup for previously-added fallback CSS providers (with widget style reset) when a GTK3 theme is active, so the imported theme can fully control styling consistently.
Kept the original Windows fallback CSS behavior for cases where no imported GTK3 theme is set, so non-GTK3-theme behavior remains unchanged.
2026-02-26 08:41:52 -07:00
d10c9654fa Added a GTK3 theme directory resolver that supports versioned theme folders (gtk-3.x) and selects the best match for the running GTK minor version, instead of assuming only gtk-3.0. This improves compatibility with real-world GTK3 themes that split selectors/styles by GTK minor versions.
Updated theme application to load CSS/resources from the resolved gtk-3.x folder and updated the error path/message when a valid gtk-3.x/gtk.css layout is missing.

Exposed the resolver in the GTK frontend header so other GTK UI code can validate theme layouts consistently.

Updated the Preferences GTK3 theme picker to validate themes via the resolver (so themes with e.g. gtk-3.24/gtk.css now appear as valid).

Updated archive import validation to recognize gtk-3.x directories (not just gtk-3.0) and adjusted user-facing validation messages accordingly.
2026-02-26 08:35:37 -07:00
a823047104 Fixed imported GTK3 theme precedence so theme CSS can override ZoiteChat app-level CSS (including button background rules) by changing the provider priority from GTK_STYLE_PROVIDER_PRIORITY_APPLICATION to GTK_STYLE_PROVIDER_PRIORITY_USER.
Updated the inline comment to document why user-level priority is needed for correct themed button styling behavior.
2026-02-26 08:26:03 -07:00
bf349a27b1 Updated GTK3 theme provider registration to use GTK_STYLE_PROVIDER_PRIORITY_APPLICATION (instead of GTK_STYLE_PROVIDER_PRIORITY_THEME) when applying imported themes, so the selected GTK3 theme consistently overrides the system theme for ZoiteChat widgets.
Added an inline code comment explaining the priority choice and the consistency intent.
2026-02-26 08:21:15 -07:00
90ada474a0 Added GTK3 theme resource lifecycle management so ZoiteChat now unregisters prior theme resources and can register a theme’s gtk.gresource bundle when available (gtk-3.0/gtk.gresource). This improves compatibility with full GTK3 themes that depend on bundled resource URIs, not just plain CSS files.
Extended fe_apply_gtk3_theme_with_reload to:

detect gtk.gresource,

register it before applying CSS,

unregister resources when no resource file is present or when switching back to system theme,

and clean up allocated paths consistently on error paths
2026-02-26 08:04:27 -07:00
4a996c9135 Added a reload-capable GTK3 theme apply API (fe_apply_gtk3_theme_with_reload) and kept fe_apply_gtk3_theme as the default fast-path wrapper (force_reload = FALSE).
Updated the same-theme early return so it is bypassed when reload is requested, while preserving the existing provider reset/replacement flow, gtk_style_context_reset_widgets, and top-level reapply behavior.

Wired setup import/apply flow to force a reload on the next apply after successful archive import, ensuring same-name imported themes are reloaded from disk; the flag is cleared after apply and when switching back to system theme.
2026-02-26 02:38:12 -07:00
c9682d98f3 Updated setup_theme_gtk3_import_cb so the GTK3 theme import file chooser dialog now calls fe_apply_theme_to_toplevel(dialog) immediately after gtk_file_chooser_dialog_new, ensuring it receives app-level theme classes like other dialogs.
Added the same short explanatory comment style already used in setup_theme_show_message (“Window classes are required for GTK CSS selectors like .zoitechat-dark / .zoitechat-light.”), keeping behavior/style aligned with existing code conventions.

    Verified the open/cancel/import flow is unchanged: the same GTK_RESPONSE_ACCEPT gate, early return on cancel, file extraction path retrieval, and import success/error handling remain intact.
2026-02-26 02:33:52 -07:00
8a4ecf8649 Fixed the GTK3 theme persistence bug in Preferences by syncing the in-dialog working copy (setup_prefs.hex_gui_gtk3_theme_name) whenever a GTK3 theme is applied, so pressing OK no longer overwrites the newly selected theme with stale state.
Fixed the same persistence path for “Use System GTK Theme” by clearing both prefs and setup_prefs, preventing the theme from reverting after the dialog closes.

    Reverted GTK3 provider priority back to GTK_STYLE_PROVIDER_PRIORITY_THEME (from application priority), which addresses the dropdown/menu rendering regression introduced by the previous commit.
2026-02-26 02:30:34 -07:00
252f4a3c07 Fixed GTK3 theme teardown to force a full widget style refresh after removing ZoiteChat’s GTK3 theme provider, so switching back to system theme updates existing windows/widgets immediately.
Changed imported GTK3 theme provider registration from GTK_STYLE_PROVIDER_PRIORITY_APPLICATION to GTK_STYLE_PROVIDER_PRIORITY_THEME, so GTK3 theme rules apply correctly across themed controls (including menu/dropdown-related GTK theme elements) instead of being overly overridden by app-priority styling.
2026-02-26 02:22:00 -07:00
d21a5c1b60 Removed the legacy .hct/.zct theme-path behavior and made theme argument detection GTK3-archive-only (.zip, .tar, .tar.gz, .tgz, .tar.xz, .txz).
Deleted the old ZoiteChat palette/event theme import/apply implementation and kept GTK3 archive import as the sole theme import backend; the GTK3 import API now optionally returns the imported theme name for better caller messaging.

Updated startup and /URL handling so theme archives are imported into gtk3-themes and users are prompted to apply them via Theme settings, instead of auto-applying old ZoiteChat themes.

Simplified the Theme Manager UI/data model to GTK3-only by removing the old “ZoiteChat Theme” controls and keeping the GTK3 import/apply/use-system workflow.
2026-02-26 02:18:22 -07:00
a1ba30865a Kept legacy .zct/.hct import flow untouched while improving GTK3 archive validation logic: GTK3 import now explicitly distinguishes between archives that include gtk-3.0 but miss gtk.css, versus archives that are not GTK3 theme layouts at all. This is implemented in the GTK3 archive scan/import path only (zoitechat_find_gtk3_theme_root + zoitechat_import_gtk3_theme_archive). 2026-02-26 01:01:09 -07:00
607faa80ca Fixed the GTK3 dropdown regression by switching from combo-box ID/path behavior to an explicit internal index→full-path mapping (gtk3_theme_paths) while keeping user-facing theme names in the dropdown text. This preserves internal full path mapping without relying on active-id lookups.
Kept GTK3 discovery restricted to the app-local install directory (get_xdir()/gtk3-themes) and only included directories containing gtk-3.0/gtk.css.

Ensured theme selection is restored/saved correctly by matching saved pref name against discovered entries and applying via the selected internal path, then persisting prefs.hex_gui_gtk3_theme_name with save_config().

Preserved/updated empty-invalid status behavior to show “No valid GTK3 themes found.” when no usable themes exist (or saved selection is invalid).

Added proper cleanup for the new internal mapping via setup_theme_ui_free and wired it into g_object_set_data_full.
2026-02-26 00:56:17 -07:00
1c1110847c Fixed the GTK3 theme dropdown population to include all expected sources again (ZoiteChat local store, user local themes, and system theme dirs), which resolves the “messed up selector” behavior from the previous change.
Restored proper initial selection logic so the dropdown now prefers saved gui_gtk3_theme_name when present, and otherwise falls back to the current GTK gtk-theme-name.

    Fixed selection UX by not forcing index 0; it starts unselected and selects only if a real match is found. Also made Apply button sensitivity follow actual selection state.

    Updated the status text to reflect mixed-source theme discovery and added cleanup for allocated selection strings/path entries in this code path.
2026-02-26 00:47:04 -07:00
a796f78884 Fixed the GTK3 theme import crash by replacing the dark-variant notification call from the signal macro path to fe_message(..., FE_MSG_INFO), avoiding the segfaulting path during import completion while preserving user feedback. 2026-02-26 00:35:42 -07:00
4354aaa57a Added a new backend API, zoitechat_import_gtk3_theme_archive(const char *archive_path, GError **error), and exposed it in the common header so GTK setup code can call a shared import path instead of inlining extraction logic.
Updated the GTK3 import callback in setup.c to:

    open a file chooser titled Import GTK3 Theme ZIP,

    enforce a ZIP-focused chooser filter (*.zip, *.ZIP),

    call the new backend function,

    show success/failure dialogs through setup_theme_show_message,

    refresh the GTK3 theme list immediately after successful import.

Updated the GTK3 section button label to Import GTK3 Theme ZIP and added/used new translatable strings for the new button text and import messages.
2026-02-26 00:16:05 -07:00
4aeb5b5697 Split the theme setup page into two explicit framed sections: “ZoiteChat Theme” (legacy colors.conf / pevents.conf flow) and “GTK3 Theme” (GTK theme import/select/apply flow), with distinct button labels like “Apply ZoiteChat Theme” and “Apply GTK3 Theme.”
Expanded setup_theme_ui to track separate widget state for ZoiteChat controls and GTK3 controls (zoitechat_* and gtk3_* fields), so the two flows are no longer sharing ambiguous UI pointers.

    Kept the existing ZoiteChat apply path using zoitechat_apply_theme, but updated selection/status/apply messaging so it clearly indicates palette/event updates rather than GTK theme activation.

    Added GTK3 theme management behavior:

        discovery/population from standard theme directories,

        import from archive via file chooser into ~/.themes/<basename>,

        apply via GtkSettings (gtk-theme-name),

        status/info messages that explicitly say GTK3 activation does not change ZoiteChat palette settings.
2026-02-26 00:11:05 -07:00
30609ba6db Updated Win32 theme CSS generation in fe_apply_windows_theme to build .zoitechat-dark and .zoitechat-light styles dynamically from palette values (COL_FG/COL_BG) instead of fixed hex literals, via a new helper that serializes PaletteColor with gdk_rgba_to_string.
Kept native titlebar dark-mode behavior intact by leaving the existing Win32 titlebar flow untouched and continuing to call theme preference updates in fe_apply_windows_theme.
Ensured theme/palette apply paths refresh CSS by routing both FE_GUI_APPLY and setup theme apply through fe_apply_theme_for_mode(...), which re-runs Win32 CSS application and re-applies classes to toplevel windows.
2026-02-25 23:53:31 -07:00
6b8e41b4c6 Updated mg_create_tabmenu to remove the hardcoded inline foreground="#3344cc" color from the top menu title and keep only bold emphasis (<b>…</b>), so GTK theme colors are used naturally. 2026-02-25 23:47:42 -07:00
0edab77fac Removed the local menu_icon_exists_in_resource probe from menu.c, so this file no longer carries a hardcoded /icons/menu/light resource check path. menu_icon_widget_new now calls gtkutil_menu_icon_exists for the zc-menu-* candidate check. This keeps the same fallback flow for custom icons: direct readable path first, then config-dir path, then stock/themed lookup.
Added gtkutil_menu_icon_exists to gtkutil.c and declared it in gtkutil.h. The implementation uses a shared helper that resolves menu icon resources with theme-variant-aware lookup (variant first, then light fallback) for both png and svg, so probing behavior now lives in the same subsystem as gtkutil_image_new_from_stock.
2026-02-25 23:40:38 -07:00
c8ae4f3b18 Removed the unused gtkutil_menu_icon_pixbuf_new helper from src/fe-gtk/gtkutil.c (it no longer appears between the surrounding menu icon helper and stock-icon mapping functions).
Kept gtkutil_menu_icon_image_new as the single menu-icon loading path, which already contains the requested theme variant selection and light-variant fallback behavior for both PNG and SVG assets.
2026-02-25 23:36:01 -07:00
578a417804 Added theming to menu_about() immediately after gtk_about_dialog_new() by calling fe_apply_theme_to_toplevel (GTK_WIDGET (dialog));, with the requested short CSS-selector comment style.
Added theming to setup_browsefont_cb() immediately after gtk_font_chooser_dialog_new(...) by calling fe_apply_theme_to_toplevel (dialog);, plus the same short comment for consistency/discoverability.
Added theming to setup_color_cb() immediately after gtk_color_chooser_dialog_new(...) by calling fe_apply_theme_to_toplevel (dialog);, again with the matching comment.
2026-02-25 23:30:35 -07:00
440e9ecf5a Updated fe_ctrl_gui()’s FE_GUI_APPLY branch to match the Preferences → Theme apply sequence by loading palette data first, then re-applying current dark-mode state, and only then calling setup_apply_real(TRUE, TRUE, TRUE, FALSE);.
Added an inline regression-prevention comment noting this path should stay in parity with setup_theme_apply_cb.

    Verified palette.h is already included in src/fe-gtk/fe-gtk.c (no include changes required).
2026-02-25 23:27:21 -07:00
ac2ab1443c Added fe_apply_theme_to_toplevel() for:
Standard GTK file chooser fallback dialog in src/fe-gtk/gtkutil.c

        Font chooser dialog in src/fe-gtk/setup.c

        Color chooser dialog in src/fe-gtk/setup.c

        About dialog in src/fe-gtk/menu.c
2026-02-25 23:18:19 -07:00
ce5128e4fb Added a theme_name null guard in the Adwaita/Yaru workaround condition inside create_input_style() so g_str_has_prefix() is only called when theme_name is non-null, while preserving existing behavior for non-null theme names.
Left allocation/free flow unchanged; theme_name is still freed exactly once at the existing cleanup point after the reload block.
2026-02-25 22:52:40 -07:00
c37faa1492 Refactored the Preferences color-page edit-source model to use an explicit snapshot pointer (setup_color_edit_source_colors) and a helper that selects light vs dark snapshot palettes, so the page preview is decoupled from the runtime colors[] palette. This affects selector refresh, page initialization, and cleanup lifecycle.
Updated edit-target and dark-mode combo callbacks on the color page to stop applying global palette mode directly; they now only switch/refresh the Preferences page source palette.
Changed color-chooser behavior so setup_color_response_cb() writes edits to the selected target snapshot via palette_user_set_color / palette_dark_set_color, then refreshes only page widgets (no runtime palette apply). Also updated dialog initialization to read from the current edit-source snapshot.
Added palette snapshot accessors (palette_user_colors, palette_dark_colors) in the palette API and implementation so Preferences can render from light/dark snapshots safely without mutating runtime palette state.
Runtime palette changes remain on the real apply paths (theme/dark-mode application), not edit-target toggles.
2026-02-25 22:48:55 -07:00
685989fa25 Updated fe_system_prefers_dark() to remove the old !has_theme_name gate and always read gtk-application-prefer-dark-theme whenever that property exists. Theme-name matching is still kept as an additional signal.
Added deterministic dark-mode precedence by combining explicit signals (theme_name_prefers_dark || property_prefers_dark), while preserving Windows system preference as another dark-enabling signal when available. This ensures “dark if any explicit dark signal is true.”
Re-checked the relevant callers (fe_auto_dark_mode_changed() and fe_apply_theme_for_mode()): they consume fe_system_prefers_dark() output and do not depend on the removed !has_theme_name conditional behavior.
2026-02-25 22:42:43 -07:00
cbc474477b Updated gtkutil_menu_icon_theme_variant() to select the menu icon theme variant from the app’s effective dark-mode state first (fe_dark_mode_state_is_initialized() + fe_dark_mode_is_enabled()), and only use GTK/theme-name heuristics as fallback before app state is initialized. This preserves the existing light-asset fallback behavior in menu icon loading logic.
Added a new frontend helper declaration fe_dark_mode_state_is_initialized() in the GTK frontend header so callers can check whether dark-mode state is ready.
Implemented dark-mode initialization tracking in fe-gtk.c via a new static flag, set when auto mode state is externally set and during fe_init(), and exposed it through fe_dark_mode_state_is_initialized().
2026-02-25 22:31:52 -07:00
51f8795d1a Updated create_input_style() so input selection CSS now prefers palette-derived COL_MARK_FG / COL_MARK_BG colors directly (as #RRGGBB from sel_* values), instead of defaulting to @theme_selected_*.
Added a guarded fallback path that only uses theme lookup/string conversion when palette selection components are invalid (isfinite checks fail), with palette values as secondary fallback there too. This keeps theme colors out of the primary path while preserving robustness for malformed data.
Kept needs_reload dependent on last_sel_* tracking, so changing selection colors in Preferences still triggers immediate CSS regeneration (including across light/dark mode).
2026-02-25 22:25:35 -07:00
faacd95dfc Added setup-local state for palette editing target (setup_color_edit_dark_palette) plus Light/Dark option strings, so palette editing is no longer coupled to runtime AUTO dark-mode detection. This keeps the choice in Preferences state only.
Added a new Editing: combo control near the dark-mode selector in setup_create_color_page, with a callback that switches between Light/Dark palette targets and refreshes swatches immediately using the same refresh path as dark-mode UI updates.
    Updated the dark-mode combo callback to refresh color swatches based on the explicit editing target (not runtime dark-mode detection), preserving existing runtime theme behavior while editing persisted overrides.
    Updated setup_color_response_cb to write color changes to palette_dark_set_color vs palette_user_set_color based on the new edit-target state.
    Initialized the editing target in setup_create_color_page from explicit Dark mode selection only (hex_gui_dark_mode == ZOITECHAT_DARK_MODE_DARK), avoiding AUTO runtime detection for editing context.
2026-02-25 21:28:08 -07:00
ed02b21228 Extended setup_dark_mode_menu_cb so after setup_menu_cb it now computes the selected dark-mode state via fe_dark_mode_is_enabled_for(setup_prefs.hex_gui_dark_mode), swaps the in-memory editor palette with palette_apply_dark_mode(...), and immediately refreshes color swatches. This keeps the editor model synchronized with the dark-mode combo selection.
Added setup_refresh_color_selector_widgets() to iterate color_selector_widgets, resolve each button’s stored color index, and re-run setup_color_button_apply against colors[] so all selectors repaint to the active palette.
    Updated setup_create_color_button to register each widget’s palette index (zoitechat-color-index), enabling correct per-button repaint during mode switches.
    Preserved existing persistence behavior in setup_color_response_cb (palette_dark_set_color vs palette_user_set_color) so changes remain non-destructive until existing OK/Save flow persists them.
2026-02-25 21:22:56 -07:00
d321717da8 Hardened create_input_style() so selection-color CSS no longer hard-depends on @theme_selected_*: it now first tries symbolic theme colors via gtk_style_context_lookup_color(), and only falls back when unavailable.
Added a fallback path that derives selection colors from palette values (COL_MARK_FG/COL_MARK_BG) and then samples selected-state colors from a GTK style context when possible, emitting explicit color strings for CSS.
Ensured fallback regeneration is tied to the same reload flow by extending needs_reload tracking with selection palette channels and persisting those last-seen values.
Kept Preferences > Colors foreground/background behavior intact for normal input rendering; only selection-specific CSS is conditionally overridden. Base text/background/caret rules remain unchanged while only text selection uses the new selection logic.
2026-02-25 20:59:07 -07:00
6310ab245c Updated fe_apply_theme_for_mode() to immediately reapply fe_apply_theme_to_toplevel() across all currently-open GTK toplevel windows (gtk_window_list_toplevels()), so mode switches update existing windows without reopening them.
Added/kept lifecycle call sites for main and detached/channel windows, with comments explaining that .zoitechat-dark / .zoitechat-light classes are required for GTK CSS selectors. This includes top/tab windows and server list/edit windows.
Wired dialog/toplevel creation paths in src/fe-gtk/ to call fe_apply_theme_to_toplevel() (without duplicating platform logic), including common prompt/message dialogs and feature-specific dialogs (join, notify, setup, ignore/ban, etc.), each with the requested brief comment near the new call site.
2026-02-25 20:39:48 -07:00
5952006662 Refactored fe_system_prefers_dark() to always allow gtk-application-prefer-dark-theme to be read as a fallback (when no explicit theme name is set), removing the previous suppression tied to app-written state. This preserves priority for explicit dark theme names and platform-native preference checks before the GTK property fallback.
Added a short in-code comment explaining why app-written GTK property state must not permanently block future reads in AUTO mode.
Removed the now-unneeded app_set_prefer_dark tracking behavior from fe_set_gtk_prefer_dark_theme(), so writes no longer disable later fallback reads.
Confirmed fe_auto_dark_mode_changed() still prevents reapply loops by early-returning when enabled == auto_dark_mode_enabled before calling theme/apply logic.
2026-02-25 20:32:44 -07:00
e4cb453915 Added a centralized theme-class application in gtkutil_window_new() so any toplevel created through this helper gets fe_apply_theme_to_toplevel() during construction. This covers most non-session windows consistently at creation time.
Updated Preferences window creation to explicitly apply the toplevel theme after gtk_widget_show_all() in setup_window_open(), matching your requested timing there.
Added explicit theme application for non-session windows that bypass gtkutil_window_new() and use direct gtk_window_new() in server list UI (editserv and servlist).
Extended mg_apply_setup() to iterate all current GTK toplevels (gtk_window_list_toplevels()) and reapply fe_apply_theme_to_toplevel() during setup/theme reapply flows, ensuring existing windows stay consistent after theme changes. Palette override widgets were not touched.
2026-02-25 20:27:15 -07:00
361e35de7f Updated chanview_apply_theme() in src/fe-gtk/chanview.c to apply the palette colors unconditionally (for tree view chanview), instead of branching on raw dark-mode enum checks. This keys behavior off the already-resolved palette output, which matches how setup_apply_to_sess() / palette_apply_dark_mode() flows are intended to work.
Preserved font handling exactly as before (input_style->font_desc when available).
Added inline rationale comment clarifying that AUTO/light should continue using palette-managed colors and should not revert to GTK theme defaults in a way that clears custom colors.
2026-02-25 20:23:01 -07:00
bbde2e5578 Added a new internal state flag, app_set_prefer_dark, and now set it whenever fe_set_gtk_prefer_dark_theme() writes gtk-application-prefer-dark-theme, so AUTO detection can distinguish app-authored writes from system/user signals.
Reworked fe_system_prefers_dark() to prioritize non-app-written sources in AUTO flow:

        first: gtk-theme-name dark-variant heuristic,

        then (Windows): native platform detection via fe_win32_try_get_system_dark,

        only then fallback to gtk-application-prefer-dark-theme when no theme-name signal was available and the app has not written that property.

    Kept manual LIGHT/DARK behavior unchanged by preserving the existing mode application path (fe_apply_theme_for_mode() still drives fe_set_gtk_prefer_dark_theme() directly).

    Verified fe_auto_dark_mode_changed() and fe_init() signal wiring still trigger AUTO palette/theme refreshes on system theme changes (notify::gtk-theme-name and existing notify callback flow remain in place).
2026-02-25 20:03:20 -07:00
97c6f36b20 Adjusted create_input_style so it no longer globally forces color on #zoitechat-inputbox, reducing over-constraining while still applying the user-selected background and caret color where intended. This keeps non-text-node state rendering more theme-driven.
Kept user override support for baseline text color and caret color on the input’s text node (#zoitechat-inputbox text).
Added targeted state-aware text rules for :focus and :backdrop (to preserve readability when theme state styling changes), plus a softened :disabled text rule instead of broad global color forcing.
Added explicit selection-node handling (#zoitechat-inputbox text selection) using theme selection tokens so selected text remains readable and aligned with Adwaita/Yaru-style theme behavior.
Preserved the existing Adwaita/Yaru background-image workaround logic, so fallback behavior remains compatible with those themes.
2026-02-25 19:43:19 -07:00
98 changed files with 4431 additions and 11775 deletions

View File

@@ -29,7 +29,6 @@ jobs:
build-essential pkg-config meson ninja-build cmake \
gettext \
libcanberra-dev libdbus-glib-1-dev libglib2.0-dev \
libarchive-dev \
libgtk-3-dev \
libwayland-client0 libwayland-cursor0 libwayland-egl1 \
libxkbcommon0 \

79
.github/workflows/solus-eopkg-build.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: Solus eopkg build
on:
workflow_dispatch:
inputs:
package_yml:
description: "Path to Solus package.yml for ypkg build"
required: false
default: "packaging/solus/package.yml"
solus_image:
description: "Solus container image"
required: false
default: "docker.io/silkeh/solus:latest"
push:
branches:
- main
- master
permissions:
contents: read
packages: read
jobs:
build-eopkg:
runs-on: ubuntu-latest
env:
SOLUS_IMAGE: ${{ inputs.solus_image || 'docker.io/silkeh/solus:latest' }}
PACKAGE_YML: ${{ inputs.package_yml || 'packaging/solus/package.yml' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to GHCR (for ghcr.io images)
if: startsWith(env.SOLUS_IMAGE, 'ghcr.io/')
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build eopkg package in Solus container
run: |
if [ ! -f "$PACKAGE_YML" ]; then
echo "Expected Solus packaging file at $PACKAGE_YML" >&2
echo "Available package.yml files:" >&2
find . -name "package.yml" -print >&2 || true
echo "Add a package.yml (ypkg) file or update the workflow input PACKAGE_YML." >&2
exit 1
fi
if ! docker pull "$SOLUS_IMAGE"; then
echo "Failed to pull SOLUS_IMAGE=$SOLUS_IMAGE" >&2
echo "Set workflow input 'solus_image' to a valid image that provides eopkg/ypkg." >&2
exit 1
fi
docker run --rm \
-v "$PWD":/workspace \
-w /workspace \
-e PACKAGE_YML="$PACKAGE_YML" \
"$SOLUS_IMAGE" \
sh -lc '
set -euo pipefail
eopkg update-repo -y
# Keep file-conflict handling on: some base images still carry
# openssl-11 leftovers, and Meson tooling is not always preinstalled.
eopkg install -y --ignore-file-conflicts \
ypkg git meson ninja pkgconf gcc gettext
ypkg build "$PACKAGE_YML"
mkdir -p /workspace/artifacts
find . -maxdepth 3 -name "*.eopkg" -type f -exec cp -v {} /workspace/artifacts/ \;
'
- name: Upload eopkg artifacts
uses: actions/upload-artifact@v4
with:
name: solus-eopkg
path: artifacts/*.eopkg
if-no-files-found: error

View File

@@ -36,74 +36,22 @@ jobs:
run: |
New-Item -Name "deps" -ItemType "Directory" -Force | Out-Null
python -m pip install --upgrade pip
python -m pip install cffi
python -m pip install zstandard
$ProgressPreference = 'SilentlyContinue'
function Download-WithRetry {
param(
[string]$Url,
[string]$OutFile,
[int]$MaxAttempts = 5,
[int]$InitialDelaySeconds = 2
)
for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
try {
Invoke-WebRequest -Uri $Url -OutFile $OutFile -ErrorAction Stop
return
}
catch {
if ($attempt -eq $MaxAttempts) {
throw
}
Start-Sleep -Seconds ($InitialDelaySeconds * [math]::Pow(2, $attempt - 1))
}
}
}
Download-WithRetry -Url https://files.jrsoftware.org/is/6/innosetup-6.7.0.exe -OutFile deps\innosetup-unicode.exe
Invoke-WebRequest https://files.jrsoftware.org/is/6/innosetup-6.7.0.exe -OutFile deps\innosetup-unicode.exe
& deps\innosetup-unicode.exe /VERYSILENT | Out-Null
Download-WithRetry -Url https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/idpsetup-1.5.1.exe -OutFile deps\idpsetup.exe
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/idpsetup-1.5.1.exe -OutFile deps\idpsetup.exe
& deps\idpsetup.exe /VERYSILENT
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre1/GTK3_Gvsbuild_zoitechat-2.18.0-pre1_${{ matrix.platform }}.7z -OutFile deps\gtk-${{ matrix.arch }}.7z
Invoke-WebRequest https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre1/GTK3_Gvsbuild_zoitechat-2.18.0-pre1_${{ matrix.platform }}.7z -OutFile deps\gtk-${{ matrix.arch }}.7z
& 7z.exe x deps\gtk-${{ matrix.arch }}.7z -oC:\gtk-build\gtk\x64\release
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-hicolor-icon-theme-0.18-1-any.pkg.tar.zst -OutFile deps\hicolor-icon-theme.pkg.tar.zst
python -c "import tarfile,zstandard,pathlib;archive=pathlib.Path(r'deps\\hicolor-icon-theme.pkg.tar.zst');target=pathlib.Path(r'C:\\gtk-build\\gtk\\x64\\release');dctx=zstandard.ZstdDecompressor();f=archive.open('rb');reader=dctx.stream_reader(f);tf=tarfile.open(fileobj=reader,mode='r|');[tf.extract(m,path=target) for m in tf if m.name.startswith('mingw64/share/icons/hicolor/')];tf.close();reader.close();f.close()"
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-libarchive-3.8.1-1-any.pkg.tar.zst -OutFile deps\libarchive.pkg.tar.zst
python -c "import tarfile,zstandard,pathlib;archive=pathlib.Path(r'deps\\libarchive.pkg.tar.zst');target=pathlib.Path(r'C:\\gtk-build\\gtk\\x64\\release');dctx=zstandard.ZstdDecompressor();f=archive.open('rb');reader=dctx.stream_reader(f);tf=tarfile.open(fileobj=reader,mode='r|');[tf.extract(m,path=target) for m in tf if m.name.startswith(('mingw64/include/archive','mingw64/lib/libarchive','mingw64/bin/libarchive'))];tf.close();reader.close();f.close()"
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64\share\icons\hicolor) {
New-Item -Path C:\gtk-build\gtk\x64\release\share\icons -ItemType Directory -Force | Out-Null
Copy-Item -Path C:\gtk-build\gtk\x64\release\mingw64\share\icons\hicolor -Destination C:\gtk-build\gtk\x64\release\share\icons\hicolor -Recurse -Force
}
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64\include) {
New-Item -Path C:\gtk-build\gtk\x64\release\include -ItemType Directory -Force | Out-Null
Copy-Item -Path C:\gtk-build\gtk\x64\release\mingw64\include\archive* -Destination C:\gtk-build\gtk\x64\release\include -Recurse -Force
}
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64\lib) {
New-Item -Path C:\gtk-build\gtk\x64\release\lib -ItemType Directory -Force | Out-Null
Copy-Item -Path C:\gtk-build\gtk\x64\release\mingw64\lib\libarchive* -Destination C:\gtk-build\gtk\x64\release\lib -Force
}
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64\bin) {
New-Item -Path C:\gtk-build\gtk\x64\release\bin -ItemType Directory -Force | Out-Null
Copy-Item -Path C:\gtk-build\gtk\x64\release\mingw64\bin\libarchive*.dll -Destination C:\gtk-build\gtk\x64\release\bin -Force
}
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64) {
Remove-Item -Path C:\gtk-build\gtk\x64\release\mingw64 -Recurse -Force
}
Download-WithRetry -Url https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/gendef-20111031.7z -OutFile deps\gendef.7z
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/gendef-20111031.7z -OutFile deps\gendef.7z
& 7z.exe x deps\gendef.7z -oC:\gtk-build
Download-WithRetry -Url https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/WinSparkle-20151011.7z -OutFile deps\WinSparkle.7z
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/WinSparkle-20151011.7z -OutFile deps\WinSparkle.7z
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle
Download-WithRetry -Url https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/perl-5.20.0-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/perl-5.20.0-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.20\${{ matrix.platform }}
$pyRoot = $env:pythonLocation
@@ -116,6 +64,9 @@ jobs:
New-Item -Path $pyDir -Name "${{ matrix.platform }}" -ItemType Junction -Value $pyRoot | Out-Null
}
python -m pip install --upgrade pip
python -m pip install cffi
- name: Build
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
@@ -130,8 +81,6 @@ jobs:
set "LIB=%PYTHON_DIR%\libs;%LIB%"
set "INCLUDE=%PYTHON_DIR%\include;%INCLUDE%"
powershell -NoProfile -ExecutionPolicy Bypass -Command "$archiveLib='C:\gtk-build\gtk\x64\release\lib\libarchive.lib'; if (-not (Test-Path $archiveLib)) { $archiveDll = Get-ChildItem 'C:\gtk-build\gtk\x64\release\bin\libarchive*.dll' | Select-Object -First 1; if ($archiveDll) { Push-Location 'C:\gtk-build\gtk\x64\release\lib'; & 'C:\gtk-build\gendef\gendef.exe' $archiveDll.FullName | Out-Null; $archiveDef = Get-ChildItem 'libarchive*.def' | Select-Object -First 1; if ($archiveDef) { & lib /def:$archiveDef.Name /machine:${{ matrix.platform }} /out:libarchive.lib | Out-Null }; Pop-Location } }"
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
shell: cmd

View File

@@ -43,6 +43,10 @@ if get_option('gtk-frontend')
install_dir: appdir
)
install_data('net.zoite.Zoitechat.mime.xml',
install_dir: mimedir
)
if desktop_utils.found()
test('Validate net.zoite.Zoitechat.desktop', desktop_utils,
args: [zoitechat_desktop]

View File

@@ -11,7 +11,7 @@ Categories=GTK;Network;IRCClient;
StartupNotify=true
StartupWMClass=net.zoite.Zoitechat
X-GNOME-UsesNotifications=true
MimeType=x-scheme-handler/irc;x-scheme-handler/ircs;
MimeType=x-scheme-handler/irc;x-scheme-handler/ircs;application/x-zoitechat-theme;application/x-hexchat-theme;
Actions=SafeMode;
[Desktop Action SafeMode]

View File

@@ -1,3 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-zoitechat-theme">
<comment>ZoiteChat Theme</comment>
<glob pattern="*.zct"/>
</mime-type>
<mime-type type="application/x-hexchat-theme">
<comment>HexChat Theme</comment>
<glob pattern="*.hct"/>
</mime-type>
</mime-info>

View File

@@ -0,0 +1,33 @@
name : zoitechat
version : 2.18.0~pre2
release : 2
source :
- https://github.com/ZoiteChat/zoitechat/archive/e060d57baee1be22bee1f9c3b047be3fa71c6d35.tar.gz : ed315a0b1c46e798912fd830d3845427972857c43ccaa16284969c6f542add38
homepage : https://zoitechat.zoite.net/
license : GPL-2.0-only
component : network.irc
summary : HexChat-based IRC client
description: |
ZoiteChat is a HexChat-based IRC client for Windows and UNIX-like systems.
builddeps :
- pkgconfig(glib-2.0)
- pkgconfig(gmodule-2.0)
- pkgconfig(gtk+-3.0)
- pkgconfig(ayatana-appindicator3-0.1)
- pkgconfig(dbus-glib-1)
- pkgconfig(libcanberra)
- pkgconfig(openssl)
- pkgconfig(iso-codes)
- meson
- ninja
- pkgconf
- gcc
- gettext
setup : |
%meson_configure \
-Dgtk-frontend=true \
-Dinstall-appdata=true
build : |
%ninja_build
install : |
%ninja_install

View File

@@ -5,6 +5,10 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -23,10 +27,21 @@
<TargetName>hcchecksum</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;CHECKSUM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<ModuleDefinitionFile>checksum.def</ModuleDefinitionFile>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;CHECKSUM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<ModuleDefinitionFile>checksum.def</ModuleDefinitionFile>

View File

@@ -5,6 +5,10 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -23,6 +27,15 @@
<TargetName>hcexec</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;EXEC_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src\common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<ModuleDefinitionFile>exec.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;EXEC_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@@ -5,6 +5,10 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -23,10 +27,21 @@
<TargetName>hcfishlim</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;FISHLIM_EXPORTS;HAVE_DH_SET0_PQG;HAVE_DH_GET0_KEY;HAVE_DH_SET0_KEY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<ModuleDefinitionFile>fishlim.def</ModuleDefinitionFile>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;FISHLIM_EXPORTS;HAVE_DH_SET0_PQG;HAVE_DH_GET0_KEY;HAVE_DH_SET0_KEY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<ModuleDefinitionFile>fishlim.def</ModuleDefinitionFile>

View File

@@ -5,6 +5,10 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -23,6 +27,16 @@
<TargetName>$(LuaOutput)</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(Glib);$(LuaInclude);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>"$(LuaLib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@@ -5,6 +5,10 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -23,6 +27,26 @@
<TargetName>hcperl</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;PERL520_EXPORTS;HAS_BOOL;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(IntDir);..\..\src\common;$(ZoiteChatLib);$(PerlPath)\lib\CORE;$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(IntDir);$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(PerlLib).lib;$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>perl.def</ModuleDefinitionFile>
<DelayLoadDLLs>$(PerlLib).dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
<PreBuildEvent>
<Command>"$(GendefPath)\gendef" "$(PerlPath)\bin\$(PerlLib).dll"
move $(PerlLib).def "$(IntDir)"
lib /nologo /machine:x86 "/def:$(IntDir)$(PerlLib).def" "/out:$(IntDir)\$(PerlLib).lib"
"$(PerlPath)\bin\perl.exe" generate_header
move irc.pm.h "$(IntDir)"
move zoitechat.pm.h "$(IntDir)"</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;PERL520_EXPORTS;HAS_BOOL;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@@ -27,6 +27,23 @@
<TargetName>$(Python3Output)</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;PYTHON_EXPORTS;Py_NO_LINK_LIB;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UndefinePreprocessorDefinitions>_DEBUG;Py_DEBUG;Py_REF_DEBUG;Py_TRACE_REFS;%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
<AdditionalOptions>/U_DEBUG /UPy_DEBUG /UPy_REF_DEBUG /UPy_TRACE_REFS %(AdditionalOptions)</AdditionalOptions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<AdditionalIncludeDirectories>$(Glib);$(Python3Path)\include;..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<ModuleDefinitionFile>python.def</ModuleDefinitionFile>
<AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<PreBuildEvent>
<Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\zoitechat-plugin.h python.py "$(IntDir)python.c"</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;PYTHON_EXPORTS;Py_NO_LINK_LIB;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@@ -28,15 +28,29 @@
<TargetName>hcsysinfo</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;SYSINFO_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src\common;$(DepsRoot)\include;$(OpenSslInclude);$(Glib);$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SYSINFO_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src\common;$(DepsRoot)\include;$(OpenSslInclude);$(Glib);$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(ZoiteChatLib)common.lib;wbemuuid.lib;$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(ZoiteChatLib)common.lib;wbemuuid.lib;comsupp.lib;$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>comsupp.lib</IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile>sysinfo.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;SYSINFO_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src\common;$(DepsRoot)\include;$(OpenSslInclude);$(Glib);$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(ZoiteChatLib)common.lib;wbemuuid.lib;comsupp.lib;$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>comsupp.lib</IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile>sysinfo.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
@@ -54,4 +68,4 @@
<ClInclude Include="sysinfo.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
</Project>

View File

@@ -27,6 +27,17 @@
<TargetName>hcupd</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;UPD_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src\common;$(WinSparklePath);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<ModuleDefinitionFile>upd.def</ModuleDefinitionFile>
<AdditionalDependencies>$(DepLibs);WinSparkle.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;$(WinSparklePath);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;UPD_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@@ -5,6 +5,10 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -23,6 +27,11 @@
<TargetName>hcwinamp</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;WINAMP_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;WINAMP_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -30,7 +39,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<ModuleDefinitionFile>winamp.def</ModuleDefinitionFile>

View File

@@ -258,13 +258,12 @@ int
cfg_get_color (char *cfg, char *var, guint16 *r, guint16 *g, guint16 *b)
{
char str[128];
int matched;
if (!cfg_get_str (cfg, var, str, sizeof (str)))
return 0;
matched = sscanf (str, "%04hx %04hx %04hx", r, g, b);
return matched == 3;
sscanf (str, "%04hx %04hx %04hx", r, g, b);
return 1;
}
int
@@ -420,6 +419,7 @@ const struct prefs vars[] =
{"gui_input_nick", P_OFFINT (hex_gui_input_nick), TYPE_BOOL},
{"gui_input_spell", P_OFFINT (hex_gui_input_spell), TYPE_BOOL},
{"gui_input_style", P_OFFINT (hex_gui_input_style), TYPE_BOOL},
{"gui_gtk3_theme_name", P_OFFSET (hex_gui_gtk3_theme_name), TYPE_STR},
{"gui_join_dialog", P_OFFINT (hex_gui_join_dialog), TYPE_BOOL},
{"gui_lagometer", P_OFFINT (hex_gui_lagometer), TYPE_INT},
{"gui_lang", P_OFFINT (hex_gui_lang), TYPE_INT},
@@ -439,7 +439,6 @@ const struct prefs vars[] =
{"gui_tab_dots", P_OFFINT (hex_gui_tab_dots), TYPE_BOOL},
{"gui_tab_icons", P_OFFINT (hex_gui_tab_icons), TYPE_BOOL},
{"gui_dark_mode", P_OFFINT (hex_gui_dark_mode), TYPE_INT},
{"gui_gtk3_variant", P_OFFINT (hex_gui_gtk3_variant), TYPE_INT},
{"gui_tab_layout", P_OFFINT (hex_gui_tab_layout), TYPE_INT},
{"gui_tab_middleclose", P_OFFINT (hex_gui_tab_middleclose), TYPE_BOOL},
{"gui_tab_newtofront", P_OFFINT (hex_gui_tab_newtofront), TYPE_INT},
@@ -569,7 +568,6 @@ const struct prefs vars[] =
{"text_font", P_OFFSET (hex_text_font), TYPE_STR},
{"text_font_main", P_OFFSET (hex_text_font_main), TYPE_STR},
{"text_font_alternative", P_OFFSET (hex_text_font_alternative), TYPE_STR},
{"gui_gtk3_theme", P_OFFSET (hex_gui_gtk3_theme), TYPE_STR},
{"text_indent", P_OFFINT (hex_text_indent), TYPE_BOOL},
{"text_max_indent", P_OFFINT (hex_text_max_indent), TYPE_INT},
{"text_max_lines", P_OFFINT (hex_text_max_lines), TYPE_INT},

View File

@@ -5,6 +5,10 @@
<ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -45,8 +49,6 @@
<ClInclude Include="zoitechat-plugin.h" />
<ClInclude Include="zoitechat.h" />
<ClInclude Include="zoitechatc.h" />
<ClInclude Include="theme-service.h" />
<ClInclude Include="gtk3-theme-service.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="cfgfiles.c" />
@@ -77,8 +79,6 @@
<ClCompile Include="userlist.c" />
<ClCompile Include="util.c" />
<ClCompile Include="zoitechat.c" />
<ClCompile Include="theme-service.c" />
<ClCompile Include="gtk3-theme-service.c" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\win32\config.h.tt" />
@@ -96,10 +96,16 @@
<PropertyGroup>
<OutDir>$(ZoiteChatLib)</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ZoiteChatLib);$(DepsRoot)\include;$(OpenSslInclude);$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_LIB;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ZoiteChatLib);$(DepsRoot)\include;$(ArchiveInclude);$(OpenSslInclude);$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ZoiteChatLib);$(DepsRoot)\include;$(OpenSslInclude);$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
</ItemDefinitionGroup>

View File

@@ -119,12 +119,6 @@
<ClInclude Include="sysinfo\sysinfo.h">
<Filter>Source Files\sysinfo</Filter>
</ClInclude>
<ClInclude Include="theme-service.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="gtk3-theme-service.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="cfgfiles.c">
@@ -208,12 +202,6 @@
<ClCompile Include="sysinfo\win32\backend.c">
<Filter>Source Files\sysinfo\win32</Filter>
</ClCompile>
<ClCompile Include="theme-service.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="gtk3-theme-service.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\win32\config.h.tt" />

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +0,0 @@
#ifndef ZOITECHAT_GTK3_THEME_SERVICE_H
#define ZOITECHAT_GTK3_THEME_SERVICE_H
#include <glib.h>
typedef enum
{
ZOITECHAT_GTK3_THEME_SOURCE_SYSTEM = 0,
ZOITECHAT_GTK3_THEME_SOURCE_USER = 1
} ZoitechatGtk3ThemeSource;
typedef struct
{
char *id;
char *display_name;
char *path;
gboolean has_dark_variant;
char *thumbnail_path;
ZoitechatGtk3ThemeSource source;
} ZoitechatGtk3Theme;
char *zoitechat_gtk3_theme_service_get_user_themes_dir (void);
GPtrArray *zoitechat_gtk3_theme_service_discover (void);
void zoitechat_gtk3_theme_free (ZoitechatGtk3Theme *theme);
ZoitechatGtk3Theme *zoitechat_gtk3_theme_find_by_id (const char *theme_id);
gboolean zoitechat_gtk3_theme_service_import (const char *source_path, char **imported_id, GError **error);
gboolean zoitechat_gtk3_theme_service_remove_user_theme (const char *theme_id, GError **error);
char *zoitechat_gtk3_theme_pick_css_dir_for_minor (const char *theme_root, int preferred_minor);
char *zoitechat_gtk3_theme_pick_css_dir (const char *theme_root);
GPtrArray *zoitechat_gtk3_theme_build_inheritance_chain (const char *theme_root);
#endif

View File

@@ -3,7 +3,6 @@ common_sources = [
'chanopt.c',
'ctcp.c',
'dcc.c',
'gtk3-theme-service.c',
'zoitechat.c',
'history.c',
'ignore.c',
@@ -28,17 +27,12 @@ common_sources = [
]
common_sysinfo_deps = []
libarchive_dep = dependency('libarchive', required: host_machine.system() != 'windows')
common_deps = [
libgio_dep,
libcanberra_dep,
] + global_deps
if libarchive_dep.found()
common_deps += libarchive_dep
endif
common_includes = [
config_h_include,
include_directories('.')
@@ -139,18 +133,3 @@ zoitechat_plugin_dep = declare_dependency(
compile_args: common_cflags,
dependencies: global_deps,
)
gtk3_theme_service_tests = executable('gtk3_theme_service_tests',
[
'tests/test-gtk3-theme-service.c',
'gtk3-theme-service.c',
],
include_directories: [config_h_include, include_directories('.')],
dependencies: [libgio_dep] + (libarchive_dep.found() ? [libarchive_dep] : []),
)
test('GTK3 Theme Service Tests', gtk3_theme_service_tests,
protocol: 'tap',
timeout: 120,
)

View File

@@ -3770,6 +3770,37 @@ cmd_url (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
if (word[2][0])
{
char *theme_path = NULL;
if (zoitechat_theme_path_from_arg (word[2], &theme_path))
{
GError *error = NULL;
char *theme_name = NULL;
if (zoitechat_import_gtk3_theme_archive (theme_path, &theme_name, &error))
{
if (theme_name)
{
char *message = g_strdup_printf (_("GTK3 theme \"%s\" imported. Use Theme settings to apply it."), theme_name);
fe_message (message, FE_MSG_INFO);
g_free (message);
}
else
{
fe_message (_("GTK3 theme imported. Use Theme settings to apply it."), FE_MSG_INFO);
}
}
else
{
fe_message (error ? error->message : _("Failed to import GTK3 theme archive."),
FE_MSG_ERROR);
g_clear_error (&error);
}
g_free (theme_name);
g_free (theme_path);
return TRUE;
}
char *server_name = NULL;
char *port = NULL;
char *channel = NULL;

View File

@@ -1,684 +0,0 @@
#include <glib.h>
#include <glib/gstdio.h>
#include "../gtk3-theme-service.h"
#include "../cfgfiles.h"
char *xdir = NULL;
char *
get_xdir (void)
{
return xdir;
}
static void
write_text_file (const char *path, const char *contents)
{
g_file_set_contents (path, contents, -1, NULL);
}
static char *
make_theme_dir (const char *base, const char *name, gboolean dark, gboolean with_index)
{
char *root = g_build_filename (base, name, NULL);
char *gtk_dir = g_build_filename (root, "gtk-3.0", NULL);
char *css = g_build_filename (gtk_dir, "gtk.css", NULL);
g_mkdir_with_parents (gtk_dir, 0700);
write_text_file (css, "button { background-image: url(\"../assets/a.png\"); }");
if (dark)
{
char *dark_css = g_build_filename (gtk_dir, "gtk-dark.css", NULL);
write_text_file (dark_css, "button { color: #eee; }");
g_free (dark_css);
}
if (with_index)
{
char *index = g_build_filename (root, "index.theme", NULL);
write_text_file (index, "[Desktop Entry]\nName=Indexed Theme\n");
g_free (index);
}
g_free (css);
g_free (gtk_dir);
return root;
}
static void
setup_test_xdir (char **tmp_root)
{
char *root = g_dir_make_tmp ("zoitechat-gtk3-service-test-XXXXXX", NULL);
xdir = g_build_filename (root, "config", NULL);
g_mkdir_with_parents (xdir, 0700);
*tmp_root = root;
}
static void
teardown_test_xdir (char *tmp_root)
{
char *cmd;
cmd = g_strdup_printf ("rm -rf %s", tmp_root);
g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL);
g_free (cmd);
g_free (xdir);
xdir = NULL;
g_free (tmp_root);
}
static guint
count_extract_temp_dirs (void)
{
GDir *dir;
const char *name;
guint count = 0;
const char *tmp_dir = g_get_tmp_dir ();
dir = g_dir_open (tmp_dir, 0, NULL);
if (!dir)
return 0;
while ((name = g_dir_read_name (dir)) != NULL)
{
if (g_str_has_prefix (name, "zoitechat-gtk3-theme-"))
count++;
}
g_dir_close (dir);
return count;
}
static char *
make_theme_dir_with_inherits (const char *base, const char *name, const char *inherits)
{
char *root = make_theme_dir (base, name, FALSE, FALSE);
char *index = g_build_filename (root, "index.theme", NULL);
char *contents;
if (inherits && inherits[0])
contents = g_strdup_printf ("[Desktop Entry]\nName=%s\nInherits=%s\n", name, inherits);
else
contents = g_strdup_printf ("[Desktop Entry]\nName=%s\n", name);
write_text_file (index, contents);
g_free (contents);
g_free (index);
return root;
}
static void
test_inheritance_chain_single_parent (void)
{
char *tmp_root;
char *themes_root;
char *adwaita;
char *child;
GPtrArray *chain;
setup_test_xdir (&tmp_root);
themes_root = g_build_filename (tmp_root, "themes", NULL);
g_mkdir_with_parents (themes_root, 0700);
adwaita = make_theme_dir_with_inherits (themes_root, "Adwaita", NULL);
child = make_theme_dir_with_inherits (themes_root, "Child", "Adwaita");
chain = zoitechat_gtk3_theme_build_inheritance_chain (child);
g_assert_nonnull (chain);
g_assert_cmpuint (chain->len, ==, 2);
g_assert_cmpstr (g_ptr_array_index (chain, 0), ==, adwaita);
g_assert_cmpstr (g_ptr_array_index (chain, 1), ==, child);
g_ptr_array_unref (chain);
g_free (child);
g_free (adwaita);
g_free (themes_root);
teardown_test_xdir (tmp_root);
}
static void
test_inheritance_chain_multi_level (void)
{
char *tmp_root;
char *themes_root;
char *base;
char *middle;
char *child;
GPtrArray *chain;
setup_test_xdir (&tmp_root);
themes_root = g_build_filename (tmp_root, "themes", NULL);
g_mkdir_with_parents (themes_root, 0700);
base = make_theme_dir_with_inherits (themes_root, "Base", NULL);
middle = make_theme_dir_with_inherits (themes_root, "Middle", "Base");
child = make_theme_dir_with_inherits (themes_root, "Child", "Middle");
chain = zoitechat_gtk3_theme_build_inheritance_chain (child);
g_assert_nonnull (chain);
g_assert_cmpuint (chain->len, ==, 3);
g_assert_cmpstr (g_ptr_array_index (chain, 0), ==, base);
g_assert_cmpstr (g_ptr_array_index (chain, 1), ==, middle);
g_assert_cmpstr (g_ptr_array_index (chain, 2), ==, child);
g_ptr_array_unref (chain);
g_free (child);
g_free (middle);
g_free (base);
g_free (themes_root);
teardown_test_xdir (tmp_root);
}
static void
test_inheritance_chain_missing_parent (void)
{
char *tmp_root;
char *themes_root;
char *child;
GPtrArray *chain;
setup_test_xdir (&tmp_root);
themes_root = g_build_filename (tmp_root, "themes", NULL);
g_mkdir_with_parents (themes_root, 0700);
child = make_theme_dir_with_inherits (themes_root, "Child", "MissingParent");
chain = zoitechat_gtk3_theme_build_inheritance_chain (child);
g_assert_nonnull (chain);
g_assert_cmpuint (chain->len, ==, 1);
g_assert_cmpstr (g_ptr_array_index (chain, 0), ==, child);
g_ptr_array_unref (chain);
g_free (child);
g_free (themes_root);
teardown_test_xdir (tmp_root);
}
static void
test_inheritance_chain_parent_from_xdg_data_home (void)
{
char *tmp_root;
char *child_root;
char *home_dir;
char *user_data_dir;
char *saved_home;
char *saved_xdg_data_home;
char *parent;
char *child;
GPtrArray *chain;
setup_test_xdir (&tmp_root);
child_root = g_build_filename (tmp_root, "themes", NULL);
home_dir = g_build_filename (tmp_root, "home", NULL);
user_data_dir = g_build_filename (tmp_root, "xdg-data-home", NULL);
g_mkdir_with_parents (child_root, 0700);
g_mkdir_with_parents (home_dir, 0700);
g_mkdir_with_parents (user_data_dir, 0700);
saved_home = g_strdup (g_getenv ("HOME"));
saved_xdg_data_home = g_strdup (g_getenv ("XDG_DATA_HOME"));
g_setenv ("HOME", home_dir, TRUE);
g_setenv ("XDG_DATA_HOME", user_data_dir, TRUE);
{
char *user_themes = g_build_filename (user_data_dir, "themes", NULL);
g_mkdir_with_parents (user_themes, 0700);
parent = make_theme_dir_with_inherits (user_themes, "ParentFromDataHome", NULL);
g_free (user_themes);
}
child = make_theme_dir_with_inherits (child_root, "Child", "ParentFromDataHome");
chain = zoitechat_gtk3_theme_build_inheritance_chain (child);
g_assert_nonnull (chain);
g_assert_cmpuint (chain->len, ==, 2);
g_assert_cmpstr (g_ptr_array_index (chain, 0), ==, parent);
g_assert_cmpstr (g_ptr_array_index (chain, 1), ==, child);
g_ptr_array_unref (chain);
if (saved_home)
g_setenv ("HOME", saved_home, TRUE);
else
g_unsetenv ("HOME");
if (saved_xdg_data_home)
g_setenv ("XDG_DATA_HOME", saved_xdg_data_home, TRUE);
else
g_unsetenv ("XDG_DATA_HOME");
g_free (child);
g_free (parent);
g_free (saved_xdg_data_home);
g_free (saved_home);
g_free (user_data_dir);
g_free (home_dir);
g_free (child_root);
teardown_test_xdir (tmp_root);
}
static void
test_inheritance_chain_parent_from_xdg_data_dirs (void)
{
char *tmp_root;
char *child_root;
char *home_dir;
char *system_data_dir;
char *system_data_dirs;
char *saved_home;
char *saved_xdg_data_dirs;
char *parent;
char *child;
GPtrArray *chain;
setup_test_xdir (&tmp_root);
child_root = g_build_filename (tmp_root, "themes", NULL);
home_dir = g_build_filename (tmp_root, "home", NULL);
system_data_dir = g_build_filename (tmp_root, "xdg-data-system", NULL);
system_data_dirs = g_strdup_printf ("%s:/usr/share", system_data_dir);
g_mkdir_with_parents (child_root, 0700);
g_mkdir_with_parents (home_dir, 0700);
g_mkdir_with_parents (system_data_dir, 0700);
saved_home = g_strdup (g_getenv ("HOME"));
saved_xdg_data_dirs = g_strdup (g_getenv ("XDG_DATA_DIRS"));
g_setenv ("HOME", home_dir, TRUE);
g_setenv ("XDG_DATA_DIRS", system_data_dirs, TRUE);
{
char *system_themes = g_build_filename (system_data_dir, "themes", NULL);
g_mkdir_with_parents (system_themes, 0700);
parent = make_theme_dir_with_inherits (system_themes, "ParentFromDataDirs", NULL);
g_free (system_themes);
}
child = make_theme_dir_with_inherits (child_root, "Child", "ParentFromDataDirs");
chain = zoitechat_gtk3_theme_build_inheritance_chain (child);
g_assert_nonnull (chain);
g_assert_cmpuint (chain->len, ==, 2);
g_assert_cmpstr (g_ptr_array_index (chain, 0), ==, parent);
g_assert_cmpstr (g_ptr_array_index (chain, 1), ==, child);
g_ptr_array_unref (chain);
if (saved_home)
g_setenv ("HOME", saved_home, TRUE);
else
g_unsetenv ("HOME");
if (saved_xdg_data_dirs)
g_setenv ("XDG_DATA_DIRS", saved_xdg_data_dirs, TRUE);
else
g_unsetenv ("XDG_DATA_DIRS");
g_free (child);
g_free (parent);
g_free (saved_xdg_data_dirs);
g_free (saved_home);
g_free (system_data_dirs);
g_free (system_data_dir);
g_free (home_dir);
g_free (child_root);
teardown_test_xdir (tmp_root);
}
static void
test_invalid_archive_reports_extract_error (void)
{
char *tmp_root;
char *bad_archive;
char *imported_id = NULL;
GError *error = NULL;
guint before_count;
guint after_count;
setup_test_xdir (&tmp_root);
bad_archive = g_build_filename (tmp_root, "bad-theme.tar.xz", NULL);
write_text_file (bad_archive, "this is not a real archive");
before_count = count_extract_temp_dirs ();
g_assert_false (zoitechat_gtk3_theme_service_import (bad_archive, &imported_id, &error));
g_assert_null (imported_id);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED);
g_assert_cmpstr (error->message, ==, "Failed to extract theme archive.");
g_error_free (error);
after_count = count_extract_temp_dirs ();
g_assert_cmpuint (after_count, ==, before_count);
g_free (bad_archive);
teardown_test_xdir (tmp_root);
}
static void
test_archive_without_theme_reports_css_error (void)
{
char *tmp_root;
char *archive_root;
char *archive_path;
char *command;
char *imported_id = NULL;
GError *error = NULL;
setup_test_xdir (&tmp_root);
archive_root = g_build_filename (tmp_root, "invalid-theme-root", NULL);
g_mkdir_with_parents (archive_root, 0700);
{
char *readme = g_build_filename (archive_root, "README.txt", NULL);
write_text_file (readme, "not a gtk theme");
g_free (readme);
}
archive_path = g_build_filename (tmp_root, "invalid-theme.zip", NULL);
command = g_strdup_printf ("cd %s && zip -qr %s .", archive_root, archive_path);
g_assert_true (g_spawn_command_line_sync (command, NULL, NULL, NULL, NULL));
g_free (command);
g_assert_false (zoitechat_gtk3_theme_service_import (archive_path, &imported_id, &error));
g_assert_null (imported_id);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL);
g_assert_cmpstr (error->message, ==, "No GTK3 gtk.css file found in the selected theme.");
g_error_free (error);
g_free (archive_path);
g_free (archive_root);
teardown_test_xdir (tmp_root);
}
static void
test_import_rejects_theme_missing_index_theme (void)
{
char *tmp_root;
char *src_root;
char *theme_root;
char *imported_id = NULL;
GError *error = NULL;
setup_test_xdir (&tmp_root);
src_root = g_build_filename (tmp_root, "src", NULL);
g_mkdir_with_parents (src_root, 0700);
theme_root = make_theme_dir (src_root, "NoIndex", FALSE, FALSE);
g_assert_false (zoitechat_gtk3_theme_service_import (theme_root, &imported_id, &error));
g_assert_null (imported_id);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL);
g_assert_nonnull (g_strstr_len (error->message, -1, "missing required index.theme"));
g_assert_nonnull (g_strstr_len (error->message, -1, "NoIndex"));
g_error_free (error);
g_free (theme_root);
g_free (src_root);
teardown_test_xdir (tmp_root);
}
static void
test_import_rejects_index_without_desktop_entry (void)
{
char *tmp_root;
char *src_root;
char *theme_root;
char *index_path;
char *imported_id = NULL;
GError *error = NULL;
setup_test_xdir (&tmp_root);
src_root = g_build_filename (tmp_root, "src", NULL);
g_mkdir_with_parents (src_root, 0700);
theme_root = make_theme_dir (src_root, "NoDesktopEntry", FALSE, FALSE);
index_path = g_build_filename (theme_root, "index.theme", NULL);
write_text_file (index_path, "[X-GNOME-Metatheme]\nName=Broken\n");
g_assert_false (zoitechat_gtk3_theme_service_import (theme_root, &imported_id, &error));
g_assert_null (imported_id);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL);
g_assert_nonnull (g_strstr_len (error->message, -1, "missing the [Desktop Entry] section"));
g_assert_nonnull (g_strstr_len (error->message, -1, "index.theme"));
g_error_free (error);
g_free (index_path);
g_free (theme_root);
g_free (src_root);
teardown_test_xdir (tmp_root);
}
static void
test_import_rejects_unresolved_inherits (void)
{
char *tmp_root;
char *src_root;
char *theme_root;
char *imported_id = NULL;
GError *error = NULL;
setup_test_xdir (&tmp_root);
src_root = g_build_filename (tmp_root, "src", NULL);
g_mkdir_with_parents (src_root, 0700);
theme_root = make_theme_dir_with_inherits (src_root, "ChildTheme", "MissingParent");
g_assert_false (zoitechat_gtk3_theme_service_import (theme_root, &imported_id, &error));
g_assert_null (imported_id);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL);
g_assert_nonnull (g_strstr_len (error->message, -1, "MissingParent"));
g_assert_nonnull (g_strstr_len (error->message, -1, "could not be resolved"));
g_error_free (error);
g_free (theme_root);
g_free (src_root);
teardown_test_xdir (tmp_root);
}
static void
test_import_collision_and_dark_detection (void)
{
char *tmp_root;
char *src_root;
char *theme_one;
char *id_one = NULL;
char *id_two = NULL;
ZoitechatGtk3Theme *found;
setup_test_xdir (&tmp_root);
src_root = g_build_filename (tmp_root, "src", NULL);
g_mkdir_with_parents (src_root, 0700);
theme_one = make_theme_dir (src_root, "Ocean", TRUE, FALSE);
g_assert_true (zoitechat_gtk3_theme_service_import (theme_one, &id_one, NULL));
g_assert_true (zoitechat_gtk3_theme_service_import (theme_one, &id_two, NULL));
g_assert_nonnull (id_one);
g_assert_nonnull (id_two);
g_assert_cmpstr (id_one, !=, id_two);
found = zoitechat_gtk3_theme_find_by_id (id_two);
g_assert_nonnull (found);
g_assert_true (found->has_dark_variant);
g_assert_true (g_str_has_suffix (found->path, "Ocean-1"));
zoitechat_gtk3_theme_free (found);
g_free (id_one);
g_free (id_two);
g_free (theme_one);
g_free (src_root);
teardown_test_xdir (tmp_root);
}
static void
test_discover_includes_user_and_system_data_dirs (void)
{
char *tmp_root;
char *home_dir;
char *user_data_dir;
char *system_data_dir;
char *system_data_dirs;
char *saved_home;
char *saved_xdg_data_home;
char *saved_xdg_data_dirs;
char *user_themes_dir;
char *system_themes_dir;
char *user_theme;
char *system_theme;
GPtrArray *themes;
guint i;
gboolean found_user = FALSE;
gboolean found_system = FALSE;
setup_test_xdir (&tmp_root);
home_dir = g_build_filename (tmp_root, "home", NULL);
user_data_dir = g_build_filename (tmp_root, "xdg-data-home", NULL);
system_data_dir = g_build_filename (tmp_root, "xdg-data-system", NULL);
system_data_dirs = g_strdup_printf ("%s:/usr/share", system_data_dir);
user_themes_dir = g_build_filename (user_data_dir, "themes", NULL);
system_themes_dir = g_build_filename (system_data_dir, "themes", NULL);
g_mkdir_with_parents (home_dir, 0700);
g_mkdir_with_parents (user_themes_dir, 0700);
g_mkdir_with_parents (system_themes_dir, 0700);
user_theme = make_theme_dir (user_themes_dir, "UserDataTheme", FALSE, FALSE);
system_theme = make_theme_dir (system_themes_dir, "SystemDataTheme", FALSE, FALSE);
saved_home = g_strdup (g_getenv ("HOME"));
saved_xdg_data_home = g_strdup (g_getenv ("XDG_DATA_HOME"));
saved_xdg_data_dirs = g_strdup (g_getenv ("XDG_DATA_DIRS"));
g_setenv ("HOME", home_dir, TRUE);
g_setenv ("XDG_DATA_HOME", user_data_dir, TRUE);
g_setenv ("XDG_DATA_DIRS", system_data_dirs, TRUE);
themes = zoitechat_gtk3_theme_service_discover ();
g_assert_nonnull (themes);
for (i = 0; i < themes->len; i++)
{
ZoitechatGtk3Theme *theme = g_ptr_array_index (themes, i);
if (g_strcmp0 (theme->path, user_theme) == 0)
{
found_user = TRUE;
g_assert_cmpint (theme->source, ==, ZOITECHAT_GTK3_THEME_SOURCE_USER);
}
if (g_strcmp0 (theme->path, system_theme) == 0)
{
found_system = TRUE;
g_assert_cmpint (theme->source, ==, ZOITECHAT_GTK3_THEME_SOURCE_SYSTEM);
}
}
g_assert_true (found_user);
g_assert_true (found_system);
g_ptr_array_unref (themes);
if (saved_home)
g_setenv ("HOME", saved_home, TRUE);
else
g_unsetenv ("HOME");
if (saved_xdg_data_home)
g_setenv ("XDG_DATA_HOME", saved_xdg_data_home, TRUE);
else
g_unsetenv ("XDG_DATA_HOME");
if (saved_xdg_data_dirs)
g_setenv ("XDG_DATA_DIRS", saved_xdg_data_dirs, TRUE);
else
g_unsetenv ("XDG_DATA_DIRS");
g_free (saved_xdg_data_dirs);
g_free (saved_xdg_data_home);
g_free (saved_home);
g_free (system_theme);
g_free (user_theme);
g_free (system_themes_dir);
g_free (user_themes_dir);
g_free (system_data_dirs);
g_free (system_data_dir);
g_free (user_data_dir);
g_free (home_dir);
teardown_test_xdir (tmp_root);
}
static void
test_archive_root_detection_prefers_index (void)
{
char *tmp_root;
char *archive_root;
char *theme_a;
char *theme_b_parent;
char *theme_b;
char *archive_path;
char *command;
char *imported_id = NULL;
ZoitechatGtk3Theme *found;
setup_test_xdir (&tmp_root);
archive_root = g_build_filename (tmp_root, "archive-root", NULL);
g_mkdir_with_parents (archive_root, 0700);
theme_a = make_theme_dir (archive_root, "Flat", FALSE, FALSE);
theme_b_parent = g_build_filename (archive_root, "nested", NULL);
g_mkdir_with_parents (theme_b_parent, 0700);
theme_b = make_theme_dir (theme_b_parent, "Indexed", FALSE, TRUE);
archive_path = g_build_filename (tmp_root, "themes.tar.xz", NULL);
command = g_strdup_printf ("tar -cJf %s -C %s .", archive_path, archive_root);
g_assert_true (g_spawn_command_line_sync (command, NULL, NULL, NULL, NULL));
g_free (command);
g_assert_true (zoitechat_gtk3_theme_service_import (archive_path, &imported_id, NULL));
found = zoitechat_gtk3_theme_find_by_id (imported_id);
g_assert_nonnull (found);
g_assert_true (g_str_has_suffix (found->path, "Indexed"));
zoitechat_gtk3_theme_free (found);
g_free (imported_id);
g_free (archive_path);
g_free (theme_b);
g_free (theme_b_parent);
g_free (theme_a);
g_free (archive_root);
teardown_test_xdir (tmp_root);
}
static void
test_zip_import_nested_root (void)
{
char *tmp_root;
char *zip_root;
char *nested;
char *theme;
char *archive_path;
char *command;
char *imported_id = NULL;
ZoitechatGtk3Theme *found;
setup_test_xdir (&tmp_root);
zip_root = g_build_filename (tmp_root, "zip-root", NULL);
nested = g_build_filename (zip_root, "bundle", "themes", NULL);
g_mkdir_with_parents (nested, 0700);
theme = make_theme_dir (nested, "Juno-ocean", TRUE, FALSE);
archive_path = g_build_filename (tmp_root, "themes.zip", NULL);
command = g_strdup_printf ("cd %s && zip -qr %s .", zip_root, archive_path);
g_assert_true (g_spawn_command_line_sync (command, NULL, NULL, NULL, NULL));
g_free (command);
g_assert_true (zoitechat_gtk3_theme_service_import (archive_path, &imported_id, NULL));
found = zoitechat_gtk3_theme_find_by_id (imported_id);
g_assert_nonnull (found);
g_assert_true (found->has_dark_variant);
g_assert_true (g_str_has_suffix (found->path, "Juno-ocean"));
zoitechat_gtk3_theme_free (found);
g_free (imported_id);
g_free (archive_path);
g_free (theme);
g_free (nested);
g_free (zip_root);
teardown_test_xdir (tmp_root);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/gtk3-theme-service/inheritance-single-parent", test_inheritance_chain_single_parent);
g_test_add_func ("/gtk3-theme-service/inheritance-multi-level", test_inheritance_chain_multi_level);
g_test_add_func ("/gtk3-theme-service/inheritance-missing-parent", test_inheritance_chain_missing_parent);
g_test_add_func ("/gtk3-theme-service/inheritance-parent-from-xdg-data-home", test_inheritance_chain_parent_from_xdg_data_home);
g_test_add_func ("/gtk3-theme-service/inheritance-parent-from-xdg-data-dirs", test_inheritance_chain_parent_from_xdg_data_dirs);
g_test_add_func ("/gtk3-theme-service/import-collision-dark", test_import_collision_and_dark_detection);
g_test_add_func ("/gtk3-theme-service/discover-user-and-system-data-dirs", test_discover_includes_user_and_system_data_dirs);
g_test_add_func ("/gtk3-theme-service/archive-root-detection", test_archive_root_detection_prefers_index);
g_test_add_func ("/gtk3-theme-service/zip-import-nested-root", test_zip_import_nested_root);
g_test_add_func ("/gtk3-theme-service/invalid-archive-extract-error", test_invalid_archive_reports_extract_error);
g_test_add_func ("/gtk3-theme-service/archive-without-theme-css-error", test_archive_without_theme_reports_css_error);
g_test_add_func ("/gtk3-theme-service/import-missing-index-theme", test_import_rejects_theme_missing_index_theme);
g_test_add_func ("/gtk3-theme-service/import-missing-desktop-entry", test_import_rejects_index_without_desktop_entry);
g_test_add_func ("/gtk3-theme-service/import-unresolved-inherits", test_import_rejects_unresolved_inherits);
return g_test_run ();
}

View File

@@ -1,176 +0,0 @@
#include <errno.h>
#include <gio/gio.h>
#include <glib/gstdio.h>
#include "cfgfiles.h"
#include "zoitechat.h"
#include "theme-service.h"
static zoitechat_theme_post_apply_callback zoitechat_theme_post_apply_cb;
static gboolean
zoitechat_theme_service_copy_file (const char *src, const char *dest, GError **error)
{
char *data = NULL;
gsize len = 0;
if (!g_file_get_contents (src, &data, &len, error))
return FALSE;
if (!g_file_set_contents (dest, data, len, error))
{
g_free (data);
return FALSE;
}
g_free (data);
return TRUE;
}
char *
zoitechat_theme_service_get_themes_dir (void)
{
return g_build_filename (get_xdir (), "themes", NULL);
}
static gboolean
zoitechat_theme_service_validate (const char *theme_name,
char **colors_src,
char **events_src,
GError **error)
{
char *themes_dir;
char *theme_dir;
if (!theme_name || !*theme_name)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
_("No theme name specified."));
return FALSE;
}
themes_dir = zoitechat_theme_service_get_themes_dir ();
theme_dir = g_build_filename (themes_dir, theme_name, NULL);
g_free (themes_dir);
*colors_src = g_build_filename (theme_dir, "colors.conf", NULL);
*events_src = g_build_filename (theme_dir, "pevents.conf", NULL);
if (!g_file_test (*colors_src, G_FILE_TEST_IS_REGULAR))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
_("This theme is missing a colors.conf file."));
g_free (*events_src);
g_free (*colors_src);
*events_src = NULL;
*colors_src = NULL;
g_free (theme_dir);
return FALSE;
}
g_free (theme_dir);
return TRUE;
}
gboolean
zoitechat_theme_service_apply (const char *theme_name, GError **error)
{
char *colors_src = NULL;
char *colors_dest = NULL;
char *events_src = NULL;
char *events_dest = NULL;
gboolean ok = FALSE;
if (!zoitechat_theme_service_validate (theme_name, &colors_src, &events_src, error))
return FALSE;
colors_dest = g_build_filename (get_xdir (), "colors.conf", NULL);
events_dest = g_build_filename (get_xdir (), "pevents.conf", NULL);
if (!zoitechat_theme_service_copy_file (colors_src, colors_dest, error))
goto cleanup;
if (g_file_test (events_src, G_FILE_TEST_IS_REGULAR))
{
if (!zoitechat_theme_service_copy_file (events_src, events_dest, error))
goto cleanup;
}
else if (g_file_test (events_dest, G_FILE_TEST_EXISTS))
{
if (g_unlink (events_dest) != 0)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Failed to remove existing event settings."));
goto cleanup;
}
}
zoitechat_theme_service_run_post_apply_callback ();
ok = TRUE;
cleanup:
g_free (events_dest);
g_free (events_src);
g_free (colors_dest);
g_free (colors_src);
return ok;
}
GStrv
zoitechat_theme_service_discover_themes (void)
{
char *themes_dir;
GDir *dir;
const char *name;
GPtrArray *themes;
GStrv result;
themes_dir = zoitechat_theme_service_get_themes_dir ();
if (!g_file_test (themes_dir, G_FILE_TEST_IS_DIR))
g_mkdir_with_parents (themes_dir, 0700);
themes = g_ptr_array_new_with_free_func (g_free);
dir = g_dir_open (themes_dir, 0, NULL);
if (dir)
{
while ((name = g_dir_read_name (dir)))
{
char *theme_dir = g_build_filename (themes_dir, name, NULL);
char *colors_path;
if (!g_file_test (theme_dir, G_FILE_TEST_IS_DIR))
{
g_free (theme_dir);
continue;
}
colors_path = g_build_filename (theme_dir, "colors.conf", NULL);
if (g_file_test (colors_path, G_FILE_TEST_IS_REGULAR))
g_ptr_array_add (themes, g_strdup (name));
g_free (colors_path);
g_free (theme_dir);
}
g_dir_close (dir);
}
g_ptr_array_sort (themes, (GCompareFunc) g_strcmp0);
g_ptr_array_add (themes, NULL);
result = (GStrv) g_ptr_array_free (themes, FALSE);
g_free (themes_dir);
return result;
}
void
zoitechat_theme_service_set_post_apply_callback (zoitechat_theme_post_apply_callback callback)
{
zoitechat_theme_post_apply_cb = callback;
}
void
zoitechat_theme_service_run_post_apply_callback (void)
{
if (zoitechat_theme_post_apply_cb)
zoitechat_theme_post_apply_cb ();
}

View File

@@ -1,12 +0,0 @@
#ifndef ZOITECHAT_THEME_SERVICE_H
#define ZOITECHAT_THEME_SERVICE_H
#include <glib.h>
char *zoitechat_theme_service_get_themes_dir (void);
GStrv zoitechat_theme_service_discover_themes (void);
gboolean zoitechat_theme_service_apply (const char *theme_name, GError **error);
void zoitechat_theme_service_set_post_apply_callback (void (*callback) (void));
void zoitechat_theme_service_run_post_apply_callback (void);
#endif

View File

@@ -112,9 +112,46 @@ struct zoitechatprefs prefs;
gboolean
zoitechat_theme_path_from_arg (const char *arg, char **path_out)
{
(void) arg;
(void) path_out;
return FALSE;
char *path = NULL;
gboolean valid_ext = FALSE;
if (!arg)
return FALSE;
if (g_str_has_prefix (arg, "file://"))
path = g_filename_from_uri (arg, NULL, NULL);
else
path = g_strdup (arg);
if (!path)
return FALSE;
if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
{
char *path_lower = g_ascii_strdown (path, -1);
valid_ext = g_str_has_suffix (path_lower, ".zip")
|| g_str_has_suffix (path_lower, ".tar")
|| g_str_has_suffix (path_lower, ".tar.gz")
|| g_str_has_suffix (path_lower, ".tgz")
|| g_str_has_suffix (path_lower, ".tar.xz")
|| g_str_has_suffix (path_lower, ".txz");
g_free (path_lower);
}
if (!valid_ext)
{
g_free (path);
return FALSE;
}
if (path_out)
*path_out = path;
else
g_free (path);
return TRUE;
}
#ifdef WIN32
@@ -224,21 +261,621 @@ zoitechat_remote_win32 (void)
}
#endif
static zoitechat_theme_post_apply_callback zoitechat_theme_post_apply_cb;
void
zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback)
static gboolean
zoitechat_is_safe_archive_entry (const char *entry)
{
zoitechat_theme_post_apply_cb = callback;
char **parts;
gboolean safe = TRUE;
guint i;
if (!entry || !*entry)
return FALSE;
if (g_path_is_absolute (entry) || entry[0] == '/' || entry[0] == '\\')
return FALSE;
if (g_ascii_isalpha (entry[0]) && entry[1] == ':')
return FALSE;
parts = g_strsplit_set (entry, "/\\", -1);
for (i = 0; parts[i] != NULL; i++)
{
if (strcmp (parts[i], "..") == 0)
{
safe = FALSE;
break;
}
}
g_strfreev (parts);
return safe;
}
void
zoitechat_run_theme_post_apply_callback (void)
static gboolean
zoitechat_remove_tree (const char *path, GError **error)
{
if (zoitechat_theme_post_apply_cb)
zoitechat_theme_post_apply_cb ();
GDir *dir;
const char *name;
if (!g_file_test (path, G_FILE_TEST_EXISTS))
return TRUE;
if (!g_file_test (path, G_FILE_TEST_IS_DIR))
return g_remove (path) == 0;
dir = g_dir_open (path, 0, error);
if (!dir)
return FALSE;
while ((name = g_dir_read_name (dir)))
{
char *child = g_build_filename (path, name, NULL);
if (!zoitechat_remove_tree (child, error))
{
g_free (child);
g_dir_close (dir);
return FALSE;
}
g_free (child);
}
g_dir_close (dir);
if (g_rmdir (path) != 0)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Failed to remove temporary directory."));
return FALSE;
}
return TRUE;
}
static gboolean
zoitechat_copy_directory_recursive (const char *src_dir, const char *dest_dir, GError **error)
{
GDir *dir;
const char *name;
if (g_mkdir_with_parents (dest_dir, 0700) != 0)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Failed to create destination theme directory."));
return FALSE;
}
dir = g_dir_open (src_dir, 0, error);
if (!dir)
return FALSE;
while ((name = g_dir_read_name (dir)))
{
char *src_path = g_build_filename (src_dir, name, NULL);
char *dest_path = g_build_filename (dest_dir, name, NULL);
if (g_file_test (src_path, G_FILE_TEST_IS_DIR))
{
if (!zoitechat_copy_directory_recursive (src_path, dest_path, error))
{
g_free (dest_path);
g_free (src_path);
g_dir_close (dir);
return FALSE;
}
}
else if (g_file_test (src_path, G_FILE_TEST_IS_REGULAR))
{
GFile *src_file = g_file_new_for_path (src_path);
GFile *dest_file = g_file_new_for_path (dest_path);
gboolean copied = g_file_copy (src_file, dest_file,
G_FILE_COPY_OVERWRITE,
NULL, NULL, NULL, error);
g_object_unref (dest_file);
g_object_unref (src_file);
if (!copied)
{
g_free (dest_path);
g_free (src_path);
g_dir_close (dir);
return FALSE;
}
}
g_free (dest_path);
g_free (src_path);
}
g_dir_close (dir);
return TRUE;
}
static gboolean
zoitechat_find_gtk3_theme_root (const char *search_dir,
char **theme_root_out,
gboolean *has_dark_css_out,
gboolean *missing_gtk_css_out,
GError **error)
{
GDir *dir;
const char *name;
dir = g_dir_open (search_dir, 0, error);
if (!dir)
return FALSE;
while ((name = g_dir_read_name (dir)))
{
char *child = g_build_filename (search_dir, name, NULL);
if (g_file_test (child, G_FILE_TEST_IS_DIR))
{
if (g_str_has_prefix (name, "gtk-3."))
{
char *gtk_css = g_build_filename (child, "gtk.css", NULL);
if (g_file_test (gtk_css, G_FILE_TEST_IS_REGULAR))
{
char *dark_css = g_build_filename (child, "gtk-dark.css", NULL);
if (theme_root_out)
*theme_root_out = g_strdup (search_dir);
if (has_dark_css_out)
*has_dark_css_out = g_file_test (dark_css, G_FILE_TEST_IS_REGULAR);
g_free (dark_css);
g_free (gtk_css);
g_free (child);
g_dir_close (dir);
return TRUE;
}
g_free (gtk_css);
if (missing_gtk_css_out)
*missing_gtk_css_out = TRUE;
}
else if (zoitechat_find_gtk3_theme_root (child,
theme_root_out,
has_dark_css_out,
missing_gtk_css_out,
error))
{
g_free (child);
g_dir_close (dir);
return TRUE;
}
if (error && *error)
{
g_free (child);
g_dir_close (dir);
return FALSE;
}
}
g_free (child);
}
g_dir_close (dir);
return FALSE;
}
typedef enum
{
ZOITECHAT_GTK3_ARCHIVE_UNKNOWN = 0,
ZOITECHAT_GTK3_ARCHIVE_ZIP,
ZOITECHAT_GTK3_ARCHIVE_TAR
} ZoiteChatGtk3ArchiveType;
static ZoiteChatGtk3ArchiveType
zoitechat_detect_gtk3_archive_type (const char *archive_path)
{
char *archive_path_lower;
ZoiteChatGtk3ArchiveType type = ZOITECHAT_GTK3_ARCHIVE_UNKNOWN;
if (!archive_path)
return ZOITECHAT_GTK3_ARCHIVE_UNKNOWN;
archive_path_lower = g_ascii_strdown (archive_path, -1);
if (g_str_has_suffix (archive_path_lower, ".zip"))
type = ZOITECHAT_GTK3_ARCHIVE_ZIP;
else if (g_str_has_suffix (archive_path_lower, ".tar")
|| g_str_has_suffix (archive_path_lower, ".tar.gz")
|| g_str_has_suffix (archive_path_lower, ".tgz")
|| g_str_has_suffix (archive_path_lower, ".tar.xz")
|| g_str_has_suffix (archive_path_lower, ".txz"))
type = ZOITECHAT_GTK3_ARCHIVE_TAR;
g_free (archive_path_lower);
return type;
}
#ifndef WIN32
static gboolean
zoitechat_validate_zip_entries_unix (const char *archive_path, char **archive_root_out, GError **error)
{
char *stdout_buf = NULL;
char *stderr_buf = NULL;
char *argv[] = {"unzip", "-Z1", (char *)archive_path, NULL};
char *archive_root = NULL;
char **lines;
gboolean ok;
int status = 0;
guint i;
ok = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, &stdout_buf, &stderr_buf, &status, error);
if (!ok)
goto cleanup;
if (!g_spawn_check_exit_status (status, error))
{
ok = FALSE;
goto cleanup;
}
lines = g_strsplit (stdout_buf ? stdout_buf : "", "\n", -1);
for (i = 0; lines[i] != NULL; i++)
{
const char *entry = lines[i];
char **parts;
if (!entry[0])
continue;
if (!zoitechat_is_safe_archive_entry (entry))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
_("Archive contains unsafe path: %s"), entry);
ok = FALSE;
break;
}
parts = g_strsplit (entry, "/", 2);
if (parts[0] && *parts[0])
{
if (!archive_root)
archive_root = g_strdup (parts[0]);
else if (strcmp (archive_root, parts[0]) != 0)
g_clear_pointer (&archive_root, g_free);
}
g_strfreev (parts);
}
g_strfreev (lines);
if (ok && archive_root_out)
*archive_root_out = g_strdup (archive_root);
cleanup:
g_free (archive_root);
g_free (stderr_buf);
g_free (stdout_buf);
return ok;
}
#endif
static gboolean
zoitechat_validate_tar_entries_unix (const char *archive_path, char **archive_root_out, GError **error)
{
char *stdout_buf = NULL;
char *stderr_buf = NULL;
char *argv[] = {"tar", "-tf", (char *)archive_path, NULL};
char *archive_root = NULL;
char **lines;
gboolean ok;
int status = 0;
guint i;
ok = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, &stdout_buf, &stderr_buf, &status, error);
if (!ok)
goto cleanup;
if (!g_spawn_check_exit_status (status, error))
{
ok = FALSE;
goto cleanup;
}
lines = g_strsplit (stdout_buf ? stdout_buf : "", "\n", -1);
for (i = 0; lines[i] != NULL; i++)
{
const char *entry = lines[i];
char **parts;
if (!entry[0])
continue;
if (!zoitechat_is_safe_archive_entry (entry))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
_("Archive contains unsafe path: %s"), entry);
ok = FALSE;
break;
}
parts = g_strsplit_set (entry, "/\\", 2);
if (parts[0] && *parts[0])
{
if (!archive_root)
archive_root = g_strdup (parts[0]);
else if (strcmp (archive_root, parts[0]) != 0)
g_clear_pointer (&archive_root, g_free);
}
g_strfreev (parts);
}
g_strfreev (lines);
if (ok && archive_root_out)
*archive_root_out = g_strdup (archive_root);
cleanup:
g_free (archive_root);
g_free (stderr_buf);
g_free (stdout_buf);
return ok;
}
gboolean
zoitechat_import_gtk3_theme_archive (const char *archive_path,
char **theme_name_out,
GError **error)
{
ZoiteChatGtk3ArchiveType archive_type;
char *temp_dir = NULL;
char *archive_root = NULL;
char *theme_root = NULL;
char *theme_name = NULL;
char *store_dir = NULL;
char *dest_dir = NULL;
int status = 0;
gboolean ok = FALSE;
gboolean has_dark_css = FALSE;
gboolean missing_gtk_css = FALSE;
if (theme_name_out)
*theme_name_out = NULL;
if (!archive_path)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
_("No GTK3 theme archive specified."));
return FALSE;
}
archive_type = zoitechat_detect_gtk3_archive_type (archive_path);
if (archive_type == ZOITECHAT_GTK3_ARCHIVE_UNKNOWN)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Unsupported archive format. Use .zip, .tar, .tar.gz, .tgz, .tar.xz, or .txz."));
return FALSE;
}
{
char *basename = g_path_get_basename (archive_path);
char *dot;
if (basename && *basename)
{
dot = strrchr (basename, '.');
if (dot)
*dot = '\0';
if (g_str_has_suffix (basename, ".tar"))
basename[strlen (basename) - 4] = '\0';
if (*basename)
archive_root = g_strdup (basename);
}
g_free (basename);
}
temp_dir = g_dir_make_tmp ("zoitechat-gtk3-theme-XXXXXX", error);
if (!temp_dir)
return FALSE;
#ifdef WIN32
if (archive_type == ZOITECHAT_GTK3_ARCHIVE_ZIP)
{
char *powershell = NULL;
char *command = NULL;
GString *escaped_path = g_string_new ("'");
GString *escaped_dir = g_string_new ("'");
const char *cursor;
powershell = g_find_program_in_path ("powershell.exe");
if (!powershell)
powershell = g_find_program_in_path ("powershell");
if (!powershell)
{
g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT,
_("No archive extractor was found."));
g_string_free (escaped_path, TRUE);
g_string_free (escaped_dir, TRUE);
goto cleanup;
}
for (cursor = archive_path; *cursor != '\0'; cursor++)
{
if (*cursor == '\'')
g_string_append (escaped_path, "''");
else
g_string_append_c (escaped_path, *cursor);
}
g_string_append_c (escaped_path, '\'');
for (cursor = temp_dir; *cursor != '\0'; cursor++)
{
if (*cursor == '\'')
g_string_append (escaped_dir, "''");
else
g_string_append_c (escaped_dir, *cursor);
}
g_string_append_c (escaped_dir, '\'');
command = g_strdup_printf (
"Add-Type -AssemblyName System.IO.Compression.FileSystem; "
"$ErrorActionPreference='Stop'; "
"$archive=[System.IO.Compression.ZipFile]::OpenRead(%s); "
"try { "
"foreach ($entry in $archive.Entries) { "
"$name=$entry.FullName; "
"if ([string]::IsNullOrEmpty($name)) { continue }; "
"if ([System.IO.Path]::IsPathRooted($name) -or $name.StartsWith('/') -or $name.StartsWith('\\') -or $name.Contains('..\\') -or $name.Contains('../')) { throw 'Archive contains unsafe path: ' + $name } "
"}; "
"[System.IO.Compression.ZipFile]::ExtractToDirectory(%s,%s,$true); "
"} finally { $archive.Dispose(); }",
escaped_path->str,
escaped_path->str,
escaped_dir->str);
g_string_free (escaped_path, TRUE);
g_string_free (escaped_dir, TRUE);
{
char *ps_argv[] = {powershell, "-NoProfile", "-NonInteractive", "-Command", command, NULL};
ok = g_spawn_sync (NULL, ps_argv, NULL, 0, NULL, NULL,
NULL, NULL, &status, error);
}
if (ok)
ok = g_spawn_check_exit_status (status, error);
g_free (command);
g_free (powershell);
if (!ok)
goto cleanup;
}
else
{
char *argv[] = {"tar", "-xf", (char *)archive_path, "-C", temp_dir, NULL};
char *extracted_root = NULL;
if (!zoitechat_validate_tar_entries_unix (archive_path, &extracted_root, error))
goto cleanup;
if (extracted_root)
{
g_free (archive_root);
archive_root = extracted_root;
}
ok = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
NULL, NULL, &status, error);
if (!ok)
goto cleanup;
if (!g_spawn_check_exit_status (status, error))
goto cleanup;
}
#else
if (archive_type == ZOITECHAT_GTK3_ARCHIVE_ZIP)
{
char *argv[] = {"unzip", "-o", (char *)archive_path, "-d", temp_dir, NULL};
char *extracted_root = NULL;
if (!zoitechat_validate_zip_entries_unix (archive_path, &extracted_root, error))
goto cleanup;
if (extracted_root)
{
g_free (archive_root);
archive_root = extracted_root;
}
ok = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
NULL, NULL, &status, error);
if (!ok)
goto cleanup;
if (!g_spawn_check_exit_status (status, error))
goto cleanup;
}
else
{
char *argv[] = {"tar", "-xf", (char *)archive_path, "-C", temp_dir, NULL};
char *extracted_root = NULL;
if (!zoitechat_validate_tar_entries_unix (archive_path, &extracted_root, error))
goto cleanup;
if (extracted_root)
{
g_free (archive_root);
archive_root = extracted_root;
}
ok = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
NULL, NULL, &status, error);
if (!ok)
goto cleanup;
if (!g_spawn_check_exit_status (status, error))
goto cleanup;
}
#endif
if (!zoitechat_find_gtk3_theme_root (temp_dir, &theme_root, &has_dark_css, &missing_gtk_css, error))
{
if (error && *error)
goto cleanup;
if (missing_gtk_css)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
_("Archive contains a gtk-3.x directory, but gtk.css is missing."));
else
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
_("Archive is not a GTK3 theme. Expected layout: <theme-name>/gtk-3.x/gtk.css."));
goto cleanup;
}
theme_name = g_path_get_basename (theme_root);
if (!theme_name || !*theme_name)
{
g_clear_pointer (&theme_name, g_free);
if (archive_root)
theme_name = g_strdup (archive_root);
}
if (!theme_name || !*theme_name)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
_("Unable to determine GTK3 theme directory name from archive."));
goto cleanup;
}
store_dir = g_build_filename (get_xdir (), "gtk3-themes", NULL);
dest_dir = g_build_filename (store_dir, theme_name, NULL);
if (g_file_test (dest_dir, G_FILE_TEST_EXISTS) && !zoitechat_remove_tree (dest_dir, error))
goto cleanup;
if (!zoitechat_copy_directory_recursive (theme_root, dest_dir, error))
goto cleanup;
if (has_dark_css)
fe_message (_("Imported GTK3 theme includes gtk-dark.css."), FE_MSG_INFO);
if (theme_name_out)
*theme_name_out = g_strdup (theme_name);
ok = TRUE;
cleanup:
if (temp_dir)
{
GError *cleanup_error = NULL;
if (!zoitechat_remove_tree (temp_dir, &cleanup_error))
g_clear_error (&cleanup_error);
}
g_free (dest_dir);
g_free (store_dir);
g_free (theme_name);
g_free (theme_root);
g_free (archive_root);
g_free (temp_dir);
return ok;
}
/*
* Update the priority queue of the "interesting sessions"
* (sess_list_by_lastact).
@@ -540,6 +1177,7 @@ irc_init (session *sess)
{
static int done_init = FALSE;
char *buf;
char *theme_path;
if (done_init)
return;
@@ -562,10 +1200,43 @@ irc_init (session *sess)
if (arg_url != NULL)
{
buf = g_strdup_printf ("server %s", arg_url);
handle_command (sess, buf, FALSE);
g_free (buf);
g_free (arg_url);
theme_path = NULL;
if (zoitechat_theme_path_from_arg (arg_url, &theme_path))
{
GError *error = NULL;
char *theme_name = NULL;
if (zoitechat_import_gtk3_theme_archive (theme_path, &theme_name, &error))
{
if (theme_name)
{
char *message = g_strdup_printf (_("GTK3 theme \"%s\" imported. Use Theme settings to apply it."), theme_name);
fe_message (message, FE_MSG_INFO);
g_free (message);
}
else
{
fe_message (_("GTK3 theme imported. Use Theme settings to apply it."), FE_MSG_INFO);
}
}
else
{
fe_message (error ? error->message : _("Failed to import GTK3 theme archive."),
FE_MSG_ERROR);
g_clear_error (&error);
}
g_free (theme_name);
}
else
{
buf = g_strdup_printf ("server %s", arg_url);
handle_command (sess, buf, FALSE);
g_free (buf);
}
g_free (theme_path);
g_free (arg_url); /* from GOption */
}
if (arg_urls != NULL)
@@ -573,9 +1244,42 @@ irc_init (session *sess)
guint i;
for (i = 0; i < g_strv_length (arg_urls); i++)
{
buf = g_strdup_printf ("%s %s", i == 0 ? "server" : "newserver", arg_urls[i]);
handle_command (sess, buf, FALSE);
g_free (buf);
theme_path = NULL;
if (zoitechat_theme_path_from_arg (arg_urls[i], &theme_path))
{
GError *error = NULL;
char *theme_name = NULL;
if (zoitechat_import_gtk3_theme_archive (theme_path, &theme_name, &error))
{
if (theme_name)
{
char *message = g_strdup_printf (_("GTK3 theme \"%s\" imported. Use Theme settings to apply it."), theme_name);
fe_message (message, FE_MSG_INFO);
g_free (message);
}
else
{
fe_message (_("GTK3 theme imported. Use Theme settings to apply it."), FE_MSG_INFO);
}
}
else
{
fe_message (error ? error->message : _("Failed to import GTK3 theme archive."),
FE_MSG_ERROR);
g_clear_error (&error);
}
g_free (theme_name);
}
else
{
buf = g_strdup_printf ("%s %s", i==0? "server" : "newserver", arg_urls[i]);
handle_command (sess, buf, FALSE);
g_free (buf);
}
g_free (theme_path);
}
g_strfreev (arg_urls);
}
@@ -1149,6 +1853,10 @@ main (int argc, char *argv[])
{
int i;
int ret;
#ifdef WIN32
char **win32_argv = NULL;
int win32_argc;
#endif
#ifdef WIN32
HRESULT coinit_result;
@@ -1156,6 +1864,23 @@ main (int argc, char *argv[])
srand ((unsigned int) time (NULL)); /* CL: do this only once! */
#ifdef WIN32
win32_argv = g_win32_get_command_line ();
if (win32_argv != NULL)
{
win32_argc = g_strv_length (win32_argv);
if (win32_argc == 0 || win32_argv[0] == NULL || win32_argv[0][0] == '\0')
{
g_strfreev (win32_argv);
win32_argv = g_new0 (char *, 2);
win32_argv[0] = g_strdup ("zoitechat");
win32_argc = 1;
}
argv = win32_argv;
argc = win32_argc;
}
#endif
/* We must check for the config dir parameter, otherwise load_config() will behave incorrectly.
* load_config() must come before fe_args() because fe_args() calls gtk_init() which needs to
* know the language which is set in the config. The code below is copy-pasted from fe_args()
@@ -1206,11 +1931,19 @@ main (int argc, char *argv[])
ret = fe_args (argc, argv);
if (ret != -1)
{
#ifdef WIN32
g_strfreev (win32_argv);
#endif
return ret;
}
#ifdef WIN32
if (zoitechat_remote_win32 ())
{
g_strfreev (win32_argv);
return 0;
}
#endif
#ifdef USE_DBUS
@@ -1263,5 +1996,9 @@ main (int argc, char *argv[])
WSACleanup ();
#endif
#ifdef WIN32
g_strfreev (win32_argv);
#endif
return 0;
}

View File

@@ -30,9 +30,10 @@
#define ZOITECHAT_H
gboolean zoitechat_theme_path_from_arg (const char *arg, char **path_out);
typedef void (*zoitechat_theme_post_apply_callback) (void);
void zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback);
void zoitechat_run_theme_post_apply_callback (void);
/* Imports a GTK3 theme archive into ZoiteChat's own gtk3-themes store. */
gboolean zoitechat_import_gtk3_theme_archive (const char *archive_path,
char **theme_name_out,
GError **error);
#ifdef USE_OPENSSL
#ifdef __APPLE__
@@ -141,7 +142,6 @@ struct zoitechatprefs
unsigned int hex_gui_tab_dots;
unsigned int hex_gui_tab_icons;
unsigned int hex_gui_dark_mode;
unsigned int hex_gui_gtk3_variant;
unsigned int hex_gui_tab_scrollchans;
unsigned int hex_gui_tab_server;
unsigned int hex_gui_tab_sort;
@@ -297,6 +297,7 @@ struct zoitechatprefs
char hex_dcc_completed_dir[PATHLEN + 1];
char hex_dcc_dir[PATHLEN + 1];
char hex_dcc_ip[DOMAINLEN + 1];
char hex_gui_gtk3_theme_name[128];
char hex_gui_ulist_doubleclick[256];
char hex_input_command_char[4];
char hex_irc_extra_hilight[300];
@@ -322,7 +323,6 @@ struct zoitechatprefs
char hex_text_font[4 * FONTNAMELEN + 1];
char hex_text_font_main[FONTNAMELEN + 1];
char hex_text_font_alternative[3 * FONTNAMELEN + 1];
char hex_gui_gtk3_theme[256];
char hex_text_spell_langs[64];
/* these are the private variables */

View File

@@ -27,7 +27,6 @@
#endif
#include "fe-gtk.h"
#include "theme/theme-manager.h"
#include "../common/zoitechat.h"
#include "../common/fe.h"
@@ -563,8 +562,10 @@ banlist_clear (GtkWidget * wid, banlist_info *banl)
dialog = gtk_message_dialog_new (NULL, 0,
GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
_("Are you sure you want to remove all listed items in %s?"), banl->sess->channel);
theme_manager_attach_window (dialog);
_("Are you sure you want to remove all listed items in %s?"), banl->sess->channel);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
g_signal_connect (G_OBJECT (dialog), "response",
G_CALLBACK (banlist_clear_cb), banl);

View File

@@ -36,21 +36,6 @@ static int ignore_toggle = FALSE;
static int tab_left_is_moving = 0;
static int tab_right_is_moving = 0;
typedef struct
{
GtkAdjustment *adj;
gdouble current_value;
gdouble target_value;
gint direction;
gdouble step_size;
guint source_id;
int *moving_flag;
gboolean is_left;
} tab_scroll_animation;
static tab_scroll_animation *tab_left_animation;
static tab_scroll_animation *tab_right_animation;
/* userdata for gobjects used here:
*
* tab (togglebuttons inside boxes):
@@ -164,92 +149,6 @@ tab_search_offset (GtkWidget *inner, gint start_offset,
return 0;
}
static gboolean
tab_scroll_animation_tick (gpointer userdata)
{
tab_scroll_animation *animation = userdata;
gboolean reached_target;
animation->current_value += animation->step_size * animation->direction;
if (animation->direction < 0)
reached_target = animation->current_value <= animation->target_value;
else
reached_target = animation->current_value >= animation->target_value;
if (reached_target)
animation->current_value = animation->target_value;
gtk_adjustment_set_value (animation->adj, animation->current_value);
if (!reached_target)
return G_SOURCE_CONTINUE;
*animation->moving_flag = 0;
animation->source_id = 0;
if (animation->is_left)
tab_left_animation = NULL;
else
tab_right_animation = NULL;
g_object_unref (animation->adj);
g_free (animation);
return G_SOURCE_REMOVE;
}
static void
tab_scroll_animation_cancel (tab_scroll_animation **animation)
{
if (*animation == NULL)
return;
if ((*animation)->source_id != 0)
g_source_remove ((*animation)->source_id);
*(*animation)->moving_flag = 0;
g_object_unref ((*animation)->adj);
g_free (*animation);
*animation = NULL;
}
static void
tab_scroll_animation_start (tab_scroll_animation **slot, GtkAdjustment *adj,
gdouble current_value, gdouble target_value,
gint direction, int *moving_flag,
gboolean is_left)
{
tab_scroll_animation *animation;
gdouble distance;
gdouble frames;
distance = target_value - current_value;
if (distance < 0.0)
distance = -distance;
if (distance <= 0.0)
{
gtk_adjustment_set_value (adj, target_value);
*moving_flag = 0;
return;
}
animation = g_new0 (tab_scroll_animation, 1);
animation->adj = g_object_ref (adj);
animation->current_value = current_value;
animation->target_value = target_value;
animation->direction = direction;
frames = 12.0;
animation->step_size = distance / MAX (1.0, frames);
animation->moving_flag = moving_flag;
animation->is_left = is_left;
*moving_flag = 1;
animation->source_id = g_timeout_add (16, tab_scroll_animation_tick, animation);
*slot = animation;
}
static void
tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
{
@@ -258,6 +157,7 @@ tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
gfloat new_value;
GtkWidget *inner;
GdkWindow *parent_win;
gdouble i;
inner = ((tabview *)cv)->inner;
parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
@@ -277,15 +177,25 @@ tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
if (new_value + viewport_size > gtk_adjustment_get_upper (adj))
new_value = gtk_adjustment_get_upper (adj) - viewport_size;
if (tab_left_is_moving)
if (!tab_left_is_moving)
{
tab_scroll_animation_cancel (&tab_left_animation);
return;
}
tab_left_is_moving = 1;
tab_scroll_animation_start (&tab_left_animation, adj,
gtk_adjustment_get_value (adj), new_value,
-1, &tab_left_is_moving, TRUE);
for (i = gtk_adjustment_get_value (adj); ((i > new_value) && (tab_left_is_moving)); i -= 0.1)
{
gtk_adjustment_set_value (adj, i);
while (g_main_context_pending (NULL))
g_main_context_iteration (NULL, TRUE);
}
gtk_adjustment_set_value (adj, new_value);
tab_left_is_moving = 0;
}
else
{
tab_left_is_moving = 0;
}
}
static void
@@ -296,6 +206,7 @@ tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv)
gfloat new_value;
GtkWidget *inner;
GdkWindow *parent_win;
gdouble i;
inner = ((tabview *)cv)->inner;
parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
@@ -315,15 +226,25 @@ tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv)
if (new_value == 0 || new_value + viewport_size > gtk_adjustment_get_upper (adj))
new_value = gtk_adjustment_get_upper (adj) - viewport_size;
if (tab_right_is_moving)
if (!tab_right_is_moving)
{
tab_scroll_animation_cancel (&tab_right_animation);
return;
}
tab_right_is_moving = 1;
tab_scroll_animation_start (&tab_right_animation, adj,
gtk_adjustment_get_value (adj), new_value,
1, &tab_right_is_moving, FALSE);
for (i = gtk_adjustment_get_value (adj); ((i < new_value) && (tab_right_is_moving)); i += 0.1)
{
gtk_adjustment_set_value (adj, i);
while (g_main_context_pending (NULL))
g_main_context_iteration (NULL, TRUE);
}
gtk_adjustment_set_value (adj, new_value);
tab_right_is_moving = 0;
}
else
{
tab_right_is_moving = 0;
}
}
static gboolean

View File

@@ -27,8 +27,6 @@ typedef struct
#include <gdk/gdk.h>
#include "theme/theme-access.h"
static void /* row-activated, when a row is double clicked */
cv_tree_activated_cb (GtkTreeView *view, GtkTreePath *path,
GtkTreeViewColumn *column, gpointer data)
@@ -138,14 +136,14 @@ cv_tree_init (chanview *cv)
gtk_widget_set_hexpand (view, TRUE);
gtk_widget_set_vexpand (view, TRUE);
gtk_widget_set_name (view, "zoitechat-tree");
if (
cv->font_desc
)
{
ThemeWidgetStyleValues style_values;
theme_get_widget_style_values_for_widget (view, &style_values);
gtkutil_apply_palette (view, &style_values.background, &style_values.foreground,
gtkutil_apply_palette (view, &colors[COL_BG], &colors[COL_FG],
cv->font_desc);
}
/*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[THEME_LEGACY_TEXT_BACKGROUND]);*/
/*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[COL_BG]);*/
gtk_widget_set_can_focus (view, FALSE);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);

View File

@@ -28,8 +28,7 @@
#include "maingui.h"
#include "gtkutil.h"
#include "chanview.h"
#include "theme/theme-manager.h"
#include "theme/theme-access.h"
#include "palette.h"
/* treeStore columns */
#define COL_NAME 0 /* (char *) */
@@ -76,7 +75,6 @@ struct _chanview
unsigned int sorted:1;
unsigned int vertical:1;
unsigned int use_icons:1;
guint theme_listener_id;
};
struct _chan
@@ -93,7 +91,6 @@ struct _chan
static chan *cv_find_chan_by_number (chanview *cv, int num);
static int cv_find_number_of_chan (chanview *cv, chan *find_ch);
static void cv_find_neighbors_for_removal (chanview *cv, chan *find_ch, chan **left_ch, chan **first_ch);
/* ======= TABS ======= */
@@ -130,17 +127,15 @@ chanview_apply_theme (chanview *cv)
if (input_style)
font = input_style->font_desc;
theme_manager_apply_channel_tree_style (w,
theme_manager_get_channel_tree_palette_behavior (font));
}
static void
chanview_theme_changed (const ThemeChangedEvent *event, gpointer userdata)
{
chanview *cv = userdata;
(void) event;
chanview_apply_theme (cv);
/*
* setup_apply_to_sess() and palette_apply_dark_mode() treat all dark-mode
* preference modes as palette-driven: dark uses curated dark colors, while
* light/auto-light use the user's saved palette.
*
* Keep chanview aligned with that resolved behavior so AUTO doesn't
* accidentally revert to theme defaults and clear custom colors.
*/
gtkutil_apply_palette (w, &colors[COL_BG], &colors[COL_FG], font);
}
static char *
@@ -283,12 +278,6 @@ chanview_destroy_store (chanview *cv) /* free every (chan *) in the store */
static void
chanview_destroy (chanview *cv)
{
if (cv->theme_listener_id)
{
theme_listener_unregister (cv->theme_listener_id);
cv->theme_listener_id = 0;
}
if (cv->func_cleanup)
cv->func_cleanup (cv);
@@ -323,7 +312,6 @@ chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons,
cv->use_icons = use_icons;
gtk_widget_show (cv->box);
chanview_set_impl (cv, type);
cv->theme_listener_id = theme_listener_register ("chanview", chanview_theme_changed, cv);
g_signal_connect (G_OBJECT (cv->box), "destroy",
G_CALLBACK (chanview_box_destroy_cb), cv);
@@ -620,45 +608,6 @@ cv_find_chan_by_number (chanview *cv, int num)
return NULL;
}
static void
cv_find_neighbors_for_removal (chanview *cv, chan *find_ch, chan **left_ch, chan **first_ch)
{
GtkTreeIter iter, inner;
chan *ch;
chan *prev = NULL;
*left_ch = NULL;
*first_ch = NULL;
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), &iter))
{
do
{
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
if (ch == find_ch)
*left_ch = prev;
else if (*first_ch == NULL)
*first_ch = ch;
prev = ch;
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &inner, &iter))
{
do
{
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &inner, COL_CHAN, &ch, -1);
if (ch == find_ch)
*left_ch = prev;
else if (*first_ch == NULL)
*first_ch = ch;
prev = ch;
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &inner));
}
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
}
}
static void
chan_emancipate_children (chan *ch)
{
@@ -689,7 +638,7 @@ gboolean
chan_remove (chan *ch, gboolean force)
{
chan *new_ch;
chan *first_ch;
int i, num;
extern int zoitechat_is_quitting;
if (zoitechat_is_quitting) /* avoid lots of looping on exit */
@@ -709,14 +658,25 @@ chan_remove (chan *ch, gboolean force)
{
ch->cv->focused = NULL;
cv_find_neighbors_for_removal (ch->cv, ch, &new_ch, &first_ch);
/* try to move the focus to some other valid channel */
num = cv_find_number_of_chan (ch->cv, ch);
/* move to the one left of the closing tab */
new_ch = cv_find_chan_by_number (ch->cv, num - 1);
if (new_ch && new_ch != ch)
{
chan_focus (new_ch);
}
else if (first_ch && first_ch != ch)
chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */
} else
{
chan_focus (first_ch);
/* if it fails, try focus from tab 0 and up */
for (i = 0; i < ch->cv->size; i++)
{
new_ch = cv_find_chan_by_number (ch->cv, i);
if (new_ch && new_ch != ch)
{
chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */
break;
}
}
}
}

View File

@@ -34,9 +34,8 @@
#include "../common/util.h"
#include "../common/network.h"
#include "gtkutil.h"
#include "theme/theme-gtk.h"
#include "palette.h"
#include "maingui.h"
#include "theme/theme-access.h"
#define ICON_DCC_CANCEL "dialog-cancel"
#define ICON_DCC_ACCEPT "dialog-apply"
@@ -56,7 +55,7 @@ enum /* DCC SEND/RECV */
COL_ETA,
COL_NICK,
COL_DCC, /* struct DCC * */
COL_COLOR, /* GdkRGBA */
COL_COLOR, /* PaletteColor */
N_COLUMNS
};
@@ -68,7 +67,7 @@ enum /* DCC CHAT */
CCOL_SENT,
CCOL_START,
CCOL_DCC, /* struct DCC * */
CCOL_COLOR, /* GdkRGBA * */
CCOL_COLOR, /* PaletteColor * */
CN_COLUMNS
};
@@ -178,15 +177,15 @@ fe_dcc_send_filereq (struct session *sess, char *nick, int maxcps, int passive)
static void
dcc_store_color (GtkListStore *store, GtkTreeIter *iter, int column, int color_index)
{
GdkRGBA rgba;
const GdkRGBA *color = NULL;
const PaletteColor *color = NULL;
if (color_index != 1 && theme_get_mirc_color ((unsigned int) color_index, &rgba))
color = &rgba;
if (color_index != 1)
color = &colors[color_index];
if (color)
{
gtk_list_store_set (store, iter, column, color, -1);
GdkRGBA rgba = *color;
gtk_list_store_set (store, iter, column, &rgba, -1);
}
else
{
@@ -624,7 +623,15 @@ clear_completed (GtkWidget * wid, gpointer none)
static void
browse_folder (char *dir)
{
#ifdef WIN32
/* no need for file:// in ShellExecute() */
fe_open_url (dir);
#else
char buf[512];
g_snprintf (buf, sizeof (buf), "file://%s", dir);
fe_open_url (buf);
#endif
}
static void
@@ -746,7 +753,7 @@ dcc_add_column (GtkWidget *tree, int textcol, int colorcol, char *title, gboolea
if (right_justified)
g_object_set (G_OBJECT (renderer), "xalign", (float) 1.0, NULL);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1, title, renderer,
"text", textcol, THEME_GTK_FOREGROUND_PROPERTY, colorcol,
"text", textcol, PALETTE_FOREGROUND_PROPERTY, colorcol,
NULL);
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
}
@@ -832,7 +839,7 @@ fe_dcc_open_recv_win (int passive)
store = gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
G_TYPE_STRING, G_TYPE_POINTER, PALETTE_GDK_TYPE);
view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1);
view_scrolled = gtk_widget_get_parent (view);
gtk_widget_set_hexpand (view_scrolled, TRUE);
@@ -1104,7 +1111,7 @@ fe_dcc_open_chat_win (int passive)
store = gtk_list_store_new (CN_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
G_TYPE_POINTER, PALETTE_GDK_TYPE);
view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1);
scroll = gtk_widget_get_parent (view);
gtk_box_set_child_packing (GTK_BOX (vbox), scroll, TRUE, TRUE, 0, GTK_PACK_START);

File diff suppressed because it is too large Load Diff

View File

@@ -114,7 +114,6 @@ typedef struct restore_gui
/* information stored when this tab isn't front-most */
GtkListStore *user_model; /* for filling the GtkTreeView */
GHashTable *user_row_refs;
void *buffer; /* xtext_Buffer */
char *input_text; /* input text buffer (while not-front tab) */
char *topic_text; /* topic GtkEntry buffer */
@@ -179,8 +178,6 @@ typedef struct session_gui
int pane_left_size; /*last position of the pane*/
int pane_right_size;
guint theme_window_listener_id;
guint theme_userlist_listener_id;
guint16 is_tab; /* is tab or toplevel? */
guint16 ul_hidden; /* userlist hidden? */
@@ -192,13 +189,17 @@ extern cairo_surface_t *dialogwin_pix;
gboolean fe_dark_mode_is_enabled (void);
gboolean fe_dark_mode_is_enabled_for (unsigned int mode);
gboolean fe_dark_mode_state_is_initialized (void);
void fe_set_auto_dark_mode_state (gboolean enabled);
#ifdef G_OS_WIN32
gboolean fe_win32_high_contrast_is_enabled (void);
gboolean fe_win32_try_get_system_dark (gboolean *prefer_dark);
#endif
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark_mode);
void fe_refresh_auto_dark_mode (void);
gboolean fe_apply_theme_for_mode (unsigned int mode, gboolean *palette_changed);
gboolean fe_apply_gtk3_theme (const char *theme_name, GError **error);
gboolean fe_apply_gtk3_theme_with_reload (const char *theme_name, gboolean force_reload,
GError **error);
gboolean fe_resolve_gtk3_theme_dir (const char *theme_root,
char **gtk3_dir_out,
gboolean *has_dark_css_out);
void fe_apply_theme_to_toplevel (GtkWidget *window);
#define SPELL_ENTRY_GET_TEXT(e) ((char *)(gtk_entry_get_text (GTK_ENTRY(e))))
#define SPELL_ENTRY_SET_TEXT(e,txt) gtk_entry_set_text(GTK_ENTRY(e),txt)

View File

@@ -5,6 +5,10 @@
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -23,6 +27,18 @@
<TargetName>zoitechat</TargetName>
<OutDir>$(ZoiteChatRel)</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\common;$(ZoiteChatLib);$(DepsRoot)\include;$(OpenSslInclude);$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;comsupp.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -30,8 +46,8 @@
<DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(ArchiveLibDir);$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;comsupp.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
@@ -58,7 +74,6 @@ powershell "Get-Content -Encoding UTF8 '$(ZoiteChatLib)zoitechat.rc.utf8' | Out-
<ClInclude Include="fe-gtk.h" />
<ClInclude Include="fkeys.h" />
<ClInclude Include="gtkutil.h" />
<ClInclude Include="icon-resolver.h" />
<ClInclude Include="joind.h" />
<ClInclude Include="maingui.h" />
<ClInclude Include="menu.h" />
@@ -77,16 +92,6 @@ powershell "Get-Content -Encoding UTF8 '$(ZoiteChatLib)zoitechat.rc.utf8' | Out-
<ClInclude Include="urlgrab.h" />
<ClInclude Include="userlistgui.h" />
<ClInclude Include="xtext.h" />
<ClInclude Include="theme\theme-manager.h" />
<ClInclude Include="theme\theme-palette.h" />
<ClInclude Include="theme\theme-application.h" />
<ClInclude Include="theme\theme-policy.h" />
<ClInclude Include="theme\theme-css.h" />
<ClInclude Include="theme\theme-runtime.h" />
<ClInclude Include="theme\theme-access.h" />
<ClInclude Include="theme\theme-gtk.h" />
<ClInclude Include="theme\theme-gtk3.h" />
<ClInclude Include="theme\theme-preferences.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ascii.c" />
@@ -99,13 +104,13 @@ powershell "Get-Content -Encoding UTF8 '$(ZoiteChatLib)zoitechat.rc.utf8' | Out-
<ClCompile Include="fe-gtk.c" />
<ClCompile Include="fkeys.c" />
<ClCompile Include="gtkutil.c" />
<ClCompile Include="icon-resolver.c" />
<ClCompile Include="ignoregui.c" />
<ClCompile Include="joind.c" />
<ClCompile Include="maingui.c" />
<ClCompile Include="menu.c" />
<ClCompile Include="notifications\notification-windows.c" />
<ClCompile Include="notifygui.c" />
<ClCompile Include="palette.c" />
<ClCompile Include="pixmaps.c" />
<ClCompile Include="plugin-notification.c" />
<ClCompile Include="plugin-tray.c" />
@@ -120,15 +125,6 @@ powershell "Get-Content -Encoding UTF8 '$(ZoiteChatLib)zoitechat.rc.utf8' | Out-
<ClCompile Include="urlgrab.c" />
<ClCompile Include="userlistgui.c" />
<ClCompile Include="xtext.c" />
<ClCompile Include="theme\theme-manager.c" />
<ClCompile Include="theme\theme-palette.c" />
<ClCompile Include="theme\theme-application.c" />
<ClCompile Include="theme\theme-policy.c" />
<ClCompile Include="theme\theme-css.c" />
<ClCompile Include="theme\theme-runtime.c" />
<ClCompile Include="theme\theme-access.c" />
<ClCompile Include="theme\theme-gtk3.c" />
<ClCompile Include="theme\theme-preferences.c" />
</ItemGroup>
<ItemGroup>
<Manifest Include="..\..\win32\zoitechat.exe.manifest" />
@@ -139,12 +135,6 @@ powershell "Get-Content -Encoding UTF8 '$(ZoiteChatLib)zoitechat.rc.utf8' | Out-
</ItemGroup>
<ItemGroup>
<None Include="..\..\data\icons\zoitechat.ico" />
<None Include="theme\tests\test-theme-manager-policy.c" />
<None Include="theme\tests\test-theme-runtime-persistence.c" />
<None Include="theme\tests\test-theme-access-routing.c" />
<None Include="theme\tests\src/fe-gtk/theme/tests/test-theme-manager-auto-refresh.c" />
<None Include="theme\tests\src/fe-gtk/theme/tests/test-theme-manager-dispatch-routing.c" />
<None Include="theme\tests\src/fe-gtk/theme/tests/test-theme-application-input-style.c" />
</ItemGroup>
<ItemGroup>
<Xml Include="..\..\data\zoitechat.gresource.xml" />

View File

@@ -42,9 +42,6 @@
<ClInclude Include="gtkutil.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="icon-resolver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="joind.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -99,36 +96,6 @@
<ClInclude Include="notifications\notification-backend.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-manager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-palette.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-application.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-policy.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-css.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-runtime.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-access.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-gtk.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-gtk3.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="theme\theme-preferences.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ascii.c">
@@ -161,9 +128,6 @@
<ClCompile Include="gtkutil.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="icon-resolver.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ignoregui.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -179,6 +143,9 @@
<ClCompile Include="notifygui.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="palette.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pixmaps.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -224,33 +191,6 @@
<ClCompile Include="notifications\notification-windows.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-manager.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-palette.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-application.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-policy.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-css.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-runtime.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-access.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-gtk3.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="theme\theme-preferences.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\..\win32\zoitechat.exe.manifest">
@@ -266,27 +206,9 @@
<None Include="..\..\data\icons\zoitechat.ico">
<Filter>Resource Files</Filter>
</None>
<None Include="theme\tests\test-theme-manager-policy.c">
<Filter>Source Files</Filter>
</None>
<None Include="theme\tests\test-theme-runtime-persistence.c">
<Filter>Source Files</Filter>
</None>
<None Include="\theme\tests\test-theme-access-routing.c">
<Filter>Source Files</Filter>
</None>
<None Include="\theme\tests\test-theme-manager-dispatch-routing.c">
<Filter>Source Files</Filter>
</None>
<None Include="\theme\tests\test-theme-manager-auto-refresh.c">
<Filter>Source Files</Filter>
</None>
<None Include="\theme\tests\test-theme-application-input-style.c">
<Filter>Source Files</Filter>
</None>
<None Include="zoitechat.rc.tt" />
</ItemGroup>
<ItemGroup>
<Xml Include="..\..\data\zoitechat.gresource.xml" />
</ItemGroup>
</Project>
</Project>

View File

@@ -48,10 +48,7 @@
#include "gtkutil.h"
#include "menu.h"
#include "xtext.h"
#include "theme/theme-access.h"
#include "theme/theme-manager.h"
#include "theme/theme-css.h"
#include "theme/theme-gtk3.h"
#include "palette.h"
#include "maingui.h"
#include "textgui.h"
#include "fkeys.h"
@@ -145,53 +142,6 @@ static int key_action_put_history (GtkWidget * wid, GdkEventKey * evt,
static GSList *keybind_list = NULL;
#define KEY_DIALOG_THEME_LISTENER_ID_KEY "fkeys.theme-listener-id"
static void
key_dialog_theme_apply (GtkWidget *window)
{
GtkWidget *xtext;
XTextColor xtext_palette[XTEXT_COLS];
if (!window)
return;
xtext = g_object_get_data (G_OBJECT (window), "xtext");
if (!xtext)
return;
theme_get_xtext_colors (xtext_palette, XTEXT_COLS);
gtk_xtext_set_palette (GTK_XTEXT (xtext), xtext_palette);
}
static void
key_dialog_theme_changed (const ThemeChangedEvent *event, gpointer userdata)
{
GtkWidget *window = userdata;
if (!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PALETTE) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_THEME_PACK) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_MODE))
return;
key_dialog_theme_apply (window);
}
static void
key_dialog_theme_destroy_cb (GtkWidget *widget, gpointer userdata)
{
guint listener_id;
(void) userdata;
listener_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), KEY_DIALOG_THEME_LISTENER_ID_KEY));
if (listener_id)
{
theme_listener_unregister (listener_id);
g_object_set_data (G_OBJECT (widget), KEY_DIALOG_THEME_LISTENER_ID_KEY, NULL);
}
}
static gsize
key_action_decode_escapes (const char *input, char *output)
{
@@ -796,9 +746,9 @@ key_dialog_treeview_new (GtkWidget *box)
"changed", G_CALLBACK (key_dialog_selection_changed), NULL);
gtk_widget_set_name (view, "fkeys-treeview");
if (!theme_gtk3_is_active ())
{
GtkCssProvider *provider = gtk_css_provider_new ();
GtkStyleContext *context = gtk_widget_get_style_context (view);
gtk_css_provider_load_from_data (
provider,
@@ -810,7 +760,8 @@ key_dialog_treeview_new (GtkWidget *box)
"}",
-1,
NULL);
theme_css_apply_widget_provider (view, GTK_STYLE_PROVIDER (provider));
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
}
@@ -953,16 +904,13 @@ key_dialog_show ()
NULL, 600, 360, &vbox, 0);
view = key_dialog_treeview_new (vbox);
theme_get_xtext_colors (xtext_palette, XTEXT_COLS);
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
xtext = gtk_xtext_new (xtext_palette, 0);
gtk_box_pack_start (GTK_BOX (vbox), xtext, FALSE, TRUE, 2);
gtk_xtext_set_font (GTK_XTEXT (xtext), prefs.hex_text_font);
g_object_set_data (G_OBJECT (key_dialog), "view", view);
g_object_set_data (G_OBJECT (key_dialog), "xtext", xtext);
g_object_set_data (G_OBJECT (key_dialog), KEY_DIALOG_THEME_LISTENER_ID_KEY,
GUINT_TO_POINTER (theme_listener_register ("fkeys.window", key_dialog_theme_changed, key_dialog)));
g_signal_connect (G_OBJECT (key_dialog), "destroy", G_CALLBACK (key_dialog_theme_destroy_cb), NULL);
box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
gtk_button_box_set_layout (GTK_BUTTON_BOX (box), GTK_BUTTONBOX_SPREAD);

View File

@@ -42,9 +42,7 @@
#include "../common/zoitechatc.h"
#include "../common/typedef.h"
#include "gtkutil.h"
#include "icon-resolver.h"
#include "pixmaps.h"
#include "theme/theme-manager.h"
#ifdef WIN32
#include <io.h>
@@ -64,56 +62,272 @@ struct file_req
int flags; /* FRF_* flags */
};
static GdkPixbuf *
gtkutil_menu_icon_pixbuf_new (const char *icon_name)
static const char *
gtkutil_menu_custom_icon_from_stock (const char *stock_name)
{
GdkPixbuf *pixbuf = NULL;
char *resource_path;
const char *system_icon_name = NULL;
int action;
static const struct
{
const char *stock;
const char *custom_icon;
} icon_map[] = {
{ "gtk-new", "zc-menu-new" },
{ "gtk-index", "zc-menu-network-list" },
{ "gtk-revert-to-saved", "zc-menu-load-plugin" },
{ "gtk-redo", "zc-menu-detach" },
{ "gtk-close", "zc-menu-close" },
{ "gtk-quit", "zc-menu-quit" },
{ "gtk-disconnect", "zc-menu-disconnect" },
{ "gtk-connect", "zc-menu-connect" },
{ "gtk-jump-to", "zc-menu-join" },
{ "gtk-preferences", "zc-menu-preferences" },
{ "gtk-clear", "zc-menu-clear" },
{ "gtk-copy", "zc-menu-copy" },
{ "gtk-delete", "zc-menu-delete" },
{ "gtk-add", "zc-menu-add" },
{ "gtk-remove", "zc-menu-remove" },
{ "gtk-spell-check", "zc-menu-spell-check" },
{ "gtk-save", "zc-menu-save" },
{ "gtk-save-as", "zc-menu-save-as" },
{ "gtk-refresh", "zc-menu-refresh" },
{ "gtk-justify-left", "zc-menu-search" },
{ "gtk-find", "zc-menu-find" },
{ "gtk-go-back", "zc-menu-previous" },
{ "gtk-go-forward", "zc-menu-next" },
{ "gtk-help", "zc-menu-help" },
{ "gtk-about", "zc-menu-about" },
{ "gtk-convert", "zc-menu-emoji" },
};
size_t i;
if (!icon_name || !icon_resolver_menu_action_from_name (icon_name, &action))
if (!stock_name)
return NULL;
resource_path = icon_resolver_resolve_path (ICON_RESOLVER_ROLE_MENU_ACTION, action,
GTK_ICON_SIZE_MENU, "menu",
ICON_RESOLVER_THEME_SYSTEM,
&system_icon_name);
if (!resource_path)
return NULL;
for (i = 0; i < G_N_ELEMENTS (icon_map); i++)
{
if (strcmp (stock_name, icon_map[i].stock) == 0)
return icon_map[i].custom_icon;
}
if (g_str_has_prefix (resource_path, "/icons/"))
pixbuf = gdk_pixbuf_new_from_resource (resource_path, NULL);
else
pixbuf = gdk_pixbuf_new_from_file (resource_path, NULL);
g_free (resource_path);
return pixbuf;
return NULL;
}
static const char *
gtkutil_menu_custom_icon_from_icon_name (const char *icon_name)
{
static const struct
{
const char *icon;
const char *custom_icon;
} icon_map[] = {
{ "document-new", "zc-menu-new" },
{ "view-list", "zc-menu-network-list" },
{ "document-open", "zc-menu-load-plugin" },
{ "edit-redo", "zc-menu-detach" },
{ "window-close", "zc-menu-close" },
{ "application-exit", "zc-menu-quit" },
{ "network-disconnect", "zc-menu-disconnect" },
{ "network-connect", "zc-menu-connect" },
{ "go-jump", "zc-menu-join" },
{ "preferences-system", "zc-menu-preferences" },
{ "edit-clear", "zc-menu-clear" },
{ "edit-copy", "zc-menu-copy" },
{ "edit-delete", "zc-menu-delete" },
{ "list-add", "zc-menu-add" },
{ "list-remove", "zc-menu-remove" },
{ "tools-check-spelling", "zc-menu-spell-check" },
{ "document-save", "zc-menu-save" },
{ "document-save-as", "zc-menu-save-as" },
{ "view-refresh", "zc-menu-refresh" },
{ "edit-find", "zc-menu-find" },
{ "go-previous", "zc-menu-previous" },
{ "go-next", "zc-menu-next" },
{ "help-browser", "zc-menu-help" },
{ "help-about", "zc-menu-about" },
{ "face-smile", "zc-menu-emoji" },
{ "insert-emoticon", "zc-menu-emoji" },
{ "software-update-available", "zc-menu-update" },
{ "network-workgroup", "zc-menu-chanlist" },
};
size_t i;
if (!icon_name)
return NULL;
for (i = 0; i < G_N_ELEMENTS (icon_map); i++)
{
if (strcmp (icon_name, icon_map[i].icon) == 0)
return icon_map[i].custom_icon;
}
return NULL;
}
const char *
gtkutil_icon_name_from_stock (const char *stock_name)
{
return icon_resolver_icon_name_from_stock (stock_name);
static const struct
{
const char *stock;
const char *icon;
} icon_map[] = {
{ "gtk-new", "document-new" },
{ "gtk-open", "document-open" },
{ "gtk-revert-to-saved", "document-open" },
{ "gtk-save", "document-save" },
{ "gtk-save-as", "document-save-as" },
{ "gtk-add", "list-add" },
{ "gtk-cancel", "dialog-cancel" },
{ "gtk-ok", "dialog-ok" },
{ "gtk-no", "dialog-cancel" },
{ "gtk-yes", "dialog-ok" },
{ "gtk-apply", "dialog-apply" },
{ "gtk-dialog-error", "dialog-error" },
{ "gtk-copy", "edit-copy" },
{ "gtk-delete", "edit-delete" },
{ "gtk-remove", "list-remove" },
{ "gtk-clear", "edit-clear" },
{ "gtk-redo", "edit-redo" },
{ "gtk-find", "edit-find" },
{ "gtk-justify-left", "edit-find" },
{ "gtk-refresh", "view-refresh" },
{ "gtk-go-back", "go-previous" },
{ "gtk-go-forward", "go-next" },
{ "gtk-index", "view-list" },
{ "gtk-jump-to", "go-jump" },
{ "gtk-media-play", "media-playback-start" },
{ "gtk-preferences", "preferences-system" },
{ "gtk-help", "help-browser" },
{ "gtk-about", "help-about" },
{ "gtk-close", "window-close" },
{ "gtk-quit", "application-exit" },
{ "gtk-connect", "network-connect" },
{ "gtk-disconnect", "network-disconnect" },
{ "gtk-network", "network-workgroup" },
{ "gtk-spell-check", "tools-check-spelling" },
};
size_t i;
if (!stock_name)
return NULL;
for (i = 0; i < G_N_ELEMENTS (icon_map); i++)
{
if (strcmp (stock_name, icon_map[i].stock) == 0)
return icon_map[i].icon;
}
return stock_name;
}
static const char *
gtkutil_menu_icon_theme_variant (void)
{
GtkSettings *settings;
gboolean prefer_dark = FALSE;
char *theme_name = NULL;
char *theme_name_lower = NULL;
const char *theme_variant = "light";
/* Prefer ZoiteChat's explicit dark-mode selection when available so icon
* variants stay in sync with the app mode, not only the system theme. */
if (fe_dark_mode_state_is_initialized () || prefs.hex_gui_dark_mode != ZOITECHAT_DARK_MODE_AUTO)
return fe_dark_mode_is_enabled () ? "dark" : "light";
settings = gtk_settings_get_default ();
if (settings)
{
g_object_get (G_OBJECT (settings), "gtk-application-prefer-dark-theme", &prefer_dark, NULL);
g_object_get (G_OBJECT (settings), "gtk-theme-name", &theme_name, NULL);
}
if (theme_name)
theme_name_lower = g_ascii_strdown (theme_name, -1);
if (prefer_dark || (theme_name_lower && g_strrstr (theme_name_lower, "dark")))
theme_variant = "dark";
g_free (theme_name_lower);
g_free (theme_name);
return theme_variant;
}
static char *
gtkutil_menu_icon_resource_path (const char *icon_name, const char *extension)
{
char *resource_path;
const char *variant;
if (!icon_name || !extension || !g_str_has_prefix (icon_name, "zc-menu-"))
return NULL;
variant = gtkutil_menu_icon_theme_variant ();
resource_path = g_strdup_printf ("/icons/menu/%s/%s.%s", variant,
icon_name + strlen ("zc-menu-"), extension);
if (!g_resources_get_info (resource_path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL))
{
g_free (resource_path);
resource_path = g_strdup_printf ("/icons/menu/light/%s.%s",
icon_name + strlen ("zc-menu-"), extension);
if (!g_resources_get_info (resource_path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL))
{
g_free (resource_path);
return NULL;
}
}
return resource_path;
}
gboolean
gtkutil_menu_icon_exists (const char *icon_name)
{
char *resource_path;
gboolean found;
resource_path = gtkutil_menu_icon_resource_path (icon_name, "png");
if (!resource_path)
resource_path = gtkutil_menu_icon_resource_path (icon_name, "svg");
found = resource_path != NULL;
g_free (resource_path);
return found;
}
static GtkWidget *
gtkutil_menu_icon_image_new (const char *icon_name, GtkIconSize size)
{
GtkWidget *image = NULL;
GdkPixbuf *pixbuf;
gint width;
gint height;
GdkPixbuf *pixbuf = NULL;
char *resource_path;
pixbuf = gtkutil_menu_icon_pixbuf_new (icon_name);
if (!pixbuf)
return NULL;
resource_path = gtkutil_menu_icon_resource_path (icon_name, "png");
if (!resource_path)
resource_path = gtkutil_menu_icon_resource_path (icon_name, "svg");
image = gtk_image_new_from_pixbuf (pixbuf);
g_object_unref (pixbuf);
if (resource_path)
{
pixbuf = gdk_pixbuf_new_from_resource (resource_path, NULL);
if (pixbuf)
{
image = gtk_image_new_from_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
}
if (gtk_icon_size_lookup (size, &width, &height))
gtk_image_set_pixel_size (GTK_IMAGE (image), MAX (width, height));
g_free (resource_path);
if (image)
{
GtkIconSize tmp_size;
gint width;
gint height;
tmp_size = size;
if (gtk_icon_size_lookup (tmp_size, &width, &height))
gtk_image_set_pixel_size (GTK_IMAGE (image), MAX (width, height));
}
return image;
}
@@ -123,24 +337,25 @@ gtkutil_image_new_from_stock (const char *stock, GtkIconSize size)
{
GtkWidget *image;
const char *icon_name;
const char *resolved_icon_name = NULL;
int action;
const char *custom_icon_name;
icon_name = gtkutil_icon_name_from_stock (stock);
if (!icon_name && stock && g_str_has_prefix (stock, "zc-menu-"))
icon_name = stock;
/* Use ZoiteChat's themed icon resources consistently across menu and button
* images so dark/light mode swaps all app icons together. */
custom_icon_name = gtkutil_menu_custom_icon_from_stock (stock);
if (!custom_icon_name)
custom_icon_name = gtkutil_menu_custom_icon_from_icon_name (icon_name);
if (custom_icon_name)
icon_name = custom_icon_name;
image = gtkutil_menu_icon_image_new (icon_name, size);
if (image)
return image;
if (icon_resolver_menu_action_from_name (icon_name, &action))
resolved_icon_name = icon_resolver_system_icon_name (ICON_RESOLVER_ROLE_MENU_ACTION, action);
if (!resolved_icon_name)
resolved_icon_name = icon_name;
return gtk_image_new_from_icon_name (resolved_icon_name, size);
return gtk_image_new_from_icon_name (icon_name, size);
}
GtkWidget *
@@ -278,7 +493,66 @@ void
gtkutil_apply_palette (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
const PangoFontDescription *font_desc)
{
theme_manager_apply_palette_widget (widget, bg, fg, font_desc);
if (!widget)
return;
{
static const char *class_name = "zoitechat-palette";
GtkStyleContext *context = gtk_widget_get_style_context (widget);
GtkCssProvider *provider = g_object_get_data (G_OBJECT (widget),
"zoitechat-palette-provider");
gboolean new_provider = FALSE;
GString *css;
gchar *bg_color = NULL;
gchar *fg_color = NULL;
if (!bg && !fg && !font_desc)
{
gtk_style_context_remove_class (context, class_name);
if (provider)
{
gtk_style_context_remove_provider (context, GTK_STYLE_PROVIDER (provider));
g_object_set_data (G_OBJECT (widget), "zoitechat-palette-provider", NULL);
}
return;
}
if (!provider)
{
provider = gtk_css_provider_new ();
g_object_set_data_full (G_OBJECT (widget), "zoitechat-palette-provider",
provider, g_object_unref);
new_provider = TRUE;
}
css = g_string_new (".");
g_string_append (css, class_name);
g_string_append (css, " {");
if (bg)
{
bg_color = gdk_rgba_to_string (bg);
g_string_append_printf (css, " background-color: %s;", bg_color);
}
if (fg)
{
fg_color = gdk_rgba_to_string (fg);
g_string_append_printf (css, " color: %s;", fg_color);
}
gtkutil_append_font_css (css, font_desc);
g_string_append (css, " }");
gtk_css_provider_load_from_data (provider, css->str, -1, NULL);
if (new_provider)
{
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
gtk_style_context_add_class (context, class_name);
g_string_free (css, TRUE);
g_free (bg_color);
g_free (fg_color);
}
}
static void
@@ -538,23 +812,25 @@ gtkutil_file_req (GtkWindow *parent, const char *title, void *callback, void *us
if (flags & FRF_WRITE)
{
dialog = gtk_file_chooser_dialog_new (title, NULL,
GTK_FILE_CHOOSER_ACTION_SAVE,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Save"), GTK_RESPONSE_ACCEPT,
NULL);
dialog = gtk_file_chooser_dialog_new (title, effective_parent,
GTK_FILE_CHOOSER_ACTION_SAVE,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Save"), GTK_RESPONSE_ACCEPT,
NULL);
if (!(flags & FRF_NOASKOVERWRITE))
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
}
else
dialog = gtk_file_chooser_dialog_new (title, NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Open"), GTK_RESPONSE_ACCEPT,
NULL);
dialog = gtk_file_chooser_dialog_new (title, effective_parent,
GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Open"), GTK_RESPONSE_ACCEPT,
NULL);
theme_manager_attach_window (dialog);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
if (filter && filter[0] && (flags & FRF_FILTERISINITIAL))
{
@@ -625,7 +901,8 @@ gtkutil_file_req (GtkWindow *parent, const char *title, void *callback, void *us
g_signal_connect (G_OBJECT (dialog), "destroy",
G_CALLBACK (gtkutil_file_req_destroy), (gpointer) freq);
if (effective_parent)
if (effective_parent &&
gtk_window_get_transient_for (GTK_WINDOW (dialog)) != effective_parent)
gtk_window_set_transient_for (GTK_WINDOW (dialog), effective_parent);
if (flags & FRF_MODAL)
@@ -714,7 +991,9 @@ fe_get_str (char *msg, char *def, void *callback, void *userdata)
_("_Cancel"), GTK_RESPONSE_REJECT,
_("_OK"), GTK_RESPONSE_ACCEPT,
NULL);
theme_manager_attach_window (dialog);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
gtk_box_set_homogeneous (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), TRUE);
@@ -810,7 +1089,9 @@ fe_get_int (char *msg, int def, void *callback, void *userdata)
_("_Cancel"), GTK_RESPONSE_REJECT,
_("_OK"), GTK_RESPONSE_ACCEPT,
NULL);
theme_manager_attach_window (dialog);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
gtk_box_set_homogeneous (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), TRUE);
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
@@ -851,7 +1132,9 @@ fe_get_bool (char *title, char *prompt, void *callback, void *userdata)
_("_No"), GTK_RESPONSE_REJECT,
_("_Yes"), GTK_RESPONSE_ACCEPT,
NULL);
theme_manager_attach_window (dialog);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
gtk_box_set_homogeneous (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), TRUE);
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
@@ -968,7 +1251,6 @@ gtkutil_window_new (char *title, char *role, int width, int height, int flags)
GtkWidget *win;
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
theme_manager_attach_window (win);
gtkutil_set_icon (win);
#ifdef WIN32
gtk_window_set_wmclass (GTK_WINDOW (win), "ZoiteChat", "zoitechat");
@@ -976,6 +1258,7 @@ gtkutil_window_new (char *title, char *role, int width, int height, int flags)
gtk_window_set_title (GTK_WINDOW (win), title);
gtk_window_set_default_size (GTK_WINDOW (win), width, height);
gtk_window_set_role (GTK_WINDOW (win), role);
fe_apply_theme_to_toplevel (win);
if (flags & 1)
gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_MOUSE);
if ((flags & 2) && parent_window)

View File

@@ -22,7 +22,7 @@
#include <gtk/gtk.h>
#include "../common/fe.h"
#include "theme/theme-gtk.h"
#include "palette.h"
typedef void (*filereqcallback) (void *, char *file);
@@ -41,6 +41,7 @@ GtkWidget *gtkutil_button (GtkWidget *box, char *stock, char *tip, void *callbac
void *userdata, char *labeltext);
GtkWidget *gtkutil_image_new_from_stock (const char *stock, GtkIconSize size);
GtkWidget *gtkutil_button_new_from_stock (const char *stock, const char *label);
gboolean gtkutil_menu_icon_exists (const char *icon_name);
const char *gtkutil_icon_name_from_stock (const char *stock_name);
void gtkutil_label_new (char *text, GtkWidget * box);
GtkWidget *gtkutil_entry_new (int max, GtkWidget * box, void *callback,

View File

@@ -1,380 +0,0 @@
#include <string.h>
#include "fe-gtk.h"
#include "icon-resolver.h"
#include "theme/theme-policy.h"
#include "../common/cfgfiles.h"
typedef struct
{
IconResolverRole role;
int item;
const char *custom_icon_name;
const char *system_icon_name;
const char *resource_name;
} IconRegistryEntry;
typedef struct
{
const char *stock_name;
const char *system_icon_name;
} StockIconMap;
static const IconRegistryEntry icon_registry[] = {
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_NEW, "zc-menu-new", "document-new", "new" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_NETWORK_LIST, "zc-menu-network-list", "view-list", "network-list" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_LOAD_PLUGIN, "zc-menu-load-plugin", "document-open", "load-plugin" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_DETACH, "zc-menu-detach", "edit-redo", "detach" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_CLOSE, "zc-menu-close", "window-close", "close" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_QUIT, "zc-menu-quit", "application-exit", "quit" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_DISCONNECT, "zc-menu-disconnect", "network-disconnect", "disconnect" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_CONNECT, "zc-menu-connect", "network-connect", "connect" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_JOIN, "zc-menu-join", "go-jump", "join" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_PREFERENCES, "zc-menu-preferences", "preferences-system", "preferences" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_CLEAR, "zc-menu-clear", "edit-clear", "clear" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_COPY, "zc-menu-copy", "edit-copy", "copy" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_DELETE, "zc-menu-delete", "edit-delete", "delete" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_ADD, "zc-menu-add", "list-add", "add" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_REMOVE, "zc-menu-remove", "list-remove", "remove" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_SPELL_CHECK, "zc-menu-spell-check", "tools-check-spelling", "spell-check" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_SAVE, "zc-menu-save", "document-save", "save" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_SAVE_AS, "zc-menu-save-as", "document-save-as", "save-as" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_REFRESH, "zc-menu-refresh", "view-refresh", "refresh" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_SEARCH, "zc-menu-search", "edit-find", "search" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_FIND, "zc-menu-find", "edit-find", "find" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_PREVIOUS, "zc-menu-previous", "go-previous", "previous" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_NEXT, "zc-menu-next", "go-next", "next" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_HELP, "zc-menu-help", "help-browser", "help" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_ABOUT, "zc-menu-about", "help-about", "about" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_EMOJI, "zc-menu-emoji", "face-smile", "emoji" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_UPDATE, "zc-menu-update", "software-update-available", "update" },
{ ICON_RESOLVER_ROLE_MENU_ACTION, ICON_RESOLVER_MENU_ACTION_CHANLIST, "zc-menu-chanlist", "network-workgroup", "chanlist" },
{ ICON_RESOLVER_ROLE_TRAY_STATE, ICON_RESOLVER_TRAY_STATE_NORMAL, NULL, "zoitechat", "tray_normal" },
{ ICON_RESOLVER_ROLE_TRAY_STATE, ICON_RESOLVER_TRAY_STATE_FILEOFFER, NULL, "mail-attachment", "tray_fileoffer" },
{ ICON_RESOLVER_ROLE_TRAY_STATE, ICON_RESOLVER_TRAY_STATE_HIGHLIGHT, NULL, "dialog-warning", "tray_highlight" },
{ ICON_RESOLVER_ROLE_TRAY_STATE, ICON_RESOLVER_TRAY_STATE_MESSAGE, NULL, "mail-unread", "tray_message" },
{ ICON_RESOLVER_ROLE_TREE_TYPE, ICON_RESOLVER_TREE_TYPE_CHANNEL, NULL, "folder", "tree_channel" },
{ ICON_RESOLVER_ROLE_TREE_TYPE, ICON_RESOLVER_TREE_TYPE_DIALOG, NULL, "mail-message-new", "tree_dialog" },
{ ICON_RESOLVER_ROLE_TREE_TYPE, ICON_RESOLVER_TREE_TYPE_SERVER, NULL, "network-server", "tree_server" },
{ ICON_RESOLVER_ROLE_TREE_TYPE, ICON_RESOLVER_TREE_TYPE_UTIL, NULL, "applications-utilities", "tree_util" },
{ ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_VOICE, NULL, "emblem-ok", "ulist_voice" },
{ ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_HALFOP, NULL, "emblem-shared", "ulist_halfop" },
{ ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_OP, NULL, "emblem-default", "ulist_op" },
{ ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_OWNER, NULL, "emblem-favorite", "ulist_owner" },
{ ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_FOUNDER, NULL, "emblem-favorite", "ulist_founder" },
{ ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_NETOP, NULL, "emblem-system", "ulist_netop" }
};
static const StockIconMap stock_icon_map[] = {
{ "gtk-new", "document-new" },
{ "gtk-open", "document-open" },
{ "gtk-revert-to-saved", "document-open" },
{ "gtk-save", "document-save" },
{ "gtk-save-as", "document-save-as" },
{ "gtk-add", "list-add" },
{ "gtk-cancel", "dialog-cancel" },
{ "gtk-ok", "dialog-ok" },
{ "gtk-no", "dialog-cancel" },
{ "gtk-yes", "dialog-ok" },
{ "gtk-apply", "dialog-apply" },
{ "gtk-dialog-error", "dialog-error" },
{ "gtk-copy", "edit-copy" },
{ "gtk-delete", "edit-delete" },
{ "gtk-remove", "list-remove" },
{ "gtk-clear", "edit-clear" },
{ "gtk-redo", "edit-redo" },
{ "gtk-find", "edit-find" },
{ "gtk-justify-left", "edit-find" },
{ "gtk-refresh", "view-refresh" },
{ "gtk-go-back", "go-previous" },
{ "gtk-go-forward", "go-next" },
{ "gtk-index", "view-list" },
{ "gtk-jump-to", "go-jump" },
{ "gtk-media-play", "media-playback-start" },
{ "gtk-preferences", "preferences-system" },
{ "gtk-help", "help-browser" },
{ "gtk-about", "help-about" },
{ "gtk-close", "window-close" },
{ "gtk-quit", "application-exit" },
{ "gtk-connect", "network-connect" },
{ "gtk-disconnect", "network-disconnect" },
{ "gtk-network", "network-workgroup" },
{ "gtk-spell-check", "tools-check-spelling" }
};
static int
menu_action_from_icon_name (const char *icon_name)
{
size_t i;
if (!icon_name)
return -1;
for (i = 0; i < G_N_ELEMENTS (icon_registry); i++)
{
if (icon_registry[i].role != ICON_RESOLVER_ROLE_MENU_ACTION)
continue;
if (icon_registry[i].system_icon_name && strcmp (icon_name, icon_registry[i].system_icon_name) == 0)
return icon_registry[i].item;
}
return -1;
}
static const IconRegistryEntry *
icon_registry_find (IconResolverRole role, int item)
{
size_t i;
for (i = 0; i < G_N_ELEMENTS (icon_registry); i++)
{
if (icon_registry[i].role == role && icon_registry[i].item == item)
return &icon_registry[i];
}
return NULL;
}
static const IconRegistryEntry *
icon_registry_find_custom (const char *custom_icon_name)
{
size_t i;
if (!custom_icon_name)
return NULL;
for (i = 0; i < G_N_ELEMENTS (icon_registry); i++)
{
if (icon_registry[i].custom_icon_name && strcmp (icon_registry[i].custom_icon_name, custom_icon_name) == 0)
return &icon_registry[i];
}
return NULL;
}
const char *
icon_resolver_icon_name_from_stock (const char *stock_name)
{
size_t i;
if (!stock_name)
return NULL;
for (i = 0; i < G_N_ELEMENTS (stock_icon_map); i++)
{
if (strcmp (stock_name, stock_icon_map[i].stock_name) == 0)
return stock_icon_map[i].system_icon_name;
}
return stock_name;
}
gboolean
icon_resolver_menu_action_from_name (const char *name, int *action_out)
{
size_t i;
if (!name)
return FALSE;
for (i = 0; i < G_N_ELEMENTS (icon_registry); i++)
{
if (icon_registry[i].role != ICON_RESOLVER_ROLE_MENU_ACTION)
continue;
if ((icon_registry[i].custom_icon_name && strcmp (name, icon_registry[i].custom_icon_name) == 0) ||
(icon_registry[i].system_icon_name && strcmp (name, icon_registry[i].system_icon_name) == 0))
{
if (action_out)
*action_out = icon_registry[i].item;
return TRUE;
}
}
for (i = 0; i < G_N_ELEMENTS (stock_icon_map); i++)
{
if (strcmp (name, stock_icon_map[i].stock_name) == 0)
{
int action = menu_action_from_icon_name (stock_icon_map[i].system_icon_name);
if (action >= 0)
{
if (action_out)
*action_out = action;
return TRUE;
}
}
}
return FALSE;
}
const char *
icon_resolver_icon_name_for_menu_custom (const char *custom_icon_name)
{
const IconRegistryEntry *entry = icon_registry_find_custom (custom_icon_name);
if (!entry)
return NULL;
return entry->system_icon_name;
}
gboolean
icon_resolver_menu_action_from_custom (const char *custom_icon_name, int *action_out)
{
const IconRegistryEntry *entry = icon_registry_find_custom (custom_icon_name);
if (!entry || entry->role != ICON_RESOLVER_ROLE_MENU_ACTION)
return FALSE;
if (action_out)
*action_out = entry->item;
return TRUE;
}
const char *
icon_resolver_system_icon_name (IconResolverRole role, int item)
{
const IconRegistryEntry *entry = icon_registry_find (role, item);
if (!entry)
return NULL;
return entry->system_icon_name;
}
IconResolverThemeVariant
icon_resolver_detect_theme_variant (void)
{
if (theme_policy_is_app_dark_mode_active ())
return ICON_RESOLVER_THEME_DARK;
return ICON_RESOLVER_THEME_LIGHT;
}
static gboolean
resource_exists (const char *resource_path)
{
return g_resources_get_info (resource_path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL);
}
static char *
resolve_user_override (const IconRegistryEntry *entry, IconResolverThemeVariant variant)
{
const char *variant_name = variant == ICON_RESOLVER_THEME_DARK ? "dark" : "light";
char *path;
path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "icons" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S "%s.png",
get_xdir (), variant_name, entry->resource_name);
if (g_file_test (path, G_FILE_TEST_EXISTS))
return path;
g_free (path);
path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "icons" G_DIR_SEPARATOR_S "%s-%s.png",
get_xdir (), entry->resource_name, variant_name);
if (g_file_test (path, G_FILE_TEST_EXISTS))
return path;
g_free (path);
path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "icons" G_DIR_SEPARATOR_S "%s.png",
get_xdir (), entry->resource_name);
if (g_file_test (path, G_FILE_TEST_EXISTS))
return path;
g_free (path);
return NULL;
}
static char *
resolve_bundled_variant (const IconRegistryEntry *entry, IconResolverThemeVariant variant)
{
const char *variant_name = variant == ICON_RESOLVER_THEME_DARK ? "dark" : "light";
char *path;
if (entry->role == ICON_RESOLVER_ROLE_MENU_ACTION)
{
path = g_strdup_printf ("/icons/menu/%s/%s.png", variant_name, entry->resource_name);
if (resource_exists (path))
return path;
g_free (path);
path = g_strdup_printf ("/icons/menu/%s/%s.svg", variant_name, entry->resource_name);
if (resource_exists (path))
return path;
g_free (path);
}
else
{
path = g_strdup_printf ("/icons/%s-%s.png", entry->resource_name, variant_name);
if (resource_exists (path))
return path;
g_free (path);
}
return NULL;
}
static char *
resolve_bundled_neutral (const IconRegistryEntry *entry)
{
char *path;
if (entry->role == ICON_RESOLVER_ROLE_MENU_ACTION)
{
path = g_strdup_printf ("/icons/menu/light/%s.png", entry->resource_name);
if (resource_exists (path))
return path;
g_free (path);
path = g_strdup_printf ("/icons/menu/light/%s.svg", entry->resource_name);
if (resource_exists (path))
return path;
g_free (path);
}
else
{
path = g_strdup_printf ("/icons/%s.png", entry->resource_name);
if (resource_exists (path))
return path;
g_free (path);
}
return NULL;
}
char *
icon_resolver_resolve_path (IconResolverRole role, int item, GtkIconSize size,
const char *context, IconResolverThemeVariant variant,
const char **system_icon_name)
{
const IconRegistryEntry *entry;
IconResolverThemeVariant effective_variant = variant;
char *path;
(void)size;
(void)context;
entry = icon_registry_find (role, item);
if (!entry)
return NULL;
if (system_icon_name)
*system_icon_name = entry->system_icon_name;
if (effective_variant == ICON_RESOLVER_THEME_SYSTEM)
effective_variant = icon_resolver_detect_theme_variant ();
path = resolve_user_override (entry, effective_variant);
if (path)
return path;
path = resolve_bundled_variant (entry, effective_variant);
if (path)
return path;
return resolve_bundled_neutral (entry);
}

View File

@@ -1,89 +0,0 @@
#ifndef ZOITECHAT_ICON_RESOLVER_H
#define ZOITECHAT_ICON_RESOLVER_H
#include <gtk/gtk.h>
typedef enum
{
ICON_RESOLVER_ROLE_MENU_ACTION,
ICON_RESOLVER_ROLE_TRAY_STATE,
ICON_RESOLVER_ROLE_TREE_TYPE,
ICON_RESOLVER_ROLE_USERLIST_RANK
} IconResolverRole;
typedef enum
{
ICON_RESOLVER_THEME_SYSTEM,
ICON_RESOLVER_THEME_LIGHT,
ICON_RESOLVER_THEME_DARK
} IconResolverThemeVariant;
typedef enum
{
ICON_RESOLVER_MENU_ACTION_NEW,
ICON_RESOLVER_MENU_ACTION_NETWORK_LIST,
ICON_RESOLVER_MENU_ACTION_LOAD_PLUGIN,
ICON_RESOLVER_MENU_ACTION_DETACH,
ICON_RESOLVER_MENU_ACTION_CLOSE,
ICON_RESOLVER_MENU_ACTION_QUIT,
ICON_RESOLVER_MENU_ACTION_DISCONNECT,
ICON_RESOLVER_MENU_ACTION_CONNECT,
ICON_RESOLVER_MENU_ACTION_JOIN,
ICON_RESOLVER_MENU_ACTION_PREFERENCES,
ICON_RESOLVER_MENU_ACTION_CLEAR,
ICON_RESOLVER_MENU_ACTION_COPY,
ICON_RESOLVER_MENU_ACTION_DELETE,
ICON_RESOLVER_MENU_ACTION_ADD,
ICON_RESOLVER_MENU_ACTION_REMOVE,
ICON_RESOLVER_MENU_ACTION_SPELL_CHECK,
ICON_RESOLVER_MENU_ACTION_SAVE,
ICON_RESOLVER_MENU_ACTION_SAVE_AS,
ICON_RESOLVER_MENU_ACTION_REFRESH,
ICON_RESOLVER_MENU_ACTION_SEARCH,
ICON_RESOLVER_MENU_ACTION_FIND,
ICON_RESOLVER_MENU_ACTION_PREVIOUS,
ICON_RESOLVER_MENU_ACTION_NEXT,
ICON_RESOLVER_MENU_ACTION_HELP,
ICON_RESOLVER_MENU_ACTION_ABOUT,
ICON_RESOLVER_MENU_ACTION_EMOJI,
ICON_RESOLVER_MENU_ACTION_UPDATE,
ICON_RESOLVER_MENU_ACTION_CHANLIST
} IconResolverMenuAction;
typedef enum
{
ICON_RESOLVER_TRAY_STATE_NORMAL,
ICON_RESOLVER_TRAY_STATE_FILEOFFER,
ICON_RESOLVER_TRAY_STATE_HIGHLIGHT,
ICON_RESOLVER_TRAY_STATE_MESSAGE
} IconResolverTrayState;
typedef enum
{
ICON_RESOLVER_TREE_TYPE_CHANNEL,
ICON_RESOLVER_TREE_TYPE_DIALOG,
ICON_RESOLVER_TREE_TYPE_SERVER,
ICON_RESOLVER_TREE_TYPE_UTIL
} IconResolverTreeType;
typedef enum
{
ICON_RESOLVER_USERLIST_RANK_VOICE,
ICON_RESOLVER_USERLIST_RANK_HALFOP,
ICON_RESOLVER_USERLIST_RANK_OP,
ICON_RESOLVER_USERLIST_RANK_OWNER,
ICON_RESOLVER_USERLIST_RANK_FOUNDER,
ICON_RESOLVER_USERLIST_RANK_NETOP
} IconResolverUserlistRank;
const char *icon_resolver_icon_name_from_stock (const char *stock_name);
const char *icon_resolver_icon_name_for_menu_custom (const char *custom_icon_name);
gboolean icon_resolver_menu_action_from_custom (const char *custom_icon_name, int *action_out);
gboolean icon_resolver_menu_action_from_name (const char *name, int *action_out);
const char *icon_resolver_system_icon_name (IconResolverRole role, int item);
IconResolverThemeVariant icon_resolver_detect_theme_variant (void);
char *icon_resolver_resolve_path (IconResolverRole role, int item, GtkIconSize size,
const char *context, IconResolverThemeVariant variant,
const char **system_icon_name);
#endif

View File

@@ -23,7 +23,6 @@
#include <sys/stat.h>
#include <fcntl.h>
#include "fe-gtk.h"
#include "theme/theme-manager.h"
#include "../common/zoitechat.h"
#include "../common/ignore.h"
@@ -295,8 +294,10 @@ ignore_clear_entry_clicked (GtkWidget * wid)
dialog = gtk_message_dialog_new (NULL, 0,
GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
_("Are you sure you want to remove all ignores?"));
theme_manager_attach_window (dialog);
_("Are you sure you want to remove all ignores?"));
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
g_signal_connect (G_OBJECT (dialog), "response",
G_CALLBACK (ignore_clear_cb), NULL);
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);

View File

@@ -39,7 +39,6 @@
#include "fe-gtk.h"
#include "chanlist.h"
#include "gtkutil.h"
#include "theme/theme-manager.h"
#define ICON_JOIND_NETWORK "network-workgroup"
@@ -130,7 +129,9 @@ joind_show_dialog (server *serv)
char buf2[256];
serv->gui->joind_win = dialog1 = gtk_dialog_new ();
theme_manager_attach_window (dialog1);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog1);
g_snprintf(buf, sizeof(buf), _("Connection Complete - %s"), _(DISPLAY_NAME));
gtk_window_set_title (GTK_WINDOW (dialog1), buf);
gtk_window_set_type_hint (GTK_WINDOW (dialog1), GDK_WINDOW_TYPE_HINT_DIALOG);

View File

@@ -41,13 +41,10 @@
#include "../common/cfgfiles.h"
#include "fe-gtk.h"
#include "theme/theme-manager.h"
#include "theme/theme-css.h"
#include "banlist.h"
#include "gtkutil.h"
#include "joind.h"
#include "theme/theme-access.h"
#include "theme/theme-palette.h"
#include "palette.h"
#include "maingui.h"
#include "menu.h"
#include "fkeys.h"
@@ -219,7 +216,8 @@ mg_apply_font_css (GtkWidget *widget, const PangoFontDescription *desc,
g_string_free (css, TRUE);
gtk_style_context_add_class (context, class_name);
theme_css_apply_widget_provider (widget, GTK_STYLE_PROVIDER (provider));
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
static void
@@ -407,7 +405,7 @@ mg_attr_list_create (const XTextColor *col, int size)
static void
mg_create_tab_colors (void)
{
XTextColor gui_palette[XTEXT_COLS];
XTextColor gui_palette[MAX_COL + 1];
if (plain_list)
{
@@ -418,12 +416,12 @@ mg_create_tab_colors (void)
pango_attr_list_unref (away_list);
}
theme_get_xtext_colors (gui_palette, G_N_ELEMENTS (gui_palette));
palette_get_xtext_colors (gui_palette, G_N_ELEMENTS (gui_palette));
plain_list = mg_attr_list_create (NULL, prefs.hex_gui_tab_small);
newdata_list = mg_attr_list_create (&gui_palette[THEME_TOKEN_TAB_NEW_DATA], prefs.hex_gui_tab_small);
nickseen_list = mg_attr_list_create (&gui_palette[THEME_TOKEN_TAB_HIGHLIGHT], prefs.hex_gui_tab_small);
newmsg_list = mg_attr_list_create (&gui_palette[THEME_TOKEN_TAB_NEW_MESSAGE], prefs.hex_gui_tab_small);
away_list = mg_attr_list_create (&gui_palette[THEME_TOKEN_TAB_AWAY], FALSE);
newdata_list = mg_attr_list_create (&gui_palette[COL_NEW_DATA], prefs.hex_gui_tab_small);
nickseen_list = mg_attr_list_create (&gui_palette[COL_HILIGHT], prefs.hex_gui_tab_small);
newmsg_list = mg_attr_list_create (&gui_palette[COL_NEW_MSG], prefs.hex_gui_tab_small);
away_list = mg_attr_list_create (&gui_palette[COL_AWAY], FALSE);
}
static void
@@ -432,47 +430,12 @@ set_window_urgency (GtkWidget *win, gboolean set)
gtk_window_set_urgency_hint (GTK_WINDOW (win), set);
}
static gboolean
is_wayland_display (void)
{
GdkDisplay *display = gdk_display_get_default ();
const char *name;
if (!display)
return FALSE;
name = gdk_display_get_name (display);
if (!name)
return FALSE;
return g_str_has_prefix (name, "wayland");
}
static gboolean
is_kde_desktop (void)
{
const char *desktop = g_getenv ("XDG_CURRENT_DESKTOP");
if (desktop && strstr (desktop, "KDE"))
return TRUE;
return g_getenv ("KDE_FULL_SESSION") != NULL;
}
static gboolean
is_kde_wayland (void)
{
return is_wayland_display () && is_kde_desktop ();
}
static void
flash_window (GtkWidget *win)
{
#ifdef HAVE_GTK_MAC
gtkosx_application_attention_request (osx_app, INFO_REQUEST);
#endif
if (is_kde_wayland ())
gtk_window_present (GTK_WINDOW (win));
set_window_urgency (win, TRUE);
}
@@ -1399,7 +1362,9 @@ mg_tab_close (session *sess)
GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
_("This server still has %d channels or dialogs associated with it. "
"Close them all?"), i);
theme_manager_attach_window (dialog);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
g_signal_connect (G_OBJECT (dialog), "response",
G_CALLBACK (mg_tab_close_cb), sess);
if (prefs.hex_gui_tab_layout)
@@ -1497,7 +1462,9 @@ mg_open_quit_dialog (gboolean minimize_button)
}
dialog = gtk_dialog_new ();
theme_manager_attach_window (dialog);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
gtk_window_set_title (GTK_WINDOW (dialog), _("Quit ZoiteChat?"));
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
@@ -1790,13 +1757,7 @@ mg_create_color_menu (GtkWidget *menu, session *sess)
guint16 green;
guint16 blue;
GdkRGBA color;
if (!theme_get_mirc_color ((unsigned int) i, &color))
continue;
red = (guint16) CLAMP (color.red * 65535.0 + 0.5, 0.0, 65535.0);
green = (guint16) CLAMP (color.green * 65535.0 + 0.5, 0.0, 65535.0);
blue = (guint16) CLAMP (color.blue * 65535.0 + 0.5, 0.0, 65535.0);
palette_color_get_rgb16 (&colors[i], &red, &green, &blue);
sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
" </span></tt>",
i, red >> 8, green >> 8, blue >> 8);
@@ -1811,13 +1772,7 @@ mg_create_color_menu (GtkWidget *menu, session *sess)
guint16 green;
guint16 blue;
GdkRGBA color;
if (!theme_get_mirc_color ((unsigned int) i, &color))
continue;
red = (guint16) CLAMP (color.red * 65535.0 + 0.5, 0.0, 65535.0);
green = (guint16) CLAMP (color.green * 65535.0 + 0.5, 0.0, 65535.0);
blue = (guint16) CLAMP (color.blue * 65535.0 + 0.5, 0.0, 65535.0);
palette_color_get_rgb16 (&colors[i], &red, &green, &blue);
sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
" </span></tt>",
i, red >> 8, green >> 8, blue >> 8);
@@ -1914,7 +1869,7 @@ mg_create_tabmenu (session *sess, GdkEventButton *event, chan *ch)
if (sess)
{
char *name = g_markup_escape_text (sess->channel[0] ? sess->channel : _("<none>"), -1);
g_snprintf (buf, sizeof (buf), "<span foreground=\"#3344cc\"><b>%s</b></span>", name);
g_snprintf (buf, sizeof (buf), "<b>%s</b>", name);
g_free (name);
item = gtk_menu_item_new_with_label ("");
@@ -2410,7 +2365,8 @@ mg_limit_entry_cb (GtkWidget * igad, gpointer userdata)
static void
mg_apply_entry_style (GtkWidget *entry)
{
theme_manager_apply_entry_palette (entry, input_style->font_desc);
gtkutil_apply_palette (entry, &colors[COL_BG], &colors[COL_FG],
input_style->font_desc);
}
static void
@@ -2654,7 +2610,7 @@ mg_update_xtext (GtkWidget *wid)
const gchar *font_name;
XTextColor xtext_palette[XTEXT_COLS];
theme_get_xtext_colors_for_widget (wid, xtext_palette, XTEXT_COLS);
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
gtk_xtext_set_palette (xtext, xtext_palette);
gtk_xtext_set_max_lines (xtext, prefs.hex_text_max_lines);
gtk_xtext_set_background (xtext, channelwin_pix);
@@ -2701,7 +2657,7 @@ mg_create_textarea (session *sess, GtkWidget *box)
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (inbox), frame, TRUE, TRUE, 0);
theme_get_xtext_colors_for_widget (frame, xtext_palette, XTEXT_COLS);
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
gui->xtext = gtk_xtext_new (xtext_palette, TRUE);
xtext = GTK_XTEXT (gui->xtext);
gtk_xtext_set_max_indent (xtext, prefs.hex_text_max_indent);
@@ -2820,81 +2776,6 @@ mg_update_meters (session_gui *gui)
gtk_widget_show_all (gui->meter_box);
}
static void
mg_theme_apply_userlist_style (session_gui *gui)
{
const PangoFontDescription *font = NULL;
if (!gui || !gui->user_tree)
return;
if (input_style)
font = input_style->font_desc;
theme_manager_apply_userlist_style (gui->user_tree,
theme_manager_get_userlist_palette_behavior (font));
}
static void
mg_theme_userlist_changed (const ThemeChangedEvent *event, gpointer userdata)
{
session_gui *gui = userdata;
if (!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_USERLIST) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PALETTE) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_WIDGET_STYLE) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_MODE) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_THEME_PACK))
return;
mg_theme_apply_userlist_style (gui);
}
static void
mg_theme_window_changed (const ThemeChangedEvent *event, gpointer userdata)
{
session_gui *gui = userdata;
if (!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_MODE) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_THEME_PACK) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_WIDGET_STYLE))
return;
if (gui)
theme_manager_apply_to_window (gui->window);
}
static void
mg_theme_userlist_destroy_cb (GtkWidget *widget, gpointer userdata)
{
session_gui *gui = userdata;
(void) widget;
if (!gui)
return;
if (gui->theme_userlist_listener_id)
{
theme_listener_unregister (gui->theme_userlist_listener_id);
gui->theme_userlist_listener_id = 0;
}
}
static void
mg_theme_window_destroy_cb (GtkWidget *widget, gpointer userdata)
{
session_gui *gui = userdata;
(void) widget;
if (!gui)
return;
theme_manager_detach_window (gui->window);
if (gui->theme_window_listener_id)
{
theme_listener_unregister (gui->theme_window_listener_id);
gui->theme_window_listener_id = 0;
}
}
static void
mg_create_userlist (session_gui *gui, GtkWidget *box)
{
@@ -2912,10 +2793,19 @@ mg_create_userlist (session_gui *gui, GtkWidget *box)
gui->user_tree = ulist = userlist_create (vbox);
if (!gui->theme_userlist_listener_id)
gui->theme_userlist_listener_id = theme_listener_register ("maingui.userlist", mg_theme_userlist_changed, gui);
g_signal_connect (G_OBJECT (ulist), "destroy", G_CALLBACK (mg_theme_userlist_destroy_cb), gui);
mg_theme_apply_userlist_style (gui);
/*
* Keep the user list in sync with the chat palette.
*
* - When "Use the text box font and colors" is enabled, we already want the
* palette background.
* - When "Dark mode" is enabled, we also force the user list to use the
* palette colors so it doesn't stay blindingly white on dark themes.
*/
if (prefs.hex_gui_ulist_style || fe_dark_mode_is_enabled ())
{
gtkutil_apply_palette (ulist, &colors[COL_BG], &colors[COL_FG],
input_style ? input_style->font_desc : NULL);
}
mg_create_meters (gui, vbox);
@@ -3742,6 +3632,7 @@ mg_create_topwindow (session *sess)
g_signal_connect (G_OBJECT (win), "configure_event",
G_CALLBACK (mg_configure_cb), sess);
palette_alloc (win);
table = gtk_grid_new ();
/* spacing under the menubar */
@@ -3796,10 +3687,9 @@ mg_create_topwindow (session *sess)
mg_place_userlist_and_chanview (sess->gui);
gtk_widget_show (win);
if (!sess->gui->theme_window_listener_id)
sess->gui->theme_window_listener_id = theme_listener_register ("maingui.window", mg_theme_window_changed, sess->gui);
g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (mg_theme_window_destroy_cb), sess->gui);
theme_manager_attach_window (win);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (win);
#ifdef G_OS_WIN32
parent_win = gtk_widget_get_window (win);
@@ -3848,7 +3738,7 @@ mg_win32_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
if (msg->message == WM_SETTINGCHANGE || msg->message == WM_THEMECHANGED)
{
theme_manager_refresh_auto_mode ();
fe_refresh_auto_dark_mode ();
return GDK_FILTER_CONTINUE;
}
@@ -3936,6 +3826,7 @@ mg_create_tabwindow (session *sess)
g_signal_connect (G_OBJECT (win), "window_state_event",
G_CALLBACK (mg_windowstate_cb), NULL);
palette_alloc (win);
sess->gui->main_table = table = gtk_grid_new ();
/* spacing under the menubar */
@@ -3974,10 +3865,9 @@ mg_create_tabwindow (session *sess)
mg_place_userlist_and_chanview (sess->gui);
gtk_widget_show (win);
if (!sess->gui->theme_window_listener_id)
sess->gui->theme_window_listener_id = theme_listener_register ("maingui.window", mg_theme_window_changed, sess->gui);
g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (mg_theme_window_destroy_cb), sess->gui);
theme_manager_attach_window (win);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (win);
#ifdef G_OS_WIN32
parent_win = gtk_widget_get_window (win);
@@ -3988,6 +3878,7 @@ mg_create_tabwindow (session *sess)
void
mg_apply_setup (void)
{
GList *toplevels, *node;
GSList *list = sess_list;
session *sess;
int done_main = FALSE;
@@ -4001,10 +3892,17 @@ mg_apply_setup (void)
((xtext_buffer *)sess->res->buffer)->needs_recalc = TRUE;
if (!sess->gui->is_tab || !done_main)
mg_place_userlist_and_chanview (sess->gui);
if (sess->gui->window)
fe_apply_theme_to_toplevel (sess->gui->window);
if (sess->gui->is_tab)
done_main = TRUE;
list = list->next;
}
toplevels = gtk_window_list_toplevels ();
for (node = toplevels; node; node = node->next)
fe_apply_theme_to_toplevel (GTK_WIDGET (node->data));
g_list_free (toplevels);
}
static chan *
@@ -4336,8 +4234,6 @@ fe_session_callback (session *sess)
{
gtk_xtext_buffer_free (sess->res->buffer);
g_object_unref (G_OBJECT (sess->res->user_model));
if (sess->res->user_row_refs)
g_hash_table_destroy (sess->res->user_row_refs);
if (sess->res->banlist && sess->res->banlist->window)
mg_close_gen (NULL, sess->res->banlist->window);

View File

@@ -54,8 +54,7 @@
#include "notifygui.h"
#include "pixmaps.h"
#include "rawlog.h"
#include "theme/theme-gtk.h"
#include "theme/theme-manager.h"
#include "palette.h"
#include "plugingui.h"
#include "search.h"
#include "textgui.h"
@@ -66,28 +65,6 @@
static GSList *submenu_list;
static gboolean
menu_icon_exists_in_resource (const char *icon_name)
{
char *resource_path;
gboolean found;
if (!icon_name || !g_str_has_prefix (icon_name, "zc-menu-"))
return FALSE;
resource_path = g_strdup_printf ("/icons/menu/light/%s.png", icon_name + strlen ("zc-menu-"));
found = g_resources_get_info (resource_path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL);
if (!found)
{
g_free (resource_path);
resource_path = g_strdup_printf ("/icons/menu/light/%s.svg", icon_name + strlen ("zc-menu-"));
found = g_resources_get_info (resource_path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL);
}
g_free (resource_path);
return found;
}
static GtkWidget *
menu_icon_widget_new (const char *icon)
{
@@ -113,7 +90,7 @@ menu_icon_widget_new (const char *icon)
{
char *menu_icon_name = g_strdup_printf ("zc-menu-%s", icon);
if (menu_icon_exists_in_resource (menu_icon_name))
if (gtkutil_menu_icon_exists (menu_icon_name))
img = gtkutil_image_new_from_stock (menu_icon_name, GTK_ICON_SIZE_MENU);
else
img = gtkutil_image_new_from_stock (icon, GTK_ICON_SIZE_MENU);
@@ -1521,7 +1498,9 @@ menu_join (GtkWidget * wid, gpointer none)
_("_Cancel"), GTK_RESPONSE_REJECT,
_("_OK"), GTK_RESPONSE_ACCEPT,
NULL);
theme_manager_attach_window (dialog);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
{
GtkWidget *button;
@@ -1858,7 +1837,9 @@ static void
menu_about (GtkWidget *wid, gpointer sess)
{
GtkAboutDialog *dialog = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
theme_manager_attach_window (GTK_WIDGET (dialog));
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark/.zoitechat-light. */
fe_apply_theme_to_toplevel (GTK_WIDGET (dialog));
char comment[512];
char *license = "This program is free software; you can redistribute it and/or modify\n" \
"it under the terms of the GNU General Public License as published by\n" \
@@ -2486,7 +2467,6 @@ menu_create_main (void *accel_group, int bar, int away, int toplevel,
if (bar)
{
menu_bar = gtk_menu_bar_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (menu_bar), GTK_STYLE_CLASS_MENUBAR);
#ifdef HAVE_GTK_MAC
gtkosx_application_set_menu_bar (osx_app, GTK_MENU_SHELL (menu_bar));
#endif

View File

@@ -1,15 +1,3 @@
zoitechat_theme_sources = [
'theme/theme-access.c',
'theme/theme-application.c',
'theme/theme-css.c',
'theme/theme-gtk3.c',
'theme/theme-manager.c',
'theme/theme-palette.c',
'theme/theme-preferences.c',
'theme/theme-policy.c',
'theme/theme-runtime.c',
]
zoitechat_gtk_sources = [
'ascii.c',
'banlist.c',
@@ -21,12 +9,12 @@ zoitechat_gtk_sources = [
'fe-gtk.c',
'fkeys.c',
'gtkutil.c',
'icon-resolver.c',
'ignoregui.c',
'joind.c',
'menu.c',
'maingui.c',
'notifygui.c',
'palette.c',
'pixmaps.c',
'plugin-tray.c',
'plugin-notification.c',
@@ -49,8 +37,6 @@ zoitechat_gtk_deps = [
gtk_dep = dependency('gtk+-3.0', version: '>= 3.22')
zoitechat_theme_deps = [gtk_dep]
if host_machine.system() != 'windows'
appindicator_opt = get_option('appindicator')
@@ -90,19 +76,11 @@ zoitechat_gtk_ldflags = []
if host_machine.system() == 'windows'
zoitechat_gtk_sources += 'notifications/notification-windows.c'
zoitechat_gtk_deps += cc.find_library('dwmapi', required: true)
zoitechat_theme_deps += cc.find_library('dwmapi', required: true)
else
zoitechat_gtk_sources += 'notifications/notification-freedesktop.c'
endif
zoitechat_theme_lib = static_library('zoitechat_theme',
sources: zoitechat_theme_sources,
include_directories: [config_h_include],
dependencies: zoitechat_theme_deps,
install: false,
)
iso_codes = dependency('iso-codes', required: false)
if iso_codes.found()
zoitechat_gtk_sources += 'sexy-iso-codes.c'
@@ -130,124 +108,9 @@ endif
executable('zoitechat',
sources: resources + zoitechat_gtk_sources,
dependencies: zoitechat_gtk_deps,
link_with: zoitechat_theme_lib,
c_args: zoitechat_gtk_cflags,
link_args: zoitechat_gtk_ldflags,
pie: true,
install: true,
gui_app: true,
)
theme_manager_policy_tests = executable('theme_manager_policy_tests',
[
'theme/tests/test-theme-manager-policy.c',
'theme/theme-manager.c',
'theme/theme-palette.c',
'theme/tests/test-theme-gtk3-stub.c',
],
include_directories: [config_h_include],
dependencies: [gtk_dep],
)
test('Theme Manager/Policy Tests', theme_manager_policy_tests,
protocol: 'tap',
timeout: 120,
)
theme_manager_dispatch_tests = executable('theme_manager_dispatch_routing_tests',
[
'theme/tests/test-theme-manager-dispatch-routing.c',
'theme/theme-manager.c',
'theme/theme-palette.c',
'theme/tests/test-theme-gtk3-stub.c',
],
include_directories: [config_h_include],
dependencies: [gtk_dep],
)
test('Theme Manager Dispatch Routing Tests', theme_manager_dispatch_tests,
protocol: 'tap',
timeout: 120,
)
theme_manager_auto_refresh_tests = executable('theme_manager_auto_refresh_tests',
[
'theme/tests/test-theme-manager-auto-refresh.c',
'theme/theme-manager.c',
'theme/theme-palette.c',
'theme/tests/test-theme-gtk3-stub.c',
],
include_directories: [config_h_include],
dependencies: [gtk_dep],
)
test('Theme Manager Auto Refresh Tests', theme_manager_auto_refresh_tests,
protocol: 'tap',
timeout: 120,
)
theme_application_input_style_tests = executable('theme_application_input_style_tests',
[
'theme/tests/test-theme-application-input-style.c',
'theme/theme-application.c',
'theme/tests/test-theme-gtk3-stub.c',
],
include_directories: [config_h_include],
dependencies: [gtk_dep],
)
test('Theme Application Input Style Tests', theme_application_input_style_tests,
protocol: 'tap',
timeout: 120,
)
theme_runtime_tests = executable('theme_runtime_persistence_tests',
'theme/tests/test-theme-runtime-persistence.c',
include_directories: [config_h_include],
dependencies: [gtk_dep],
link_with: zoitechat_theme_lib,
)
test('Theme Runtime Persistence Tests', theme_runtime_tests,
protocol: 'tap',
timeout: 120,
)
theme_access_tests = executable('theme_access_routing_tests',
'theme/tests/test-theme-access-routing.c',
include_directories: [config_h_include],
dependencies: [gtk_dep],
link_with: zoitechat_theme_lib,
)
test('Theme Access Routing Tests', theme_access_tests,
protocol: 'tap',
timeout: 120,
)
theme_gtk3_settings_tests = executable('theme_gtk3_settings_tests',
[
'theme/tests/test-theme-gtk3-settings.c',
'theme/theme-gtk3.c',
],
include_directories: [config_h_include],
dependencies: [gtk_dep],
)
test('Theme GTK3 Settings Tests', theme_gtk3_settings_tests,
protocol: 'tap',
timeout: 120,
)
theme_preferences_gtk3_populate_tests = executable('theme_preferences_gtk3_populate_tests',
'theme/tests/test-theme-preferences-gtk3-populate.c',
include_directories: [config_h_include],
dependencies: [gtk_dep],
)
test('Theme Preferences GTK3 Populate Tests', theme_preferences_gtk3_populate_tests,
protocol: 'tap',
timeout: 120,
)

View File

@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -30,6 +34,19 @@
<TargetName>hcnotifications-winrt</TargetName>
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY;_CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT;NDEBUG;_WINDOWS;_USRDLL;NOTIFICATIONS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalUsingDirectories>$(VC_LibraryPath_VC_x86_Store)\references;$(WindowsSDK_UnionMetadataPath);$(VCInstallDir)vcpackages;$(FrameworkSdkDir)References\CommonConfiguration\Neutral;%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<CompileAsWinRT>true</CompileAsWinRT>
</ClCompile>
<Link>
<AdditionalDependencies>$(DepLibs);mincore.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
<MinimumRequiredVersion>6.03</MinimumRequiredVersion>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY;_CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT;NDEBUG;_WINDOWS;_USRDLL;NOTIFICATIONS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@@ -34,10 +34,8 @@
#include "../common/outbound.h"
#include "gtkutil.h"
#include "maingui.h"
#include "theme/theme-gtk.h"
#include "palette.h"
#include "notifygui.h"
#include "theme/theme-access.h"
#include "theme/theme-manager.h"
#define ICON_NOTIFY_NEW "document-new"
#define ICON_NOTIFY_DELETE "edit-delete"
@@ -71,7 +69,7 @@ notify_closegui (void)
}
/* Need this to be able to set the foreground colour property of a row
* from a GdkRGBA * in the model -Vince
* from a PaletteColor * in the model -Vince
*/
static void
notify_treecell_property_mapper (GtkTreeViewColumn *col, GtkCellRenderer *cell,
@@ -79,21 +77,21 @@ notify_treecell_property_mapper (GtkTreeViewColumn *col, GtkCellRenderer *cell,
gpointer data)
{
gchar *text;
GdkRGBA *colour;
PaletteColor *colour;
int model_column = GPOINTER_TO_INT (data);
gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
COLOUR_COLUMN, &colour,
model_column, &text, -1);
g_object_set (G_OBJECT (cell), "text", text,
THEME_GTK_FOREGROUND_PROPERTY, colour, NULL);
PALETTE_FOREGROUND_PROPERTY, colour, NULL);
if (colour)
gdk_rgba_free (colour);
g_free (text);
}
static void
notify_store_color (GtkListStore *store, GtkTreeIter *iter, const GdkRGBA *color)
notify_store_color (GtkListStore *store, GtkTreeIter *iter, const PaletteColor *color)
{
if (color)
{
@@ -137,7 +135,7 @@ notify_treeview_new (GtkWidget *box)
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
THEME_GTK_COLOR_TYPE,
PALETTE_GDK_TYPE,
G_TYPE_POINTER
);
g_return_val_if_fail (store != NULL, NULL);
@@ -172,7 +170,6 @@ notify_gui_update (void)
GSList *slist;
gchar *name, *status, *server, *seen;
int online, servcount, lastseenminutes;
GdkRGBA color;
time_t lastseen;
char agobuf[128];
@@ -228,10 +225,7 @@ notify_gui_update (void)
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter, 0, name, 1, status,
2, server, 3, seen, 5, NULL, -1);
if (theme_get_color (THEME_TOKEN_MIRC_4, &color))
notify_store_color (store, &iter, &color);
else
notify_store_color (store, &iter, NULL);
notify_store_color (store, &iter, &colors[4]);
if (valid)
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
@@ -257,10 +251,7 @@ notify_gui_update (void)
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter, 0, name, 1, status,
2, server, 3, seen, 5, servnot, -1);
if (theme_get_color (THEME_TOKEN_MIRC_3, &color))
notify_store_color (store, &iter, &color);
else
notify_store_color (store, &iter, NULL);
notify_store_color (store, &iter, &colors[3]);
if (valid)
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
@@ -382,7 +373,9 @@ fe_notify_ask (char *nick, char *networks)
LABEL_NOTIFY_CANCEL, GTK_RESPONSE_REJECT,
LABEL_NOTIFY_OK, GTK_RESPONSE_ACCEPT,
NULL);
theme_manager_attach_window (dialog);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
if (parent_window)
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);

474
src/fe-gtk/palette.c Normal file
View File

@@ -0,0 +1,474 @@
/* X-Chat
* Copyright (C) 1998 Peter Zelezny.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include "fe-gtk.h"
#include "palette.h"
#include "../common/zoitechat.h"
#include "../common/zoitechatc.h" /* prefs */
#include "../common/util.h"
#include "../common/cfgfiles.h"
#include "../common/typedef.h"
#define PALETTE_COLOR_INIT(r, g, b) { (r) / 65535.0, (g) / 65535.0, (b) / 65535.0, 1.0 }
static void
palette_color_set_rgb16 (PaletteColor *color, guint16 red, guint16 green, guint16 blue)
{
char color_string[16];
GdkRGBA parsed = { 0 };
gboolean parsed_ok;
g_snprintf (color_string, sizeof (color_string), "#%04x%04x%04x",
red, green, blue);
parsed_ok = gdk_rgba_parse (&parsed, color_string);
if (!parsed_ok)
{
parsed.red = red / 65535.0;
parsed.green = green / 65535.0;
parsed.blue = blue / 65535.0;
parsed.alpha = 1.0;
}
*color = parsed;
}
static XTextColor
palette_color_from_gdk (const PaletteColor *color)
{
XTextColor result;
result.red = color->red;
result.green = color->green;
result.blue = color->blue;
result.alpha = color->alpha;
return result;
}
PaletteColor colors[] = {
/* colors for xtext */
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf), /* 0 white */
PALETTE_COLOR_INIT (0x2e2e, 0x3434, 0x3636), /* 1 black */
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4), /* 2 blue */
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606), /* 3 green */
PALETTE_COLOR_INIT (0xcccc, 0x0000, 0x0000), /* 4 red */
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202), /* 5 light red */
PALETTE_COLOR_INIT (0x5c5c, 0x3535, 0x6666), /* 6 purple */
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000), /* 7 orange */
PALETTE_COLOR_INIT (0xc4c4, 0xa0a0, 0x0000), /* 8 yellow */
PALETTE_COLOR_INIT (0x7373, 0xd2d2, 0x1616), /* 9 green */
PALETTE_COLOR_INIT (0x1111, 0xa8a8, 0x7979), /* 10 aqua */
PALETTE_COLOR_INIT (0x5858, 0xa1a1, 0x9d9d), /* 11 light aqua */
PALETTE_COLOR_INIT (0x5757, 0x7979, 0x9e9e), /* 12 blue */
PALETTE_COLOR_INIT (0xa0d0, 0x42d4, 0x6562), /* 13 light purple */
PALETTE_COLOR_INIT (0x5555, 0x5757, 0x5353), /* 14 grey */
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585), /* 15 light grey */
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf), /* 16 white */
PALETTE_COLOR_INIT (0x2e2e, 0x3434, 0x3636), /* 17 black */
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4), /* 18 blue */
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606), /* 19 green */
PALETTE_COLOR_INIT (0xcccc, 0x0000, 0x0000), /* 20 red */
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202), /* 21 light red */
PALETTE_COLOR_INIT (0x5c5c, 0x3535, 0x6666), /* 22 purple */
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000), /* 23 orange */
PALETTE_COLOR_INIT (0xc4c4, 0xa0a0, 0x0000), /* 24 yellow */
PALETTE_COLOR_INIT (0x7373, 0xd2d2, 0x1616), /* 25 green */
PALETTE_COLOR_INIT (0x1111, 0xa8a8, 0x7979), /* 26 aqua */
PALETTE_COLOR_INIT (0x5858, 0xa1a1, 0x9d9d), /* 27 light aqua */
PALETTE_COLOR_INIT (0x5757, 0x7979, 0x9e9e), /* 28 blue */
PALETTE_COLOR_INIT (0xa0d0, 0x42d4, 0x6562), /* 29 light purple */
PALETTE_COLOR_INIT (0x5555, 0x5757, 0x5353), /* 30 grey */
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585), /* 31 light grey */
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf), /* 32 marktext Fore (white) */
PALETTE_COLOR_INIT (0x2020, 0x4a4a, 0x8787), /* 33 marktext Back (blue) */
PALETTE_COLOR_INIT (0x2512, 0x29e8, 0x2b85), /* 34 foreground (black) */
PALETTE_COLOR_INIT (0xfae0, 0xfae0, 0xf8c4), /* 35 background (white) */
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202), /* 36 marker line (red) */
/* colors for GUI */
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4), /* 37 tab New Data (dark red) */
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606), /* 38 tab Nick Mentioned (blue) */
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000), /* 39 tab New Message (red) */
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585), /* 40 away user (grey) */
PALETTE_COLOR_INIT (0xa4a4, 0x0000, 0x0000), /* 41 spell checker color (red) */
};
/* User palette snapshot (what we write to colors.conf) */
static PaletteColor user_colors[MAX_COL + 1];
static gboolean user_colors_valid = FALSE;
/* Dark palette snapshot (saved separately so dark mode can have its own custom palette). */
static PaletteColor dark_user_colors[MAX_COL + 1];
static gboolean dark_user_colors_valid = FALSE;
/* ZoiteChat's curated dark palette (applies when prefs.hex_gui_dark_mode is enabled). */
static const PaletteColor dark_colors[MAX_COL + 1] = {
/* mIRC colors 0-15 */
PALETTE_COLOR_INIT (0xe5e5, 0xe5e5, 0xe5e5), /* 0 white */
PALETTE_COLOR_INIT (0x3c3c, 0x3c3c, 0x3c3c), /* 1 black (dark gray for contrast) */
PALETTE_COLOR_INIT (0x5656, 0x9c9c, 0xd6d6), /* 2 blue */
PALETTE_COLOR_INIT (0x0d0d, 0xbcbc, 0x7979), /* 3 green */
PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747), /* 4 red */
PALETTE_COLOR_INIT (0xcece, 0x9191, 0x7878), /* 5 light red / brown */
PALETTE_COLOR_INIT (0xc5c5, 0x8686, 0xc0c0), /* 6 purple */
PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d), /* 7 orange */
PALETTE_COLOR_INIT (0xdcdc, 0xdcdc, 0xaaaa), /* 8 yellow */
PALETTE_COLOR_INIT (0xb5b5, 0xcece, 0xa8a8), /* 9 light green */
PALETTE_COLOR_INIT (0x4e4e, 0xc9c9, 0xb0b0), /* 10 aqua */
PALETTE_COLOR_INIT (0x9c9c, 0xdcdc, 0xfefe), /* 11 light aqua */
PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff), /* 12 light blue */
PALETTE_COLOR_INIT (0xd6d6, 0x7070, 0xd6d6), /* 13 pink */
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), /* 14 gray */
PALETTE_COLOR_INIT (0xc0c0, 0xc0c0, 0xc0c0), /* 15 light gray */
/* mIRC colors 16-31 (repeat) */
PALETTE_COLOR_INIT (0xe5e5, 0xe5e5, 0xe5e5), PALETTE_COLOR_INIT (0x3c3c, 0x3c3c, 0x3c3c),
PALETTE_COLOR_INIT (0x5656, 0x9c9c, 0xd6d6), PALETTE_COLOR_INIT (0x0d0d, 0xbcbc, 0x7979),
PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747), PALETTE_COLOR_INIT (0xcece, 0x9191, 0x7878),
PALETTE_COLOR_INIT (0xc5c5, 0x8686, 0xc0c0), PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d),
PALETTE_COLOR_INIT (0xdcdc, 0xdcdc, 0xaaaa), PALETTE_COLOR_INIT (0xb5b5, 0xcece, 0xa8a8),
PALETTE_COLOR_INIT (0x4e4e, 0xc9c9, 0xb0b0), PALETTE_COLOR_INIT (0x9c9c, 0xdcdc, 0xfefe),
PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff), PALETTE_COLOR_INIT (0xd6d6, 0x7070, 0xd6d6),
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), PALETTE_COLOR_INIT (0xc0c0, 0xc0c0, 0xc0c0),
/* selection colors */
PALETTE_COLOR_INIT (0xffff, 0xffff, 0xffff), /* 32 COL_MARK_FG */
PALETTE_COLOR_INIT (0x2626, 0x4f4f, 0x7878), /* 33 COL_MARK_BG */
/* foreground/background */
PALETTE_COLOR_INIT (0xd4d4, 0xd4d4, 0xd4d4), /* 34 COL_FG */
PALETTE_COLOR_INIT (0x1e1e, 0x1e1e, 0x1e1e), /* 35 COL_BG */
/* interface colors */
PALETTE_COLOR_INIT (0x4040, 0x4040, 0x4040), /* 36 COL_MARKER (marker line) */
PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff), /* 37 COL_NEW_DATA (tab: new data) */
PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d), /* 38 COL_HILIGHT (tab: nick mentioned) */
PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747), /* 39 COL_NEW_MSG (tab: new message) */
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), /* 40 COL_AWAY (tab: away) */
PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747), /* 41 COL_SPELL (spellcheck underline) */
};
void
palette_get_xtext_colors (XTextColor *palette, size_t palette_len)
{
size_t i;
size_t count = palette_len < G_N_ELEMENTS (colors) ? palette_len : G_N_ELEMENTS (colors);
for (i = 0; i < count; i++)
{
palette[i] = palette_color_from_gdk (&colors[i]);
}
}
void
palette_user_set_color (int idx, const PaletteColor *col)
{
if (!col)
return;
if (idx < 0 || idx > MAX_COL)
return;
if (!user_colors_valid)
{
memcpy (user_colors, colors, sizeof (user_colors));
user_colors_valid = TRUE;
}
user_colors[idx] = *col;
}
void
palette_dark_set_color (int idx, const PaletteColor *col)
{
if (!col)
return;
if (idx < 0 || idx > MAX_COL)
return;
if (!dark_user_colors_valid)
{
/* Start from the currently active palette (should be dark when editing dark mode). */
memcpy (dark_user_colors, colors, sizeof (dark_user_colors));
dark_user_colors_valid = TRUE;
}
dark_user_colors[idx] = *col;
}
const PaletteColor *
palette_user_colors (void)
{
if (!user_colors_valid)
{
memcpy (user_colors, colors, sizeof (user_colors));
user_colors_valid = TRUE;
}
return user_colors;
}
const PaletteColor *
palette_dark_colors (void)
{
if (!dark_user_colors_valid)
return dark_colors;
return dark_user_colors;
}
void
palette_alloc (GtkWidget * widget)
{
(void) widget;
}
void
palette_load (void)
{
int i, j, fh;
char prefname[256];
struct stat st;
char *cfg;
guint16 red, green, blue;
gboolean dark_found = FALSE;
fh = zoitechat_open_file ("colors.conf", O_RDONLY, 0, 0);
if (fh != -1)
{
fstat (fh, &st);
cfg = g_malloc0 (st.st_size + 1);
read (fh, cfg, st.st_size);
/* Light palette (default behavior): mIRC colors 0-31. */
for (i = 0; i < 32; i++)
{
g_snprintf (prefname, sizeof prefname, "color_%d", i);
if (cfg_get_color (cfg, prefname, &red, &green, &blue))
{
palette_color_set_rgb16 (&colors[i], red, green, blue);
}
}
/* Light palette: our special colors are mapped at 256+. */
for (i = 256, j = 32; j < MAX_COL + 1; i++, j++)
{
g_snprintf (prefname, sizeof prefname, "color_%d", i);
if (cfg_get_color (cfg, prefname, &red, &green, &blue))
{
palette_color_set_rgb16 (&colors[j], red, green, blue);
}
}
/* Dark palette: start from curated defaults and optionally override from colors.conf. */
memcpy (dark_user_colors, dark_colors, sizeof (dark_user_colors));
for (i = 0; i < 32; i++)
{
g_snprintf (prefname, sizeof prefname, "dark_color_%d", i);
if (cfg_get_color (cfg, prefname, &red, &green, &blue))
{
palette_color_set_rgb16 (&dark_user_colors[i], red, green, blue);
dark_found = TRUE;
}
}
for (i = 256, j = 32; j < MAX_COL + 1; i++, j++)
{
g_snprintf (prefname, sizeof prefname, "dark_color_%d", i);
if (cfg_get_color (cfg, prefname, &red, &green, &blue))
{
palette_color_set_rgb16 (&dark_user_colors[j], red, green, blue);
dark_found = TRUE;
}
}
dark_user_colors_valid = dark_found;
g_free (cfg);
close (fh);
}
/* Snapshot the user's (light) palette for dark mode toggling. */
memcpy (user_colors, colors, sizeof (user_colors));
user_colors_valid = TRUE;
}
void
palette_save (void)
{
int i, j, fh;
char prefname[256];
const PaletteColor *lightpal = colors;
const PaletteColor *darkpal = NULL;
gboolean dark_mode_active = fe_dark_mode_is_enabled ();
/* If we're currently in dark mode, keep colors.conf's legacy keys as the user's light palette. */
if (dark_mode_active && user_colors_valid)
lightpal = user_colors;
/* If we're currently in light mode, ensure the snapshot stays in sync. */
if (!dark_mode_active)
{
memcpy (user_colors, colors, sizeof (user_colors));
user_colors_valid = TRUE;
}
/* If dark mode is enabled but we haven't snapshotted a custom dark palette yet, capture it now. */
if (dark_mode_active && !dark_user_colors_valid)
{
memcpy (dark_user_colors, colors, sizeof (dark_user_colors));
dark_user_colors_valid = TRUE;
}
if (dark_user_colors_valid)
darkpal = dark_user_colors;
else if (dark_mode_active)
darkpal = colors; /* current dark palette (likely defaults) */
fh = zoitechat_open_file ("colors.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
if (fh != -1)
{
/* Light palette (legacy keys) */
for (i = 0; i < 32; i++)
{
g_snprintf (prefname, sizeof prefname, "color_%d", i);
guint16 red;
guint16 green;
guint16 blue;
palette_color_get_rgb16 (&lightpal[i], &red, &green, &blue);
cfg_put_color (fh, red, green, blue, prefname);
}
for (i = 256, j = 32; j < MAX_COL + 1; i++, j++)
{
g_snprintf (prefname, sizeof prefname, "color_%d", i);
{
guint16 red;
guint16 green;
guint16 blue;
palette_color_get_rgb16 (&lightpal[j], &red, &green, &blue);
cfg_put_color (fh, red, green, blue, prefname);
}
}
/* Dark palette (new keys) */
if (darkpal)
{
for (i = 0; i < 32; i++)
{
g_snprintf (prefname, sizeof prefname, "dark_color_%d", i);
guint16 red;
guint16 green;
guint16 blue;
palette_color_get_rgb16 (&darkpal[i], &red, &green, &blue);
cfg_put_color (fh, red, green, blue, prefname);
}
for (i = 256, j = 32; j < MAX_COL + 1; i++, j++)
{
g_snprintf (prefname, sizeof prefname, "dark_color_%d", i);
{
guint16 red;
guint16 green;
guint16 blue;
palette_color_get_rgb16 (&darkpal[j], &red, &green, &blue);
cfg_put_color (fh, red, green, blue, prefname);
}
}
}
close (fh);
}
}
static gboolean
palette_color_eq (const PaletteColor *a, const PaletteColor *b)
{
guint16 red_a;
guint16 green_a;
guint16 blue_a;
guint16 red_b;
guint16 green_b;
guint16 blue_b;
palette_color_get_rgb16 (a, &red_a, &green_a, &blue_a);
palette_color_get_rgb16 (b, &red_b, &green_b, &blue_b);
return red_a == red_b && green_a == green_b && blue_a == blue_b;
}
gboolean
palette_apply_dark_mode (gboolean enable)
{
PaletteColor old_colors[MAX_COL + 1];
int i;
gboolean changed = FALSE;
memcpy (old_colors, colors, sizeof (old_colors));
/* Ensure we have a snapshot of the user's palette before overriding anything. */
if (!user_colors_valid)
{
memcpy (user_colors, colors, sizeof (user_colors));
user_colors_valid = TRUE;
}
if (enable)
{
if (dark_user_colors_valid)
memcpy (colors, dark_user_colors, sizeof (colors));
else
memcpy (colors, dark_colors, sizeof (colors));
}
else
memcpy (colors, user_colors, sizeof (colors));
/* Track whether any palette entries changed. */
(void) i;
for (i = 0; i <= MAX_COL; i++)
{
if (!palette_color_eq (&old_colors[i], &colors[i]))
{
changed = TRUE;
break;
}
}
return changed;
}

View File

@@ -20,12 +20,63 @@
#ifndef ZOITECHAT_PALETTE_H
#define ZOITECHAT_PALETTE_H
#include "theme/theme-gtk.h"
#include "theme/theme-palette.h"
#include <stddef.h>
#include "xtext-color.h"
typedef GdkRGBA PaletteColor;
#define PALETTE_GDK_TYPE THEME_GTK_COLOR_TYPE
#define PALETTE_FOREGROUND_PROPERTY THEME_GTK_FOREGROUND_PROPERTY
#define PALETTE_GDK_TYPE GDK_TYPE_RGBA
#define PALETTE_FOREGROUND_PROPERTY "foreground-rgba"
extern PaletteColor colors[];
static inline void
palette_color_get_rgb16 (const PaletteColor *color, guint16 *red, guint16 *green, guint16 *blue)
{
*red = (guint16) CLAMP (color->red * 65535.0 + 0.5, 0.0, 65535.0);
*green = (guint16) CLAMP (color->green * 65535.0 + 0.5, 0.0, 65535.0);
*blue = (guint16) CLAMP (color->blue * 65535.0 + 0.5, 0.0, 65535.0);
}
#define COL_MARK_FG 32
#define COL_MARK_BG 33
#define COL_FG 34
#define COL_BG 35
#define COL_MARKER 36
#define COL_NEW_DATA 37
#define COL_HILIGHT 38
#define COL_NEW_MSG 39
#define COL_AWAY 40
#define COL_SPELL 41
#define MAX_COL 41
void palette_alloc (GtkWidget * widget);
void palette_load (void);
void palette_save (void);
/* Keep a copy of the user's palette so dark mode can be toggled without losing it. */
void palette_user_set_color (int idx, const PaletteColor *col);
void palette_dark_set_color (int idx, const PaletteColor *col);
const PaletteColor *palette_user_colors (void);
const PaletteColor *palette_dark_colors (void);
/*
* Apply ZoiteChat's built-in "dark mode" palette.
*
* When enabled, ZoiteChat switches to a curated dark-friendly palette for:
* - message colors (mIRC palette)
* - selection colors
* - tab highlight colors
* - chat/user/channel list background + foreground
*
* The user's palette is preserved in-memory and written to colors.conf even
* while dark mode is enabled, so disabling dark mode restores the previous
* colors without surprises.
*
* Returns TRUE if any palette entries were changed.
*/
gboolean palette_apply_dark_mode (gboolean enable);
void palette_get_xtext_colors (XTextColor *palette, size_t palette_len);
#endif

View File

@@ -25,7 +25,6 @@
#include "../common/zoitechat.h"
#include "../common/fe.h"
#include "resources.h"
#include "icon-resolver.h"
#include <gio/gio.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
@@ -118,43 +117,11 @@ pixmap_load_from_file_real (char *file)
{
GdkPixbuf *img;
cairo_surface_t *surface;
int width;
int height;
const int max_dimension = 4096;
img = gdk_pixbuf_new_from_file (file, 0);
if (!img)
return NULL;
width = gdk_pixbuf_get_width (img);
height = gdk_pixbuf_get_height (img);
if (width > max_dimension || height > max_dimension)
{
GdkPixbuf *scaled;
double scale;
int target_width;
int target_height;
if (width >= height)
scale = (double)max_dimension / (double)width;
else
scale = (double)max_dimension / (double)height;
target_width = (int)(width * scale);
target_height = (int)(height * scale);
if (target_width < 1)
target_width = 1;
if (target_height < 1)
target_height = 1;
scaled = gdk_pixbuf_scale_simple (img, target_width, target_height, GDK_INTERP_BILINEAR);
if (scaled)
{
g_object_unref (img);
img = scaled;
}
}
surface = pixbuf_to_cairo_surface (img);
g_object_unref (img);
@@ -184,36 +151,25 @@ pixmap_load_from_file (char *filename)
/* load custom icons from <config>/icons, don't mess in system folders */
static GdkPixbuf *
load_pixmap (IconResolverRole role, int item)
load_pixmap (const char *filename)
{
GdkPixbuf *pixbuf = NULL;
GdkPixbuf *scaledpixbuf;
GdkPixbuf *pixbuf, *scaledpixbuf;
const char *scale;
int iscale;
char *path;
const char *system_icon_name = NULL;
path = icon_resolver_resolve_path (role, item, GTK_ICON_SIZE_MENU, "pixmap",
ICON_RESOLVER_THEME_SYSTEM, &system_icon_name);
if (path)
gchar *path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "icons" G_DIR_SEPARATOR_S "%s.png", get_xdir (), filename);
pixbuf = gdk_pixbuf_new_from_file (path, 0);
g_free (path);
if (!pixbuf)
{
if (g_str_has_prefix (path, "/icons/"))
pixbuf = gdk_pixbuf_new_from_resource (path, NULL);
else
pixbuf = gdk_pixbuf_new_from_file (path, 0);
path = g_strdup_printf ("/icons/%s.png", filename);
pixbuf = gdk_pixbuf_new_from_resource (path, NULL);
g_free (path);
}
if (!pixbuf && system_icon_name)
{
GtkIconTheme *theme = gtk_icon_theme_get_default ();
if (theme)
pixbuf = gtk_icon_theme_load_icon (theme, system_icon_name, 16, GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
}
scale = g_getenv ("GDK_SCALE");
if (scale && pixbuf)
if (scale)
{
iscale = atoi (scale);
if (iscale > 0)
@@ -239,26 +195,26 @@ pixmaps_init (void)
{
zoitechat_register_resource();
pix_ulist_voice = load_pixmap (ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_VOICE);
pix_ulist_halfop = load_pixmap (ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_HALFOP);
pix_ulist_op = load_pixmap (ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_OP);
pix_ulist_owner = load_pixmap (ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_OWNER);
pix_ulist_founder = load_pixmap (ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_FOUNDER);
pix_ulist_netop = load_pixmap (ICON_RESOLVER_ROLE_USERLIST_RANK, ICON_RESOLVER_USERLIST_RANK_NETOP);
pix_ulist_voice = load_pixmap ("ulist_voice");
pix_ulist_halfop = load_pixmap ("ulist_halfop");
pix_ulist_op = load_pixmap ("ulist_op");
pix_ulist_owner = load_pixmap ("ulist_owner");
pix_ulist_founder = load_pixmap ("ulist_founder");
pix_ulist_netop = load_pixmap ("ulist_netop");
pix_tray_normal = load_pixmap (ICON_RESOLVER_ROLE_TRAY_STATE, ICON_RESOLVER_TRAY_STATE_NORMAL);
pix_tray_fileoffer = load_pixmap (ICON_RESOLVER_ROLE_TRAY_STATE, ICON_RESOLVER_TRAY_STATE_FILEOFFER);
pix_tray_highlight = load_pixmap (ICON_RESOLVER_ROLE_TRAY_STATE, ICON_RESOLVER_TRAY_STATE_HIGHLIGHT);
pix_tray_message = load_pixmap (ICON_RESOLVER_ROLE_TRAY_STATE, ICON_RESOLVER_TRAY_STATE_MESSAGE);
pix_tray_normal = load_pixmap ("tray_normal");
pix_tray_fileoffer = load_pixmap ("tray_fileoffer");
pix_tray_highlight = load_pixmap ("tray_highlight");
pix_tray_message = load_pixmap ("tray_message");
pix_tree_channel = load_pixmap (ICON_RESOLVER_ROLE_TREE_TYPE, ICON_RESOLVER_TREE_TYPE_CHANNEL);
pix_tree_dialog = load_pixmap (ICON_RESOLVER_ROLE_TREE_TYPE, ICON_RESOLVER_TREE_TYPE_DIALOG);
pix_tree_server = load_pixmap (ICON_RESOLVER_ROLE_TREE_TYPE, ICON_RESOLVER_TREE_TYPE_SERVER);
pix_tree_util = load_pixmap (ICON_RESOLVER_ROLE_TREE_TYPE, ICON_RESOLVER_TREE_TYPE_UTIL);
pix_tree_channel = load_pixmap ("tree_channel");
pix_tree_dialog = load_pixmap ("tree_dialog");
pix_tree_server = load_pixmap ("tree_server");
pix_tree_util = load_pixmap ("tree_util");
/* non-replaceable book pixmap */
pix_book = gdk_pixbuf_new_from_resource ("/icons/book.png", NULL);
/* used in About window, tray icon and WindowManager icon. */
pix_zoitechat = gdk_pixbuf_new_from_resource ("/icons/zoitechat.png", NULL);
pix_zoitechat = load_pixmap ("zoitechat");
}

View File

@@ -36,8 +36,7 @@
#include "../common/cfgfiles.h"
#include "../common/server.h"
#include "gtkutil.h"
#include "theme/theme-access.h"
#include "theme/theme-manager.h"
#include "palette.h"
#include "maingui.h"
#include "rawlog.h"
#include "xtext.h"
@@ -46,53 +45,6 @@
#define ICON_RAWLOG_CLEAR "zc-menu-clear"
#define ICON_RAWLOG_SAVE_AS "zc-menu-save-as"
#define RAWLOG_THEME_LISTENER_ID_KEY "rawlog.theme-listener-id"
static void
rawlog_theme_apply (GtkWidget *window)
{
GtkWidget *xtext_widget;
XTextColor xtext_palette[XTEXT_COLS];
if (!window)
return;
xtext_widget = g_object_get_data (G_OBJECT (window), "rawlog-xtext");
if (!xtext_widget)
return;
theme_get_xtext_colors_for_widget (xtext_widget, xtext_palette, XTEXT_COLS);
gtk_xtext_set_palette (GTK_XTEXT (xtext_widget), xtext_palette);
}
static void
rawlog_theme_changed (const ThemeChangedEvent *event, gpointer userdata)
{
GtkWidget *window = userdata;
if (!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PALETTE) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_THEME_PACK) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_MODE))
return;
rawlog_theme_apply (window);
}
static void
rawlog_theme_destroy_cb (GtkWidget *widget, gpointer userdata)
{
guint listener_id;
(void) userdata;
listener_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), RAWLOG_THEME_LISTENER_ID_KEY));
if (listener_id)
{
theme_listener_unregister (listener_id);
g_object_set_data (G_OBJECT (widget), RAWLOG_THEME_LISTENER_ID_KEY, NULL);
}
}
static void
close_rawlog (GtkWidget *wid, server *serv)
{
@@ -174,12 +126,11 @@ open_rawlog (struct server *serv)
gtk_widget_set_vexpand (scrolledwindow, TRUE);
gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
theme_get_xtext_colors_for_widget (scrolledwindow, xtext_palette, XTEXT_COLS);
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
serv->gui->rawlog_textlist = gtk_xtext_new (xtext_palette, 0);
gtk_container_add (GTK_CONTAINER (scrolledwindow), serv->gui->rawlog_textlist);
gtk_xtext_set_font (GTK_XTEXT (serv->gui->rawlog_textlist), prefs.hex_text_font);
GTK_XTEXT (serv->gui->rawlog_textlist)->ignore_hidden = 1;
g_object_set_data (G_OBJECT (serv->gui->rawlog_window), "rawlog-xtext", serv->gui->rawlog_textlist);
bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
@@ -193,12 +144,8 @@ open_rawlog (struct server *serv)
/* Copy selection to clipboard when Ctrl+Shift+C is pressed AND text auto-copy is disabled */
g_signal_connect (G_OBJECT (serv->gui->rawlog_window), "key_press_event", G_CALLBACK (rawlog_key_cb), serv->gui->rawlog_textlist);
g_object_set_data (G_OBJECT (serv->gui->rawlog_window), RAWLOG_THEME_LISTENER_ID_KEY,
GUINT_TO_POINTER (theme_listener_register ("rawlog.window", rawlog_theme_changed, serv->gui->rawlog_window)));
g_signal_connect (G_OBJECT (serv->gui->rawlog_window), "destroy", G_CALLBACK (rawlog_theme_destroy_cb), NULL);
gtk_widget_show_all (serv->gui->rawlog_window);
rawlog_theme_apply (serv->gui->rawlog_window);
}
void

View File

@@ -35,7 +35,6 @@
#include "menu.h"
#include "pixmaps.h"
#include "fkeys.h"
#include "theme/theme-manager.h"
#define SERVLIST_X_PADDING 4 /* horizontal paddig in the network editor */
#define SERVLIST_Y_PADDING 0 /* vertical padding in the network editor */
@@ -782,13 +781,15 @@ servlist_deletenet_cb (GtkWidget *item, ircnet *net)
if (!net)
return;
dialog = gtk_message_dialog_new (GTK_WINDOW (serverlist_win),
GTK_DIALOG_DESTROY_WITH_PARENT |
GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_OK_CANCEL,
_("Really remove network \"%s\" and all its servers?"),
net->name);
theme_manager_attach_window (dialog);
GTK_DIALOG_DESTROY_WITH_PARENT |
GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_OK_CANCEL,
_("Really remove network \"%s\" and all its servers?"),
net->name);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (dialog);
g_signal_connect (dialog, "response",
G_CALLBACK (servlist_deletenetdialog_cb), net);
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
@@ -1793,7 +1794,6 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
char buf[128];
editwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
theme_manager_attach_window (editwindow);
gtk_container_set_border_width (GTK_CONTAINER (editwindow), 4);
g_snprintf (buf, sizeof (buf), _("Edit %s - %s"), net->name, _(DISPLAY_NAME));
gtk_window_set_title (GTK_WINDOW (editwindow), buf);
@@ -1802,6 +1802,9 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
gtk_window_set_modal (GTK_WINDOW (editwindow), TRUE);
gtk_window_set_type_hint (GTK_WINDOW (editwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
gtk_window_set_role (GTK_WINDOW (editwindow), "editserv");
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (editwindow);
vbox5 = gtkutil_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
gtk_container_add (GTK_CONTAINER (editwindow), vbox5);
@@ -2075,13 +2078,15 @@ servlist_open_networks (void)
char buf[128];
servlist = gtk_window_new (GTK_WINDOW_TOPLEVEL);
theme_manager_attach_window (servlist);
gtk_container_set_border_width (GTK_CONTAINER (servlist), 4);
g_snprintf(buf, sizeof(buf), _("Network List - %s"), _(DISPLAY_NAME));
gtk_window_set_title (GTK_WINDOW (servlist), buf);
gtk_window_set_default_size (GTK_WINDOW (servlist), netlist_win_width, netlist_win_height);
gtk_window_set_role (GTK_WINDOW (servlist), "servlist");
gtk_window_set_type_hint (GTK_WINDOW (servlist), GDK_WINDOW_TYPE_HINT_DIALOG);
/* Window classes are required for GTK CSS selectors like
* .zoitechat-dark / .zoitechat-light. */
fe_apply_theme_to_toplevel (servlist);
if (current_sess)
gtk_window_set_transient_for (GTK_WINDOW (servlist), GTK_WINDOW (current_sess->gui->window));

File diff suppressed because it is too large Load Diff

View File

@@ -20,8 +20,6 @@
#ifndef ZOITECHAT_SETUP_H
#define ZOITECHAT_SETUP_H
#include "theme/theme-manager.h"
void setup_apply_real (const ThemeChangedEvent *event);
void setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd);
#endif

View File

@@ -47,8 +47,7 @@
#include "../common/cfgfiles.h"
#include "../common/zoitechatc.h"
#include "theme/theme-access.h"
#include "theme/theme-palette.h"
#include "palette.h"
#include "xtext.h"
#include "gtkutil.h"
@@ -57,34 +56,6 @@
#define ICON_REMOVE "zc-menu-remove"
#define ICON_SPELL_CHECK "zc-menu-spell-check"
static void
color_to_rgb16 (const GdkRGBA *color, guint16 *red, guint16 *green, guint16 *blue)
{
*red = (guint16) CLAMP (color->red * 65535.0 + 0.5, 0.0, 65535.0);
*green = (guint16) CLAMP (color->green * 65535.0 + 0.5, 0.0, 65535.0);
*blue = (guint16) CLAMP (color->blue * 65535.0 + 0.5, 0.0, 65535.0);
}
static void
theme_token_color_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue)
{
GdkRGBA color = { 0 };
if (!theme_get_color (token, &color))
return;
color_to_rgb16 (&color, red, green, blue);
}
static void
theme_mirc_color_rgb16 (int mirc_idx, guint16 *red, guint16 *green, guint16 *blue)
{
GdkRGBA color = { 0 };
if (!theme_get_mirc_color ((unsigned int) mirc_idx, &color))
return;
color_to_rgb16 (&color, red, green, blue);
}
/*
* Bunch of poop to make enchant into a runtime dependency rather than a
* compile-time dependency. This makes it so I don't have to hear the
@@ -382,7 +353,7 @@ insert_underline_error (SexySpellEntry *entry, guint start, guint end)
guint16 green;
guint16 blue;
theme_token_color_rgb16 (THEME_TOKEN_SPELL, &red, &green, &blue);
palette_color_get_rgb16 (&colors[COL_SPELL], &red, &green, &blue);
ucolor = pango_attr_underline_color_new (red, green, blue);
unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR);
@@ -450,27 +421,27 @@ insert_color (SexySpellEntry *entry, guint start, int fgcolor, int bgcolor)
guint16 green;
guint16 blue;
if (fgcolor < 0)
if (fgcolor < 0 || fgcolor > MAX_COL)
{
theme_token_color_rgb16 (THEME_TOKEN_TEXT_FOREGROUND, &red, &green, &blue);
palette_color_get_rgb16 (&colors[COL_FG], &red, &green, &blue);
fgattr = pango_attr_foreground_new (red, green, blue);
ulattr = pango_attr_underline_color_new (red, green, blue);
}
else
{
theme_mirc_color_rgb16 (fgcolor, &red, &green, &blue);
palette_color_get_rgb16 (&colors[fgcolor], &red, &green, &blue);
fgattr = pango_attr_foreground_new (red, green, blue);
ulattr = pango_attr_underline_color_new (red, green, blue);
}
if (bgcolor < 0)
if (bgcolor < 0 || bgcolor > MAX_COL)
{
theme_token_color_rgb16 (THEME_TOKEN_TEXT_BACKGROUND, &red, &green, &blue);
palette_color_get_rgb16 (&colors[COL_BG], &red, &green, &blue);
bgattr = pango_attr_background_new (red, green, blue);
}
else
{
theme_mirc_color_rgb16 (bgcolor, &red, &green, &blue);
palette_color_get_rgb16 (&colors[bgcolor], &red, &green, &blue);
bgattr = pango_attr_background_new (red, green, blue);
}
@@ -1082,7 +1053,7 @@ check_attributes (SexySpellEntry *entry, const char *text, int len)
case ATTR_REVERSE:
insert_hiddenchar (entry, i, i + 1);
insert_color (entry, i, THEME_TOKEN_TEXT_BACKGROUND, THEME_TOKEN_TEXT_FOREGROUND);
insert_color (entry, i, COL_BG, COL_FG);
goto check_color;
case '\n':

View File

@@ -35,8 +35,7 @@
#include "gtkutil.h"
#include "xtext.h"
#include "maingui.h"
#include "theme/theme-access.h"
#include "theme/theme-manager.h"
#include "palette.h"
#include "textgui.h"
#define ICON_TEXTEVENT_SAVE_AS "document-save-as"
@@ -50,8 +49,6 @@ extern char *pntevts[];
static GtkWidget *pevent_dialog = NULL, *pevent_dialog_twid,
*pevent_dialog_list, *pevent_dialog_hlist;
#define PEVENT_THEME_LISTENER_ID_KEY "textgui.theme-listener-id"
enum
{
EVENT_COLUMN,
@@ -154,51 +151,6 @@ pevent_dialog_close (GtkWidget *wid, gpointer arg)
pevent_save (NULL);
}
static void
pevent_dialog_theme_apply (GtkWidget *window)
{
GtkWidget *xtext;
XTextColor xtext_palette[XTEXT_COLS];
if (!window)
return;
xtext = g_object_get_data (G_OBJECT (window), "xtext");
if (!xtext)
return;
theme_get_xtext_colors_for_widget (xtext, xtext_palette, XTEXT_COLS);
gtk_xtext_set_palette (GTK_XTEXT (xtext), xtext_palette);
}
static void
pevent_dialog_theme_changed (const ThemeChangedEvent *event, gpointer userdata)
{
GtkWidget *window = userdata;
if (!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PALETTE) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_THEME_PACK) &&
!theme_changed_event_has_reason (event, THEME_CHANGED_REASON_MODE))
return;
pevent_dialog_theme_apply (window);
}
static void
pevent_dialog_theme_destroy_cb (GtkWidget *widget, gpointer userdata)
{
guint listener_id;
(void) userdata;
listener_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), PEVENT_THEME_LISTENER_ID_KEY));
if (listener_id)
{
theme_listener_unregister (listener_id);
g_object_set_data (G_OBJECT (widget), PEVENT_THEME_LISTENER_ID_KEY, NULL);
}
}
static void
pevent_edited (GtkCellRendererText *render, gchar *pathstr, gchar *new_text, gpointer data)
{
@@ -515,16 +467,12 @@ pevent_dialog_show ()
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (wid), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
gtk_box_pack_start (GTK_BOX (vbox), wid, FALSE, TRUE, 0);
theme_get_xtext_colors_for_widget (wid, xtext_palette, XTEXT_COLS);
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
pevent_dialog_twid = gtk_xtext_new (xtext_palette, 0);
gtk_widget_set_sensitive (pevent_dialog_twid, FALSE);
gtk_widget_set_size_request (pevent_dialog_twid, -1, 75);
gtk_container_add (GTK_CONTAINER (wid), pevent_dialog_twid);
gtk_xtext_set_font (GTK_XTEXT (pevent_dialog_twid), prefs.hex_text_font);
g_object_set_data (G_OBJECT (pevent_dialog), "xtext", pevent_dialog_twid);
g_object_set_data (G_OBJECT (pevent_dialog), PEVENT_THEME_LISTENER_ID_KEY,
GUINT_TO_POINTER (theme_listener_register ("textgui.events", pevent_dialog_theme_changed, pevent_dialog)));
g_signal_connect (G_OBJECT (pevent_dialog), "destroy", G_CALLBACK (pevent_dialog_theme_destroy_cb), NULL);
hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
@@ -539,5 +487,4 @@ pevent_dialog_show ()
NULL, _("OK"));
gtk_widget_show_all (pevent_dialog);
pevent_dialog_theme_apply (pevent_dialog);
}

View File

@@ -1,349 +0,0 @@
#include <math.h>
#include <gtk/gtk.h>
#include "../theme-access.h"
#include "../theme-manager.h"
#include "../theme-runtime.h"
#include "../../xtext-color.h"
#include "../../../common/zoitechat.h"
struct session *current_sess;
struct session *current_tab;
struct session *lastact_sess;
struct zoitechatprefs prefs;
static gboolean stub_dark_active;
static gboolean stub_gtk3_active;
static ThemeSemanticToken stub_last_color_token;
static int stub_runtime_get_color_calls;
static int stub_runtime_widget_calls;
static int stub_runtime_xtext_calls;
static int stub_runtime_xtext_mapped_calls;
static size_t stub_runtime_xtext_last_len;
static ThemeGtkPaletteMap stub_last_gtk_map;
static gboolean stub_last_gtk_map_valid;
static gboolean gtk_available;
static GdkRGBA stub_light_colors[THEME_TOKEN_COUNT];
static GdkRGBA stub_dark_colors[THEME_TOKEN_COUNT];
void
setup_apply_real (const ThemeChangedEvent *event)
{
(void) event;
}
gboolean
fe_dark_mode_is_enabled_for (unsigned int mode)
{
return mode == ZOITECHAT_DARK_MODE_DARK;
}
gboolean
theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
{
if (mode == ZOITECHAT_DARK_MODE_DARK)
stub_dark_active = TRUE;
if (mode == ZOITECHAT_DARK_MODE_LIGHT)
stub_dark_active = FALSE;
if (dark_active)
*dark_active = stub_dark_active;
return TRUE;
}
void
theme_runtime_load (void)
{
}
void
theme_runtime_save (void)
{
}
void
theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *col)
{
(void) token;
(void) col;
}
void
theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *col)
{
(void) token;
(void) col;
}
gboolean
theme_runtime_mode_has_user_colors (gboolean dark_mode)
{
(void) dark_mode;
return FALSE;
}
gboolean
theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
{
g_assert_nonnull (out_rgba);
stub_runtime_get_color_calls++;
stub_last_color_token = token;
*out_rgba = stub_dark_active ? stub_dark_colors[token] : stub_light_colors[token];
return TRUE;
}
void
theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values)
{
stub_runtime_widget_calls++;
gdk_rgba_parse (&out_values->background, "#010203");
gdk_rgba_parse (&out_values->foreground, "#fdfcfa");
}
void
theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len)
{
size_t i;
stub_runtime_xtext_calls++;
stub_runtime_xtext_last_len = palette_len;
for (i = 0; i < palette_len; i++)
{
palette[i].red = (unsigned short) (i + 1);
palette[i].green = (unsigned short) (i + 2);
palette[i].blue = (unsigned short) (i + 3);
}
}
gboolean
theme_runtime_is_dark_active (void)
{
return stub_dark_active;
}
void
theme_runtime_get_widget_style_values_mapped (const ThemeGtkPaletteMap *gtk_map, ThemeWidgetStyleValues *out_values)
{
(void) gtk_map;
theme_runtime_get_widget_style_values (out_values);
}
void
theme_runtime_get_xtext_colors_mapped (const ThemeGtkPaletteMap *gtk_map, XTextColor *palette, size_t palette_len)
{
size_t i;
stub_runtime_xtext_mapped_calls++;
stub_last_gtk_map = *gtk_map;
stub_last_gtk_map_valid = TRUE;
stub_runtime_xtext_last_len = palette_len;
for (i = 0; i < palette_len; i++)
{
palette[i].red = (unsigned short) (100 + i);
palette[i].green = (unsigned short) (200 + i);
palette[i].blue = (unsigned short) (300 + i);
}
}
gboolean
theme_gtk3_is_active (void)
{
return stub_gtk3_active;
}
static gboolean
rgba_equal (const GdkRGBA *a, const GdkRGBA *b)
{
return a->red == b->red && a->green == b->green && a->blue == b->blue && a->alpha == b->alpha;
}
static void
reset_stubs (void)
{
size_t i;
char light[32];
char dark[32];
stub_dark_active = FALSE;
stub_last_color_token = THEME_TOKEN_MIRC_0;
stub_runtime_get_color_calls = 0;
stub_runtime_widget_calls = 0;
stub_runtime_xtext_calls = 0;
stub_runtime_xtext_mapped_calls = 0;
stub_runtime_xtext_last_len = 0;
stub_last_gtk_map_valid = FALSE;
stub_gtk3_active = FALSE;
for (i = 0; i < THEME_TOKEN_COUNT; i++)
{
g_snprintf (light, sizeof (light), "#%02x%02x%02x", (unsigned int) (i + 1), 0x11, 0x22);
g_snprintf (dark, sizeof (dark), "#%02x%02x%02x", (unsigned int) (i + 1), 0xaa, 0xbb);
g_assert_true (gdk_rgba_parse (&stub_light_colors[i], light));
g_assert_true (gdk_rgba_parse (&stub_dark_colors[i], dark));
}
}
static gboolean
rgba_close (const GdkRGBA *a, const GdkRGBA *b)
{
return fabs (a->red - b->red) < 0.0001 &&
fabs (a->green - b->green) < 0.0001 &&
fabs (a->blue - b->blue) < 0.0001 &&
fabs (a->alpha - b->alpha) < 0.0001;
}
static void
test_access_semantic_token_routes_directly (void)
{
ThemeSemanticToken token;
GdkRGBA color;
size_t i;
reset_stubs ();
for (i = 0; i < theme_palette_token_def_count (); i++)
{
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
g_assert_nonnull (def);
token = def->token;
g_assert_true (theme_get_color (token, &color));
g_assert_cmpint (stub_last_color_token, ==, token);
g_assert_true (rgba_equal (&color, &stub_light_colors[token]));
}
}
static void
test_access_token_routes_without_legacy_accessor (void)
{
ThemeSemanticToken token = THEME_TOKEN_MIRC_0;
GdkRGBA color;
size_t i;
reset_stubs ();
for (i = 0; i < theme_palette_token_def_count (); i++)
{
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
g_assert_nonnull (def);
g_assert_true (theme_palette_legacy_index_to_token (def->legacy_index, &token));
g_assert_true (theme_get_color (token, &color));
g_assert_cmpint (stub_last_color_token, ==, token);
g_assert_true (rgba_equal (&color, &stub_light_colors[token]));
}
}
static void
test_access_xtext_palette_forwarding (void)
{
XTextColor palette[4] = { 0 };
reset_stubs ();
theme_get_xtext_colors (palette, G_N_ELEMENTS (palette));
g_assert_cmpint (stub_runtime_xtext_calls, ==, 1);
g_assert_cmpuint (stub_runtime_xtext_last_len, ==, G_N_ELEMENTS (palette));
g_assert_cmpuint (palette[0].red, ==, 1);
g_assert_cmpuint (palette[1].green, ==, 3);
g_assert_cmpuint (palette[3].blue, ==, 6);
}
static void
test_access_widget_style_forwarding (void)
{
ThemeWidgetStyleValues values;
reset_stubs ();
theme_get_widget_style_values (&values);
g_assert_cmpint (stub_runtime_widget_calls, ==, 1);
g_assert_true (fabs (values.background.red - (0x01 / 255.0)) < 0.0001);
g_assert_true (fabs (values.foreground.green - (0xfc / 255.0)) < 0.0001);
}
static void
test_access_xtext_palette_widget_mapping_when_gtk3_active (void)
{
GtkWidget *window;
GtkWidget *label;
GtkStyleContext *context;
GtkCssProvider *provider;
XTextColor palette[2] = { 0 };
GdkRGBA expected;
if (!gtk_available)
{
g_test_skip ("GTK display not available");
return;
}
reset_stubs ();
stub_gtk3_active = TRUE;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
label = gtk_label_new ("mapped");
gtk_container_add (GTK_CONTAINER (window), label);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider,
"label { color: #112233; background-color: #445566; }"
"label:selected { color: #778899; background-color: #aabbcc; }"
"label:link { color: #123456; }",
-1,
NULL);
context = gtk_widget_get_style_context (label);
gtk_style_context_add_provider (context,
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_widget_realize (window);
theme_get_xtext_colors_for_widget (label, palette, G_N_ELEMENTS (palette));
g_assert_cmpint (stub_runtime_xtext_mapped_calls, ==, 1);
g_assert_cmpint (stub_runtime_xtext_calls, ==, 0);
g_assert_true (stub_last_gtk_map_valid);
g_assert_true (gdk_rgba_parse (&expected, "#112233"));
g_assert_true (rgba_close (&stub_last_gtk_map.text_foreground, &expected));
g_assert_true (gdk_rgba_parse (&expected, "#445566"));
g_assert_true (rgba_close (&stub_last_gtk_map.text_background, &expected));
g_assert_true (gdk_rgba_parse (&expected, "#778899"));
g_assert_true (rgba_close (&stub_last_gtk_map.selection_foreground, &expected));
g_assert_true (gdk_rgba_parse (&expected, "#aabbcc"));
g_assert_true (rgba_close (&stub_last_gtk_map.selection_background, &expected));
g_assert_true (gdk_rgba_parse (&expected, "#123456"));
g_assert_true (rgba_close (&stub_last_gtk_map.accent, &expected));
g_assert_cmpuint (palette[0].red, ==, 100);
g_assert_cmpuint (palette[1].green, ==, 201);
gtk_widget_destroy (window);
g_object_unref (provider);
}
static void
test_access_dark_light_switch_affects_token_consumers (void)
{
ThemeSemanticToken token;
GdkRGBA light;
GdkRGBA dark;
reset_stubs ();
token = THEME_TOKEN_TEXT_FOREGROUND;
g_assert_true (theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, NULL));
g_assert_true (theme_get_color (token, &light));
g_assert_true (rgba_equal (&light, &stub_light_colors[token]));
g_assert_true (theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_DARK, NULL));
g_assert_true (theme_get_color (token, &dark));
g_assert_true (rgba_equal (&dark, &stub_dark_colors[token]));
g_assert_false (rgba_equal (&light, &dark));
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/theme/access/semantic_token_routes_directly", test_access_semantic_token_routes_directly);
g_test_add_func ("/theme/access/token_routes_without_legacy_accessor", test_access_token_routes_without_legacy_accessor);
g_test_add_func ("/theme/access/xtext_palette_forwarding", test_access_xtext_palette_forwarding);
g_test_add_func ("/theme/access/xtext_palette_widget_mapping_when_gtk3_active",
test_access_xtext_palette_widget_mapping_when_gtk3_active);
g_test_add_func ("/theme/access/widget_style_forwarding", test_access_widget_style_forwarding);
g_test_add_func ("/theme/access/dark_light_switch_affects_token_consumers",
test_access_dark_light_switch_affects_token_consumers);
gtk_available = gtk_init_check (&argc, &argv);
return g_test_run ();
}

View File

@@ -1,122 +0,0 @@
#include <gtk/gtk.h>
#include "../theme-application.h"
#include "../../maingui.h"
#include "../../../common/zoitechat.h"
#include "../../../common/zoitechatc.h"
struct session *current_sess;
struct session *current_tab;
struct session *lastact_sess;
struct zoitechatprefs prefs;
InputStyle *input_style;
static gboolean css_enabled;
static PangoFontDescription *css_font_desc;
static int css_reload_calls;
static int message_calls;
void
fe_message (char *msg, int flags)
{
(void) msg;
(void) flags;
message_calls++;
}
void
theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
{
css_enabled = enabled;
if (css_font_desc)
pango_font_description_free (css_font_desc);
css_font_desc = font_desc ? pango_font_description_copy (font_desc) : NULL;
css_reload_calls++;
}
void
theme_runtime_load (void)
{
}
gboolean
theme_runtime_apply_mode (unsigned int mode, gboolean *palette_changed)
{
(void) mode;
if (palette_changed)
*palette_changed = FALSE;
return FALSE;
}
static void
reset_state (void)
{
if (css_font_desc)
{
pango_font_description_free (css_font_desc);
css_font_desc = NULL;
}
if (input_style)
{
if (input_style->font_desc)
pango_font_description_free (input_style->font_desc);
g_free (input_style);
input_style = NULL;
}
css_enabled = FALSE;
css_reload_calls = 0;
message_calls = 0;
}
static void
test_invalid_font_falls_back_to_sans_11 (void)
{
InputStyle *style;
reset_state ();
g_strlcpy (prefs.hex_text_font, "Sans", sizeof (prefs.hex_text_font));
prefs.hex_gui_input_style = TRUE;
style = theme_application_update_input_style (NULL);
g_assert_nonnull (style);
g_assert_nonnull (style->font_desc);
g_assert_cmpstr (pango_font_description_get_family (style->font_desc), ==, "sans");
g_assert_cmpint (pango_font_description_get_size (style->font_desc), ==, 11 * PANGO_SCALE);
g_assert_cmpint (message_calls, ==, 1);
g_assert_cmpint (css_reload_calls, ==, 1);
g_assert_true (css_enabled);
g_assert_nonnull (css_font_desc);
if (style->font_desc)
pango_font_description_free (style->font_desc);
g_free (style);
}
static void
test_style_toggle_routes_enabled_flag (void)
{
reset_state ();
g_strlcpy (prefs.hex_text_font, "sans 13", sizeof (prefs.hex_text_font));
prefs.hex_gui_input_style = FALSE;
input_style = NULL;
theme_application_reload_input_style ();
g_assert_nonnull (input_style);
g_assert_cmpint (css_reload_calls, ==, 1);
g_assert_false (css_enabled);
g_assert_nonnull (css_font_desc);
g_assert_cmpstr (pango_font_description_get_family (css_font_desc), ==, "sans");
g_assert_cmpint (pango_font_description_get_size (css_font_desc), ==, 13 * PANGO_SCALE);
g_assert_cmpint (message_calls, ==, 0);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/theme/application/input_style/invalid_font_fallback",
test_invalid_font_falls_back_to_sans_11);
g_test_add_func ("/theme/application/input_style/style_toggle_routes_enabled_flag",
test_style_toggle_routes_enabled_flag);
return g_test_run ();
}

View File

@@ -1,325 +0,0 @@
#include <glib.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "../theme-gtk3.h"
#include "../../../common/gtk3-theme-service.h"
#include "../../../common/zoitechat.h"
#include "../../../common/zoitechatc.h"
struct session *current_sess;
struct session *current_tab;
struct session *lastact_sess;
struct zoitechatprefs prefs;
static gboolean gtk_available;
static char *temp_root;
static char *theme_parent_root;
static char *theme_child_root;
static char *theme_switch_root;
gboolean
theme_policy_system_prefers_dark (void)
{
return FALSE;
}
static void
remove_tree (const char *path)
{
GDir *dir;
const char *name;
if (!path || !g_file_test (path, G_FILE_TEST_EXISTS))
return;
if (!g_file_test (path, G_FILE_TEST_IS_DIR))
{
g_remove (path);
return;
}
dir = g_dir_open (path, 0, NULL);
if (dir)
{
while ((name = g_dir_read_name (dir)) != NULL)
{
char *child = g_build_filename (path, name, NULL);
remove_tree (child);
g_free (child);
}
g_dir_close (dir);
}
g_rmdir (path);
}
static void
write_file (const char *path, const char *contents)
{
gboolean ok = g_file_set_contents (path, contents, -1, NULL);
g_assert_true (ok);
}
static void
ensure_css_dir (const char *theme_root, const char *css_dir)
{
char *dir = g_build_filename (theme_root, css_dir, NULL);
char *css = g_build_filename (dir, "gtk.css", NULL);
int rc = g_mkdir_with_parents (dir, 0700);
g_assert_cmpint (rc, ==, 0);
write_file (css, "* { }\n");
g_free (css);
g_free (dir);
}
static void
write_settings (const char *theme_root, const char *css_dir, const char *settings)
{
char *path = g_build_filename (theme_root, css_dir, "settings.ini", NULL);
write_file (path, settings);
g_free (path);
}
static ZoitechatGtk3Theme *
make_theme (const char *id, const char *path)
{
ZoitechatGtk3Theme *theme = g_new0 (ZoitechatGtk3Theme, 1);
theme->id = g_strdup (id);
theme->display_name = g_strdup (id);
theme->path = g_strdup (path);
theme->source = ZOITECHAT_GTK3_THEME_SOURCE_USER;
return theme;
}
void
zoitechat_gtk3_theme_free (ZoitechatGtk3Theme *theme)
{
if (!theme)
return;
g_free (theme->id);
g_free (theme->display_name);
g_free (theme->path);
g_free (theme->thumbnail_path);
g_free (theme);
}
ZoitechatGtk3Theme *
zoitechat_gtk3_theme_find_by_id (const char *theme_id)
{
if (g_strcmp0 (theme_id, "layered") == 0)
return make_theme (theme_id, theme_child_root);
if (g_strcmp0 (theme_id, "switch") == 0)
return make_theme (theme_id, theme_switch_root);
return NULL;
}
char *
zoitechat_gtk3_theme_pick_css_dir_for_minor (const char *theme_root, int preferred_minor)
{
char *path;
(void) preferred_minor;
path = g_build_filename (theme_root, "gtk-3.24", "gtk.css", NULL);
if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
{
g_free (path);
return g_strdup ("gtk-3.24");
}
g_free (path);
path = g_build_filename (theme_root, "gtk-3.0", "gtk.css", NULL);
if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
{
g_free (path);
return g_strdup ("gtk-3.0");
}
g_free (path);
return NULL;
}
char *
zoitechat_gtk3_theme_pick_css_dir (const char *theme_root)
{
return zoitechat_gtk3_theme_pick_css_dir_for_minor (theme_root, -1);
}
GPtrArray *
zoitechat_gtk3_theme_build_inheritance_chain (const char *theme_root)
{
GPtrArray *chain = g_ptr_array_new_with_free_func (g_free);
if (g_strcmp0 (theme_root, theme_child_root) == 0)
{
g_ptr_array_add (chain, g_strdup (theme_parent_root));
g_ptr_array_add (chain, g_strdup (theme_child_root));
return chain;
}
if (g_strcmp0 (theme_root, theme_switch_root) == 0)
{
g_ptr_array_add (chain, g_strdup (theme_switch_root));
return chain;
}
g_ptr_array_unref (chain);
return NULL;
}
static gboolean
get_bool_setting (const char *name)
{
GtkSettings *settings = gtk_settings_get_default ();
gboolean value = FALSE;
g_object_get (settings, name, &value, NULL);
return value;
}
static gint
get_int_setting (const char *name)
{
GtkSettings *settings = gtk_settings_get_default ();
gint value = 0;
g_object_get (settings, name, &value, NULL);
return value;
}
static void
setup_themes (void)
{
char *path;
temp_root = g_dir_make_tmp ("zoitechat-theme-gtk3-settings-XXXXXX", NULL);
g_assert_nonnull (temp_root);
theme_parent_root = g_build_filename (temp_root, "parent", NULL);
theme_child_root = g_build_filename (temp_root, "child", NULL);
theme_switch_root = g_build_filename (temp_root, "switch", NULL);
g_assert_cmpint (g_mkdir_with_parents (theme_parent_root, 0700), ==, 0);
g_assert_cmpint (g_mkdir_with_parents (theme_child_root, 0700), ==, 0);
g_assert_cmpint (g_mkdir_with_parents (theme_switch_root, 0700), ==, 0);
ensure_css_dir (theme_parent_root, "gtk-3.24");
write_settings (theme_parent_root, "gtk-3.24",
"[Settings]\n"
"gtk-enable-animations=true\n"
"gtk-cursor-blink-time=111\n");
ensure_css_dir (theme_child_root, "gtk-3.0");
ensure_css_dir (theme_child_root, "gtk-3.24");
write_settings (theme_child_root, "gtk-3.0",
"[Settings]\n"
"gtk-enable-animations=false\n"
"gtk-cursor-blink-time=222\n");
write_settings (theme_child_root, "gtk-3.24",
"[Settings]\n"
"gtk-cursor-blink-time=333\n");
ensure_css_dir (theme_switch_root, "gtk-3.24");
write_settings (theme_switch_root, "gtk-3.24",
"[Settings]\n"
"gtk-enable-animations=false\n"
"gtk-cursor-blink-time=444\n");
path = g_build_filename (theme_parent_root, "index.theme", NULL);
write_file (path, "[Desktop Entry]\nName=parent\n");
g_free (path);
path = g_build_filename (theme_child_root, "index.theme", NULL);
write_file (path, "[Desktop Entry]\nName=child\nInherits=parent\n");
g_free (path);
path = g_build_filename (theme_switch_root, "index.theme", NULL);
write_file (path, "[Desktop Entry]\nName=switch\n");
g_free (path);
}
static void
teardown_themes (void)
{
g_assert_nonnull (temp_root);
remove_tree (temp_root);
g_free (theme_parent_root);
g_free (theme_child_root);
g_free (theme_switch_root);
g_free (temp_root);
theme_parent_root = NULL;
theme_child_root = NULL;
theme_switch_root = NULL;
temp_root = NULL;
}
static void
test_settings_layer_precedence (void)
{
GError *error = NULL;
if (!gtk_available)
{
g_test_skip ("GTK display not available");
return;
}
g_assert_true (theme_gtk3_apply ("layered", THEME_GTK3_VARIANT_PREFER_LIGHT, &error));
g_assert_no_error (error);
g_assert_false (get_bool_setting ("gtk-enable-animations"));
g_assert_cmpint (get_int_setting ("gtk-cursor-blink-time"), ==, 333);
g_assert_true (theme_gtk3_is_active ());
theme_gtk3_disable ();
}
static void
test_settings_restored_on_disable_and_switch (void)
{
GError *error = NULL;
gboolean default_animations;
gint default_blink;
char *default_theme_name = NULL;
char *active_theme_name = NULL;
if (!gtk_available)
{
g_test_skip ("GTK display not available");
return;
}
default_animations = get_bool_setting ("gtk-enable-animations");
default_blink = get_int_setting ("gtk-cursor-blink-time");
g_object_get (gtk_settings_get_default (), "gtk-theme-name", &default_theme_name, NULL);
g_assert_true (theme_gtk3_apply ("layered", THEME_GTK3_VARIANT_PREFER_LIGHT, &error));
g_assert_no_error (error);
g_assert_cmpint (get_int_setting ("gtk-cursor-blink-time"), ==, 333);
g_object_get (gtk_settings_get_default (), "gtk-theme-name", &active_theme_name, NULL);
g_assert_cmpstr (active_theme_name, ==, "child");
g_free (active_theme_name);
active_theme_name = NULL;
g_assert_true (theme_gtk3_apply ("switch", THEME_GTK3_VARIANT_PREFER_LIGHT, &error));
g_assert_no_error (error);
g_assert_false (get_bool_setting ("gtk-enable-animations"));
g_assert_cmpint (get_int_setting ("gtk-cursor-blink-time"), ==, 444);
theme_gtk3_disable ();
g_assert_cmpint (get_int_setting ("gtk-cursor-blink-time"), ==, default_blink);
g_assert_cmpint (get_bool_setting ("gtk-enable-animations"), ==, default_animations);
g_object_get (gtk_settings_get_default (), "gtk-theme-name", &active_theme_name, NULL);
g_assert_cmpstr (active_theme_name, ==, default_theme_name);
g_free (active_theme_name);
g_free (default_theme_name);
g_assert_false (theme_gtk3_is_active ());
}
int
main (int argc, char **argv)
{
int rc;
g_test_init (&argc, &argv, NULL);
gtk_available = gtk_init_check (&argc, &argv);
setup_themes ();
g_test_add_func ("/theme/gtk3/settings_layer_precedence", test_settings_layer_precedence);
g_test_add_func ("/theme/gtk3/settings_restored_on_disable_and_switch", test_settings_restored_on_disable_and_switch);
prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_PREFER_LIGHT;
if (!gtk_available)
g_test_message ("Skipping GTK3 settings tests because GTK initialization failed");
rc = g_test_run ();
theme_gtk3_disable ();
teardown_themes ();
return rc;
}

View File

@@ -1,71 +0,0 @@
#include <glib.h>
#include "../theme-gtk3.h"
static int apply_current_calls;
void
test_theme_gtk3_stub_reset (void)
{
apply_current_calls = 0;
}
int
test_theme_gtk3_stub_apply_current_calls (void)
{
return apply_current_calls;
}
void
theme_gtk3_init (void)
{
}
gboolean
theme_gtk3_apply_current (GError **error)
{
(void) error;
apply_current_calls++;
return TRUE;
}
gboolean
theme_gtk3_apply (const char *theme_id, ThemeGtk3Variant variant, GError **error)
{
(void) theme_id;
(void) variant;
(void) error;
return TRUE;
}
gboolean
theme_gtk3_refresh (const char *theme_id, ThemeGtk3Variant variant, GError **error)
{
(void) theme_id;
(void) variant;
(void) error;
return TRUE;
}
ThemeGtk3Variant
theme_gtk3_variant_for_theme (const char *theme_id)
{
(void) theme_id;
return THEME_GTK3_VARIANT_PREFER_LIGHT;
}
void
theme_gtk3_invalidate_provider_cache (void)
{
}
void
theme_gtk3_disable (void)
{
}
gboolean
theme_gtk3_is_active (void)
{
return FALSE;
}

View File

@@ -1,272 +0,0 @@
#include <gtk/gtk.h>
#include "../theme-manager.h"
#include "../theme-gtk3.h"
#include "../../fe-gtk.h"
#include "../../../common/zoitechat.h"
#include "../../../common/zoitechatc.h"
struct session *current_sess;
struct session *current_tab;
struct session *lastact_sess;
struct zoitechatprefs prefs;
static gboolean stub_apply_mode_palette_changed;
static gboolean stub_system_prefers_dark;
static int auto_state_calls;
static gboolean last_auto_state;
static int listener_calls;
static ThemeChangedEvent last_event;
static int idle_add_calls;
static guint next_idle_source_id = 33;
void test_theme_gtk3_stub_reset (void);
int test_theme_gtk3_stub_apply_current_calls (void);
void setup_apply_real (const ThemeChangedEvent *event)
{
(void) event;
}
gboolean fe_dark_mode_is_enabled_for (unsigned int mode)
{
return mode == ZOITECHAT_DARK_MODE_DARK;
}
void fe_set_auto_dark_mode_state (gboolean enabled)
{
auto_state_calls++;
last_auto_state = enabled;
}
gboolean fe_win32_high_contrast_is_enabled (void)
{
return FALSE;
}
gboolean fe_win32_try_get_system_dark (gboolean *enabled)
{
(void) enabled;
return FALSE;
}
void zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback)
{
(void) callback;
}
gboolean theme_policy_is_dark_mode_active (unsigned int mode)
{
return mode == ZOITECHAT_DARK_MODE_DARK;
}
gboolean theme_policy_system_prefers_dark (void)
{
return stub_system_prefers_dark;
}
gboolean theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
{
(void) mode;
if (palette_changed)
*palette_changed = stub_apply_mode_palette_changed;
return TRUE;
}
void theme_application_reload_input_style (void)
{
}
void theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *color)
{
(void) token;
(void) color;
}
void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *color)
{
(void) token;
(void) color;
}
void theme_runtime_reset_mode_colors (gboolean dark_mode)
{
(void) dark_mode;
}
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
{
(void) mode;
(void) dark_active;
return FALSE;
}
void theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
{
(void) enabled;
(void) font_desc;
}
void theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
const PangoFontDescription *font_desc)
{
(void) widget;
(void) bg;
(void) fg;
(void) font_desc;
}
void theme_runtime_load (void)
{
}
void theme_runtime_save (void)
{
}
gboolean theme_runtime_is_dark_active (void)
{
return FALSE;
}
void gtkutil_apply_palette (GtkWidget *widget, const GdkRGBA *background, const GdkRGBA *foreground,
const PangoFontDescription *font_desc)
{
(void) widget;
(void) background;
(void) foreground;
(void) font_desc;
}
void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
{
gdk_rgba_parse (&out_values->background, "#101010");
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
}
void theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
{
(void) widget;
theme_get_widget_style_values (out_values);
}
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
{
(void) window;
(void) dark;
}
static void
auto_listener (const ThemeChangedEvent *event, gpointer userdata)
{
(void) userdata;
listener_calls++;
last_event = *event;
}
static guint
immediate_idle_add (GSourceFunc function, gpointer data)
{
idle_add_calls++;
function (data);
return next_idle_source_id++;
}
static void
reset_state (void)
{
stub_apply_mode_palette_changed = FALSE;
stub_system_prefers_dark = FALSE;
auto_state_calls = 0;
last_auto_state = FALSE;
listener_calls = 0;
idle_add_calls = 0;
next_idle_source_id = 33;
prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_FOLLOW_SYSTEM;
test_theme_gtk3_stub_reset ();
}
static void
test_auto_refresh_dispatches_mode_palette_and_style_reasons (void)
{
guint listener_id;
reset_state ();
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_AUTO;
stub_apply_mode_palette_changed = TRUE;
stub_system_prefers_dark = TRUE;
listener_id = theme_listener_register ("auto.refresh", auto_listener, NULL);
theme_manager_set_idle_add_func (immediate_idle_add);
theme_manager_refresh_auto_mode ();
g_assert_cmpint (idle_add_calls, ==, 1);
g_assert_cmpint (auto_state_calls, ==, 2);
g_assert_true (last_auto_state);
g_assert_cmpint (listener_calls, ==, 1);
g_assert_cmpint (test_theme_gtk3_stub_apply_current_calls (), ==, 1);
g_assert_true (theme_changed_event_has_reason (&last_event, THEME_CHANGED_REASON_PALETTE));
g_assert_true (theme_changed_event_has_reason (&last_event, THEME_CHANGED_REASON_WIDGET_STYLE));
g_assert_true (theme_changed_event_has_reason (&last_event, THEME_CHANGED_REASON_USERLIST));
g_assert_true (theme_changed_event_has_reason (&last_event, THEME_CHANGED_REASON_MODE));
theme_manager_set_idle_add_func (NULL);
theme_listener_unregister (listener_id);
}
static void
test_auto_refresh_ignores_non_auto_mode (void)
{
guint listener_id;
reset_state ();
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_DARK;
stub_apply_mode_palette_changed = TRUE;
listener_id = theme_listener_register ("auto.nonauto", auto_listener, NULL);
theme_manager_set_idle_add_func (immediate_idle_add);
theme_manager_refresh_auto_mode ();
g_assert_cmpint (idle_add_calls, ==, 1);
g_assert_cmpint (auto_state_calls, ==, 0);
g_assert_cmpint (listener_calls, ==, 0);
g_assert_cmpint (test_theme_gtk3_stub_apply_current_calls (), ==, 0);
theme_manager_set_idle_add_func (NULL);
theme_listener_unregister (listener_id);
}
static void
test_auto_refresh_reapplies_gtk3_for_follow_system_variant (void)
{
guint listener_id;
reset_state ();
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_DARK;
prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_FOLLOW_SYSTEM;
listener_id = theme_listener_register ("auto.gtk3", auto_listener, NULL);
theme_manager_set_idle_add_func (immediate_idle_add);
theme_manager_refresh_auto_mode ();
g_assert_cmpint (idle_add_calls, ==, 1);
g_assert_cmpint (auto_state_calls, ==, 0);
g_assert_cmpint (listener_calls, ==, 0);
g_assert_cmpint (test_theme_gtk3_stub_apply_current_calls (), ==, 1);
theme_manager_set_idle_add_func (NULL);
theme_listener_unregister (listener_id);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/theme/manager/auto_refresh_dispatches_mode_palette_and_style_reasons",
test_auto_refresh_dispatches_mode_palette_and_style_reasons);
g_test_add_func ("/theme/manager/auto_refresh_ignores_non_auto_mode",
test_auto_refresh_ignores_non_auto_mode);
g_test_add_func ("/theme/manager/auto_refresh_reapplies_gtk3_for_follow_system_variant",
test_auto_refresh_reapplies_gtk3_for_follow_system_variant);
return g_test_run ();
}

View File

@@ -1,284 +0,0 @@
#include <gtk/gtk.h>
#include <string.h>
#include "../theme-manager.h"
#include "../../fe-gtk.h"
#include "../../../common/zoitechat.h"
#include "../../../common/zoitechatc.h"
struct session *current_sess;
struct session *current_tab;
struct session *lastact_sess;
struct zoitechatprefs prefs;
static int window_refresh_calls;
static int widget_style_calls;
static int palette_reapply_calls;
static int unmatched_listener_calls;
void setup_apply_real (const ThemeChangedEvent *event)
{
(void) event;
}
gboolean fe_dark_mode_is_enabled_for (unsigned int mode)
{
return mode == ZOITECHAT_DARK_MODE_DARK;
}
void fe_set_auto_dark_mode_state (gboolean enabled)
{
(void) enabled;
}
gboolean fe_win32_high_contrast_is_enabled (void)
{
return FALSE;
}
gboolean fe_win32_try_get_system_dark (gboolean *enabled)
{
(void) enabled;
return FALSE;
}
void zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback)
{
(void) callback;
}
gboolean theme_policy_is_dark_mode_active (unsigned int mode)
{
return mode == ZOITECHAT_DARK_MODE_DARK;
}
gboolean theme_policy_system_prefers_dark (void)
{
return FALSE;
}
gboolean theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
{
(void) mode;
if (palette_changed)
*palette_changed = FALSE;
return TRUE;
}
void theme_application_reload_input_style (void)
{
}
void theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *color)
{
(void) token;
(void) color;
}
void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *color)
{
(void) token;
(void) color;
}
void theme_runtime_reset_mode_colors (gboolean dark_mode)
{
(void) dark_mode;
}
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
{
(void) mode;
(void) dark_active;
return FALSE;
}
void theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
{
(void) enabled;
(void) font_desc;
}
void theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
const PangoFontDescription *font_desc)
{
(void) widget;
(void) bg;
(void) fg;
(void) font_desc;
}
void theme_runtime_load (void)
{
}
void theme_runtime_save (void)
{
}
gboolean theme_runtime_is_dark_active (void)
{
return FALSE;
}
void gtkutil_apply_palette (GtkWidget *widget, const GdkRGBA *background, const GdkRGBA *foreground,
const PangoFontDescription *font_desc)
{
(void) widget;
(void) background;
(void) foreground;
(void) font_desc;
}
void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
{
gdk_rgba_parse (&out_values->background, "#101010");
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
}
void theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
{
(void) widget;
theme_get_widget_style_values (out_values);
}
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
{
(void) window;
(void) dark;
}
static void
window_refresh_listener (const ThemeChangedEvent *event, gpointer userdata)
{
(void) userdata;
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PALETTE))
palette_reapply_calls++;
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PALETTE) ||
theme_changed_event_has_reason (event, THEME_CHANGED_REASON_WIDGET_STYLE))
window_refresh_calls++;
}
static void
widget_style_listener (const ThemeChangedEvent *event, gpointer userdata)
{
(void) userdata;
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_WIDGET_STYLE))
widget_style_calls++;
}
static void
unmatched_reason_listener (const ThemeChangedEvent *event, gpointer userdata)
{
(void) userdata;
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_IDENTD))
unmatched_listener_calls++;
}
static void
reset_counters (void)
{
window_refresh_calls = 0;
widget_style_calls = 0;
palette_reapply_calls = 0;
unmatched_listener_calls = 0;
}
static void
test_dispatch_filters_reasons_across_multiple_subscribers (void)
{
guint listener_window;
guint listener_widget;
guint listener_unmatched;
reset_counters ();
listener_window = theme_listener_register ("refresh.window", window_refresh_listener, NULL);
listener_widget = theme_listener_register ("refresh.widget", widget_style_listener, NULL);
listener_unmatched = theme_listener_register ("refresh.unmatched", unmatched_reason_listener, NULL);
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE);
g_assert_cmpint (window_refresh_calls, ==, 1);
g_assert_cmpint (palette_reapply_calls, ==, 1);
g_assert_cmpint (widget_style_calls, ==, 0);
g_assert_cmpint (unmatched_listener_calls, ==, 0);
theme_manager_dispatch_changed (THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST);
g_assert_cmpint (window_refresh_calls, ==, 2);
g_assert_cmpint (palette_reapply_calls, ==, 1);
g_assert_cmpint (widget_style_calls, ==, 1);
g_assert_cmpint (unmatched_listener_calls, ==, 0);
theme_manager_dispatch_changed (THEME_CHANGED_REASON_LAYOUT);
g_assert_cmpint (window_refresh_calls, ==, 2);
g_assert_cmpint (palette_reapply_calls, ==, 1);
g_assert_cmpint (widget_style_calls, ==, 1);
g_assert_cmpint (unmatched_listener_calls, ==, 0);
theme_listener_unregister (listener_unmatched);
theme_listener_unregister (listener_widget);
theme_listener_unregister (listener_window);
}
static void
test_preferences_change_synthesizes_theme_reasons (void)
{
struct zoitechatprefs old_prefs = { 0 };
struct zoitechatprefs new_prefs = { 0 };
ThemeChangedEvent event;
gboolean color_change = TRUE;
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_DARK;
old_prefs.hex_gui_dark_mode = prefs.hex_gui_dark_mode;
new_prefs.hex_gui_dark_mode = prefs.hex_gui_dark_mode;
strcpy (old_prefs.hex_text_background, "old.png");
strcpy (new_prefs.hex_text_background, "new.png");
old_prefs.hex_gui_tab_dots = 0;
new_prefs.hex_gui_tab_dots = 1;
old_prefs.hex_identd_port = 113;
new_prefs.hex_identd_port = 114;
old_prefs.hex_gui_ulist_color = 0;
new_prefs.hex_gui_ulist_color = 1;
event = theme_manager_on_preferences_changed (&old_prefs, &new_prefs, prefs.hex_gui_dark_mode, &color_change);
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_PIXMAP));
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_LAYOUT));
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_IDENTD));
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_USERLIST));
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_WIDGET_STYLE));
}
static void
test_preferences_change_omits_reasons_without_differences (void)
{
struct zoitechatprefs old_prefs = { 0 };
struct zoitechatprefs new_prefs = { 0 };
ThemeChangedEvent event;
gboolean color_change = FALSE;
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_DARK;
old_prefs.hex_gui_dark_mode = prefs.hex_gui_dark_mode;
new_prefs.hex_gui_dark_mode = prefs.hex_gui_dark_mode;
event = theme_manager_on_preferences_changed (&old_prefs, &new_prefs, prefs.hex_gui_dark_mode, &color_change);
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_PIXMAP));
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_LAYOUT));
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_IDENTD));
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_USERLIST));
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_WIDGET_STYLE));
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/theme/manager/dispatch_filters_reasons_across_multiple_subscribers",
test_dispatch_filters_reasons_across_multiple_subscribers);
g_test_add_func ("/theme/manager/preferences_change_synthesizes_theme_reasons",
test_preferences_change_synthesizes_theme_reasons);
g_test_add_func ("/theme/manager/preferences_change_omits_reasons_without_differences",
test_preferences_change_omits_reasons_without_differences);
return g_test_run ();
}

View File

@@ -1,379 +0,0 @@
#include <gtk/gtk.h>
#include "../theme-palette.h"
#include "../theme-manager.h"
#include "../../fe-gtk.h"
#include "../../../common/zoitechat.h"
#include "../../../common/zoitechatc.h"
struct session *current_sess;
struct session *current_tab;
struct session *lastact_sess;
struct zoitechatprefs prefs;
static gboolean stub_policy_dark;
static unsigned int stub_policy_mode;
static gboolean stub_apply_mode_result;
static gboolean stub_apply_mode_palette_changed;
static int stub_dark_set_calls;
static int stub_user_set_calls;
static int stub_apply_mode_calls;
static int stub_reload_style_calls;
static ThemeSemanticToken stub_last_dark_token;
static ThemeSemanticToken stub_last_user_token;
static int listener_a_calls;
static int listener_b_calls;
static ThemeChangedEvent listener_last_event;
void setup_apply_real (const ThemeChangedEvent *event)
{
(void) event;
}
gboolean fe_dark_mode_is_enabled_for (unsigned int mode)
{
return mode == ZOITECHAT_DARK_MODE_DARK;
}
void fe_set_auto_dark_mode_state (gboolean enabled)
{
(void) enabled;
}
gboolean fe_win32_high_contrast_is_enabled (void)
{
return FALSE;
}
gboolean fe_win32_try_get_system_dark (gboolean *enabled)
{
(void) enabled;
return FALSE;
}
void zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback)
{
(void) callback;
}
gboolean theme_policy_is_dark_mode_active (unsigned int mode)
{
stub_policy_mode = mode;
return stub_policy_dark;
}
gboolean theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
{
(void) mode;
if (palette_changed)
*palette_changed = stub_apply_mode_palette_changed;
return stub_apply_mode_result;
}
void theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *color)
{
(void) color;
stub_dark_set_calls++;
stub_last_dark_token = token;
}
void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *color)
{
(void) color;
stub_user_set_calls++;
stub_last_user_token = token;
}
void theme_runtime_reset_mode_colors (gboolean dark_mode)
{
(void) dark_mode;
}
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
{
(void) mode;
(void) dark_active;
stub_apply_mode_calls++;
return TRUE;
}
void theme_application_reload_input_style (void)
{
stub_reload_style_calls++;
}
void theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
{
(void) enabled;
(void) font_desc;
}
void theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
const PangoFontDescription *font_desc)
{
(void) widget;
(void) bg;
(void) fg;
(void) font_desc;
}
void theme_runtime_load (void)
{
}
void theme_runtime_save (void)
{
}
gboolean theme_runtime_is_dark_active (void)
{
return FALSE;
}
gboolean theme_policy_system_prefers_dark (void)
{
return FALSE;
}
void gtkutil_apply_palette (GtkWidget *widget, const GdkRGBA *background, const GdkRGBA *foreground,
const PangoFontDescription *font_desc)
{
(void) widget;
(void) background;
(void) foreground;
(void) font_desc;
}
void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
{
gdk_rgba_parse (&out_values->background, "#101010");
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
}
void theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
{
(void) widget;
theme_get_widget_style_values (out_values);
}
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
{
(void) window;
(void) dark;
}
static void
listener_a (const ThemeChangedEvent *event, gpointer userdata)
{
(void) userdata;
listener_a_calls++;
listener_last_event = *event;
}
static void
listener_b (const ThemeChangedEvent *event, gpointer userdata)
{
(void) userdata;
(void) event;
listener_b_calls++;
}
static void
reset_manager_stubs (void)
{
stub_policy_dark = FALSE;
stub_policy_mode = 999;
stub_apply_mode_result = TRUE;
stub_apply_mode_palette_changed = FALSE;
stub_dark_set_calls = 0;
stub_user_set_calls = 0;
stub_apply_mode_calls = 0;
stub_reload_style_calls = 0;
stub_last_dark_token = -1;
stub_last_user_token = -1;
listener_a_calls = 0;
listener_b_calls = 0;
}
static void
test_token_roundtrip (void)
{
size_t i;
for (i = 0; i < theme_palette_token_def_count (); i++)
{
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
int legacy_idx = -1;
ThemeSemanticToken token = THEME_TOKEN_MIRC_0;
g_assert_nonnull (def);
g_assert_true (theme_palette_token_to_legacy_index (def->token, &legacy_idx));
g_assert_cmpint (legacy_idx, ==, def->legacy_index);
g_assert_true (theme_palette_legacy_index_to_token (legacy_idx, &token));
g_assert_cmpint (token, ==, def->token);
}
}
static void
test_policy_mode_resolution (void)
{
g_assert_false (theme_policy_is_dark_mode_active (ZOITECHAT_DARK_MODE_LIGHT));
g_assert_true (theme_policy_is_dark_mode_active (ZOITECHAT_DARK_MODE_DARK));
}
static void
test_manager_set_token_color_routes_by_mode (void)
{
GdkRGBA color = { 0.1, 0.2, 0.3, 1.0 };
gboolean palette_changed = FALSE;
reset_manager_stubs ();
stub_policy_dark = FALSE;
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, THEME_TOKEN_MIRC_2, &color, &palette_changed);
g_assert_cmpint (stub_policy_mode, ==, ZOITECHAT_DARK_MODE_LIGHT);
g_assert_cmpint (stub_user_set_calls, ==, 1);
g_assert_cmpint (stub_dark_set_calls, ==, 0);
g_assert_cmpint (stub_apply_mode_calls, ==, 1);
g_assert_cmpint (stub_reload_style_calls, ==, 1);
g_assert_true (palette_changed);
reset_manager_stubs ();
stub_policy_dark = TRUE;
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_DARK, THEME_TOKEN_MIRC_2, &color, &palette_changed);
g_assert_cmpint (stub_policy_mode, ==, ZOITECHAT_DARK_MODE_DARK);
g_assert_cmpint (stub_user_set_calls, ==, 0);
g_assert_cmpint (stub_dark_set_calls, ==, 1);
reset_manager_stubs ();
stub_policy_dark = TRUE;
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_AUTO, THEME_TOKEN_MIRC_2, &color, &palette_changed);
g_assert_cmpint (stub_policy_mode, ==, ZOITECHAT_DARK_MODE_AUTO);
g_assert_cmpint (stub_user_set_calls, ==, 0);
g_assert_cmpint (stub_dark_set_calls, ==, 1);
}
static void
test_manager_set_token_color_routes_setup_indexes (void)
{
GdkRGBA color = { 0.7, 0.3, 0.2, 1.0 };
gboolean palette_changed = FALSE;
size_t i;
for (i = 0; i < theme_palette_token_def_count (); i++)
{
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
g_assert_nonnull (def);
reset_manager_stubs ();
stub_policy_dark = FALSE;
palette_changed = FALSE;
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, def->token, &color, &palette_changed);
g_assert_cmpint (stub_user_set_calls, ==, 1);
g_assert_cmpint (stub_last_user_token, ==, def->token);
g_assert_cmpint (stub_dark_set_calls, ==, 0);
g_assert_true (palette_changed);
reset_manager_stubs ();
stub_policy_dark = TRUE;
palette_changed = FALSE;
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_DARK, def->token, &color, &palette_changed);
g_assert_cmpint (stub_dark_set_calls, ==, 1);
g_assert_cmpint (stub_last_dark_token, ==, def->token);
g_assert_cmpint (stub_user_set_calls, ==, 0);
g_assert_true (palette_changed);
}
}
static void
test_manager_listener_registration_dispatch_and_unregister (void)
{
guint id_a;
guint id_b;
reset_manager_stubs ();
id_a = theme_listener_register ("test.a", listener_a, NULL);
id_b = theme_listener_register ("test.b", listener_b, NULL);
g_assert_cmpuint (id_a, >, 0);
g_assert_cmpuint (id_b, >, 0);
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PIXMAP | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_IDENTD | THEME_CHANGED_REASON_WIDGET_STYLE);
g_assert_cmpint (listener_a_calls, ==, 1);
g_assert_cmpint (listener_b_calls, ==, 1);
g_assert_true (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_PIXMAP));
g_assert_true (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_USERLIST));
g_assert_false (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_LAYOUT));
g_assert_true (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_IDENTD));
g_assert_true (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_WIDGET_STYLE));
theme_listener_unregister (id_a);
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PIXMAP | THEME_CHANGED_REASON_LAYOUT | THEME_CHANGED_REASON_WIDGET_STYLE);
g_assert_cmpint (listener_a_calls, ==, 1);
g_assert_cmpint (listener_b_calls, ==, 2);
theme_listener_unregister (id_b);
}
static void
test_manager_window_attach_detach_idempotence (void)
{
GtkWidget *window;
gulong *first_handler_ptr;
gulong first_handler_id;
gulong *second_handler_ptr;
gulong second_handler_id;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_assert_nonnull (window);
theme_manager_attach_window (window);
first_handler_ptr = g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler");
g_assert_nonnull (first_handler_ptr);
first_handler_id = *first_handler_ptr;
g_assert_cmpuint (first_handler_id, >, 0);
g_assert_true (g_signal_handler_is_connected (G_OBJECT (window), first_handler_id));
theme_manager_attach_window (window);
second_handler_ptr = g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler");
g_assert_nonnull (second_handler_ptr);
g_assert_true (first_handler_ptr == second_handler_ptr);
g_assert_cmpuint (*second_handler_ptr, ==, first_handler_id);
theme_manager_detach_window (window);
g_assert_null (g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler"));
g_assert_false (g_signal_handler_is_connected (G_OBJECT (window), first_handler_id));
theme_manager_detach_window (window);
g_assert_null (g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler"));
theme_manager_attach_window (window);
second_handler_ptr = g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler");
g_assert_nonnull (second_handler_ptr);
second_handler_id = *second_handler_ptr;
g_assert_cmpuint (second_handler_id, >, 0);
g_assert_true (g_signal_handler_is_connected (G_OBJECT (window), second_handler_id));
theme_manager_detach_window (window);
g_assert_null (g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler"));
g_assert_false (g_signal_handler_is_connected (G_OBJECT (window), second_handler_id));
gtk_widget_destroy (window);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/theme/palette/token_roundtrip", test_token_roundtrip);
g_test_add_func ("/theme/policy/mode_resolution", test_policy_mode_resolution);
g_test_add_func ("/theme/manager/set_token_color_routes_by_mode", test_manager_set_token_color_routes_by_mode);
g_test_add_func ("/theme/manager/set_token_color_routes_setup_indexes",
test_manager_set_token_color_routes_setup_indexes);
g_test_add_func ("/theme/manager/listener_registration_dispatch_and_unregister",
test_manager_listener_registration_dispatch_and_unregister);
g_test_add_func ("/theme/manager/window_attach_detach_idempotence",
test_manager_window_attach_detach_idempotence);
return g_test_run ();
}

View File

@@ -1,280 +0,0 @@
#include <gtk/gtk.h>
#include "../../../common/zoitechat.h"
#include "../../../common/zoitechatc.h"
#include "../../../common/gtk3-theme-service.h"
#include "../../fe-gtk.h"
#include "../theme-gtk3.h"
#include "../theme-manager.h"
struct session *current_sess;
struct session *current_tab;
struct zoitechatprefs prefs;
InputStyle *input_style;
static gboolean gtk_available;
static int apply_current_calls;
static char applied_theme_id[256];
static ThemeGtk3Variant applied_variant;
static gboolean removed_selected;
GtkWidget *
gtkutil_box_new (GtkOrientation orientation, gboolean homogeneous, gint spacing)
{
(void)homogeneous;
return gtk_box_new (orientation, spacing);
}
void
gtkutil_apply_palette (GtkWidget *wid, const GdkRGBA *fg, const GdkRGBA *bg, const PangoFontDescription *font)
{
(void)wid;
(void)fg;
(void)bg;
(void)font;
}
void
fe_open_url (const char *url)
{
(void)url;
}
gboolean
theme_get_color (ThemeSemanticToken token, GdkRGBA *color)
{
(void)token;
if (color)
gdk_rgba_parse (color, "#000000");
return TRUE;
}
void
theme_manager_set_token_color (unsigned int dark_mode, ThemeSemanticToken token, const GdkRGBA *color, gboolean *changed)
{
(void)dark_mode;
(void)token;
(void)color;
if (changed)
*changed = FALSE;
}
void
theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed)
{
(void)mode;
if (palette_changed)
*palette_changed = FALSE;
}
void
theme_manager_save_preferences (void)
{
}
ThemePaletteBehavior
theme_manager_get_userlist_palette_behavior (const PangoFontDescription *font_desc)
{
ThemePaletteBehavior behavior;
behavior.font_desc = font_desc;
behavior.apply_background = FALSE;
behavior.apply_foreground = FALSE;
return behavior;
}
void
theme_manager_apply_userlist_style (GtkWidget *widget, ThemePaletteBehavior behavior)
{
(void)widget;
(void)behavior;
}
void
theme_manager_attach_window (GtkWidget *window)
{
(void)window;
}
char *
zoitechat_gtk3_theme_service_get_user_themes_dir (void)
{
return g_strdup ("/tmp");
}
static ZoitechatGtk3Theme *
new_theme (const char *id, const char *name, ZoitechatGtk3ThemeSource source)
{
ZoitechatGtk3Theme *theme = g_new0 (ZoitechatGtk3Theme, 1);
theme->id = g_strdup (id);
theme->display_name = g_strdup (name);
theme->source = source;
return theme;
}
void
zoitechat_gtk3_theme_free (ZoitechatGtk3Theme *theme)
{
if (!theme)
return;
g_free (theme->id);
g_free (theme->display_name);
g_free (theme->path);
g_free (theme->thumbnail_path);
g_free (theme);
}
GPtrArray *
zoitechat_gtk3_theme_service_discover (void)
{
GPtrArray *themes = g_ptr_array_new_with_free_func ((GDestroyNotify)zoitechat_gtk3_theme_free);
if (!removed_selected)
g_ptr_array_add (themes, new_theme ("removed-theme", "Removed Theme", ZOITECHAT_GTK3_THEME_SOURCE_USER));
g_ptr_array_add (themes, new_theme ("fallback-theme", "Fallback Theme", ZOITECHAT_GTK3_THEME_SOURCE_SYSTEM));
return themes;
}
ZoitechatGtk3Theme *
zoitechat_gtk3_theme_find_by_id (const char *theme_id)
{
(void)theme_id;
return NULL;
}
gboolean
zoitechat_gtk3_theme_service_import (const char *source_path, char **imported_id, GError **error)
{
(void)source_path;
(void)imported_id;
(void)error;
return FALSE;
}
gboolean
zoitechat_gtk3_theme_service_remove_user_theme (const char *theme_id, GError **error)
{
(void)error;
if (g_strcmp0 (theme_id, "removed-theme") == 0)
{
removed_selected = TRUE;
return TRUE;
}
return FALSE;
}
char *
zoitechat_gtk3_theme_pick_css_dir_for_minor (const char *theme_root, int preferred_minor)
{
(void)theme_root;
(void)preferred_minor;
return NULL;
}
char *
zoitechat_gtk3_theme_pick_css_dir (const char *theme_root)
{
(void)theme_root;
return NULL;
}
GPtrArray *
zoitechat_gtk3_theme_build_inheritance_chain (const char *theme_root)
{
(void)theme_root;
return NULL;
}
gboolean
theme_gtk3_apply_current (GError **error)
{
(void)error;
apply_current_calls++;
g_strlcpy (applied_theme_id, prefs.hex_gui_gtk3_theme, sizeof (applied_theme_id));
applied_variant = (ThemeGtk3Variant)prefs.hex_gui_gtk3_variant;
return TRUE;
}
void
theme_gtk3_init (void)
{
}
gboolean
theme_gtk3_apply (const char *theme_id, ThemeGtk3Variant variant, GError **error)
{
(void)theme_id;
(void)variant;
(void)error;
return TRUE;
}
ThemeGtk3Variant
theme_gtk3_variant_for_theme (const char *theme_id)
{
if (g_str_has_suffix (theme_id, "dark"))
return THEME_GTK3_VARIANT_PREFER_DARK;
return THEME_GTK3_VARIANT_PREFER_LIGHT;
}
void
theme_gtk3_disable (void)
{
}
gboolean
theme_gtk3_is_active (void)
{
return FALSE;
}
#include "../theme-preferences.c"
static void
test_removed_selected_theme_commits_fallback_and_applies (void)
{
GtkWidget *page;
theme_preferences_ui *ui;
struct zoitechatprefs setup_prefs;
if (!gtk_available)
{
g_test_skip ("GTK display not available");
return;
}
memset (&setup_prefs, 0, sizeof (setup_prefs));
memset (&prefs, 0, sizeof (prefs));
g_strlcpy (prefs.hex_gui_gtk3_theme, "removed-theme", sizeof (prefs.hex_gui_gtk3_theme));
prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_PREFER_DARK;
removed_selected = FALSE;
apply_current_calls = 0;
applied_theme_id[0] = '\0';
page = theme_preferences_create_page (NULL, &setup_prefs, NULL);
ui = g_object_get_data (G_OBJECT (page), "theme-preferences-ui");
g_assert_nonnull (ui);
g_assert_nonnull (ui->gtk3_remove);
gtk_button_clicked (GTK_BUTTON (ui->gtk3_remove));
g_assert_cmpstr (prefs.hex_gui_gtk3_theme, ==, "fallback-theme");
g_assert_cmpstr (setup_prefs.hex_gui_gtk3_theme, ==, "fallback-theme");
g_assert_cmpint (prefs.hex_gui_gtk3_variant, ==, THEME_GTK3_VARIANT_PREFER_LIGHT);
g_assert_cmpint (setup_prefs.hex_gui_gtk3_variant, ==, THEME_GTK3_VARIANT_PREFER_LIGHT);
g_assert_cmpint (apply_current_calls, ==, 1);
g_assert_cmpstr (applied_theme_id, ==, "fallback-theme");
g_assert_cmpint (applied_variant, ==, THEME_GTK3_VARIANT_PREFER_LIGHT);
gtk_widget_destroy (page);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
gtk_available = gtk_init_check (&argc, &argv);
g_test_add_func ("/theme/preferences/gtk3_removed_selection_applies_fallback",
test_removed_selected_theme_commits_fallback_and_applies);
return g_test_run ();
}

View File

@@ -1,373 +0,0 @@
#include <errno.h>
#include <math.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>
#include <unistd.h>
#include "../theme-runtime.h"
#include "../../../common/zoitechat.h"
#include "../../../common/zoitechatc.h"
struct session *current_sess;
struct session *current_tab;
struct session *lastact_sess;
struct zoitechatprefs prefs;
static char *test_home_dir;
static gboolean
read_line_value (const char *cfg, const char *key, char *out, gsize out_len)
{
char *pattern;
char *pos;
char *line_end;
gsize value_len;
pattern = g_strdup_printf ("%s = ", key);
pos = g_strstr_len (cfg, -1, pattern);
g_free (pattern);
if (!pos)
return FALSE;
pos = strchr (pos, '=');
if (!pos)
return FALSE;
pos++;
while (*pos == ' ')
pos++;
line_end = strchr (pos, '\n');
if (!line_end)
line_end = pos + strlen (pos);
value_len = (gsize) (line_end - pos);
if (value_len + 1 > out_len)
return FALSE;
memcpy (out, pos, value_len);
out[value_len] = '\0';
return TRUE;
}
int
cfg_get_color (char *cfg, char *var, guint16 *r, guint16 *g, guint16 *b)
{
char value[128];
if (!read_line_value (cfg, var, value, sizeof (value)))
return 0;
if (sscanf (value, "%04hx %04hx %04hx", r, g, b) != 3)
return 0;
return 1;
}
int
cfg_get_int (char *cfg, char *var)
{
char value[128];
if (!read_line_value (cfg, var, value, sizeof (value)))
return 0;
return atoi (value);
}
int
cfg_put_color (int fh, guint16 r, guint16 g, guint16 b, char *var)
{
char line[256];
int len;
len = g_snprintf (line, sizeof line, "%s = %04hx %04hx %04hx\n", var, r, g, b);
if (len < 0)
return 0;
return write (fh, line, (size_t) len) == len;
}
int
cfg_put_int (int fh, int value, char *var)
{
char line[128];
int len;
len = g_snprintf (line, sizeof line, "%s = %d\n", var, value);
if (len < 0)
return 0;
return write (fh, line, (size_t) len) == len;
}
int
zoitechat_open_file (const char *file, int flags, int mode, int xof_flags)
{
char *path;
int fd;
(void) xof_flags;
path = g_build_filename (test_home_dir, file, NULL);
fd = g_open (path, flags, mode);
g_free (path);
return fd;
}
gboolean
fe_dark_mode_is_enabled_for (unsigned int mode)
{
return mode == ZOITECHAT_DARK_MODE_DARK;
}
gboolean
theme_policy_system_prefers_dark (void)
{
return FALSE;
}
gboolean
theme_policy_is_dark_mode_active (unsigned int mode)
{
return mode == ZOITECHAT_DARK_MODE_DARK;
}
static void
setup_temp_home (void)
{
if (test_home_dir)
return;
test_home_dir = g_dir_make_tmp ("zoitechat-theme-tests-XXXXXX", NULL);
g_assert_nonnull (test_home_dir);
}
static char *
read_colors_conf (void)
{
char *path;
char *content = NULL;
gsize length = 0;
gboolean ok;
path = g_build_filename (test_home_dir, "colors.conf", NULL);
ok = g_file_get_contents (path, &content, &length, NULL);
g_free (path);
g_assert_true (ok);
g_assert_cmpuint (length, >, 0);
return content;
}
static gboolean
colors_equal (const GdkRGBA *a, const GdkRGBA *b)
{
return a->red == b->red && a->green == b->green && a->blue == b->blue;
}
static void
apply_ui_color_edit (unsigned int mode, ThemeSemanticToken token, const char *hex)
{
GdkRGBA color;
g_assert_true (gdk_rgba_parse (&color, hex));
if (theme_policy_is_dark_mode_active (mode))
theme_runtime_dark_set_color (token, &color);
else
theme_runtime_user_set_color (token, &color);
theme_runtime_apply_mode (mode, NULL);
}
static void
test_persistence_roundtrip_light_and_dark (void)
{
GdkRGBA light_color;
GdkRGBA dark_color;
GdkRGBA loaded;
char *cfg;
setup_temp_home ();
theme_runtime_load ();
gdk_rgba_parse (&light_color, "#123456");
theme_runtime_user_set_color (THEME_TOKEN_MIRC_0, &light_color);
theme_runtime_apply_dark_mode (FALSE);
theme_runtime_apply_dark_mode (TRUE);
gdk_rgba_parse (&dark_color, "#abcdef");
theme_runtime_dark_set_color (THEME_TOKEN_MIRC_0, &dark_color);
theme_runtime_save ();
cfg = read_colors_conf ();
g_assert_nonnull (g_strstr_len (cfg, -1, "theme.mode.light.token.mirc_0"));
g_assert_nonnull (g_strstr_len (cfg, -1, "theme.mode.dark.token.mirc_0"));
g_assert_null (g_strstr_len (cfg, -1, "color_0 = "));
g_assert_null (g_strstr_len (cfg, -1, "dark_color_0 = "));
g_free (cfg);
theme_runtime_load ();
theme_runtime_apply_dark_mode (FALSE);
g_assert_true (theme_runtime_get_color (THEME_TOKEN_MIRC_0, &loaded));
g_assert_true (colors_equal (&light_color, &loaded));
theme_runtime_apply_dark_mode (TRUE);
g_assert_true (theme_runtime_get_color (THEME_TOKEN_MIRC_0, &loaded));
g_assert_true (colors_equal (&dark_color, &loaded));
}
static void
test_loads_legacy_color_keys_via_migration_loader (void)
{
char *path;
const char *legacy_cfg =
"color_0 = 1111 2222 3333\n"
"dark_color_0 = aaaa bbbb cccc\n";
GdkRGBA loaded;
GdkRGBA light_expected;
GdkRGBA dark_expected;
gboolean ok;
setup_temp_home ();
path = g_build_filename (test_home_dir, "colors.conf", NULL);
ok = g_file_set_contents (path, legacy_cfg, -1, NULL);
g_free (path);
g_assert_true (ok);
theme_runtime_load ();
gdk_rgba_parse (&light_expected, "#111122223333");
gdk_rgba_parse (&dark_expected, "#aaaabbbbcccc");
theme_runtime_apply_dark_mode (FALSE);
g_assert_true (theme_runtime_get_color (THEME_TOKEN_MIRC_0, &loaded));
g_assert_true (colors_equal (&loaded, &light_expected));
theme_runtime_apply_dark_mode (TRUE);
g_assert_true (theme_runtime_get_color (THEME_TOKEN_MIRC_0, &loaded));
g_assert_true (colors_equal (&loaded, &dark_expected));
}
static void
test_ui_edits_persist_without_legacy_array_mutation (void)
{
GdkRGBA light_loaded;
GdkRGBA dark_loaded;
GdkRGBA light_expected;
GdkRGBA dark_expected;
setup_temp_home ();
theme_runtime_load ();
apply_ui_color_edit (ZOITECHAT_DARK_MODE_LIGHT, THEME_TOKEN_SELECTION_FOREGROUND, "#224466");
apply_ui_color_edit (ZOITECHAT_DARK_MODE_DARK, THEME_TOKEN_SELECTION_FOREGROUND, "#88aacc");
theme_runtime_save ();
theme_runtime_load ();
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, NULL);
g_assert_true (theme_runtime_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &light_loaded));
g_assert_true (gdk_rgba_parse (&light_expected, "#224466"));
g_assert_true (colors_equal (&light_loaded, &light_expected));
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_DARK, NULL);
g_assert_true (theme_runtime_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &dark_loaded));
g_assert_true (gdk_rgba_parse (&dark_expected, "#88aacc"));
g_assert_true (colors_equal (&dark_loaded, &dark_expected));
}
static void
test_gtk_map_colors_blend_with_palette_without_transparency (void)
{
ThemeGtkPaletteMap map = { 0 };
ThemeWidgetStyleValues base_values;
ThemeWidgetStyleValues values;
GdkRGBA mapped_bg;
double alpha;
double expected_red;
double expected_green;
double expected_blue;
setup_temp_home ();
theme_runtime_load ();
theme_runtime_get_widget_style_values (&base_values);
map.enabled = TRUE;
g_assert_true (gdk_rgba_parse (&map.text_foreground, "rgba(10, 20, 30, 0.25)"));
g_assert_true (gdk_rgba_parse (&map.text_background, "rgba(40, 50, 60, 0.30)"));
g_assert_true (gdk_rgba_parse (&map.selection_foreground, "rgba(70, 80, 90, 0.35)"));
g_assert_true (gdk_rgba_parse (&map.selection_background, "rgba(100, 110, 120, 0.40)"));
g_assert_true (gdk_rgba_parse (&map.accent, "rgba(130, 140, 150, 0.45)"));
theme_runtime_get_widget_style_values_mapped (&map, &values);
g_assert_cmpfloat (values.foreground.alpha, ==, 1.0);
g_assert_cmpfloat (values.background.alpha, ==, 1.0);
g_assert_cmpfloat (values.selection_foreground.alpha, ==, 1.0);
g_assert_cmpfloat (values.selection_background.alpha, ==, 1.0);
mapped_bg = map.text_background;
alpha = mapped_bg.alpha;
expected_red = (mapped_bg.red * alpha) + (base_values.background.red * (1.0 - alpha));
expected_green = (mapped_bg.green * alpha) + (base_values.background.green * (1.0 - alpha));
expected_blue = (mapped_bg.blue * alpha) + (base_values.background.blue * (1.0 - alpha));
g_assert_true (fabs (values.background.red - expected_red) < 0.0001);
g_assert_true (fabs (values.background.green - expected_green) < 0.0001);
g_assert_true (fabs (values.background.blue - expected_blue) < 0.0001);
}
static void
test_gtk_map_uses_theme_defaults_until_custom_token_is_set (void)
{
ThemeGtkPaletteMap map = { 0 };
ThemeWidgetStyleValues values;
GdkRGBA custom;
setup_temp_home ();
theme_runtime_load ();
map.enabled = TRUE;
g_assert_true (gdk_rgba_parse (&map.text_foreground, "#010203"));
g_assert_true (gdk_rgba_parse (&map.text_background, "#111213"));
g_assert_true (gdk_rgba_parse (&map.selection_foreground, "#212223"));
g_assert_true (gdk_rgba_parse (&map.selection_background, "#313233"));
g_assert_true (gdk_rgba_parse (&map.accent, "#414243"));
theme_runtime_get_widget_style_values_mapped (&map, &values);
g_assert_true (colors_equal (&values.foreground, &map.text_foreground));
g_assert_true (gdk_rgba_parse (&custom, "#a1b2c3"));
theme_runtime_user_set_color (THEME_TOKEN_TEXT_FOREGROUND, &custom);
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, NULL);
theme_runtime_get_widget_style_values_mapped (&map, &values);
g_assert_true (colors_equal (&values.foreground, &custom));
}
static void
test_save_writes_only_custom_token_keys (void)
{
GdkRGBA custom;
char *cfg;
setup_temp_home ();
theme_runtime_load ();
g_assert_true (gdk_rgba_parse (&custom, "#445566"));
theme_runtime_user_set_color (THEME_TOKEN_TEXT_FOREGROUND, &custom);
theme_runtime_save ();
cfg = read_colors_conf ();
g_assert_nonnull (g_strstr_len (cfg, -1, "theme.mode.light.token.text_foreground"));
g_assert_null (g_strstr_len (cfg, -1, "theme.mode.light.token.text_background"));
g_free (cfg);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/theme/runtime/persistence_roundtrip_light_and_dark",
test_persistence_roundtrip_light_and_dark);
g_test_add_func ("/theme/runtime/loads_legacy_color_keys_via_migration_loader",
test_loads_legacy_color_keys_via_migration_loader);
g_test_add_func ("/theme/runtime/ui_edits_persist_without_legacy_array_mutation",
test_ui_edits_persist_without_legacy_array_mutation);
g_test_add_func ("/theme/runtime/gtk_map_colors_blend_with_palette_without_transparency",
test_gtk_map_colors_blend_with_palette_without_transparency);
g_test_add_func ("/theme/runtime/gtk_map_uses_theme_defaults_until_custom_token_is_set",
test_gtk_map_uses_theme_defaults_until_custom_token_is_set);
g_test_add_func ("/theme/runtime/save_writes_only_custom_token_keys",
test_save_writes_only_custom_token_keys);
return g_test_run ();
}

View File

@@ -1,146 +0,0 @@
#include "theme-access.h"
#include "theme-runtime.h"
#include "theme-gtk3.h"
enum
{
THEME_XTEXT_FG_INDEX = 34,
THEME_XTEXT_BG_INDEX = 35
};
static gboolean
theme_token_to_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue)
{
GdkRGBA color = { 0 };
g_return_val_if_fail (red != NULL, FALSE);
g_return_val_if_fail (green != NULL, FALSE);
g_return_val_if_fail (blue != NULL, FALSE);
if (!theme_runtime_get_color (token, &color))
return FALSE;
theme_palette_color_get_rgb16 (&color, red, green, blue);
return TRUE;
}
static gboolean
theme_access_get_gtk_palette_map (GtkWidget *widget, ThemeGtkPaletteMap *out_map)
{
GtkStyleContext *context;
GdkRGBA accent;
g_return_val_if_fail (out_map != NULL, FALSE);
if (!theme_gtk3_is_active () || widget == NULL)
return FALSE;
context = gtk_widget_get_style_context (widget);
if (context == NULL)
return FALSE;
gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &out_map->text_foreground);
gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &out_map->text_background);
gtk_style_context_get_color (context, GTK_STATE_FLAG_SELECTED, &out_map->selection_foreground);
gtk_style_context_get_background_color (context, GTK_STATE_FLAG_SELECTED, &out_map->selection_background);
gtk_style_context_get_color (context, GTK_STATE_FLAG_LINK, &accent);
if (accent.alpha <= 0.0)
accent = out_map->selection_background;
out_map->accent = accent;
out_map->enabled = TRUE;
return TRUE;
}
gboolean
theme_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
{
return theme_runtime_get_color (token, out_rgba);
}
gboolean
theme_get_mirc_color (unsigned int mirc_index, GdkRGBA *out_rgba)
{
ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index);
if (mirc_index >= 32)
return FALSE;
return theme_runtime_get_color (token, out_rgba);
}
gboolean
theme_get_color_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue)
{
return theme_token_to_rgb16 (token, red, green, blue);
}
gboolean
theme_get_mirc_color_rgb16 (unsigned int mirc_index, guint16 *red, guint16 *green, guint16 *blue)
{
ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index);
if (mirc_index >= 32)
return FALSE;
return theme_token_to_rgb16 (token, red, green, blue);
}
gboolean
theme_get_legacy_color (int legacy_idx, GdkRGBA *out_rgba)
{
ThemeSemanticToken token;
g_return_val_if_fail (out_rgba != NULL, FALSE);
if (!theme_palette_legacy_index_to_token (legacy_idx, &token))
return FALSE;
return theme_runtime_get_color (token, out_rgba);
}
void
theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
{
theme_get_widget_style_values_for_widget (NULL, out_values);
}
void
theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
{
ThemeGtkPaletteMap gtk_map = { 0 };
if (theme_access_get_gtk_palette_map (widget, &gtk_map))
{
theme_runtime_get_widget_style_values_mapped (&gtk_map, out_values);
return;
}
theme_runtime_get_widget_style_values (out_values);
}
void
theme_get_xtext_colors (XTextColor *palette, size_t palette_len)
{
theme_get_xtext_colors_for_widget (NULL, palette, palette_len);
}
void
theme_get_xtext_colors_for_widget (GtkWidget *widget, XTextColor *palette, size_t palette_len)
{
ThemeWidgetStyleValues style_values;
if (!palette)
return;
theme_get_widget_style_values_for_widget (widget, &style_values);
theme_runtime_get_xtext_colors (palette, palette_len);
if (palette_len > THEME_XTEXT_FG_INDEX)
{
palette[THEME_XTEXT_FG_INDEX].red = style_values.foreground.red;
palette[THEME_XTEXT_FG_INDEX].green = style_values.foreground.green;
palette[THEME_XTEXT_FG_INDEX].blue = style_values.foreground.blue;
palette[THEME_XTEXT_FG_INDEX].alpha = style_values.foreground.alpha;
}
if (palette_len > THEME_XTEXT_BG_INDEX)
{
palette[THEME_XTEXT_BG_INDEX].red = style_values.background.red;
palette[THEME_XTEXT_BG_INDEX].green = style_values.background.green;
palette[THEME_XTEXT_BG_INDEX].blue = style_values.background.blue;
palette[THEME_XTEXT_BG_INDEX].alpha = style_values.background.alpha;
}
}

View File

@@ -1,22 +0,0 @@
#ifndef ZOITECHAT_THEME_ACCESS_H
#define ZOITECHAT_THEME_ACCESS_H
#include <stddef.h>
#include <gtk/gtk.h>
#include "theme-palette.h"
#include "../xtext-color.h"
gboolean theme_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba);
gboolean theme_get_mirc_color (unsigned int mirc_index, GdkRGBA *out_rgba);
gboolean theme_get_color_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue);
gboolean theme_get_mirc_color_rgb16 (unsigned int mirc_index, guint16 *red, guint16 *green, guint16 *blue);
G_DEPRECATED_FOR (theme_get_color)
gboolean theme_get_legacy_color (int legacy_idx, GdkRGBA *out_rgba);
void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values);
void theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values);
void theme_get_xtext_colors (XTextColor *palette, size_t palette_len);
void theme_get_xtext_colors_for_widget (GtkWidget *widget, XTextColor *palette, size_t palette_len);
#endif

View File

@@ -1,113 +0,0 @@
#include "theme-application.h"
#include "../../common/fe.h"
#include "../../common/zoitechatc.h"
#include "theme-css.h"
#include "theme-runtime.h"
#include "theme-gtk3.h"
#include "../maingui.h"
#ifdef G_OS_WIN32
#include <gtk/gtk.h>
static void
theme_application_apply_windows_theme (gboolean dark)
{
GtkSettings *settings = gtk_settings_get_default ();
static GtkCssProvider *win_theme_provider = NULL;
static gboolean win_theme_provider_installed = FALSE;
GdkScreen *screen;
gboolean prefer_dark = dark;
char *css;
if (theme_gtk3_is_active ())
{
if (prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_PREFER_DARK)
prefer_dark = TRUE;
else if (prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_PREFER_LIGHT)
prefer_dark = FALSE;
}
if (settings && g_object_class_find_property (G_OBJECT_GET_CLASS (settings),
"gtk-application-prefer-dark-theme"))
g_object_set (settings, "gtk-application-prefer-dark-theme", prefer_dark, NULL);
screen = gdk_screen_get_default ();
if (!screen)
return;
if (theme_gtk3_is_active ())
{
if (win_theme_provider_installed && win_theme_provider)
{
gtk_style_context_remove_provider_for_screen (screen,
GTK_STYLE_PROVIDER (win_theme_provider));
win_theme_provider_installed = FALSE;
}
return;
}
if (!win_theme_provider)
win_theme_provider = gtk_css_provider_new ();
css = theme_css_build_toplevel_classes ();
gtk_css_provider_load_from_data (win_theme_provider, css, -1, NULL);
g_free (css);
if (!win_theme_provider_installed)
{
gtk_style_context_add_provider_for_screen (screen,
GTK_STYLE_PROVIDER (win_theme_provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
win_theme_provider_installed = TRUE;
}
}
#endif
gboolean
theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
{
gboolean dark;
theme_runtime_load ();
dark = theme_runtime_apply_mode (mode, palette_changed);
#ifdef G_OS_WIN32
theme_application_apply_windows_theme (dark);
#endif
theme_application_reload_input_style ();
return dark;
}
void
theme_application_reload_input_style (void)
{
input_style = theme_application_update_input_style (input_style);
}
InputStyle *
theme_application_update_input_style (InputStyle *style)
{
char buf[256];
if (!style)
style = g_new0 (InputStyle, 1);
if (style->font_desc)
pango_font_description_free (style->font_desc);
style->font_desc = pango_font_description_from_string (prefs.hex_text_font);
if (pango_font_description_get_size (style->font_desc) == 0)
{
g_snprintf (buf, sizeof (buf), _("Failed to open font:\n\n%s"), prefs.hex_text_font);
fe_message (buf, FE_MSG_ERROR);
pango_font_description_free (style->font_desc);
style->font_desc = pango_font_description_from_string ("sans 11");
}
theme_css_reload_input_style (FALSE, style->font_desc);
return style;
}

View File

@@ -1,11 +0,0 @@
#ifndef ZOITECHAT_THEME_APPLICATION_H
#define ZOITECHAT_THEME_APPLICATION_H
#include <glib.h>
#include "../fe-gtk.h"
gboolean theme_application_apply_mode (unsigned int mode, gboolean *palette_changed);
void theme_application_reload_input_style (void);
InputStyle *theme_application_update_input_style (InputStyle *style);
#endif

View File

@@ -1,340 +0,0 @@
#include "theme-css.h"
#include "theme-runtime.h"
#include "theme-gtk3.h"
#include "theme-access.h"
#include "../gtkutil.h"
#include <string.h>
static const char *theme_css_selector_input = "#zoitechat-inputbox";
static const char *theme_css_selector_input_text = "#zoitechat-inputbox text";
static const char *theme_css_selector_palette_class = "zoitechat-palette";
static const char *theme_css_selector_dark_class = "zoitechat-dark";
static const char *theme_css_selector_light_class = "zoitechat-light";
static const char *theme_css_palette_provider_key = "zoitechat-palette-provider";
static const guint theme_css_provider_priority = GTK_STYLE_PROVIDER_PRIORITY_USER;
typedef struct
{
char *theme_name;
char *font;
gboolean enabled;
gboolean dark;
gboolean colors_set;
guint16 fg_red;
guint16 fg_green;
guint16 fg_blue;
guint16 bg_red;
guint16 bg_green;
guint16 bg_blue;
} ThemeCssInputFingerprint;
static GtkCssProvider *theme_css_input_provider;
static ThemeCssInputFingerprint theme_css_input_fp;
void
theme_css_apply_app_provider (GtkStyleProvider *provider)
{
GdkScreen *screen;
if (!provider)
return;
screen = gdk_screen_get_default ();
if (!screen)
return;
gtk_style_context_add_provider_for_screen (screen, provider, theme_css_provider_priority);
}
void
theme_css_remove_app_provider (GtkStyleProvider *provider)
{
GdkScreen *screen;
if (!provider)
return;
screen = gdk_screen_get_default ();
if (!screen)
return;
gtk_style_context_remove_provider_for_screen (screen, provider);
}
void
theme_css_apply_widget_provider (GtkWidget *widget, GtkStyleProvider *provider)
{
GtkStyleContext *context;
if (!widget || !provider)
return;
context = gtk_widget_get_style_context (widget);
if (!context)
return;
gtk_style_context_add_provider (context, provider, theme_css_provider_priority);
}
static gboolean
theme_css_input_fingerprint_matches (const ThemeCssInputFingerprint *next)
{
if (theme_css_input_fp.enabled != next->enabled)
return FALSE;
if (theme_css_input_fp.dark != next->dark)
return FALSE;
if (theme_css_input_fp.colors_set != next->colors_set)
return FALSE;
if (theme_css_input_fp.fg_red != next->fg_red || theme_css_input_fp.fg_green != next->fg_green
|| theme_css_input_fp.fg_blue != next->fg_blue)
return FALSE;
if (theme_css_input_fp.bg_red != next->bg_red || theme_css_input_fp.bg_green != next->bg_green
|| theme_css_input_fp.bg_blue != next->bg_blue)
return FALSE;
if (g_strcmp0 (theme_css_input_fp.theme_name, next->theme_name) != 0)
return FALSE;
if (g_strcmp0 (theme_css_input_fp.font, next->font) != 0)
return FALSE;
return TRUE;
}
static void
theme_css_input_fingerprint_replace (ThemeCssInputFingerprint *next)
{
g_free (theme_css_input_fp.theme_name);
g_free (theme_css_input_fp.font);
theme_css_input_fp = *next;
next->theme_name = NULL;
next->font = NULL;
}
static void
theme_css_input_fingerprint_clear (void)
{
g_free (theme_css_input_fp.theme_name);
g_free (theme_css_input_fp.font);
memset (&theme_css_input_fp, 0, sizeof (theme_css_input_fp));
}
static char *
theme_css_build_input (const char *theme_name, guint16 fg_red, guint16 fg_green, guint16 fg_blue,
guint16 bg_red, guint16 bg_green, guint16 bg_blue)
{
GString *css = g_string_new ("");
if (g_str_has_prefix (theme_name, "Adwaita") || g_str_has_prefix (theme_name, "Yaru"))
{
g_string_append_printf (css, "%s { background-image: none; }", theme_css_selector_input);
}
g_string_append_printf (css,
"%s {"
"background-color: #%02x%02x%02x;"
"color: #%02x%02x%02x;"
"caret-color: #%02x%02x%02x;"
"}"
"%s {"
"color: #%02x%02x%02x;"
"caret-color: #%02x%02x%02x;"
"}",
theme_css_selector_input,
(bg_red >> 8), (bg_green >> 8), (bg_blue >> 8),
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
theme_css_selector_input_text,
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8));
return g_string_free (css, FALSE);
}
void
theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
{
ThemeCssInputFingerprint next = {0};
next.enabled = enabled;
next.dark = theme_runtime_is_dark_active ();
next.theme_name = NULL;
next.font = font_desc ? pango_font_description_to_string (font_desc) : NULL;
if (enabled)
{
GtkSettings *settings = gtk_settings_get_default ();
char *theme_name = NULL;
char *css;
if (settings)
g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
next.theme_name = g_strdup (theme_name);
{
ThemeWidgetStyleValues style_values;
theme_get_widget_style_values_for_widget (NULL, &style_values);
theme_palette_color_get_rgb16 (&style_values.foreground,
&next.fg_red, &next.fg_green, &next.fg_blue);
theme_palette_color_get_rgb16 (&style_values.background,
&next.bg_red, &next.bg_green, &next.bg_blue);
next.colors_set = TRUE;
}
if (theme_css_input_fingerprint_matches (&next))
{
g_free (theme_name);
g_free (next.theme_name);
g_free (next.font);
return;
}
if (!theme_css_input_provider)
theme_css_input_provider = gtk_css_provider_new ();
css = theme_css_build_input (theme_name ? theme_name : "",
next.fg_red, next.fg_green, next.fg_blue,
next.bg_red, next.bg_green, next.bg_blue);
gtk_css_provider_load_from_data (theme_css_input_provider, css, -1, NULL);
g_free (css);
theme_css_apply_app_provider (GTK_STYLE_PROVIDER (theme_css_input_provider));
g_free (theme_name);
theme_css_input_fingerprint_replace (&next);
return;
}
if (theme_css_input_provider)
theme_css_remove_app_provider (GTK_STYLE_PROVIDER (theme_css_input_provider));
g_clear_object (&theme_css_input_provider);
theme_css_input_fingerprint_clear ();
g_free (next.theme_name);
g_free (next.font);
}
void
theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
const PangoFontDescription *font_desc)
{
GtkCssProvider *provider;
gboolean new_provider = FALSE;
GString *css;
gchar *bg_color = NULL;
gchar *fg_color = NULL;
gchar *sel_bg_color = NULL;
gchar *sel_fg_color = NULL;
if (!widget)
return;
provider = g_object_get_data (G_OBJECT (widget), theme_css_palette_provider_key);
if (!bg && !fg && !font_desc)
{
gtk_style_context_remove_class (gtk_widget_get_style_context (widget), theme_css_selector_palette_class);
if (provider)
{
gtk_style_context_remove_provider (gtk_widget_get_style_context (widget), GTK_STYLE_PROVIDER (provider));
g_object_set_data (G_OBJECT (widget), theme_css_palette_provider_key, NULL);
}
return;
}
if (!provider)
{
provider = gtk_css_provider_new ();
g_object_set_data_full (G_OBJECT (widget), theme_css_palette_provider_key,
provider, g_object_unref);
new_provider = TRUE;
}
css = g_string_new (".");
g_string_append (css, theme_css_selector_palette_class);
g_string_append (css, " {");
if (bg)
{
bg_color = gdk_rgba_to_string (bg);
g_string_append_printf (css, " background-color: %s;", bg_color);
}
if (fg)
{
fg_color = gdk_rgba_to_string (fg);
g_string_append_printf (css, " color: %s;", fg_color);
}
{
GdkRGBA selection_bg;
GdkRGBA selection_fg;
if (theme_runtime_get_color (THEME_TOKEN_SELECTION_BACKGROUND, &selection_bg))
sel_bg_color = gdk_rgba_to_string (&selection_bg);
if (theme_runtime_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &selection_fg))
sel_fg_color = gdk_rgba_to_string (&selection_fg);
}
gtkutil_append_font_css (css, font_desc);
g_string_append (css, " }");
g_string_append_printf (css, ".%s, .%s *, .%s treeview, .%s treeview.view, .%s treeview.view text, .%s treeview.view cell, .%s treeview.view row, .%s list, .%s list row, .%s text {", theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class);
if (bg)
g_string_append_printf (css, " background-color: %s;", bg_color);
if (fg)
g_string_append_printf (css, " color: %s;", fg_color);
g_string_append (css, " }");
g_string_append_printf (css, ".%s *:selected, .%s *:selected:focus, .%s *:selected:hover, .%s treeview.view:selected, .%s treeview.view:selected:focus, .%s treeview.view:selected:hover, .%s row:selected, .%s row:selected:focus, .%s row:selected:hover {", theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class);
if (sel_bg_color)
g_string_append_printf (css, " background-color: %s;", sel_bg_color);
else if (bg)
g_string_append (css, " background-color: @theme_selected_bg_color;");
if (sel_fg_color)
g_string_append_printf (css, " color: %s;", sel_fg_color);
else if (fg)
g_string_append (css, " color: @theme_selected_fg_color;");
g_string_append (css, " }");
gtk_css_provider_load_from_data (provider, css->str, -1, NULL);
if (new_provider)
theme_css_apply_widget_provider (widget, GTK_STYLE_PROVIDER (provider));
gtk_style_context_add_class (gtk_widget_get_style_context (widget), theme_css_selector_palette_class);
g_string_free (css, TRUE);
g_free (bg_color);
g_free (fg_color);
g_free (sel_bg_color);
g_free (sel_fg_color);
}
char *
theme_css_build_toplevel_classes (void)
{
return g_strdup_printf (
"window.%s, window.%s:backdrop, .%s {"
"background-color: #202020;"
"color: #f0f0f0;"
"border-color: #202020;"
"}"
"window.%s menubar, window.%s menubar:backdrop, window.%s menuitem, window.%s menuitem:backdrop {"
"background-color: #202020;"
"color: #f0f0f0;"
"border-color: #202020;"
"}"
"window.%s, window.%s:backdrop, .%s {"
"background-color: #f6f6f6;"
"color: #101010;"
"border-color: #f6f6f6;"
"}"
"window.%s menubar, window.%s menubar:backdrop, window.%s menuitem, window.%s menuitem:backdrop {"
"background-color: #f6f6f6;"
"color: #101010;"
"border-color: #f6f6f6;"
"}",
theme_css_selector_dark_class,
theme_css_selector_dark_class,
theme_css_selector_dark_class,
theme_css_selector_dark_class,
theme_css_selector_dark_class,
theme_css_selector_dark_class,
theme_css_selector_dark_class,
theme_css_selector_light_class,
theme_css_selector_light_class,
theme_css_selector_light_class,
theme_css_selector_light_class,
theme_css_selector_light_class,
theme_css_selector_light_class,
theme_css_selector_light_class);
}

View File

@@ -1,21 +0,0 @@
#ifndef ZOITECHAT_THEME_CSS_H
#define ZOITECHAT_THEME_CSS_H
#include "../fe-gtk.h"
/**
* theme_css_apply_app_provider/theme_css_remove_app_provider:
* Use for CSS providers that should apply to the entire application screen.
*
* theme_css_apply_widget_provider:
* Use for widget-local CSS providers attached to a specific widget context.
*/
void theme_css_apply_app_provider (GtkStyleProvider *provider);
void theme_css_remove_app_provider (GtkStyleProvider *provider);
void theme_css_apply_widget_provider (GtkWidget *widget, GtkStyleProvider *provider);
void theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc);
void theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
const PangoFontDescription *font_desc);
char *theme_css_build_toplevel_classes (void);
#endif

View File

@@ -1,9 +0,0 @@
#ifndef ZOITECHAT_THEME_GTK_H
#define ZOITECHAT_THEME_GTK_H
#include <gtk/gtk.h>
#define THEME_GTK_COLOR_TYPE GDK_TYPE_RGBA
#define THEME_GTK_FOREGROUND_PROPERTY "foreground-rgba"
#endif

View File

@@ -1,944 +0,0 @@
#include "theme-gtk3.h"
#include <gtk/gtk.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <string.h>
#include "theme-policy.h"
#include "../../common/gtk3-theme-service.h"
#include "../../common/zoitechat.h"
#include "../../common/zoitechatc.h"
static GPtrArray *theme_gtk3_providers_base;
static GPtrArray *theme_gtk3_providers_variant;
static GHashTable *theme_gtk3_provider_cache;
static gboolean theme_gtk3_active;
static char *theme_gtk3_current_id;
static ThemeGtk3Variant theme_gtk3_current_variant;
typedef struct
{
GHashTable *defaults;
char **icon_search_path;
gint icon_search_path_count;
gboolean icon_search_path_captured;
} ThemeGtk3SettingsState;
static ThemeGtk3SettingsState theme_gtk3_settings_state;
static gboolean settings_apply_property (GtkSettings *settings, const char *property_name, const char *raw_value);
static gboolean
theme_gtk3_theme_name_is_dark (const char *name)
{
char *lower;
gboolean dark;
if (!name || !name[0])
return FALSE;
lower = g_ascii_strdown (name, -1);
dark = strstr (lower, "dark") != NULL;
g_free (lower);
return dark;
}
static ThemeGtk3Variant
theme_gtk3_infer_variant (const ZoitechatGtk3Theme *theme)
{
char *css_dir;
char *light_css;
gboolean has_light_css;
ThemeGtk3Variant variant;
if (!theme)
return THEME_GTK3_VARIANT_PREFER_LIGHT;
css_dir = zoitechat_gtk3_theme_pick_css_dir (theme->path);
light_css = css_dir ? g_build_filename (theme->path, css_dir, "gtk.css", NULL) : NULL;
has_light_css = light_css && g_file_test (light_css, G_FILE_TEST_IS_REGULAR);
g_free (light_css);
g_free (css_dir);
variant = THEME_GTK3_VARIANT_PREFER_LIGHT;
if ((theme->has_dark_variant && !has_light_css) ||
theme_gtk3_theme_name_is_dark (theme->id) ||
theme_gtk3_theme_name_is_dark (theme->display_name))
variant = THEME_GTK3_VARIANT_PREFER_DARK;
return variant;
}
static void
settings_value_free (gpointer data)
{
GValue *value = data;
if (!value)
return;
g_value_unset (value);
g_free (value);
}
static GValue *
settings_value_dup (const GValue *source)
{
GValue *copy;
copy = g_new0 (GValue, 1);
g_value_init (copy, G_VALUE_TYPE (source));
g_value_copy (source, copy);
return copy;
}
static GHashTable *
settings_defaults_table (void)
{
if (!theme_gtk3_settings_state.defaults)
{
theme_gtk3_settings_state.defaults = g_hash_table_new_full (
g_str_hash,
g_str_equal,
g_free,
settings_value_free);
}
return theme_gtk3_settings_state.defaults;
}
static void
settings_rescan_icon_theme (void)
{
GtkIconTheme *icon_theme;
icon_theme = gtk_icon_theme_get_default ();
if (!icon_theme)
return;
gtk_icon_theme_rescan_if_needed (icon_theme);
}
static void
theme_gtk3_reset_widgets (void)
{
GdkScreen *screen = gdk_screen_get_default ();
if (screen)
gtk_style_context_reset_widgets (screen);
}
static void
settings_capture_icon_search_path (void)
{
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
if (!icon_theme || theme_gtk3_settings_state.icon_search_path_captured)
return;
gtk_icon_theme_get_search_path (icon_theme, &theme_gtk3_settings_state.icon_search_path, &theme_gtk3_settings_state.icon_search_path_count);
theme_gtk3_settings_state.icon_search_path_captured = TRUE;
}
static void
settings_append_icon_search_path (const char *path)
{
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
if (!icon_theme || !path || !g_file_test (path, G_FILE_TEST_IS_DIR))
return;
settings_capture_icon_search_path ();
gtk_icon_theme_append_search_path (icon_theme, path);
gtk_icon_theme_rescan_if_needed (icon_theme);
}
static void
settings_apply_icon_paths (const char *theme_root)
{
char *icons_dir;
char *theme_parent;
if (!theme_root)
return;
icons_dir = g_build_filename (theme_root, "icons", NULL);
theme_parent = g_path_get_dirname (theme_root);
settings_append_icon_search_path (icons_dir);
settings_append_icon_search_path (theme_root);
settings_append_icon_search_path (theme_parent);
g_free (theme_parent);
g_free (icons_dir);
}
static void
settings_restore_icon_search_path (void)
{
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
if (!icon_theme || !theme_gtk3_settings_state.icon_search_path_captured)
return;
gtk_icon_theme_set_search_path (icon_theme, (const char * const *) theme_gtk3_settings_state.icon_search_path, theme_gtk3_settings_state.icon_search_path_count);
gtk_icon_theme_rescan_if_needed (icon_theme);
g_strfreev (theme_gtk3_settings_state.icon_search_path);
theme_gtk3_settings_state.icon_search_path = NULL;
theme_gtk3_settings_state.icon_search_path_count = 0;
theme_gtk3_settings_state.icon_search_path_captured = FALSE;
}
static void
theme_gtk3_parsing_error_cb (GtkCssProvider *provider, GtkCssSection *section, const GError *error, gpointer user_data)
{
(void) provider;
(void) section;
(void) error;
(void) user_data;
g_signal_stop_emission_by_name (provider, "parsing-error");
}
static GHashTable *
theme_gtk3_provider_cache_table (void)
{
if (!theme_gtk3_provider_cache)
{
theme_gtk3_provider_cache = g_hash_table_new_full (
g_str_hash,
g_str_equal,
g_free,
g_object_unref);
}
return theme_gtk3_provider_cache;
}
static char *
theme_gtk3_provider_cache_key (const char *theme_root, const char *css_dir, gboolean prefer_dark)
{
return g_strdup_printf ("%s\n%s\n%d", theme_root, css_dir, prefer_dark ? 1 : 0);
}
static GtkCssProvider *
theme_gtk3_provider_cache_load (const char *path, GError **error)
{
GtkCssProvider *provider;
provider = gtk_css_provider_new ();
g_signal_connect (provider, "parsing-error", G_CALLBACK (theme_gtk3_parsing_error_cb), NULL);
if (!gtk_css_provider_load_from_path (provider, path, error))
{
g_object_unref (provider);
return NULL;
}
return provider;
}
static GtkCssProvider *
theme_gtk3_provider_cache_get_or_load (const char *theme_root, const char *css_dir, gboolean prefer_dark, GError **error)
{
GHashTable *cache;
char *key;
char *css_path;
GtkCssProvider *provider;
cache = theme_gtk3_provider_cache_table ();
key = theme_gtk3_provider_cache_key (theme_root, css_dir, prefer_dark);
provider = g_hash_table_lookup (cache, key);
if (provider)
{
g_object_ref (provider);
g_free (key);
return provider;
}
css_path = g_build_filename (theme_root, css_dir, prefer_dark ? "gtk-dark.css" : "gtk.css", NULL);
provider = theme_gtk3_provider_cache_load (css_path, error);
g_free (css_path);
if (!provider)
{
g_free (key);
return NULL;
}
g_hash_table_insert (cache, key, g_object_ref (provider));
return provider;
}
void
theme_gtk3_invalidate_provider_cache (void)
{
if (theme_gtk3_provider_cache)
g_hash_table_remove_all (theme_gtk3_provider_cache);
}
static void
settings_apply_for_variant (ThemeGtk3Variant variant)
{
GtkSettings *settings = gtk_settings_get_default ();
gboolean dark = FALSE;
GValue current = G_VALUE_INIT;
GParamSpec *property;
if (!settings)
return;
if (variant == THEME_GTK3_VARIANT_PREFER_DARK)
dark = TRUE;
else if (variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM)
dark = theme_policy_system_prefers_dark ();
property = g_object_class_find_property (G_OBJECT_GET_CLASS (settings), "gtk-application-prefer-dark-theme");
if (!property)
return;
g_value_init (&current, G_PARAM_SPEC_VALUE_TYPE (property));
g_object_get_property (G_OBJECT (settings), "gtk-application-prefer-dark-theme", &current);
if (g_value_get_boolean (&current) != dark)
g_object_set (settings, "gtk-application-prefer-dark-theme", dark, NULL);
g_value_unset (&current);
}
static gboolean
settings_theme_root_is_searchable (const char *theme_root)
{
char *parent;
char *user_data_themes;
char *home_themes;
const gchar *const *system_data_dirs;
guint i;
gboolean searchable = FALSE;
if (!theme_root || !theme_root[0])
return FALSE;
parent = g_path_get_dirname (theme_root);
user_data_themes = g_build_filename (g_get_user_data_dir (), "themes", NULL);
home_themes = g_build_filename (g_get_home_dir (), ".themes", NULL);
if (g_strcmp0 (parent, user_data_themes) == 0 ||
g_strcmp0 (parent, home_themes) == 0 ||
g_strcmp0 (parent, "/usr/share/themes") == 0)
searchable = TRUE;
system_data_dirs = g_get_system_data_dirs ();
for (i = 0; !searchable && system_data_dirs && system_data_dirs[i]; i++)
{
char *system_themes = g_build_filename (system_data_dirs[i], "themes", NULL);
if (g_strcmp0 (parent, system_themes) == 0)
searchable = TRUE;
g_free (system_themes);
}
g_free (home_themes);
g_free (user_data_themes);
g_free (parent);
return searchable;
}
static gboolean
settings_theme_link_search_path (const char *theme_root, const char *theme_name)
{
char *themes_root;
char *link_path;
gboolean ok = TRUE;
if (!theme_root || !theme_name || !theme_name[0])
return FALSE;
themes_root = g_build_filename (g_get_user_data_dir (), "themes", NULL);
if (g_mkdir_with_parents (themes_root, 0700) != 0)
{
g_free (themes_root);
return FALSE;
}
link_path = g_build_filename (themes_root, theme_name, NULL);
if (!g_file_test (link_path, G_FILE_TEST_EXISTS))
{
GFile *link_file = g_file_new_for_path (link_path);
GError *link_error = NULL;
ok = g_file_make_symbolic_link (link_file, theme_root, NULL, &link_error);
g_clear_error (&link_error);
g_object_unref (link_file);
}
g_free (link_path);
g_free (themes_root);
return ok;
}
static void
settings_apply_theme_name (const char *theme_root)
{
GtkSettings *settings;
char *theme_name;
if (!theme_root)
return;
settings = gtk_settings_get_default ();
if (!settings)
return;
theme_name = g_path_get_basename (theme_root);
if (theme_name && theme_name[0])
{
gboolean searchable = settings_theme_root_is_searchable (theme_root);
if (!searchable)
searchable = settings_theme_link_search_path (theme_root, theme_name);
if (searchable)
settings_apply_property (settings, "gtk-theme-name", theme_name);
}
g_free (theme_name);
}
static gboolean
settings_value_equal_typed (const GValue *a, const GValue *b, GType property_type)
{
if (property_type == G_TYPE_BOOLEAN)
return g_value_get_boolean (a) == g_value_get_boolean (b);
if (property_type == G_TYPE_STRING)
return g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0;
if (property_type == G_TYPE_INT)
return g_value_get_int (a) == g_value_get_int (b);
if (property_type == G_TYPE_UINT)
return g_value_get_uint (a) == g_value_get_uint (b);
if (property_type == G_TYPE_FLOAT)
return g_value_get_float (a) == g_value_get_float (b);
if (property_type == G_TYPE_DOUBLE)
return g_value_get_double (a) == g_value_get_double (b);
if (G_TYPE_IS_ENUM (property_type))
return g_value_get_enum (a) == g_value_get_enum (b);
if (G_TYPE_IS_FLAGS (property_type))
return g_value_get_flags (a) == g_value_get_flags (b);
return FALSE;
}
static gboolean
settings_parse_long (const char *text, glong min_value, glong max_value, glong *value)
{
char *end = NULL;
gint64 parsed;
if (!text)
return FALSE;
parsed = g_ascii_strtoll (text, &end, 10);
if (end == text || *end != '\0')
return FALSE;
if (parsed < min_value || parsed > max_value)
return FALSE;
*value = (glong) parsed;
return TRUE;
}
static void
settings_remember_default (GtkSettings *settings, const char *property_name, GParamSpec *property)
{
GHashTable *defaults;
GValue current = G_VALUE_INIT;
if (!settings || !property_name || !property)
return;
defaults = settings_defaults_table ();
if (g_hash_table_contains (defaults, property_name))
return;
g_value_init (&current, G_PARAM_SPEC_VALUE_TYPE (property));
g_object_get_property (G_OBJECT (settings), property_name, &current);
g_hash_table_insert (defaults, g_strdup (property_name), settings_value_dup (&current));
g_value_unset (&current);
}
static gboolean
settings_apply_property (GtkSettings *settings, const char *property_name, const char *raw_value)
{
GParamSpec *property;
GValue value = G_VALUE_INIT;
GValue current = G_VALUE_INIT;
GType property_type;
gboolean ok = FALSE;
gboolean changed = TRUE;
property = g_object_class_find_property (G_OBJECT_GET_CLASS (settings), property_name);
if (!property)
return FALSE;
settings_remember_default (settings, property_name, property);
property_type = G_PARAM_SPEC_VALUE_TYPE (property);
g_value_init (&value, property_type);
if (property_type == G_TYPE_BOOLEAN)
{
if (g_ascii_strcasecmp (raw_value, "true") == 0 ||
g_ascii_strcasecmp (raw_value, "yes") == 0 ||
g_strcmp0 (raw_value, "1") == 0)
{
g_value_set_boolean (&value, TRUE);
ok = TRUE;
}
else if (g_ascii_strcasecmp (raw_value, "false") == 0 ||
g_ascii_strcasecmp (raw_value, "no") == 0 ||
g_strcmp0 (raw_value, "0") == 0)
{
g_value_set_boolean (&value, FALSE);
ok = TRUE;
}
}
else if (property_type == G_TYPE_STRING)
{
g_value_set_string (&value, raw_value);
ok = TRUE;
}
else if (property_type == G_TYPE_INT)
{
glong parsed;
if (settings_parse_long (raw_value, G_MININT, G_MAXINT, &parsed))
{
g_value_set_int (&value, (gint) parsed);
ok = TRUE;
}
}
else if (property_type == G_TYPE_UINT)
{
glong parsed;
if (settings_parse_long (raw_value, 0, G_MAXUINT, &parsed))
{
g_value_set_uint (&value, (guint) parsed);
ok = TRUE;
}
}
else if (property_type == G_TYPE_DOUBLE)
{
char *end = NULL;
double parsed = g_ascii_strtod (raw_value, &end);
if (end != raw_value && *end == '\0')
{
g_value_set_double (&value, parsed);
ok = TRUE;
}
}
else if (property_type == G_TYPE_FLOAT)
{
char *end = NULL;
double parsed = g_ascii_strtod (raw_value, &end);
if (end != raw_value && *end == '\0')
{
g_value_set_float (&value, (gfloat) parsed);
ok = TRUE;
}
}
else if (G_TYPE_IS_ENUM (property_type))
{
GEnumClass *enum_class = g_type_class_ref (property_type);
GEnumValue *enum_value = g_enum_get_value_by_nick (enum_class, raw_value);
if (!enum_value)
enum_value = g_enum_get_value_by_name (enum_class, raw_value);
if (!enum_value)
{
glong parsed;
if (settings_parse_long (raw_value, G_MININT, G_MAXINT, &parsed))
enum_value = g_enum_get_value (enum_class, (gint) parsed);
}
if (enum_value)
{
g_value_set_enum (&value, enum_value->value);
ok = TRUE;
}
g_type_class_unref (enum_class);
}
else if (G_TYPE_IS_FLAGS (property_type))
{
GFlagsClass *flags_class = g_type_class_ref (property_type);
char **tokens = g_strsplit_set (raw_value, ",|", -1);
guint flags_value = 0;
guint i = 0;
for (; tokens && tokens[i]; i++)
{
char *token = g_strstrip (tokens[i]);
GFlagsValue *flag_value;
if (!token[0])
continue;
flag_value = g_flags_get_value_by_nick (flags_class, token);
if (!flag_value)
flag_value = g_flags_get_value_by_name (flags_class, token);
if (!flag_value)
{
glong parsed;
if (!settings_parse_long (token, 0, G_MAXUINT, &parsed))
{
ok = FALSE;
break;
}
flags_value |= (guint) parsed;
ok = TRUE;
continue;
}
flags_value |= flag_value->value;
ok = TRUE;
}
if (ok)
g_value_set_flags (&value, flags_value);
g_strfreev (tokens);
g_type_class_unref (flags_class);
}
if (ok)
{
g_value_init (&current, property_type);
g_object_get_property (G_OBJECT (settings), property_name, &current);
changed = !settings_value_equal_typed (&current, &value, property_type);
g_value_unset (&current);
}
if (ok && changed)
g_object_set_property (G_OBJECT (settings), property_name, &value);
g_value_unset (&value);
return ok;
}
static void
settings_restore_defaults (void)
{
GtkSettings *settings = gtk_settings_get_default ();
GHashTableIter iter;
gpointer key;
gpointer value;
if (settings && theme_gtk3_settings_state.defaults)
{
g_hash_table_iter_init (&iter, theme_gtk3_settings_state.defaults);
while (g_hash_table_iter_next (&iter, &key, &value))
g_object_set_property (G_OBJECT (settings), (const char *) key, (const GValue *) value);
g_hash_table_remove_all (theme_gtk3_settings_state.defaults);
}
settings_rescan_icon_theme ();
settings_restore_icon_search_path ();
}
static void
settings_cleanup (void)
{
if (theme_gtk3_settings_state.defaults)
{
g_hash_table_destroy (theme_gtk3_settings_state.defaults);
theme_gtk3_settings_state.defaults = NULL;
}
if (theme_gtk3_settings_state.icon_search_path_captured)
settings_restore_icon_search_path ();
}
static void
settings_apply_from_file (const char *theme_root, const char *css_dir)
{
GtkSettings *settings;
GPtrArray *settings_paths;
char *selected_path;
char *fallback_path;
guint layer;
settings = gtk_settings_get_default ();
if (!settings)
return;
if (!css_dir)
return;
settings_apply_icon_paths (theme_root);
settings_paths = g_ptr_array_new_with_free_func (g_free);
selected_path = g_build_filename (theme_root, css_dir, "settings.ini", NULL);
fallback_path = g_build_filename (theme_root, "gtk-3.0", "settings.ini", NULL);
if (g_strcmp0 (css_dir, "gtk-3.0") != 0)
g_ptr_array_add (settings_paths, fallback_path);
else
g_free (fallback_path);
g_ptr_array_add (settings_paths, selected_path);
for (layer = 0; layer < settings_paths->len; layer++)
{
GKeyFile *keyfile;
char **keys;
gsize n_keys = 0;
gsize i;
const char *settings_path = g_ptr_array_index (settings_paths, layer);
if (!g_file_test (settings_path, G_FILE_TEST_IS_REGULAR))
continue;
keyfile = g_key_file_new ();
if (!g_key_file_load_from_file (keyfile, settings_path, G_KEY_FILE_NONE, NULL))
{
g_key_file_unref (keyfile);
continue;
}
keys = g_key_file_get_keys (keyfile, "Settings", &n_keys, NULL);
for (i = 0; keys && i < n_keys; i++)
{
char *raw_value;
char *value;
raw_value = g_key_file_get_value (keyfile, "Settings", keys[i], NULL);
if (!raw_value)
continue;
value = g_strstrip (raw_value);
if (value[0] != '\0')
settings_apply_property (settings, keys[i], value);
g_free (raw_value);
}
g_strfreev (keys);
g_key_file_unref (keyfile);
}
settings_rescan_icon_theme ();
g_ptr_array_unref (settings_paths);
}
static void
theme_gtk3_remove_provider (void)
{
GdkScreen *screen = gdk_screen_get_default ();
guint i;
if (screen && theme_gtk3_providers_variant)
{
for (i = 0; i < theme_gtk3_providers_variant->len; i++)
{
GtkCssProvider *provider = g_ptr_array_index (theme_gtk3_providers_variant, i);
gtk_style_context_remove_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider));
}
}
if (screen && theme_gtk3_providers_base)
{
for (i = 0; i < theme_gtk3_providers_base->len; i++)
{
GtkCssProvider *provider = g_ptr_array_index (theme_gtk3_providers_base, i);
gtk_style_context_remove_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider));
}
}
if (theme_gtk3_providers_variant)
g_ptr_array_unref (theme_gtk3_providers_variant);
if (theme_gtk3_providers_base)
g_ptr_array_unref (theme_gtk3_providers_base);
theme_gtk3_providers_variant = NULL;
theme_gtk3_providers_base = NULL;
settings_restore_defaults ();
theme_gtk3_reset_widgets ();
theme_gtk3_active = FALSE;
}
static gboolean
load_css_with_variant (ZoitechatGtk3Theme *theme, ThemeGtk3Variant variant, GError **error)
{
gboolean prefer_dark = FALSE;
GdkScreen *screen;
GPtrArray *chain;
guint i;
if (variant == THEME_GTK3_VARIANT_PREFER_DARK)
prefer_dark = TRUE;
else if (variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM)
prefer_dark = theme_policy_system_prefers_dark ();
settings_apply_theme_name (theme->path);
chain = zoitechat_gtk3_theme_build_inheritance_chain (theme->path);
if (!chain || chain->len == 0)
{
if (chain)
g_ptr_array_unref (chain);
return g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, "GTK3 theme CSS not found."), FALSE;
}
theme_gtk3_providers_base = g_ptr_array_new_with_free_func (g_object_unref);
theme_gtk3_providers_variant = g_ptr_array_new_with_free_func (g_object_unref);
screen = gdk_screen_get_default ();
for (i = 0; i < chain->len; i++)
{
const char *theme_root = g_ptr_array_index (chain, i);
char *css_dir = zoitechat_gtk3_theme_pick_css_dir_for_minor (theme_root, gtk_get_minor_version ());
char *variant_css;
GtkCssProvider *provider;
GtkCssProvider *variant_provider;
if (!css_dir)
continue;
provider = theme_gtk3_provider_cache_get_or_load (theme_root, css_dir, FALSE, error);
if (!provider)
{
g_free (css_dir);
g_ptr_array_unref (chain);
return FALSE;
}
if (screen)
gtk_style_context_add_provider_for_screen (screen,
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_USER + (gint) (i * 2));
g_ptr_array_add (theme_gtk3_providers_base, provider);
variant_css = g_build_filename (theme_root, css_dir, "gtk-dark.css", NULL);
if (prefer_dark && g_file_test (variant_css, G_FILE_TEST_IS_REGULAR))
{
variant_provider = theme_gtk3_provider_cache_get_or_load (theme_root, css_dir, TRUE, error);
if (!variant_provider)
{
g_free (variant_css);
g_free (css_dir);
g_ptr_array_unref (chain);
return FALSE;
}
if (screen)
gtk_style_context_add_provider_for_screen (screen,
GTK_STYLE_PROVIDER (variant_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER + (gint) (i * 2) + 1);
g_ptr_array_add (theme_gtk3_providers_variant, variant_provider);
}
g_free (variant_css);
settings_apply_from_file (theme_root, css_dir);
g_free (css_dir);
}
g_ptr_array_unref (chain);
settings_apply_for_variant (variant);
theme_gtk3_reset_widgets ();
theme_gtk3_active = TRUE;
return TRUE;
}
static gboolean
theme_gtk3_apply_internal (const char *theme_id, ThemeGtk3Variant variant, gboolean force_reload, GError **error)
{
ZoitechatGtk3Theme *theme;
char *previous_id = g_strdup (theme_gtk3_current_id);
ThemeGtk3Variant previous_variant = theme_gtk3_current_variant;
gboolean had_previous = theme_gtk3_active && previous_id && previous_id[0];
gboolean ok;
if (!force_reload &&
theme_gtk3_active &&
g_strcmp0 (theme_gtk3_current_id, theme_id) == 0 &&
theme_gtk3_current_variant == variant)
return TRUE;
theme = zoitechat_gtk3_theme_find_by_id (theme_id);
if (!theme)
{
g_free (previous_id);
return g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, "GTK3 theme not found."), FALSE;
}
theme_gtk3_remove_provider ();
if (force_reload)
theme_gtk3_invalidate_provider_cache ();
ok = load_css_with_variant (theme, variant, error);
zoitechat_gtk3_theme_free (theme);
if (ok)
{
g_free (theme_gtk3_current_id);
theme_gtk3_current_id = g_strdup (theme_id);
theme_gtk3_current_variant = variant;
g_free (previous_id);
return TRUE;
}
if (had_previous)
{
GError *restore_error = NULL;
theme = zoitechat_gtk3_theme_find_by_id (previous_id);
if (theme)
{
if (load_css_with_variant (theme, previous_variant, &restore_error))
{
g_free (theme_gtk3_current_id);
theme_gtk3_current_id = g_strdup (previous_id);
theme_gtk3_current_variant = previous_variant;
}
zoitechat_gtk3_theme_free (theme);
}
g_clear_error (&restore_error);
}
g_free (previous_id);
return ok;
}
gboolean
theme_gtk3_apply (const char *theme_id, ThemeGtk3Variant variant, GError **error)
{
return theme_gtk3_apply_internal (theme_id, variant, FALSE, error);
}
gboolean
theme_gtk3_refresh (const char *theme_id, ThemeGtk3Variant variant, GError **error)
{
return theme_gtk3_apply_internal (theme_id, variant, TRUE, error);
}
ThemeGtk3Variant
theme_gtk3_variant_for_theme (const char *theme_id)
{
ZoitechatGtk3Theme *theme;
ThemeGtk3Variant variant;
theme = zoitechat_gtk3_theme_find_by_id (theme_id);
if (!theme)
return THEME_GTK3_VARIANT_PREFER_LIGHT;
variant = theme_gtk3_infer_variant (theme);
zoitechat_gtk3_theme_free (theme);
return variant;
}
void
theme_gtk3_disable (void)
{
theme_gtk3_remove_provider ();
g_clear_pointer (&theme_gtk3_current_id, g_free);
theme_gtk3_invalidate_provider_cache ();
g_clear_pointer (&theme_gtk3_provider_cache, g_hash_table_destroy);
settings_cleanup ();
}
void
theme_gtk3_init (void)
{
theme_gtk3_apply_current (NULL);
}
gboolean
theme_gtk3_apply_current (GError **error)
{
if (!prefs.hex_gui_gtk3_theme[0])
{
theme_gtk3_disable ();
return TRUE;
}
return theme_gtk3_apply (prefs.hex_gui_gtk3_theme, (ThemeGtk3Variant) prefs.hex_gui_gtk3_variant, error);
}
gboolean
theme_gtk3_is_active (void)
{
return theme_gtk3_active;
}

View File

@@ -1,22 +0,0 @@
#ifndef ZOITECHAT_THEME_GTK3_H
#define ZOITECHAT_THEME_GTK3_H
#include <glib.h>
typedef enum
{
THEME_GTK3_VARIANT_FOLLOW_SYSTEM = 0,
THEME_GTK3_VARIANT_PREFER_LIGHT = 1,
THEME_GTK3_VARIANT_PREFER_DARK = 2
} ThemeGtk3Variant;
void theme_gtk3_init (void);
gboolean theme_gtk3_apply_current (GError **error);
gboolean theme_gtk3_apply (const char *theme_id, ThemeGtk3Variant variant, GError **error);
gboolean theme_gtk3_refresh (const char *theme_id, ThemeGtk3Variant variant, GError **error);
ThemeGtk3Variant theme_gtk3_variant_for_theme (const char *theme_id);
void theme_gtk3_invalidate_provider_cache (void);
void theme_gtk3_disable (void);
gboolean theme_gtk3_is_active (void);
#endif

View File

@@ -1,681 +0,0 @@
#include "../fe-gtk.h"
#include "theme-manager.h"
#include <gtk/gtk.h>
#include <string.h>
#include "theme-application.h"
#include "theme-policy.h"
#include "theme-runtime.h"
#include "theme-access.h"
#include "theme-css.h"
#include "theme-gtk3.h"
#include "../gtkutil.h"
#include "../maingui.h"
#include "../setup.h"
#include "../../common/zoitechat.h"
#include "../../common/zoitechatc.h"
void theme_runtime_reset_mode_colors (gboolean dark_mode);
typedef struct
{
guint id;
char *component_id;
ThemeChangedCallback callback;
gpointer userdata;
} ThemeListener;
static GHashTable *theme_manager_listeners;
static guint theme_manager_next_listener_id = 1;
static guint theme_manager_setup_listener_id;
static const char theme_manager_window_destroy_handler_key[] = "theme-manager-window-destroy-handler";
static const char theme_manager_window_csd_headerbar_key[] = "theme-manager-window-csd-headerbar";
typedef struct
{
gboolean initialized;
gboolean resolved_dark_preference;
char gtk3_theme_id[sizeof prefs.hex_gui_gtk3_theme];
int gtk3_variant;
} ThemeManagerAutoRefreshCache;
static ThemeManagerAutoRefreshCache theme_manager_auto_refresh_cache;
static void theme_manager_apply_platform_window_theme (GtkWidget *window);
static void
theme_manager_apply_to_toplevel_windows (void)
{
GList *toplevels;
GList *iter;
toplevels = gtk_window_list_toplevels ();
for (iter = toplevels; iter != NULL; iter = iter->next)
{
GtkWidget *window = GTK_WIDGET (iter->data);
if (!GTK_IS_WINDOW (window) || gtk_widget_get_mapped (window) == FALSE)
continue;
theme_manager_apply_platform_window_theme (window);
}
g_list_free (toplevels);
}
static void
theme_listener_free (gpointer data)
{
ThemeListener *listener = data;
if (!listener)
return;
g_free (listener->component_id);
g_free (listener);
}
static void
theme_manager_setup_apply_listener (const ThemeChangedEvent *event, gpointer userdata)
{
(void) userdata;
theme_manager_dispatch_setup_apply (event);
}
static ThemeChangedReason
theme_manager_synthesize_preference_reasons (const struct zoitechatprefs *old_prefs,
const struct zoitechatprefs *new_prefs,
gboolean color_change)
{
ThemeChangedReason reasons = THEME_CHANGED_REASON_NONE;
if (!old_prefs || !new_prefs)
return reasons;
if (strcmp (old_prefs->hex_text_background, new_prefs->hex_text_background) != 0)
reasons |= THEME_CHANGED_REASON_PIXMAP;
if (old_prefs->hex_gui_tab_dots != new_prefs->hex_gui_tab_dots ||
old_prefs->hex_gui_tab_layout != new_prefs->hex_gui_tab_layout)
reasons |= THEME_CHANGED_REASON_LAYOUT;
if (old_prefs->hex_identd_server != new_prefs->hex_identd_server ||
old_prefs->hex_identd_port != new_prefs->hex_identd_port)
reasons |= THEME_CHANGED_REASON_IDENTD;
if (color_change ||
old_prefs->hex_gui_ulist_color != new_prefs->hex_gui_ulist_color ||
old_prefs->hex_away_size_max != new_prefs->hex_away_size_max ||
old_prefs->hex_away_track != new_prefs->hex_away_track)
reasons |= THEME_CHANGED_REASON_USERLIST;
if (reasons != THEME_CHANGED_REASON_NONE)
reasons |= THEME_CHANGED_REASON_WIDGET_STYLE;
return reasons;
}
static gboolean
theme_manager_should_refresh_gtk3 (void)
{
return prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM;
}
static void
theme_manager_auto_dark_mode_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data)
{
gboolean color_change = FALSE;
gboolean should_refresh_gtk3;
gboolean gtk3_refresh;
gboolean resolved_dark_preference;
static gboolean in_handler = FALSE;
(void) settings;
(void) pspec;
(void) data;
resolved_dark_preference = theme_policy_system_prefers_dark ();
gtk3_refresh = theme_manager_should_refresh_gtk3 ();
should_refresh_gtk3 = gtk3_refresh || prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO;
if (theme_manager_auto_refresh_cache.initialized &&
theme_manager_auto_refresh_cache.resolved_dark_preference == resolved_dark_preference &&
theme_manager_auto_refresh_cache.gtk3_variant == prefs.hex_gui_gtk3_variant &&
g_strcmp0 (theme_manager_auto_refresh_cache.gtk3_theme_id, prefs.hex_gui_gtk3_theme) == 0)
return;
theme_manager_auto_refresh_cache.initialized = TRUE;
theme_manager_auto_refresh_cache.resolved_dark_preference = resolved_dark_preference;
theme_manager_auto_refresh_cache.gtk3_variant = prefs.hex_gui_gtk3_variant;
g_strlcpy (theme_manager_auto_refresh_cache.gtk3_theme_id,
prefs.hex_gui_gtk3_theme,
sizeof (theme_manager_auto_refresh_cache.gtk3_theme_id));
if (prefs.hex_gui_dark_mode != ZOITECHAT_DARK_MODE_AUTO && !gtk3_refresh)
return;
if (in_handler)
return;
in_handler = TRUE;
if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO)
{
fe_set_auto_dark_mode_state (resolved_dark_preference);
theme_manager_commit_preferences (prefs.hex_gui_dark_mode, &color_change);
if (color_change)
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_MODE);
}
if (should_refresh_gtk3)
theme_gtk3_apply_current (NULL);
in_handler = FALSE;
}
static guint theme_manager_auto_refresh_source = 0;
static ThemeManagerIdleAddFunc theme_manager_idle_add_func = g_idle_add;
static gboolean
theme_manager_run_auto_refresh (gpointer data)
{
theme_manager_auto_refresh_source = 0;
theme_manager_auto_dark_mode_changed (NULL, NULL, data);
return G_SOURCE_REMOVE;
}
static void
theme_manager_queue_auto_refresh (GtkSettings *settings, GParamSpec *pspec, gpointer data)
{
(void) settings;
(void) pspec;
if (theme_manager_auto_refresh_source != 0)
return;
theme_manager_auto_refresh_source = theme_manager_idle_add_func (theme_manager_run_auto_refresh, data);
}
void
theme_manager_init (void)
{
GtkSettings *settings;
if (!theme_manager_listeners)
theme_manager_listeners = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
theme_listener_free);
if (!theme_manager_setup_listener_id)
theme_manager_setup_listener_id = theme_listener_register ("setup.apply", theme_manager_setup_apply_listener, NULL);
settings = gtk_settings_get_default ();
if (settings)
fe_set_auto_dark_mode_state (theme_policy_system_prefers_dark ());
theme_application_apply_mode (prefs.hex_gui_dark_mode, NULL);
theme_gtk3_init ();
zoitechat_set_theme_post_apply_callback (theme_manager_handle_theme_applied);
if (settings)
{
g_signal_connect (settings, "notify::gtk-application-prefer-dark-theme",
G_CALLBACK (theme_manager_queue_auto_refresh), NULL);
g_signal_connect (settings, "notify::gtk-theme-name",
G_CALLBACK (theme_manager_queue_auto_refresh), NULL);
}
}
gboolean
theme_manager_apply_mode (unsigned int mode, gboolean *palette_changed)
{
return theme_application_apply_mode (mode, palette_changed);
}
void
theme_manager_set_mode (unsigned int mode, gboolean *palette_changed)
{
theme_application_apply_mode (mode, palette_changed);
}
void
theme_manager_set_token_color (unsigned int mode, ThemeSemanticToken token, const GdkRGBA *color, gboolean *palette_changed)
{
gboolean changed = FALSE;
if (!color)
return;
(void) mode;
theme_runtime_user_set_color (token, color);
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, &changed);
if (palette_changed)
*palette_changed = changed;
if (changed)
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST);
theme_application_reload_input_style ();
}
void
theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed)
{
gboolean changed;
(void) mode;
theme_runtime_reset_mode_colors (FALSE);
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, &changed);
changed = TRUE;
if (palette_changed)
*palette_changed = changed;
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST);
theme_application_reload_input_style ();
}
void
theme_manager_commit_preferences (unsigned int old_mode, gboolean *color_change)
{
gboolean palette_changed = FALSE;
theme_application_apply_mode (prefs.hex_gui_dark_mode, &palette_changed);
if (color_change && (prefs.hex_gui_dark_mode != old_mode || palette_changed))
*color_change = TRUE;
if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO)
fe_set_auto_dark_mode_state (theme_policy_is_dark_mode_active (ZOITECHAT_DARK_MODE_AUTO));
}
void
theme_manager_save_preferences (void)
{
theme_runtime_save ();
}
gboolean
theme_changed_event_has_reason (const ThemeChangedEvent *event, ThemeChangedReason reason)
{
if (!event)
return FALSE;
return (event->reasons & reason) != 0;
}
void
theme_manager_apply_and_dispatch (unsigned int mode, ThemeChangedReason reasons, gboolean *palette_changed)
{
theme_application_apply_mode (mode, palette_changed);
theme_manager_dispatch_changed (reasons);
}
void
theme_manager_dispatch_changed (ThemeChangedReason reasons)
{
GHashTableIter iter;
gpointer key;
gpointer value;
ThemeChangedEvent event;
event.reasons = reasons;
if ((reasons & (THEME_CHANGED_REASON_MODE |
THEME_CHANGED_REASON_THEME_PACK |
THEME_CHANGED_REASON_WIDGET_STYLE)) != 0)
{
theme_manager_apply_to_toplevel_windows ();
}
if (!theme_manager_listeners)
return;
g_hash_table_iter_init (&iter, theme_manager_listeners);
while (g_hash_table_iter_next (&iter, &key, &value))
{
ThemeListener *listener = value;
if (listener->callback)
listener->callback (&event, listener->userdata);
}
}
guint
theme_listener_register (const char *component_id, ThemeChangedCallback callback, gpointer userdata)
{
ThemeListener *listener;
guint id;
if (!callback)
return 0;
if (!theme_manager_listeners)
theme_manager_listeners = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
theme_listener_free);
id = theme_manager_next_listener_id++;
if (theme_manager_next_listener_id == 0)
theme_manager_next_listener_id = 1;
listener = g_new0 (ThemeListener, 1);
listener->id = id;
listener->component_id = g_strdup (component_id ? component_id : "theme.listener");
listener->callback = callback;
listener->userdata = userdata;
g_hash_table_insert (theme_manager_listeners, GUINT_TO_POINTER (id), listener);
return id;
}
void
theme_listener_unregister (guint listener_id)
{
if (!theme_manager_listeners || listener_id == 0)
return;
g_hash_table_remove (theme_manager_listeners, GUINT_TO_POINTER (listener_id));
}
void
theme_manager_handle_theme_applied (void)
{
theme_gtk3_invalidate_provider_cache ();
if (prefs.hex_gui_gtk3_theme[0])
theme_gtk3_refresh (prefs.hex_gui_gtk3_theme, (ThemeGtk3Variant) prefs.hex_gui_gtk3_variant, NULL);
theme_application_apply_mode (prefs.hex_gui_dark_mode, NULL);
theme_manager_dispatch_changed (THEME_CHANGED_REASON_THEME_PACK | THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_MODE);
}
static gboolean
theme_manager_is_kde_wayland (void)
{
const char *wayland_display;
const char *desktop;
char *desktop_lower;
gboolean is_kde;
wayland_display = g_getenv ("WAYLAND_DISPLAY");
if (!wayland_display || !wayland_display[0])
return FALSE;
desktop = g_getenv ("XDG_CURRENT_DESKTOP");
if (!desktop || !desktop[0])
desktop = g_getenv ("XDG_SESSION_DESKTOP");
if (!desktop || !desktop[0])
return FALSE;
desktop_lower = g_ascii_strdown (desktop, -1);
is_kde = strstr (desktop_lower, "kde") != NULL || strstr (desktop_lower, "plasma") != NULL;
g_free (desktop_lower);
return is_kde;
}
static void
theme_manager_apply_wayland_kde_csd (GtkWidget *window)
{
GtkWindow *gtk_window;
GtkWidget *headerbar;
gboolean enable_csd;
if (!window || !GTK_IS_WINDOW (window))
return;
gtk_window = GTK_WINDOW (window);
enable_csd = theme_gtk3_is_active () && theme_manager_is_kde_wayland ();
headerbar = g_object_get_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key);
if (enable_csd)
{
if (!headerbar)
{
GtkWidget *icon_image;
GdkPixbuf *icon_pixbuf;
if (gtk_widget_get_realized (window))
return;
headerbar = gtk_header_bar_new ();
gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (headerbar), "menu:minimize,maximize,close");
icon_pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/icons/zoitechat.png", 32, 32, TRUE, NULL);
icon_image = icon_pixbuf ? gtk_image_new_from_pixbuf (icon_pixbuf) : gtk_image_new_from_resource ("/icons/zoitechat.png");
if (icon_pixbuf)
g_object_unref (icon_pixbuf);
gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), icon_image);
gtk_widget_show (icon_image);
gtk_window_set_titlebar (gtk_window, headerbar);
g_object_set_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key, headerbar);
}
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), gtk_window_get_title (gtk_window));
gtk_widget_show (headerbar);
{
GdkScreen *screen = gdk_screen_get_default ();
if (screen)
gtk_style_context_reset_widgets (screen);
}
return;
}
if (headerbar)
{
if (gtk_widget_get_realized (window))
return;
gtk_window_set_titlebar (gtk_window, NULL);
g_object_set_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key, NULL);
}
{
GdkScreen *screen = gdk_screen_get_default ();
if (screen)
gtk_style_context_reset_widgets (screen);
}
}
static void
theme_manager_apply_platform_window_theme (GtkWidget *window)
{
#ifdef G_OS_WIN32
GtkStyleContext *context;
gboolean dark;
if (!window)
return;
context = gtk_widget_get_style_context (window);
if (theme_gtk3_is_active ())
{
dark = prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_PREFER_DARK;
if (prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM)
dark = theme_policy_system_prefers_dark ();
}
else
dark = theme_runtime_is_dark_active ();
if (context)
{
gtk_style_context_remove_class (context, "zoitechat-dark");
gtk_style_context_remove_class (context, "zoitechat-light");
gtk_style_context_add_class (context, dark ? "zoitechat-dark" : "zoitechat-light");
}
fe_win32_apply_native_titlebar (window, dark);
#else
theme_manager_apply_wayland_kde_csd (window);
#endif
}
static void
theme_manager_window_destroy_cb (GtkWidget *window, gpointer userdata)
{
(void) userdata;
g_object_set_data (G_OBJECT (window), theme_manager_window_destroy_handler_key, NULL);
}
void
theme_manager_apply_to_window (GtkWidget *window)
{
if (!window)
return;
theme_manager_apply_platform_window_theme (window);
}
void
theme_manager_attach_window (GtkWidget *window)
{
gulong *handler_id;
if (!window)
return;
handler_id = g_object_get_data (G_OBJECT (window), theme_manager_window_destroy_handler_key);
if (!handler_id)
{
handler_id = g_new (gulong, 1);
*handler_id = g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (theme_manager_window_destroy_cb), NULL);
g_object_set_data_full (G_OBJECT (window), theme_manager_window_destroy_handler_key, handler_id, g_free);
}
theme_manager_apply_to_window (window);
}
void
theme_manager_detach_window (GtkWidget *window)
{
gulong *handler_id;
if (!window)
return;
handler_id = g_object_get_data (G_OBJECT (window), theme_manager_window_destroy_handler_key);
if (handler_id)
{
g_signal_handler_disconnect (G_OBJECT (window), *handler_id);
g_object_set_data (G_OBJECT (window), theme_manager_window_destroy_handler_key, NULL);
}
}
void
theme_manager_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
const PangoFontDescription *font_desc)
{
theme_css_apply_palette_widget (widget, bg, fg, font_desc);
}
void
theme_manager_apply_entry_palette (GtkWidget *widget, const PangoFontDescription *font_desc)
{
ThemeWidgetStyleValues style_values;
if (!widget || !font_desc)
return;
theme_get_widget_style_values_for_widget (widget, &style_values);
gtkutil_apply_palette (widget, &style_values.background, &style_values.foreground, font_desc);
}
ThemePaletteBehavior
theme_manager_get_userlist_palette_behavior (const PangoFontDescription *font_desc)
{
ThemePaletteBehavior behavior;
behavior.font_desc = font_desc;
behavior.apply_background = TRUE;
behavior.apply_foreground = TRUE;
return behavior;
}
ThemePaletteBehavior
theme_manager_get_channel_tree_palette_behavior (const PangoFontDescription *font_desc)
{
ThemePaletteBehavior behavior;
behavior.font_desc = font_desc;
behavior.apply_background = TRUE;
behavior.apply_foreground = TRUE;
return behavior;
}
void
theme_manager_apply_userlist_palette (GtkWidget *widget, const PangoFontDescription *font_desc,
gboolean prefer_background, gboolean prefer_foreground)
{
ThemePaletteBehavior behavior;
behavior.font_desc = font_desc;
behavior.apply_background = prefer_background;
behavior.apply_foreground = prefer_foreground;
theme_manager_apply_userlist_style (widget, behavior);
}
void
theme_manager_apply_userlist_style (GtkWidget *widget, ThemePaletteBehavior behavior)
{
ThemeWidgetStyleValues style_values;
const GdkRGBA *background = NULL;
const GdkRGBA *foreground = NULL;
if (!widget)
return;
theme_get_widget_style_values_for_widget (widget, &style_values);
if (behavior.apply_background)
background = &style_values.background;
if (behavior.apply_foreground)
foreground = &style_values.foreground;
gtkutil_apply_palette (widget, background, foreground, behavior.font_desc);
}
void
theme_manager_apply_channel_tree_style (GtkWidget *widget, ThemePaletteBehavior behavior)
{
theme_manager_apply_userlist_style (widget, behavior);
}
void
theme_manager_apply_input_style (gboolean enabled, const PangoFontDescription *font_desc)
{
theme_css_reload_input_style (enabled, font_desc);
}
void
theme_manager_reload_input_style (void)
{
theme_application_reload_input_style ();
}
void
theme_manager_refresh_auto_mode (void)
{
theme_manager_queue_auto_refresh (NULL, NULL, NULL);
}
ThemeChangedEvent
theme_manager_on_preferences_changed (const struct zoitechatprefs *old_prefs,
const struct zoitechatprefs *new_prefs,
unsigned int old_mode,
gboolean *color_change)
{
ThemeChangedEvent event;
gboolean had_color_change = color_change && *color_change;
theme_manager_commit_preferences (old_mode, color_change);
event.reasons = theme_manager_synthesize_preference_reasons (old_prefs, new_prefs,
had_color_change || (color_change && *color_change));
return event;
}
void
theme_manager_dispatch_setup_apply (const ThemeChangedEvent *event)
{
if (!event)
return;
setup_apply_real (event);
}
void
theme_manager_set_idle_add_func (ThemeManagerIdleAddFunc idle_add_func)
{
theme_manager_idle_add_func = idle_add_func ? idle_add_func : g_idle_add;
theme_manager_auto_refresh_source = 0;
}

View File

@@ -1,75 +0,0 @@
#ifndef ZOITECHAT_THEME_MANAGER_H
#define ZOITECHAT_THEME_MANAGER_H
#include <glib.h>
#include <gtk/gtk.h>
#include "theme-palette.h"
typedef struct _GtkWidget GtkWidget;
struct zoitechatprefs;
typedef enum
{
THEME_CHANGED_REASON_NONE = 0,
THEME_CHANGED_REASON_PALETTE = 1 << 0,
THEME_CHANGED_REASON_WIDGET_STYLE = 1 << 1,
THEME_CHANGED_REASON_MODE = 1 << 2,
THEME_CHANGED_REASON_THEME_PACK = 1 << 3,
THEME_CHANGED_REASON_PIXMAP = 1 << 4,
THEME_CHANGED_REASON_USERLIST = 1 << 5,
THEME_CHANGED_REASON_LAYOUT = 1 << 6,
THEME_CHANGED_REASON_IDENTD = 1 << 7
} ThemeChangedReason;
typedef struct
{
ThemeChangedReason reasons;
} ThemeChangedEvent;
typedef struct
{
const PangoFontDescription *font_desc;
gboolean apply_background;
gboolean apply_foreground;
} ThemePaletteBehavior;
typedef void (*ThemeChangedCallback) (const ThemeChangedEvent *event, gpointer userdata);
typedef guint (*ThemeManagerIdleAddFunc) (GSourceFunc function, gpointer data);
void theme_manager_init (void);
gboolean theme_manager_apply_mode (unsigned int mode, gboolean *palette_changed);
void theme_manager_set_mode (unsigned int mode, gboolean *palette_changed);
void theme_manager_set_token_color (unsigned int mode, ThemeSemanticToken token, const GdkRGBA *color, gboolean *palette_changed);
void theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed);
void theme_manager_commit_preferences (unsigned int old_mode, gboolean *color_change);
void theme_manager_save_preferences (void);
gboolean theme_changed_event_has_reason (const ThemeChangedEvent *event, ThemeChangedReason reason);
void theme_manager_apply_and_dispatch (unsigned int mode, ThemeChangedReason reasons, gboolean *palette_changed);
void theme_manager_dispatch_changed (ThemeChangedReason reasons);
guint theme_listener_register (const char *component_id, ThemeChangedCallback callback, gpointer userdata);
void theme_listener_unregister (guint listener_id);
void theme_manager_handle_theme_applied (void);
void theme_manager_apply_to_window (GtkWidget *window);
void theme_manager_attach_window (GtkWidget *window);
void theme_manager_detach_window (GtkWidget *window);
void theme_manager_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
const PangoFontDescription *font_desc);
void theme_manager_apply_entry_palette (GtkWidget *widget, const PangoFontDescription *font_desc);
ThemePaletteBehavior theme_manager_get_userlist_palette_behavior (const PangoFontDescription *font_desc);
ThemePaletteBehavior theme_manager_get_channel_tree_palette_behavior (const PangoFontDescription *font_desc);
void theme_manager_apply_userlist_palette (GtkWidget *widget, const PangoFontDescription *font_desc,
gboolean prefer_background, gboolean prefer_foreground);
void theme_manager_apply_userlist_style (GtkWidget *widget, ThemePaletteBehavior behavior);
void theme_manager_apply_channel_tree_style (GtkWidget *widget, ThemePaletteBehavior behavior);
void theme_manager_apply_input_style (gboolean enabled, const PangoFontDescription *font_desc);
void theme_manager_reload_input_style (void);
void theme_manager_refresh_auto_mode (void);
ThemeChangedEvent theme_manager_on_preferences_changed (const struct zoitechatprefs *old_prefs,
const struct zoitechatprefs *new_prefs,
unsigned int old_mode,
gboolean *color_change);
void theme_manager_dispatch_setup_apply (const ThemeChangedEvent *event);
void theme_manager_set_idle_add_func (ThemeManagerIdleAddFunc idle_add_func);
#endif

View File

@@ -1,286 +0,0 @@
#include <string.h>
#include "theme-palette.h"
static const ThemePaletteTokenDef theme_palette_token_defs[] = {
{ THEME_TOKEN_MIRC_0, 0, "mirc_0" },
{ THEME_TOKEN_MIRC_1, 1, "mirc_1" },
{ THEME_TOKEN_MIRC_2, 2, "mirc_2" },
{ THEME_TOKEN_MIRC_3, 3, "mirc_3" },
{ THEME_TOKEN_MIRC_4, 4, "mirc_4" },
{ THEME_TOKEN_MIRC_5, 5, "mirc_5" },
{ THEME_TOKEN_MIRC_6, 6, "mirc_6" },
{ THEME_TOKEN_MIRC_7, 7, "mirc_7" },
{ THEME_TOKEN_MIRC_8, 8, "mirc_8" },
{ THEME_TOKEN_MIRC_9, 9, "mirc_9" },
{ THEME_TOKEN_MIRC_10, 10, "mirc_10" },
{ THEME_TOKEN_MIRC_11, 11, "mirc_11" },
{ THEME_TOKEN_MIRC_12, 12, "mirc_12" },
{ THEME_TOKEN_MIRC_13, 13, "mirc_13" },
{ THEME_TOKEN_MIRC_14, 14, "mirc_14" },
{ THEME_TOKEN_MIRC_15, 15, "mirc_15" },
{ THEME_TOKEN_MIRC_16, 16, "mirc_16" },
{ THEME_TOKEN_MIRC_17, 17, "mirc_17" },
{ THEME_TOKEN_MIRC_18, 18, "mirc_18" },
{ THEME_TOKEN_MIRC_19, 19, "mirc_19" },
{ THEME_TOKEN_MIRC_20, 20, "mirc_20" },
{ THEME_TOKEN_MIRC_21, 21, "mirc_21" },
{ THEME_TOKEN_MIRC_22, 22, "mirc_22" },
{ THEME_TOKEN_MIRC_23, 23, "mirc_23" },
{ THEME_TOKEN_MIRC_24, 24, "mirc_24" },
{ THEME_TOKEN_MIRC_25, 25, "mirc_25" },
{ THEME_TOKEN_MIRC_26, 26, "mirc_26" },
{ THEME_TOKEN_MIRC_27, 27, "mirc_27" },
{ THEME_TOKEN_MIRC_28, 28, "mirc_28" },
{ THEME_TOKEN_MIRC_29, 29, "mirc_29" },
{ THEME_TOKEN_MIRC_30, 30, "mirc_30" },
{ THEME_TOKEN_MIRC_31, 31, "mirc_31" },
{ THEME_TOKEN_SELECTION_FOREGROUND, 32, "selection_foreground" },
{ THEME_TOKEN_SELECTION_BACKGROUND, 33, "selection_background" },
{ THEME_TOKEN_TEXT_FOREGROUND, 34, "text_foreground" },
{ THEME_TOKEN_TEXT_BACKGROUND, 35, "text_background" },
{ THEME_TOKEN_MARKER, 36, "marker" },
{ THEME_TOKEN_TAB_NEW_DATA, 37, "tab_new_data" },
{ THEME_TOKEN_TAB_HIGHLIGHT, 38, "tab_highlight" },
{ THEME_TOKEN_TAB_NEW_MESSAGE, 39, "tab_new_message" },
{ THEME_TOKEN_TAB_AWAY, 40, "tab_away" },
{ THEME_TOKEN_SPELL, 41, "spell" },
};
static const ThemePaletteTokenDef *
theme_palette_lookup_token_def (ThemeSemanticToken token)
{
size_t i;
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
{
if (theme_palette_token_defs[i].token == token)
return &theme_palette_token_defs[i];
}
return NULL;
}
static const ThemePaletteTokenDef *
theme_palette_lookup_legacy_def (int legacy_idx)
{
size_t i;
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
{
if (theme_palette_token_defs[i].legacy_index == legacy_idx)
return &theme_palette_token_defs[i];
}
return NULL;
}
size_t
theme_palette_token_count (void)
{
return THEME_TOKEN_COUNT;
}
size_t
theme_palette_token_def_count (void)
{
return G_N_ELEMENTS (theme_palette_token_defs);
}
const ThemePaletteTokenDef *
theme_palette_token_def_at (size_t index)
{
if (index >= G_N_ELEMENTS (theme_palette_token_defs))
return NULL;
return &theme_palette_token_defs[index];
}
const ThemePaletteTokenDef *
theme_palette_token_def_for_token (ThemeSemanticToken token)
{
if (token < 0 || token >= THEME_TOKEN_COUNT)
return NULL;
return theme_palette_lookup_token_def (token);
}
const char *
theme_palette_token_name (ThemeSemanticToken token)
{
const ThemePaletteTokenDef *def = theme_palette_token_def_for_token (token);
if (def == NULL)
return NULL;
return def->name;
}
gboolean
theme_palette_token_to_legacy_index (ThemeSemanticToken token, int *legacy_idx)
{
const ThemePaletteTokenDef *def;
if (legacy_idx == NULL)
return FALSE;
def = theme_palette_token_def_for_token (token);
if (def == NULL)
return FALSE;
*legacy_idx = def->legacy_index;
return TRUE;
}
gboolean
theme_palette_legacy_index_to_token (int legacy_idx, ThemeSemanticToken *token)
{
const ThemePaletteTokenDef *def;
if (token == NULL)
return FALSE;
def = theme_palette_lookup_legacy_def (legacy_idx);
if (def == NULL)
return FALSE;
*token = def->token;
return TRUE;
}
gboolean
theme_palette_set_color (ThemePalette *palette, ThemeSemanticToken token, const GdkRGBA *color)
{
if (palette == NULL || color == NULL)
return FALSE;
if (token < 0 || token >= THEME_TOKEN_COUNT)
return FALSE;
if (theme_palette_token_def_for_token (token) == NULL)
return FALSE;
palette->colors[token] = *color;
return TRUE;
}
gboolean
theme_palette_get_color (const ThemePalette *palette, ThemeSemanticToken token, GdkRGBA *color)
{
if (palette == NULL || color == NULL)
return FALSE;
if (token < 0 || token >= THEME_TOKEN_COUNT)
return FALSE;
if (theme_palette_token_def_for_token (token) == NULL)
return FALSE;
*color = palette->colors[token];
return TRUE;
}
void
theme_palette_from_legacy_colors (ThemePalette *palette, const GdkRGBA *legacy_colors, size_t legacy_len)
{
size_t i;
g_return_if_fail (palette != NULL);
g_return_if_fail (legacy_colors != NULL);
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
{
int legacy_idx = theme_palette_token_defs[i].legacy_index;
ThemeSemanticToken token = theme_palette_token_defs[i].token;
g_return_if_fail (legacy_idx >= 0);
g_return_if_fail ((size_t) legacy_idx < legacy_len);
palette->colors[token] = legacy_colors[legacy_idx];
}
}
void
theme_palette_to_legacy_colors (const ThemePalette *palette, GdkRGBA *legacy_colors, size_t legacy_len)
{
size_t i;
g_return_if_fail (palette != NULL);
g_return_if_fail (legacy_colors != NULL);
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
{
int legacy_idx = theme_palette_token_defs[i].legacy_index;
ThemeSemanticToken token = theme_palette_token_defs[i].token;
g_return_if_fail (legacy_idx >= 0);
g_return_if_fail ((size_t) legacy_idx < legacy_len);
legacy_colors[legacy_idx] = palette->colors[token];
}
}
void
theme_palette_to_xtext_colors (const ThemePalette *palette, XTextColor *xtext_colors, size_t xtext_len)
{
size_t i;
g_return_if_fail (palette != NULL);
g_return_if_fail (xtext_colors != NULL);
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
{
int legacy_idx = theme_palette_token_defs[i].legacy_index;
ThemeSemanticToken token = theme_palette_token_defs[i].token;
if ((size_t) legacy_idx >= xtext_len)
continue;
xtext_colors[legacy_idx].red = palette->colors[token].red;
xtext_colors[legacy_idx].green = palette->colors[token].green;
xtext_colors[legacy_idx].blue = palette->colors[token].blue;
xtext_colors[legacy_idx].alpha = palette->colors[token].alpha;
}
}
void
theme_palette_to_widget_style_values (const ThemePalette *palette, ThemeWidgetStyleValues *style_values)
{
g_return_if_fail (palette != NULL);
g_return_if_fail (style_values != NULL);
style_values->foreground = palette->colors[THEME_TOKEN_TEXT_FOREGROUND];
style_values->background = palette->colors[THEME_TOKEN_TEXT_BACKGROUND];
style_values->selection_foreground = palette->colors[THEME_TOKEN_SELECTION_FOREGROUND];
style_values->selection_background = palette->colors[THEME_TOKEN_SELECTION_BACKGROUND];
g_snprintf (style_values->foreground_css, sizeof (style_values->foreground_css),
"rgba(%u,%u,%u,%.3f)",
(guint) CLAMP (style_values->foreground.red * 255.0 + 0.5, 0.0, 255.0),
(guint) CLAMP (style_values->foreground.green * 255.0 + 0.5, 0.0, 255.0),
(guint) CLAMP (style_values->foreground.blue * 255.0 + 0.5, 0.0, 255.0),
CLAMP (style_values->foreground.alpha, 0.0, 1.0));
g_snprintf (style_values->background_css, sizeof (style_values->background_css),
"rgba(%u,%u,%u,%.3f)",
(guint) CLAMP (style_values->background.red * 255.0 + 0.5, 0.0, 255.0),
(guint) CLAMP (style_values->background.green * 255.0 + 0.5, 0.0, 255.0),
(guint) CLAMP (style_values->background.blue * 255.0 + 0.5, 0.0, 255.0),
CLAMP (style_values->background.alpha, 0.0, 1.0));
g_snprintf (style_values->selection_foreground_css, sizeof (style_values->selection_foreground_css),
"rgba(%u,%u,%u,%.3f)",
(guint) CLAMP (style_values->selection_foreground.red * 255.0 + 0.5, 0.0, 255.0),
(guint) CLAMP (style_values->selection_foreground.green * 255.0 + 0.5, 0.0, 255.0),
(guint) CLAMP (style_values->selection_foreground.blue * 255.0 + 0.5, 0.0, 255.0),
CLAMP (style_values->selection_foreground.alpha, 0.0, 1.0));
g_snprintf (style_values->selection_background_css, sizeof (style_values->selection_background_css),
"rgba(%u,%u,%u,%.3f)",
(guint) CLAMP (style_values->selection_background.red * 255.0 + 0.5, 0.0, 255.0),
(guint) CLAMP (style_values->selection_background.green * 255.0 + 0.5, 0.0, 255.0),
(guint) CLAMP (style_values->selection_background.blue * 255.0 + 0.5, 0.0, 255.0),
CLAMP (style_values->selection_background.alpha, 0.0, 1.0));
}
void
theme_palette_color_get_rgb16 (const GdkRGBA *color, guint16 *red, guint16 *green, guint16 *blue)
{
g_return_if_fail (color != NULL);
g_return_if_fail (red != NULL);
g_return_if_fail (green != NULL);
g_return_if_fail (blue != NULL);
*red = (guint16) CLAMP (color->red * 65535.0 + 0.5, 0.0, 65535.0);
*green = (guint16) CLAMP (color->green * 65535.0 + 0.5, 0.0, 65535.0);
*blue = (guint16) CLAMP (color->blue * 65535.0 + 0.5, 0.0, 65535.0);
}

View File

@@ -1,143 +0,0 @@
#ifndef ZOITECHAT_THEME_PALETTE_H
#define ZOITECHAT_THEME_PALETTE_H
#include <stddef.h>
#include <gtk/gtk.h>
#include "../xtext-color.h"
typedef enum
{
THEME_LEGACY_MIRC_0 = 0,
THEME_LEGACY_MIRC_1,
THEME_LEGACY_MIRC_2,
THEME_LEGACY_MIRC_3,
THEME_LEGACY_MIRC_4,
THEME_LEGACY_MIRC_5,
THEME_LEGACY_MIRC_6,
THEME_LEGACY_MIRC_7,
THEME_LEGACY_MIRC_8,
THEME_LEGACY_MIRC_9,
THEME_LEGACY_MIRC_10,
THEME_LEGACY_MIRC_11,
THEME_LEGACY_MIRC_12,
THEME_LEGACY_MIRC_13,
THEME_LEGACY_MIRC_14,
THEME_LEGACY_MIRC_15,
THEME_LEGACY_MIRC_16,
THEME_LEGACY_MIRC_17,
THEME_LEGACY_MIRC_18,
THEME_LEGACY_MIRC_19,
THEME_LEGACY_MIRC_20,
THEME_LEGACY_MIRC_21,
THEME_LEGACY_MIRC_22,
THEME_LEGACY_MIRC_23,
THEME_LEGACY_MIRC_24,
THEME_LEGACY_MIRC_25,
THEME_LEGACY_MIRC_26,
THEME_LEGACY_MIRC_27,
THEME_LEGACY_MIRC_28,
THEME_LEGACY_MIRC_29,
THEME_LEGACY_MIRC_30,
THEME_LEGACY_MIRC_31,
THEME_LEGACY_SELECTION_FOREGROUND,
THEME_LEGACY_SELECTION_BACKGROUND,
THEME_LEGACY_TEXT_FOREGROUND,
THEME_LEGACY_TEXT_BACKGROUND,
THEME_LEGACY_MARKER,
THEME_LEGACY_TAB_NEW_DATA,
THEME_LEGACY_TAB_HIGHLIGHT,
THEME_LEGACY_TAB_NEW_MESSAGE,
THEME_LEGACY_TAB_AWAY,
THEME_LEGACY_SPELL,
THEME_LEGACY_MAX = THEME_LEGACY_SPELL
} ThemeLegacyColorIndex;
typedef enum
{
THEME_TOKEN_MIRC_0 = 0,
THEME_TOKEN_MIRC_1,
THEME_TOKEN_MIRC_2,
THEME_TOKEN_MIRC_3,
THEME_TOKEN_MIRC_4,
THEME_TOKEN_MIRC_5,
THEME_TOKEN_MIRC_6,
THEME_TOKEN_MIRC_7,
THEME_TOKEN_MIRC_8,
THEME_TOKEN_MIRC_9,
THEME_TOKEN_MIRC_10,
THEME_TOKEN_MIRC_11,
THEME_TOKEN_MIRC_12,
THEME_TOKEN_MIRC_13,
THEME_TOKEN_MIRC_14,
THEME_TOKEN_MIRC_15,
THEME_TOKEN_MIRC_16,
THEME_TOKEN_MIRC_17,
THEME_TOKEN_MIRC_18,
THEME_TOKEN_MIRC_19,
THEME_TOKEN_MIRC_20,
THEME_TOKEN_MIRC_21,
THEME_TOKEN_MIRC_22,
THEME_TOKEN_MIRC_23,
THEME_TOKEN_MIRC_24,
THEME_TOKEN_MIRC_25,
THEME_TOKEN_MIRC_26,
THEME_TOKEN_MIRC_27,
THEME_TOKEN_MIRC_28,
THEME_TOKEN_MIRC_29,
THEME_TOKEN_MIRC_30,
THEME_TOKEN_MIRC_31,
THEME_TOKEN_SELECTION_FOREGROUND,
THEME_TOKEN_SELECTION_BACKGROUND,
THEME_TOKEN_TEXT_FOREGROUND,
THEME_TOKEN_TEXT_BACKGROUND,
THEME_TOKEN_MARKER,
THEME_TOKEN_TAB_NEW_DATA,
THEME_TOKEN_TAB_HIGHLIGHT,
THEME_TOKEN_TAB_NEW_MESSAGE,
THEME_TOKEN_TAB_AWAY,
THEME_TOKEN_SPELL,
THEME_TOKEN_COUNT
} ThemeSemanticToken;
typedef struct
{
ThemeSemanticToken token;
int legacy_index;
const char *name;
} ThemePaletteTokenDef;
typedef struct
{
GdkRGBA colors[THEME_TOKEN_COUNT];
} ThemePalette;
typedef struct
{
GdkRGBA foreground;
GdkRGBA background;
GdkRGBA selection_foreground;
GdkRGBA selection_background;
char foreground_css[32];
char background_css[32];
char selection_foreground_css[32];
char selection_background_css[32];
} ThemeWidgetStyleValues;
size_t theme_palette_token_count (void);
size_t theme_palette_token_def_count (void);
const ThemePaletteTokenDef *theme_palette_token_def_at (size_t index);
const ThemePaletteTokenDef *theme_palette_token_def_for_token (ThemeSemanticToken token);
const char *theme_palette_token_name (ThemeSemanticToken token);
gboolean theme_palette_token_to_legacy_index (ThemeSemanticToken token, int *legacy_idx);
gboolean theme_palette_legacy_index_to_token (int legacy_idx, ThemeSemanticToken *token);
gboolean theme_palette_set_color (ThemePalette *palette, ThemeSemanticToken token, const GdkRGBA *color);
gboolean theme_palette_get_color (const ThemePalette *palette, ThemeSemanticToken token, GdkRGBA *color);
void theme_palette_from_legacy_colors (ThemePalette *palette, const GdkRGBA *legacy_colors, size_t legacy_len);
void theme_palette_to_legacy_colors (const ThemePalette *palette, GdkRGBA *legacy_colors, size_t legacy_len);
void theme_palette_to_xtext_colors (const ThemePalette *palette, XTextColor *xtext_colors, size_t xtext_len);
void theme_palette_to_widget_style_values (const ThemePalette *palette, ThemeWidgetStyleValues *style_values);
void theme_palette_color_get_rgb16 (const GdkRGBA *color, guint16 *red, guint16 *green, guint16 *blue);
#endif

View File

@@ -1,59 +0,0 @@
#include "theme-policy.h"
#include <gtk/gtk.h>
#include "../fe-gtk.h"
#include "../../common/zoitechat.h"
#include "../../common/zoitechatc.h"
gboolean
theme_policy_system_prefers_dark (void)
{
GtkSettings *settings = gtk_settings_get_default ();
gboolean prefer_dark = FALSE;
char *theme_name = NULL;
#ifdef G_OS_WIN32
gboolean have_win_pref = FALSE;
if (fe_win32_high_contrast_is_enabled ())
return FALSE;
have_win_pref = fe_win32_try_get_system_dark (&prefer_dark);
if (!have_win_pref)
#endif
if (settings && g_object_class_find_property (G_OBJECT_GET_CLASS (settings),
"gtk-application-prefer-dark-theme"))
{
g_object_get (settings, "gtk-application-prefer-dark-theme", &prefer_dark, NULL);
}
if (settings && !prefer_dark)
{
g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
if (theme_name)
{
char *lower = g_ascii_strdown (theme_name, -1);
if (g_str_has_suffix (lower, "-dark") || g_strrstr (lower, "dark"))
prefer_dark = TRUE;
g_free (lower);
g_free (theme_name);
}
}
return prefer_dark;
}
gboolean
theme_policy_is_dark_mode_active (unsigned int mode)
{
if (mode == ZOITECHAT_DARK_MODE_AUTO)
return theme_policy_system_prefers_dark ();
return fe_dark_mode_is_enabled_for (mode);
}
gboolean
theme_policy_is_app_dark_mode_active (void)
{
return theme_policy_is_dark_mode_active (prefs.hex_gui_dark_mode);
}

View File

@@ -1,10 +0,0 @@
#ifndef ZOITECHAT_THEME_POLICY_H
#define ZOITECHAT_THEME_POLICY_H
#include <glib.h>
gboolean theme_policy_system_prefers_dark (void);
gboolean theme_policy_is_dark_mode_active (unsigned int mode);
gboolean theme_policy_is_app_dark_mode_active (void);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +0,0 @@
#ifndef ZOITECHAT_THEME_PREFERENCES_H
#define ZOITECHAT_THEME_PREFERENCES_H
#include <gtk/gtk.h>
#include "theme-access.h"
#include "../fe-gtk.h"
#include "../../common/zoitechat.h"
GtkWidget *theme_preferences_create_page (GtkWindow *parent,
struct zoitechatprefs *setup_prefs,
gboolean *color_change_flag);
GtkWidget *theme_preferences_create_color_page (GtkWindow *parent,
struct zoitechatprefs *setup_prefs,
gboolean *color_change_flag);
void theme_preferences_apply_to_session (session_gui *gui, InputStyle *input_style);
#endif

View File

@@ -1,592 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include "theme-runtime.h"
#include "theme-policy.h"
#include "../../common/zoitechat.h"
#include "../../common/zoitechatc.h"
#include "../../common/util.h"
#include "../../common/cfgfiles.h"
#include "../../common/typedef.h"
#define PALETTE_COLOR_INIT(r, g, b) { (r) / 65535.0, (g) / 65535.0, (b) / 65535.0, 1.0 }
static const GdkRGBA legacy_light_defaults[THEME_LEGACY_MAX + 1] = {
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf),
PALETTE_COLOR_INIT (0x2e2e, 0x3434, 0x3636),
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4),
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606),
PALETTE_COLOR_INIT (0xcccc, 0x0000, 0x0000),
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202),
PALETTE_COLOR_INIT (0x5c5c, 0x3535, 0x6666),
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000),
PALETTE_COLOR_INIT (0xc4c4, 0xa0a0, 0x0000),
PALETTE_COLOR_INIT (0x7373, 0xd2d2, 0x1616),
PALETTE_COLOR_INIT (0x1111, 0xa8a8, 0x7979),
PALETTE_COLOR_INIT (0x5858, 0xa1a1, 0x9d9d),
PALETTE_COLOR_INIT (0x5757, 0x7979, 0x9e9e),
PALETTE_COLOR_INIT (0xa0d0, 0x42d4, 0x6562),
PALETTE_COLOR_INIT (0x5555, 0x5757, 0x5353),
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585),
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf),
PALETTE_COLOR_INIT (0x2e2e, 0x3434, 0x3636),
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4),
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606),
PALETTE_COLOR_INIT (0xcccc, 0x0000, 0x0000),
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202),
PALETTE_COLOR_INIT (0x5c5c, 0x3535, 0x6666),
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000),
PALETTE_COLOR_INIT (0xc4c4, 0xa0a0, 0x0000),
PALETTE_COLOR_INIT (0x7373, 0xd2d2, 0x1616),
PALETTE_COLOR_INIT (0x1111, 0xa8a8, 0x7979),
PALETTE_COLOR_INIT (0x5858, 0xa1a1, 0x9d9d),
PALETTE_COLOR_INIT (0x5757, 0x7979, 0x9e9e),
PALETTE_COLOR_INIT (0xa0d0, 0x42d4, 0x6562),
PALETTE_COLOR_INIT (0x5555, 0x5757, 0x5353),
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585),
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf),
PALETTE_COLOR_INIT (0x2020, 0x4a4a, 0x8787),
PALETTE_COLOR_INIT (0x2512, 0x29e8, 0x2b85),
PALETTE_COLOR_INIT (0xfae0, 0xfae0, 0xf8c4),
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202),
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4),
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606),
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000),
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585),
PALETTE_COLOR_INIT (0xa4a4, 0x0000, 0x0000),
};
static const GdkRGBA legacy_dark_defaults[THEME_LEGACY_MAX + 1] = {
PALETTE_COLOR_INIT (0xe5e5, 0xe5e5, 0xe5e5), PALETTE_COLOR_INIT (0x3c3c, 0x3c3c, 0x3c3c),
PALETTE_COLOR_INIT (0x5656, 0x9c9c, 0xd6d6), PALETTE_COLOR_INIT (0x0d0d, 0xbcbc, 0x7979),
PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747), PALETTE_COLOR_INIT (0xcece, 0x9191, 0x7878),
PALETTE_COLOR_INIT (0xc5c5, 0x8686, 0xc0c0), PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d),
PALETTE_COLOR_INIT (0xdcdc, 0xdcdc, 0xaaaa), PALETTE_COLOR_INIT (0xb5b5, 0xcece, 0xa8a8),
PALETTE_COLOR_INIT (0x4e4e, 0xc9c9, 0xb0b0), PALETTE_COLOR_INIT (0x9c9c, 0xdcdc, 0xfefe),
PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff), PALETTE_COLOR_INIT (0xd6d6, 0x7070, 0xd6d6),
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), PALETTE_COLOR_INIT (0xc0c0, 0xc0c0, 0xc0c0),
PALETTE_COLOR_INIT (0xe5e5, 0xe5e5, 0xe5e5), PALETTE_COLOR_INIT (0x3c3c, 0x3c3c, 0x3c3c),
PALETTE_COLOR_INIT (0x5656, 0x9c9c, 0xd6d6), PALETTE_COLOR_INIT (0x0d0d, 0xbcbc, 0x7979),
PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747), PALETTE_COLOR_INIT (0xcece, 0x9191, 0x7878),
PALETTE_COLOR_INIT (0xc5c5, 0x8686, 0xc0c0), PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d),
PALETTE_COLOR_INIT (0xdcdc, 0xdcdc, 0xaaaa), PALETTE_COLOR_INIT (0xb5b5, 0xcece, 0xa8a8),
PALETTE_COLOR_INIT (0x4e4e, 0xc9c9, 0xb0b0), PALETTE_COLOR_INIT (0x9c9c, 0xdcdc, 0xfefe),
PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff), PALETTE_COLOR_INIT (0xd6d6, 0x7070, 0xd6d6),
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), PALETTE_COLOR_INIT (0xc0c0, 0xc0c0, 0xc0c0),
PALETTE_COLOR_INIT (0xffff, 0xffff, 0xffff), PALETTE_COLOR_INIT (0x2626, 0x4f4f, 0x7878),
PALETTE_COLOR_INIT (0xd4d4, 0xd4d4, 0xd4d4), PALETTE_COLOR_INIT (0x1e1e, 0x1e1e, 0x1e1e),
PALETTE_COLOR_INIT (0x4040, 0x4040, 0x4040), PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff),
PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d), PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747),
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747),
};
static ThemePalette light_palette;
static ThemePalette dark_palette;
static ThemePalette active_palette;
static gboolean user_colors_valid = FALSE;
static gboolean dark_user_colors_valid = FALSE;
static gboolean dark_mode_active = FALSE;
static gboolean light_custom_tokens[THEME_TOKEN_COUNT];
static gboolean dark_custom_tokens[THEME_TOKEN_COUNT];
#define THEME_PALETTE_MIGRATION_MARKER_KEY "theme.palette.semantic_migrated"
#define THEME_PALETTE_MIGRATION_MARKER_VALUE 1
typedef struct
{
const char *mode_name;
const char *legacy_prefix;
ThemePalette *palette;
gboolean *mode_valid;
} ThemePalettePersistenceMode;
static void
theme_runtime_resolve_color (const GdkRGBA *mapped, const GdkRGBA *fallback, GdkRGBA *resolved)
{
gdouble alpha;
g_return_if_fail (mapped != NULL);
g_return_if_fail (fallback != NULL);
g_return_if_fail (resolved != NULL);
alpha = CLAMP (mapped->alpha, 0.0, 1.0);
resolved->red = (mapped->red * alpha) + (fallback->red * (1.0 - alpha));
resolved->green = (mapped->green * alpha) + (fallback->green * (1.0 - alpha));
resolved->blue = (mapped->blue * alpha) + (fallback->blue * (1.0 - alpha));
resolved->alpha = 1.0;
}
static void
theme_runtime_apply_gtk_map (ThemePalette *palette, const ThemeGtkPaletteMap *gtk_map, const gboolean *custom_tokens)
{
GdkRGBA text_foreground;
GdkRGBA text_background;
GdkRGBA selection_foreground;
GdkRGBA selection_background;
GdkRGBA accent;
GdkRGBA fallback;
g_return_if_fail (palette != NULL);
if (gtk_map == NULL || !gtk_map->enabled || custom_tokens == NULL)
return;
g_assert (theme_palette_get_color (palette, THEME_TOKEN_TEXT_FOREGROUND, &fallback));
theme_runtime_resolve_color (&gtk_map->text_foreground, &fallback, &text_foreground);
g_assert (theme_palette_get_color (palette, THEME_TOKEN_TEXT_BACKGROUND, &fallback));
theme_runtime_resolve_color (&gtk_map->text_background, &fallback, &text_background);
g_assert (theme_palette_get_color (palette, THEME_TOKEN_SELECTION_FOREGROUND, &fallback));
theme_runtime_resolve_color (&gtk_map->selection_foreground, &fallback, &selection_foreground);
g_assert (theme_palette_get_color (palette, THEME_TOKEN_SELECTION_BACKGROUND, &fallback));
theme_runtime_resolve_color (&gtk_map->selection_background, &fallback, &selection_background);
g_assert (theme_palette_get_color (palette, THEME_TOKEN_MARKER, &fallback));
theme_runtime_resolve_color (&gtk_map->accent, &fallback, &accent);
if (!custom_tokens[THEME_TOKEN_TEXT_FOREGROUND])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TEXT_FOREGROUND, &text_foreground));
if (!custom_tokens[THEME_TOKEN_TEXT_BACKGROUND])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TEXT_BACKGROUND, &text_background));
if (!custom_tokens[THEME_TOKEN_SELECTION_FOREGROUND])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_SELECTION_FOREGROUND, &selection_foreground));
if (!custom_tokens[THEME_TOKEN_SELECTION_BACKGROUND])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_SELECTION_BACKGROUND, &selection_background));
if (!custom_tokens[THEME_TOKEN_MARKER])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_MARKER, &accent));
if (!custom_tokens[THEME_TOKEN_TAB_NEW_DATA])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TAB_NEW_DATA, &accent));
if (!custom_tokens[THEME_TOKEN_TAB_HIGHLIGHT])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TAB_HIGHLIGHT, &accent));
if (!custom_tokens[THEME_TOKEN_TAB_NEW_MESSAGE])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TAB_NEW_MESSAGE, &accent));
if (!custom_tokens[THEME_TOKEN_TAB_AWAY])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TAB_AWAY, &accent));
if (!custom_tokens[THEME_TOKEN_SPELL])
g_assert (theme_palette_set_color (palette, THEME_TOKEN_SPELL, &accent));
}
static const gboolean *
theme_runtime_active_custom_tokens (void)
{
return light_custom_tokens;
}
static void
palette_color_set_rgb16 (GdkRGBA *color, guint16 red, guint16 green, guint16 blue)
{
char color_string[16];
GdkRGBA parsed = { 0 };
gboolean parsed_ok;
g_snprintf (color_string, sizeof (color_string), "#%04x%04x%04x", red, green, blue);
parsed_ok = gdk_rgba_parse (&parsed, color_string);
if (!parsed_ok)
{
parsed.red = red / 65535.0;
parsed.green = green / 65535.0;
parsed.blue = blue / 65535.0;
parsed.alpha = 1.0;
}
*color = parsed;
}
static void
palette_init_defaults (void)
{
theme_palette_from_legacy_colors (&light_palette, legacy_light_defaults, G_N_ELEMENTS (legacy_light_defaults));
theme_palette_from_legacy_colors (&dark_palette, legacy_dark_defaults, G_N_ELEMENTS (legacy_dark_defaults));
active_palette = light_palette;
dark_mode_active = FALSE;
}
static int
palette_legacy_index_to_cfg_key (int legacy_idx)
{
g_return_val_if_fail (legacy_idx >= 0 && legacy_idx <= THEME_LEGACY_MAX, -1);
if (legacy_idx < 32)
return legacy_idx;
return (legacy_idx - 32) + 256;
}
static gboolean
palette_read_token_color (char *cfg, const char *mode_name, const ThemePaletteTokenDef *def, GdkRGBA *out_color)
{
char prefname[256];
guint16 red;
guint16 green;
guint16 blue;
g_return_val_if_fail (cfg != NULL, FALSE);
g_return_val_if_fail (mode_name != NULL, FALSE);
g_return_val_if_fail (def != NULL, FALSE);
g_return_val_if_fail (out_color != NULL, FALSE);
g_snprintf (prefname, sizeof prefname, "theme.mode.%s.token.%s", mode_name, def->name);
if (!cfg_get_color (cfg, prefname, &red, &green, &blue))
return FALSE;
palette_color_set_rgb16 (out_color, red, green, blue);
return TRUE;
}
static gboolean
palette_read_legacy_color (char *cfg, const char *legacy_prefix, int legacy_index, GdkRGBA *out_color)
{
char prefname[256];
guint16 red;
guint16 green;
guint16 blue;
int legacy_key;
g_return_val_if_fail (cfg != NULL, FALSE);
g_return_val_if_fail (legacy_prefix != NULL, FALSE);
g_return_val_if_fail (out_color != NULL, FALSE);
legacy_key = palette_legacy_index_to_cfg_key (legacy_index);
g_return_val_if_fail (legacy_key >= 0, FALSE);
g_snprintf (prefname, sizeof prefname, "%s_%d", legacy_prefix, legacy_key);
if (!cfg_get_color (cfg, prefname, &red, &green, &blue))
return FALSE;
palette_color_set_rgb16 (out_color, red, green, blue);
return TRUE;
}
static gboolean
theme_runtime_load_migrated_legacy_color (char *cfg,
const ThemePalettePersistenceMode *mode,
const ThemePaletteTokenDef *def,
GdkRGBA *out_color)
{
g_return_val_if_fail (cfg != NULL, FALSE);
g_return_val_if_fail (mode != NULL, FALSE);
g_return_val_if_fail (def != NULL, FALSE);
g_return_val_if_fail (out_color != NULL, FALSE);
return palette_read_legacy_color (cfg, mode->legacy_prefix, def->legacy_index, out_color);
}
static void
palette_write_token_color (int fh, const char *mode_name, const ThemePaletteTokenDef *def, const GdkRGBA *color)
{
char prefname[256];
guint16 red;
guint16 green;
guint16 blue;
g_return_if_fail (mode_name != NULL);
g_return_if_fail (def != NULL);
g_return_if_fail (color != NULL);
g_snprintf (prefname, sizeof prefname, "theme.mode.%s.token.%s", mode_name, def->name);
theme_palette_color_get_rgb16 (color, &red, &green, &blue);
cfg_put_color (fh, red, green, blue, prefname);
}
gboolean
theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
{
g_return_val_if_fail (out_rgba != NULL, FALSE);
return theme_palette_get_color (&active_palette, token, out_rgba);
}
gboolean
theme_runtime_mode_has_user_colors (gboolean dark_mode)
{
(void) dark_mode;
return user_colors_valid;
}
void
theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values)
{
g_return_if_fail (out_values != NULL);
theme_palette_to_widget_style_values (&active_palette, out_values);
}
void
theme_runtime_get_widget_style_values_mapped (const ThemeGtkPaletteMap *gtk_map, ThemeWidgetStyleValues *out_values)
{
ThemePalette mapped_palette;
g_return_if_fail (out_values != NULL);
mapped_palette = active_palette;
theme_runtime_apply_gtk_map (&mapped_palette, gtk_map, theme_runtime_active_custom_tokens ());
theme_palette_to_widget_style_values (&mapped_palette, out_values);
}
void
theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len)
{
g_return_if_fail (palette != NULL);
theme_palette_to_xtext_colors (&active_palette, palette, palette_len);
}
void
theme_runtime_get_xtext_colors_mapped (const ThemeGtkPaletteMap *gtk_map, XTextColor *palette, size_t palette_len)
{
ThemePalette mapped_palette;
g_return_if_fail (palette != NULL);
mapped_palette = active_palette;
theme_runtime_apply_gtk_map (&mapped_palette, gtk_map, theme_runtime_active_custom_tokens ());
theme_palette_to_xtext_colors (&mapped_palette, palette, palette_len);
}
void
theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *col)
{
if (!col)
return;
if (token < 0 || token >= THEME_TOKEN_COUNT)
return;
if (!user_colors_valid)
light_palette = active_palette;
g_assert (theme_palette_set_color (&light_palette, token, col));
light_custom_tokens[token] = TRUE;
user_colors_valid = TRUE;
}
void
theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *col)
{
if (!col)
return;
if (token < 0 || token >= THEME_TOKEN_COUNT)
return;
if (!dark_user_colors_valid)
dark_palette = active_palette;
g_assert (theme_palette_set_color (&dark_palette, token, col));
dark_custom_tokens[token] = TRUE;
dark_user_colors_valid = TRUE;
}
void
theme_runtime_reset_mode_colors (gboolean dark_mode)
{
(void) dark_mode;
theme_palette_from_legacy_colors (&light_palette, legacy_light_defaults, G_N_ELEMENTS (legacy_light_defaults));
active_palette = light_palette;
memset (light_custom_tokens, 0, sizeof light_custom_tokens);
memset (dark_custom_tokens, 0, sizeof dark_custom_tokens);
user_colors_valid = TRUE;
dark_user_colors_valid = FALSE;
dark_mode_active = FALSE;
}
void
theme_runtime_load (void)
{
size_t i;
int fh;
struct stat st;
char *cfg;
ThemePalettePersistenceMode modes[] = {
{ "light", "color", &light_palette, &user_colors_valid },
{ "dark", "dark_color", &dark_palette, &dark_user_colors_valid },
};
const size_t mode_count = G_N_ELEMENTS (modes);
palette_init_defaults ();
memset (light_custom_tokens, 0, sizeof light_custom_tokens);
memset (dark_custom_tokens, 0, sizeof dark_custom_tokens);
fh = zoitechat_open_file ("colors.conf", O_RDONLY, 0, 0);
if (fh != -1)
{
fstat (fh, &st);
cfg = g_malloc0 (st.st_size + 1);
read (fh, cfg, st.st_size);
for (i = 0; i < mode_count; i++)
{
size_t j;
gboolean mode_found = FALSE;
for (j = 0; j < theme_palette_token_def_count (); j++)
{
const ThemePaletteTokenDef *def = theme_palette_token_def_at (j);
GdkRGBA color;
gboolean found;
g_assert (def != NULL);
g_assert (theme_palette_get_color (modes[i].palette, def->token, &color));
found = palette_read_token_color (cfg, modes[i].mode_name, def, &color);
if (!found)
found = theme_runtime_load_migrated_legacy_color (cfg, &modes[i], def, &color);
if (found)
{
gboolean *custom_tokens;
g_assert (theme_palette_set_color (modes[i].palette, def->token, &color));
custom_tokens = (modes[i].palette == &dark_palette) ? dark_custom_tokens : light_custom_tokens;
custom_tokens[def->token] = TRUE;
mode_found = TRUE;
}
}
*modes[i].mode_valid = mode_found;
}
g_free (cfg);
close (fh);
}
active_palette = light_palette;
dark_mode_active = FALSE;
user_colors_valid = TRUE;
}
void
theme_runtime_save (void)
{
size_t i;
size_t j;
int fh;
ThemePalettePersistenceMode modes[] = {
{ "light", "color", &light_palette, &user_colors_valid },
{ "dark", "dark_color", &dark_palette, &dark_user_colors_valid },
};
const size_t mode_count = G_N_ELEMENTS (modes);
if (dark_mode_active && !user_colors_valid)
light_palette = active_palette;
if (!dark_mode_active)
light_palette = active_palette;
if (dark_mode_active)
{
if (!dark_user_colors_valid)
dark_palette = active_palette;
dark_user_colors_valid = TRUE;
}
user_colors_valid = TRUE;
modes[0].palette = &light_palette;
modes[0].mode_valid = &user_colors_valid;
if (dark_user_colors_valid)
modes[1].palette = &dark_palette;
else if (dark_mode_active)
modes[1].palette = &active_palette;
else
modes[1].palette = &dark_palette;
fh = zoitechat_open_file ("colors.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
if (fh == -1)
return;
cfg_put_int (fh, THEME_PALETTE_MIGRATION_MARKER_VALUE, (char *) THEME_PALETTE_MIGRATION_MARKER_KEY);
for (i = 0; i < mode_count; i++)
{
if (!*modes[i].mode_valid)
continue;
for (j = 0; j < theme_palette_token_def_count (); j++)
{
const ThemePaletteTokenDef *def = theme_palette_token_def_at (j);
const gboolean *custom_tokens;
GdkRGBA color;
g_assert (def != NULL);
custom_tokens = (modes[i].palette == &dark_palette) ? dark_custom_tokens : light_custom_tokens;
if (!custom_tokens[def->token])
continue;
g_assert (theme_palette_get_color (modes[i].palette, def->token, &color));
palette_write_token_color (fh, modes[i].mode_name, def, &color);
}
}
close (fh);
}
static gboolean
palette_color_eq (const GdkRGBA *a, const GdkRGBA *b)
{
guint16 red_a;
guint16 green_a;
guint16 blue_a;
guint16 red_b;
guint16 green_b;
guint16 blue_b;
theme_palette_color_get_rgb16 (a, &red_a, &green_a, &blue_a);
theme_palette_color_get_rgb16 (b, &red_b, &green_b, &blue_b);
return red_a == red_b && green_a == green_b && blue_a == blue_b;
}
gboolean
theme_runtime_apply_dark_mode (gboolean enable)
{
ThemePalette previous_palette;
size_t i;
gboolean changed = FALSE;
previous_palette = active_palette;
if (!user_colors_valid)
{
light_palette = active_palette;
user_colors_valid = TRUE;
}
if (enable)
active_palette = dark_palette;
else
active_palette = light_palette;
dark_mode_active = enable ? TRUE : FALSE;
for (i = 0; i < theme_palette_token_def_count (); i++)
{
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
GdkRGBA old_color;
GdkRGBA new_color;
g_assert (def != NULL);
g_assert (theme_palette_get_color (&previous_palette, def->token, &old_color));
g_assert (theme_palette_get_color (&active_palette, def->token, &new_color));
if (!palette_color_eq (&old_color, &new_color))
{
changed = TRUE;
break;
}
}
return changed;
}
gboolean
theme_runtime_apply_mode (unsigned int mode, gboolean *palette_changed)
{
gboolean changed;
(void) mode;
changed = theme_runtime_apply_dark_mode (FALSE);
if (palette_changed)
*palette_changed = changed;
return FALSE;
}
gboolean
theme_runtime_is_dark_active (void)
{
return dark_mode_active;
}

View File

@@ -1,35 +0,0 @@
#ifndef ZOITECHAT_THEME_RUNTIME_H
#define ZOITECHAT_THEME_RUNTIME_H
#include <stddef.h>
#include <gtk/gtk.h>
#include "theme-palette.h"
typedef struct
{
gboolean enabled;
GdkRGBA text_foreground;
GdkRGBA text_background;
GdkRGBA selection_foreground;
GdkRGBA selection_background;
GdkRGBA accent;
} ThemeGtkPaletteMap;
void theme_runtime_load (void);
void theme_runtime_save (void);
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *palette_changed);
gboolean theme_runtime_apply_dark_mode (gboolean enable);
void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *col);
void theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *col);
void theme_runtime_reset_mode_colors (gboolean dark_mode);
gboolean theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba);
gboolean theme_runtime_mode_has_user_colors (gboolean dark_mode);
void theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values);
void theme_runtime_get_widget_style_values_mapped (const ThemeGtkPaletteMap *gtk_map, ThemeWidgetStyleValues *out_values);
void theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len);
void theme_runtime_get_xtext_colors_mapped (const ThemeGtkPaletteMap *gtk_map, XTextColor *palette, size_t palette_len);
gboolean theme_runtime_is_dark_active (void);
#endif

View File

@@ -33,11 +33,10 @@
#include "../common/zoitechatc.h"
#include "../common/fe.h"
#include "gtkutil.h"
#include "theme/theme-gtk.h"
#include "palette.h"
#include "maingui.h"
#include "menu.h"
#include "pixmaps.h"
#include "theme/theme-access.h"
#include "userlistgui.h"
#include "fkeys.h"
@@ -47,10 +46,10 @@ enum
COL_NICK=1, /* char * */
COL_HOST=2, /* char * */
COL_USER=3, /* struct User * */
COL_GDKCOLOR=4 /* GdkRGBA */
COL_GDKCOLOR=4 /* PaletteColor */
};
static void userlist_store_color (GtkListStore *store, GtkTreeIter *iter, ThemeSemanticToken token, gboolean has_token);
static void userlist_store_color (GtkListStore *store, GtkTreeIter *iter, int color_index);
GdkPixbuf *
get_user_icon (server *serv, struct User *user)
@@ -131,81 +130,6 @@ scroll_to_iter (GtkTreeIter *iter, GtkTreeView *treeview, GtkTreeModel *model)
}
}
static GHashTable *
userlist_row_map_ensure (session *sess)
{
if (!sess->res->user_row_refs)
sess->res->user_row_refs = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) gtk_tree_row_reference_free);
return sess->res->user_row_refs;
}
static void
userlist_row_map_remove (session *sess, struct User *user)
{
if (!sess->res->user_row_refs)
return;
g_hash_table_remove (sess->res->user_row_refs, user);
}
static void
userlist_row_map_set (session *sess, GtkTreeModel *model, struct User *user, GtkTreeIter *iter)
{
GtkTreePath *path;
GtkTreeRowReference *ref;
path = gtk_tree_model_get_path (model, iter);
if (!path)
return;
ref = gtk_tree_row_reference_new (model, path);
gtk_tree_path_free (path);
if (!ref)
return;
g_hash_table_replace (userlist_row_map_ensure (sess), user, ref);
}
static gboolean
userlist_row_map_get_iter (session *sess, GtkTreeModel *model, struct User *user, GtkTreeIter *iter)
{
GtkTreeRowReference *ref;
GtkTreePath *path;
struct User *row_user;
if (!sess->res->user_row_refs)
return FALSE;
ref = g_hash_table_lookup (sess->res->user_row_refs, user);
if (!ref)
return FALSE;
path = gtk_tree_row_reference_get_path (ref);
if (!path)
{
g_hash_table_remove (sess->res->user_row_refs, user);
return FALSE;
}
if (!gtk_tree_model_get_iter (model, iter, path))
{
gtk_tree_path_free (path);
g_hash_table_remove (sess->res->user_row_refs, user);
return FALSE;
}
gtk_tree_path_free (path);
gtk_tree_model_get (model, iter, COL_USER, &row_user, -1);
if (row_user != user)
{
g_hash_table_remove (sess->res->user_row_refs, user);
return FALSE;
}
return TRUE;
}
/* select a row in the userlist by nick-name */
void
@@ -215,20 +139,8 @@ userlist_select (session *sess, char *name)
GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
GtkTreeModel *model = gtk_tree_view_get_model (treeview);
GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
struct User *user = userlist_find (sess, name);
struct User *row_user;
if (user && userlist_row_map_get_iter (sess, model, user, &iter))
{
if (gtk_tree_selection_iter_is_selected (selection, &iter))
gtk_tree_selection_unselect_iter (selection, &iter);
else
gtk_tree_selection_select_iter (selection, &iter);
scroll_to_iter (&iter, treeview, model);
return;
}
if (gtk_tree_model_get_iter_first (model, &iter))
{
do
@@ -236,7 +148,6 @@ userlist_select (session *sess, char *name)
gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
if (sess->server->p_cmp (row_user->nick, name) == 0)
{
userlist_row_map_set (sess, model, row_user, &iter);
if (gtk_tree_selection_iter_is_selected (selection, &iter))
gtk_tree_selection_unselect_iter (selection, &iter);
else
@@ -326,23 +237,13 @@ fe_userlist_set_selected (struct session *sess)
}
static GtkTreeIter *
find_row (session *sess, GtkTreeView *treeview, GtkTreeModel *model, struct User *user,
find_row (GtkTreeView *treeview, GtkTreeModel *model, struct User *user,
int *selected)
{
static GtkTreeIter iter;
struct User *row_user;
*selected = FALSE;
if (userlist_row_map_get_iter (sess, model, user, &iter))
{
if (gtk_tree_view_get_model (treeview) == model)
{
if (gtk_tree_selection_iter_is_selected (gtk_tree_view_get_selection (treeview), &iter))
*selected = TRUE;
}
return &iter;
}
if (gtk_tree_model_get_iter_first (model, &iter))
{
do
@@ -350,7 +251,6 @@ find_row (session *sess, GtkTreeView *treeview, GtkTreeModel *model, struct User
gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
if (row_user == user)
{
userlist_row_map_set (sess, model, row_user, &iter);
if (gtk_tree_view_get_model (treeview) == model)
{
if (gtk_tree_selection_iter_is_selected (gtk_tree_view_get_selection (treeview), &iter))
@@ -386,11 +286,10 @@ fe_userlist_remove (session *sess, struct User *user)
gfloat val, end;*/
int sel;
iter = find_row (sess, GTK_TREE_VIEW (sess->gui->user_tree),
GTK_TREE_MODEL(sess->res->user_model), user, &sel);
iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
GTK_TREE_MODEL(sess->res->user_model), user, &sel);
if (!iter)
return 0;
userlist_row_map_remove (sess, user);
/* adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (sess->gui->user_tree));
val = adj->value;*/
@@ -415,35 +314,22 @@ fe_userlist_rehash (session *sess, struct User *user)
{
GtkTreeIter *iter;
int sel;
ThemeSemanticToken nick_token = THEME_TOKEN_TEXT_FOREGROUND;
gboolean have_nick_token = FALSE;
int nick_color = 0;
iter = find_row (sess, GTK_TREE_VIEW (sess->gui->user_tree),
GTK_TREE_MODEL(sess->res->user_model), user, &sel);
iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
GTK_TREE_MODEL(sess->res->user_model), user, &sel);
if (!iter)
return;
userlist_row_map_set (sess, GTK_TREE_MODEL (sess->res->user_model), user, iter);
if (prefs.hex_away_track && user->away)
{
nick_token = THEME_TOKEN_TAB_AWAY;
have_nick_token = TRUE;
}
nick_color = COL_AWAY;
else if (prefs.hex_gui_ulist_color)
{
int mirc_index = text_color_of (user->nick);
if (mirc_index >= 0 && mirc_index < 32)
{
nick_token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + mirc_index);
have_nick_token = TRUE;
}
}
nick_color = text_color_of(user->nick);
gtk_list_store_set (GTK_LIST_STORE (sess->res->user_model), iter,
COL_HOST, user->hostname,
-1);
userlist_store_color (GTK_LIST_STORE (sess->res->user_model), iter, nick_token, have_nick_token);
userlist_store_color (GTK_LIST_STORE (sess->res->user_model), iter, nick_color);
}
void
@@ -453,24 +339,12 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
GdkPixbuf *pix = get_user_icon (sess->server, newuser);
GtkTreeIter iter;
char *nick;
ThemeSemanticToken nick_token = THEME_TOKEN_TEXT_FOREGROUND;
gboolean have_nick_token = FALSE;
int nick_color = 0;
if (prefs.hex_away_track && newuser->away)
{
nick_token = THEME_TOKEN_TAB_AWAY;
have_nick_token = TRUE;
}
nick_color = COL_AWAY;
else if (prefs.hex_gui_ulist_color)
{
int mirc_index = text_color_of (newuser->nick);
if (mirc_index >= 0 && mirc_index < 32)
{
nick_token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + mirc_index);
have_nick_token = TRUE;
}
}
nick_color = text_color_of(newuser->nick);
nick = newuser->nick;
if (!prefs.hex_gui_ulist_icons)
@@ -490,15 +364,13 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
COL_HOST, newuser->hostname,
COL_USER, newuser,
-1);
userlist_store_color (GTK_LIST_STORE (model), &iter, nick_token, have_nick_token);
userlist_store_color (GTK_LIST_STORE (model), &iter, nick_color);
if (!prefs.hex_gui_ulist_icons)
{
g_free (nick);
}
userlist_row_map_set (sess, model, newuser, &iter);
/* is it me? */
if (newuser->me && sess->gui->nick_box)
{
@@ -519,8 +391,6 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
void
fe_userlist_clear (session *sess)
{
if (sess->res->user_row_refs)
g_hash_table_remove_all (sess->res->user_row_refs);
gtk_list_store_clear (sess->res->user_model);
}
@@ -599,17 +469,14 @@ userlist_ops_cmp (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b,
}
static void
userlist_store_color (GtkListStore *store, GtkTreeIter *iter, ThemeSemanticToken token, gboolean has_token)
userlist_store_color (GtkListStore *store, GtkTreeIter *iter, int color_index)
{
GdkRGBA rgba;
const GdkRGBA *color = NULL;
if (has_token && theme_get_color (token, &rgba))
color = &rgba;
const PaletteColor *color = color_index ? &colors[color_index] : NULL;
if (color)
{
gtk_list_store_set (store, iter, COL_GDKCOLOR, color, -1);
GdkRGBA rgba = *color;
gtk_list_store_set (store, iter, COL_GDKCOLOR, &rgba, -1);
}
else
{
@@ -625,7 +492,7 @@ userlist_create_model (session *sess)
GtkSortType sort_type;
store = gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
G_TYPE_POINTER, PALETTE_GDK_TYPE);
switch (prefs.hex_gui_ulist_sort)
{
@@ -678,7 +545,7 @@ userlist_add_columns (GtkTreeView * treeview)
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
-1, NULL, renderer,
"text", 1, THEME_GTK_FOREGROUND_PROPERTY, 4, NULL);
"text", 1, PALETTE_FOREGROUND_PROPERTY, 4, NULL);
if (prefs.hex_gui_ulist_show_hosts)
{
@@ -815,7 +682,6 @@ userlist_create (GtkWidget *box)
treeview = gtk_tree_view_new ();
gtk_widget_set_name (treeview, "zoitechat-userlist");
gtk_widget_set_can_focus (treeview, TRUE);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
gtk_tree_selection_set_mode (gtk_tree_view_get_selection
(GTK_TREE_VIEW (treeview)),
@@ -867,48 +733,38 @@ userlist_show (session *sess)
void
fe_uselect (session *sess, char *word[], int do_clear, int scroll_to)
{
char *name;
int thisname;
char *name;
GtkTreeIter iter;
GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
GtkTreeModel *model = gtk_tree_view_get_model (treeview);
GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
struct User *user;
struct User *row_user;
if (do_clear)
gtk_tree_selection_unselect_all (selection);
thisname = 0;
while (*(name = word[thisname++]))
if (gtk_tree_model_get_iter_first (model, &iter))
{
user = userlist_find (sess, name);
if (!user)
continue;
if (do_clear)
gtk_tree_selection_unselect_all (selection);
if (userlist_row_map_get_iter (sess, model, user, &iter))
do
{
gtk_tree_selection_select_iter (selection, &iter);
if (scroll_to)
scroll_to_iter (&iter, treeview, model);
continue;
}
if (gtk_tree_model_get_iter_first (model, &iter))
{
do
if (*word[0])
{
gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
if (row_user == user)
thisname = 0;
while ( *(name = word[thisname++]) )
{
userlist_row_map_set (sess, model, row_user, &iter);
gtk_tree_selection_select_iter (selection, &iter);
if (scroll_to)
scroll_to_iter (&iter, treeview, model);
break;
if (sess->server->p_cmp (row_user->nick, name) == 0)
{
gtk_tree_selection_select_iter (selection, &iter);
if (scroll_to)
scroll_to_iter (&iter, treeview, model);
break;
}
}
}
while (gtk_tree_model_iter_next (model, &iter));
}
while (gtk_tree_model_iter_next (model, &iter));
}
}

View File

@@ -47,7 +47,6 @@
#include "fe-gtk.h"
#include "xtext.h"
#include "fkeys.h"
#include "theme/theme-access.h"
#define charlen(str) g_utf8_skip[*(guchar *)(str)]
@@ -87,10 +86,9 @@ struct textentry
gint16 left_len;
GSList *slp;
GSList *sublines;
int subline_count;
guchar tag;
guchar pad1;
guchar pad2;
guchar pad2; /* 32-bit align : 44 bytes total */
GList *marks; /* List of found strings */
};
@@ -364,107 +362,8 @@ xtext_draw_bg_offset (GtkXText *xtext, int x, int y, int width, int height, int
if (xtext->background_surface)
{
GtkAllocation allocation;
int clip_x;
int clip_y;
int clip_w;
int clip_h;
if (cairo_surface_status (xtext->background_surface) != CAIRO_STATUS_SUCCESS)
{
xtext_draw_rectangle (xtext, cr, &xtext->bgc, x, y, width, height);
cairo_destroy (cr);
return;
}
gtk_widget_get_allocation (GTK_WIDGET (xtext), &allocation);
clip_x = 0;
clip_y = 0;
clip_w = allocation.width;
clip_h = allocation.height;
if (clip_w < 1 || clip_h < 1 || clip_w > 8192 || clip_h > 8192)
{
xtext_draw_rectangle (xtext, cr, &xtext->bgc, x, y, width, height);
cairo_destroy (cr);
return;
}
if (xtext->background_clip_surface == NULL ||
xtext->background_clip_cycle != xtext->render_cycle ||
xtext->background_clip_x != clip_x ||
xtext->background_clip_y != clip_y ||
xtext->background_clip_width != clip_w ||
xtext->background_clip_height != clip_h)
{
cairo_t *bg_cr;
if (xtext->background_clip_surface)
{
cairo_surface_destroy (xtext->background_clip_surface);
xtext->background_clip_surface = NULL;
}
xtext->background_clip_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, clip_w, clip_h);
if (cairo_surface_status (xtext->background_clip_surface) != CAIRO_STATUS_SUCCESS)
{
cairo_surface_destroy (xtext->background_clip_surface);
xtext->background_clip_surface = NULL;
xtext_draw_rectangle (xtext, cr, &xtext->bgc, x, y, width, height);
cairo_destroy (cr);
return;
}
bg_cr = cairo_create (xtext->background_clip_surface);
if (cairo_surface_get_type (xtext->background_surface) == CAIRO_SURFACE_TYPE_IMAGE)
{
int src_w = cairo_image_surface_get_width (xtext->background_surface);
int src_h = cairo_image_surface_get_height (xtext->background_surface);
if (src_w > 0 && src_h > 0)
{
double scale_x = (double)clip_w / (double)src_w;
double scale_y = (double)clip_h / (double)src_h;
double scale = scale_x < scale_y ? scale_x : scale_y;
double draw_w = src_w * scale;
double draw_h = src_h * scale;
double draw_x = ((double)clip_w - draw_w) / 2.0;
double draw_y = ((double)clip_h - draw_h) / 2.0;
cairo_set_source_rgb (bg_cr, 0.0, 0.0, 0.0);
cairo_paint (bg_cr);
cairo_save (bg_cr);
cairo_translate (bg_cr, draw_x, draw_y);
cairo_scale (bg_cr, scale, scale);
cairo_set_source_surface (bg_cr, xtext->background_surface, 0.0, 0.0);
cairo_pattern_set_extend (cairo_get_source (bg_cr), CAIRO_EXTEND_NONE);
cairo_rectangle (bg_cr, 0.0, 0.0, (double)src_w, (double)src_h);
cairo_fill (bg_cr);
cairo_restore (bg_cr);
}
else
{
cairo_set_source_surface (bg_cr, xtext->background_surface, tile_x - clip_x, tile_y - clip_y);
cairo_pattern_set_extend (cairo_get_source (bg_cr), CAIRO_EXTEND_REPEAT);
cairo_rectangle (bg_cr, 0.0, 0.0, (double)clip_w, (double)clip_h);
cairo_fill (bg_cr);
}
}
else
{
cairo_set_source_surface (bg_cr, xtext->background_surface, tile_x - clip_x, tile_y - clip_y);
cairo_pattern_set_extend (cairo_get_source (bg_cr), CAIRO_EXTEND_REPEAT);
cairo_rectangle (bg_cr, 0.0, 0.0, (double)clip_w, (double)clip_h);
cairo_fill (bg_cr);
}
cairo_destroy (bg_cr);
xtext->background_clip_x = clip_x;
xtext->background_clip_y = clip_y;
xtext->background_clip_width = clip_w;
xtext->background_clip_height = clip_h;
xtext->background_clip_cycle = xtext->render_cycle;
}
cairo_set_source_surface (cr, xtext->background_clip_surface,
(double)xtext->background_clip_x, (double)xtext->background_clip_y);
cairo_set_source_surface (cr, xtext->background_surface, tile_x, tile_y);
cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
cairo_rectangle (cr, (double)x, (double)y, (double)width, (double)height);
cairo_fill (cr);
}
@@ -728,36 +627,13 @@ xtext_set_bg (GtkXText *xtext, int index)
xtext->bgc = xtext->palette[index];
}
static void
gtk_xtext_sync_palette_from_theme (GtkXText *xtext)
{
XTextColor palette[XTEXT_COLS];
theme_get_xtext_colors_for_widget (GTK_WIDGET (xtext), palette, G_N_ELEMENTS (palette));
gtk_xtext_set_palette (xtext, palette);
}
static void
gtk_xtext_style_updated (GtkWidget *widget, gpointer user_data)
{
(void) user_data;
gtk_xtext_sync_palette_from_theme (GTK_XTEXT (widget));
}
static void
gtk_xtext_init (GtkXText * xtext)
{
xtext->background_surface = NULL;
xtext->background_clip_surface = NULL;
xtext->draw_window = NULL;
xtext->draw_surface = NULL;
xtext->draw_cr = NULL;
xtext->background_clip_x = 0;
xtext->background_clip_y = 0;
xtext->background_clip_width = 0;
xtext->background_clip_height = 0;
xtext->background_clip_cycle = 0;
xtext->render_cycle = 0;
xtext->io_tag = 0;
xtext->add_io_tag = 0;
xtext->scroll_tag = 0;
@@ -791,8 +667,6 @@ gtk_xtext_init (GtkXText * xtext)
gtk_xtext_scroll_adjustments (xtext, NULL, NULL);
gtk_xtext_install_selection_targets (GTK_WIDGET (xtext));
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (xtext)), "view");
g_signal_connect (G_OBJECT (xtext), "style-updated", G_CALLBACK (gtk_xtext_style_updated), NULL);
}
static void
@@ -829,7 +703,7 @@ gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal)
if (fire_signal)
{
g_signal_emit_by_name (adj, "value-changed");
gtk_adjustment_value_changed (adj);
}
}
}
@@ -893,7 +767,6 @@ gtk_xtext_new (const XTextColor *palette, int separator)
/* GTK3 already uses the GTK render pipeline; no manual double-buffering toggle. */
gtk_xtext_set_palette (xtext, palette);
gtk_xtext_sync_palette_from_theme (xtext);
return GTK_WIDGET (xtext);
}
@@ -925,12 +798,6 @@ gtk_xtext_cleanup (GtkXText *xtext)
xtext->background_surface = NULL;
}
if (xtext->background_clip_surface)
{
cairo_surface_destroy (xtext->background_clip_surface);
xtext->background_clip_surface = NULL;
}
if (xtext->font)
{
backend_font_close (xtext);
@@ -1460,7 +1327,7 @@ gtk_xtext_draw_marker (GtkXText * xtext, textentry * ent, int y)
}
else if (xtext->buffer->marker_pos == ent->next && ent->next != NULL)
{
render_y = y + xtext->font->descent + xtext->fontsize * ent->subline_count;
render_y = y + xtext->font->descent + xtext->fontsize * g_slist_length (ent->sublines);
}
else return;
@@ -1488,12 +1355,6 @@ gtk_xtext_render (GtkWidget *widget, GdkRectangle *area, cairo_t *cr)
cairo_t *old_cr = xtext->draw_cr;
xtext->draw_cr = cr;
xtext->render_cycle++;
if (xtext->background_clip_surface)
{
cairo_surface_destroy (xtext->background_clip_surface);
xtext->background_clip_surface = NULL;
}
gtk_widget_get_allocation (widget, &allocation);
@@ -1556,11 +1417,6 @@ xit:
gtk_xtext_draw_sep (xtext, -1);
done:
if (xtext->background_clip_surface)
{
cairo_surface_destroy (xtext->background_clip_surface);
xtext->background_clip_surface = NULL;
}
xtext->draw_cr = old_cr;
}
@@ -1944,7 +1800,7 @@ gtk_xtext_scrolldown_timeout (GtkXText * xtext)
xtext->select_start_y -= xtext->fontsize;
xtext->select_start_adj++;
xtext_adj_set_value (adj, xtext_adj_get_value (adj) + 1);
g_signal_emit_by_name (adj, "value-changed");
gtk_adjustment_value_changed (adj);
gtk_xtext_selection_draw (xtext, NULL, TRUE);
gtk_xtext_render_ents (xtext, buf->pagetop_ent->next, buf->last_ent_end);
xtext->scroll_tag = g_timeout_add (gtk_xtext_timeout_ms (xtext, p_y - win_height),
@@ -1988,7 +1844,7 @@ gtk_xtext_scrollup_timeout (GtkXText * xtext)
}
xtext->select_start_y += delta_y;
xtext->select_start_adj = xtext_adj_get_value (adj);
g_signal_emit_by_name (adj, "value-changed");
gtk_adjustment_value_changed (adj);
gtk_xtext_selection_draw (xtext, NULL, TRUE);
gtk_xtext_render_ents (xtext, buf->pagetop_ent->prev, buf->last_ent_end);
xtext->scroll_tag = g_timeout_add (gtk_xtext_timeout_ms (xtext, p_y),
@@ -3113,15 +2969,19 @@ gtk_xtext_text_width (GtkXText *xtext, unsigned char *text, int len)
static int
gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
int len, int *emphasis, int str_width)
int len, int *emphasis)
{
int dofill;
int str_width, dofill;
cairo_surface_t *surface = NULL;
int dest_x = 0, dest_y = 0;
int tile_x = xtext->ts_x;
int tile_y = xtext->ts_y;
GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (xtext));
if (xtext->dont_render || len < 1 || xtext->hidden)
return 0;
if (str_width < 0)
str_width = backend_get_text_width_emph (xtext, str, len, *emphasis);
str_width = backend_get_text_width_emph (xtext, str, len, *emphasis);
if (xtext->dont_render2)
return str_width;
@@ -3140,17 +3000,74 @@ gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
goto dounder;
}
dofill = !xtext->background_surface || xtext->backcolor;
if (!window)
return str_width;
surface = gdk_window_create_similar_surface (window,
CAIRO_CONTENT_COLOR_ALPHA, str_width, xtext->fontsize);
if (surface)
{
dest_x = x;
dest_y = y - xtext->font->ascent;
tile_x = xtext->ts_x - x;
tile_y = xtext->ts_y - dest_y;
x = 0;
y = xtext->font->ascent;
xtext->draw_surface = surface;
}
dofill = TRUE;
/* backcolor is always handled by XDrawImageString */
if (!xtext->backcolor && xtext->background_surface)
{
/* draw the background surface behind the text - CAUSES FLICKER HERE!! */
xtext_draw_bg_offset (xtext, x, y - xtext->font->ascent, str_width,
xtext->fontsize, tile_x, tile_y);
dofill = FALSE; /* already drawn the background */
}
backend_draw_text_emph (xtext, dofill, x, y, str, len, str_width, *emphasis);
if (surface)
{
GdkRectangle clip;
GdkRectangle dest;
cairo_t *cr;
xtext->draw_surface = NULL;
clip.x = xtext->clip_x;
clip.y = xtext->clip_y;
clip.width = xtext->clip_x2 - xtext->clip_x;
clip.height = xtext->clip_y2 - xtext->clip_y;
dest.x = dest_x;
dest.y = dest_y;
dest.width = str_width;
dest.height = xtext->fontsize;
if (gdk_rectangle_intersect (&clip, &dest, &dest))
/* dump the DB to window, but only within the clip_x/x2/y/y2 */
{
cr = xtext_create_context (xtext);
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface (cr, surface, dest_x, dest_y);
cairo_rectangle (cr, dest.x, dest.y, dest.width, dest.height);
cairo_fill (cr);
cairo_restore (cr);
cairo_destroy (cr);
}
cairo_surface_destroy (surface);
}
if (xtext->strikethrough)
{
cairo_t *cr;
int strike_y = y - xtext->font->ascent + (xtext->fontsize / 2);
/* pango_attr_strikethrough_new does not render in the custom widget so we need to reinvent the wheel */
y = dest_y + (xtext->fontsize / 2);
cr = xtext_create_context (xtext);
xtext_draw_line (xtext, cr, &xtext->fgc, x, strike_y, x + str_width - 1, strike_y);
xtext_draw_line (xtext, cr, &xtext->fgc, dest_x, y, dest_x + str_width - 1, y);
cairo_destroy (cr);
}
@@ -3159,10 +3076,17 @@ gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
dounder:
{
cairo_t *cr;
int underline_y = y + 1;
if (surface)
y = dest_y + xtext->font->ascent + 1;
else
{
y++;
dest_x = x;
}
/* draw directly to window, it's out of the range of our DB */
cr = xtext_create_context (xtext);
xtext_draw_line (xtext, cr, &xtext->fgc, x, underline_y, x + str_width - 1, underline_y);
xtext_draw_line (xtext, cr, &xtext->fgc, dest_x, y, dest_x + str_width - 1, y);
cairo_destroy (cr);
}
}
@@ -3170,46 +3094,6 @@ dounder:
return str_width;
}
static int
gtk_xtext_lookup_run_width (textentry *ent, GSList **slp_cache, int off, int len, int emphasis)
{
GSList *lp;
int emph = emphasis & (EMPH_ITAL | EMPH_BOLD);
if (len < 1)
return 0;
lp = slp_cache ? *slp_cache : NULL;
if (!lp)
lp = ent->slp;
while (lp)
{
offlen_t *meta = lp->data;
if (meta->off < off)
{
lp = g_slist_next (lp);
continue;
}
break;
}
if (slp_cache)
*slp_cache = lp;
if (lp)
{
offlen_t *meta = lp->data;
if (meta->off == off && meta->len == len && (meta->emph & (EMPH_ITAL | EMPH_BOLD)) == emph)
return meta->width;
}
return -1;
}
static void
gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
{
@@ -3293,7 +3177,7 @@ gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off)
/* render a single line, which WONT wrap, and parse mIRC colors */
#define RENDER_FLUSH x += gtk_xtext_render_flush (xtext, x, y, pstr, j, emphasis, gtk_xtext_lookup_run_width (ent, &slp_cache, pstr - ent->str, j, *emphasis))
#define RENDER_FLUSH x += gtk_xtext_render_flush (xtext, x, y, pstr, j, emphasis)
static int
gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
@@ -3309,7 +3193,6 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
int k;
int srch_underline = FALSE;
int srch_mark = FALSE;
GSList *slp_cache = ent->slp;
xtext->in_hilight = FALSE;
@@ -3593,8 +3476,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
/* have we been told to stop rendering at this point? */
if (xtext->jump_out_offset > 0 && xtext->jump_out_offset <= (i + offset))
{
gtk_xtext_render_flush (xtext, x, y, pstr, j, emphasis,
gtk_xtext_lookup_run_width (ent, &slp_cache, pstr - ent->str, j, *emphasis));
gtk_xtext_render_flush (xtext, x, y, pstr, j, emphasis);
ret = 0; /* skip the rest of the lines, we're done. */
j = 0;
break;
@@ -3925,6 +3807,18 @@ gtk_xtext_render_line (GtkXText * xtext, textentry * ent, int line,
indent = ent->indent;
start_subline = subline;
/* draw the timestamp */
if (xtext->auto_indent && xtext->buffer->time_stamp &&
(!xtext->skip_stamp || xtext->mark_stamp || xtext->force_stamp))
{
char *time_str;
int len;
len = xtext_get_stamp_str (ent->stamp, &time_str);
gtk_xtext_render_stamp (xtext, ent, time_str, len, line, win_width);
g_free (time_str);
}
/* draw each line one by one */
do
{
@@ -3938,34 +3832,12 @@ gtk_xtext_render_line (GtkXText * xtext, textentry * ent, int line,
y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset;
if (!subline)
{
int bg_x;
int bg_w;
if (!xtext->dont_render)
{
bg_x = MAX (0, xtext->clip_x);
bg_w = MIN (win_width + MARGIN, xtext->clip_x2) - bg_x;
if (bg_w > 0)
xtext_draw_bg (xtext, bg_x, y - xtext->font->ascent, bg_w, xtext->fontsize);
}
if (entline == 1 && xtext->auto_indent && xtext->buffer->time_stamp &&
(!xtext->skip_stamp || xtext->mark_stamp || xtext->force_stamp))
{
char *time_str;
int stamp_len;
stamp_len = xtext_get_stamp_str (ent->stamp, &time_str);
gtk_xtext_render_stamp (xtext, ent, time_str, stamp_len, line, win_width);
g_free (time_str);
}
if (!gtk_xtext_render_str (xtext, y, ent, str, len, win_width,
indent, line, FALSE, NULL, &emphasis))
{
/* small optimization */
gtk_xtext_draw_marker (xtext, ent, y - xtext->fontsize * (taken + start_subline + 1));
return ent->subline_count - subline;
return g_slist_length (ent->sublines) - subline;
}
} else
{
@@ -4038,7 +3910,6 @@ static void
gtk_xtext_recalc_widths (xtext_buffer *buf, int do_str_width)
{
textentry *ent;
int prefix_width;
/* since we have a new font, we have to recalc the text widths */
ent = buf->text_first;
@@ -4050,10 +3921,10 @@ gtk_xtext_recalc_widths (xtext_buffer *buf, int do_str_width)
}
if (ent->left_len != -1)
{
prefix_width = gtk_xtext_text_width (buf->xtext, ent->str,
ent->left_len + 1);
ent->indent =
(buf->indent - prefix_width);
(buf->indent -
gtk_xtext_text_width (buf->xtext, ent->str,
ent->left_len)) - buf->xtext->space_width;
if (ent->indent < MARGIN)
ent->indent = MARGIN;
}
@@ -4102,12 +3973,6 @@ gtk_xtext_set_background (GtkXText * xtext, cairo_surface_t *surface)
xtext->background_surface = NULL;
}
if (xtext->background_clip_surface)
{
cairo_surface_destroy (xtext->background_clip_surface);
xtext->background_clip_surface = NULL;
}
dontscroll (xtext->buffer);
if (surface)
{
@@ -4150,14 +4015,12 @@ gtk_xtext_lines_taken (xtext_buffer *buf, textentry * ent)
g_slist_free (ent->sublines);
ent->sublines = NULL;
ent->subline_count = 0;
win_width = buf->window_width - MARGIN;
if (win_width >= ent->indent + ent->str_width)
{
ent->sublines = g_slist_append (ent->sublines, GINT_TO_POINTER (ent->str_len));
ent->subline_count = 1;
return ent->subline_count;
return 1;
}
indent = ent->indent;
@@ -4172,8 +4035,7 @@ gtk_xtext_lines_taken (xtext_buffer *buf, textentry * ent)
}
while (str < ent->str + ent->str_len);
ent->subline_count = g_slist_length (ent->sublines);
return ent->subline_count;
return g_slist_length (ent->sublines);
}
/* Calculate number of actual lines (with wraps), to set adj->lower. *
@@ -4250,7 +4112,7 @@ gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
ent = ent->prev;
if (!ent)
break;
lines -= ent->subline_count;
lines -= g_slist_length (ent->sublines);
}
return NULL;
}
@@ -4259,10 +4121,10 @@ gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
while (ent)
{
lines += ent->subline_count;
lines += g_slist_length (ent->sublines);
if (lines > line)
{
*subline = ent->subline_count - (lines - line);
*subline = g_slist_length (ent->sublines) - (lines - line);
return ent;
}
ent = ent->next;
@@ -4356,7 +4218,7 @@ gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb)
line -= subline;
subline = 0;
}
line += ent->subline_count;
line += g_slist_length (ent->sublines);
}
if (ent == entb)
@@ -4441,7 +4303,7 @@ gtk_xtext_render_page (GtkXText * xtext)
xtext->buffer->last_pixel_pos = pos;
#ifndef __APPLE__
if (abs (overlap) < height)
if (!xtext->background_surface && abs (overlap) < height)
{
GdkRectangle area;
cairo_t *cr;
@@ -4590,8 +4452,6 @@ gtk_xtext_kill_ent (xtext_buffer *buffer, textentry *ent)
g_slist_free_full (ent->slp, g_free);
g_slist_free (ent->sublines);
ent->sublines = NULL;
ent->subline_count = 0;
g_free (ent);
return visible;
@@ -4607,22 +4467,22 @@ gtk_xtext_remove_top (xtext_buffer *buffer)
ent = buffer->text_first;
if (!ent)
return;
buffer->num_lines -= ent->subline_count;
buffer->pagetop_line -= ent->subline_count;
buffer->last_pixel_pos -= (ent->subline_count * buffer->xtext->fontsize);
buffer->num_lines -= g_slist_length (ent->sublines);
buffer->pagetop_line -= g_slist_length (ent->sublines);
buffer->last_pixel_pos -= (g_slist_length (ent->sublines) * buffer->xtext->fontsize);
buffer->text_first = ent->next;
if (buffer->text_first)
buffer->text_first->prev = NULL;
else
buffer->text_last = NULL;
buffer->old_value -= ent->subline_count;
buffer->old_value -= g_slist_length (ent->sublines);
if (buffer->xtext->buffer == buffer) /* is it the current buffer? */
{
xtext_adj_set_value (buffer->xtext->adj,
xtext_adj_get_value (buffer->xtext->adj) -
ent->subline_count);
buffer->xtext->select_start_adj -= ent->subline_count;
g_slist_length (ent->sublines));
buffer->xtext->select_start_adj -= g_slist_length (ent->sublines);
}
if (gtk_xtext_kill_ent (buffer, ent))
@@ -4652,7 +4512,7 @@ gtk_xtext_remove_bottom (xtext_buffer *buffer)
ent = buffer->text_last;
if (!ent)
return;
buffer->num_lines -= ent->subline_count;
buffer->num_lines -= g_slist_length (ent->sublines);
buffer->text_last = ent->prev;
if (buffer->text_last)
buffer->text_last->next = NULL;
@@ -4778,7 +4638,7 @@ gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add)
lines = ((height + xtext->pixel_offset) / xtext->fontsize) + buf->pagetop_subline + add;
while (ent)
{
lines -= ent->subline_count;
lines -= g_slist_length (ent->sublines);
if (lines <= 0)
{
return FALSE;
@@ -5165,7 +5025,7 @@ gtk_xtext_search (GtkXText * xtext, const gchar *text, gtk_xtext_search_flags fl
for (value = 0, ent = buf->text_first;
ent && ent != buf->hintsearch; ent = ent->next)
{
value += ent->subline_count;
value += g_slist_length (ent->sublines);
}
if (value > xtext_adj_get_upper (adj) - xtext_adj_get_page_size (adj))
{
@@ -5174,7 +5034,7 @@ gtk_xtext_search (GtkXText * xtext, const gchar *text, gtk_xtext_search_flags fl
else if ((flags & backward) && ent)
{
value -= xtext_adj_get_page_size (adj) -
ent->subline_count;
g_slist_length (ent->sublines);
if (value < 0)
{
value = 0;
@@ -5251,7 +5111,6 @@ gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp)
ent->mark_end = -1;
ent->next = NULL;
ent->marks = NULL;
ent->subline_count = 0;
if (ent->indent < MARGIN)
ent->indent = MARGIN; /* 2 pixels is the left margin */
@@ -5335,7 +5194,7 @@ gtk_xtext_append_indent (xtext_buffer *buf,
unsigned char *str;
int space;
int tempindent;
int prefix_width;
int left_width;
int min_indent;
if (left_len == -1)
@@ -5360,12 +5219,12 @@ gtk_xtext_append_indent (xtext_buffer *buf,
memcpy (str + left_len + 1, right_text, right_len);
str[left_len + 1 + right_len] = 0;
prefix_width = gtk_xtext_text_width (buf->xtext, str, left_len + 1);
left_width = gtk_xtext_text_width (buf->xtext, left_text, left_len);
ent->left_len = left_len;
ent->str = str;
ent->str_len = left_len + 1 + right_len;
ent->indent = buf->indent - prefix_width;
ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
/* This is copied into the scratch buffer later, double check math */
g_assert (ent->str_len < sizeof (buf->xtext->scratch_buffer));
@@ -5382,7 +5241,7 @@ gtk_xtext_append_indent (xtext_buffer *buf,
buf->indent < buf->xtext->max_auto_indent &&
ent->indent < min_indent)
{
tempindent = min_indent + prefix_width;
tempindent = min_indent + buf->xtext->space_width + left_width;
/* Ignore tiny one-pixel style nudges.
* They can trigger expensive full-width recalculations and are
@@ -5393,12 +5252,12 @@ gtk_xtext_append_indent (xtext_buffer *buf,
if (buf->indent > buf->xtext->max_auto_indent)
buf->indent = buf->xtext->max_auto_indent;
if (buf->indent > ent->indent + prefix_width)
if (buf->indent > ent->indent + left_width + buf->xtext->space_width)
{
gtk_xtext_fix_indent (buf);
gtk_xtext_recalc_widths (buf, FALSE);
ent->indent = buf->indent - prefix_width;
ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
buf->xtext->force_render = TRUE;
}
}
@@ -5598,7 +5457,7 @@ gtk_xtext_moveto_marker_pos (GtkXText *xtext)
{
if (ent == buf->marker_pos)
break;
value += ent->subline_count;
value += g_slist_length (ent->sublines);
ent = ent->next;
}
if (value >= xtext_adj_get_value (adj) &&
@@ -5736,7 +5595,7 @@ gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render)
xtext_adjustment_apply (xtext->adj, lower, upper, value, page_size);
gtk_adjustment_set_page_increment (xtext->adj, page_size);
g_signal_emit_by_name (xtext->adj, "value-changed");
gtk_adjustment_value_changed (xtext->adj);
}
}
}

View File

@@ -135,16 +135,9 @@ struct _GtkXText
GtkAdjustment *adj;
cairo_surface_t *background_surface; /* 0 = use palette[19] */
cairo_surface_t *background_clip_surface;
GdkWindow *draw_window; /* points to ->window */
cairo_surface_t *draw_surface; /* temporary surface for offscreen draws */
cairo_t *draw_cr; /* GTK3 draw context */
int background_clip_x;
int background_clip_y;
int background_clip_width;
int background_clip_height;
int background_clip_cycle;
int render_cycle;
GdkCursor *hand_cursor;
GdkCursor *resize_cursor;

View File

@@ -5,6 +5,10 @@
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -23,17 +27,31 @@
<TargetName>zoitechat-text</TargetName>
<OutDir>$(ZoiteChatRel)</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_CONSOLE;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ZoiteChatLib);$(DepsRoot)\include;$(OpenSslInclude);$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ZoiteChatLib);$(DepsRoot)\include;$(OpenSslInclude);$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;comsupp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_CONSOLE;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ZoiteChatLib);$(DepsRoot)\include;$(OpenSslInclude);$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;comsupp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>

View File

@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -28,7 +32,7 @@
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\common;$(ZoiteChatLib);$(DepsRoot)\include\enchant;$(DepsRoot)\include;$(OpenSslInclude);$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\common;$(ZoiteChatLib);$(DepsRoot)\include\enchant;$(DepsRoot)\include;$(OpenSslInclude);$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>

View File

@@ -39,16 +39,3 @@ You can reset overrides after testing:
```bash
flatpak override --user --reset net.zoite.Zoitechat
```
## Theme palette migration and legacy keys
New builds store theme colors as semantic token keys in `colors.conf` (for example `theme.mode.light.token.mirc_0`).
When loading old configs that only contain legacy keys (`color_*` and `dark_color_*`), ZoiteChat migrates them automatically if `theme.palette.semantic_migrated` is not present.
By default saves write both semantic token keys and legacy keys for compatibility. To phase out legacy writes, set:
```bash
export ZOITECHAT_THEME_WRITE_LEGACY_KEYS=0
```
With that setting, saves write semantic token keys only while legacy-key loading remains available for older config files that have not yet been marked as migrated.

View File

@@ -5,6 +5,10 @@
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -26,7 +30,7 @@
<None Include="changelog.url" />
<None Include="readme.url" />
<None Include="$(DepsRoot)\bin\$(LuaRuntimeDll)" Condition="'$(LuaEnabled)'=='true' and '$(LuaRuntimeDll)'!=''" />
<None Include="$(DepsRoot)\bin\lua51.dll" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="Build">
@@ -63,15 +67,17 @@
<None Include="$(DepsRoot)\bin\*pangocairo-1.0-0.dll" />
<None Include="$(DepsRoot)\bin\*pangoft2-1.0-0.dll" />
<None Include="$(DepsRoot)\bin\*pangowin32-1.0-0.dll" />
<None Include="$(DepsRoot)\bin\*rsvg*.dll" />
<None Include="$(DepsRoot)\bin\*zlib*.dll" />
<None Include="$(DepsRoot)\bin\*girepository*.dll" Condition="'$(LuaEnabled)'=='true' and '$(LuaLibDir)'!=''" />
<None Include="$(Python3Path)\Lib\site-packages\_cffi_backend.*.pyd" Condition="'$(Python3Enabled)'=='true'" />
<LuaLib Include="$(LuaLibDir)\**\*.dll" Condition="'$(LuaEnabled)'=='true' and '$(LuaLibDir)'!=''" />
<LuaShare Include="$(LuaShareDir)\*.lua" Condition="'$(LuaEnabled)'=='true' and '$(LuaShareDir)'!=''" />
<LuaShare Include="$(LuaShareDir)\**\*.lua" Condition="'$(LuaEnabled)'=='true' and '$(LuaShareDir)'!=''" />
<Typelib Include="$(DepsRoot)\lib\girepository-1.0\*.typelib" Condition="'$(LuaEnabled)'=='true' and '$(LuaLibDir)'!=''" />
<None Include="$(DepsRoot)\bin\luajit*.dll" />
<None Include="$(DepsRoot)\bin\*girepository*.dll" />
<None Include="$(Python3Path)\Lib\site-packages\_cffi_backend.*.pyd" />
<LuaLib Include="$(DepsRoot)\lib\lua\**\*.dll" />
<LuaShare Include="$(DepsRoot)\share\lua\*.lua" />
<LuaShare Include="$(DepsRoot)\share\lua\**\*.lua" />
<LuaShare Include="$(DepsRoot)\share\lua\**\**\*.lua" />
<Typelib Include="$(DepsRoot)\lib\girepository-1.0\*.typelib" />
<EnchantProviders Include="$(DepsRoot)\lib\enchant\*.dll" />
<Gtk3Immodules Include="$(DepsRoot)\lib\gtk-3.0\3.0.0\immodules\**\*" />
@@ -82,10 +88,11 @@
<FontConfig Include="$(DepsRoot)\etc\fonts\*" />
<Share Include="share\**\*" />
<DepsRootDocs Include="$(DepsRoot)\share\doc\**\*" />
<DepsRootIcons Include="$(DepsRoot)\share\icons\**\*" />
<ZoiteChatMenuIcons Include="..\..\data\icons\menu\**\*" />
<Locale Include="$(ZoiteChatBin)locale\**\*;$(DepsRoot)\share\locale\**\*" />
<MSWindowsTheme Include="$(DepsRoot)\share\themes\MS-Windows\**\*" />
<HicolorIcons Include="$(DepsRoot)\share\icons\hicolor\index.theme" />
<HicolorIcons Include="$(DepsRoot)\share\icons\hicolor\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(None)" DestinationFolder="$(ZoiteChatRel)" />
@@ -96,21 +103,23 @@
<Copy SourceFiles="@(GdkPixbufLoaderCache)" DestinationFiles="@(GdkPixbufLoaderCache->'$(ZoiteChatRel)\lib\gdk-pixbuf-2.0\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(GSettingsSchemas)" DestinationFiles="@(GSettingsSchemas->'$(ZoiteChatRel)\share\glib-2.0\schemas\%(Filename)%(Extension)')" />
<Copy SourceFiles="@(Share)" DestinationFiles="@(Share->'$(ZoiteChatRel)\share\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(DepsRootIcons)" DestinationFiles="@(DepsRootIcons->'$(ZoiteChatRel)\share\icons\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(ZoiteChatMenuIcons)" DestinationFiles="@(ZoiteChatMenuIcons->'$(ZoiteChatRel)\share\icons\menu\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="..\..\COPYING" DestinationFolder="$(ZoiteChatRel)\share\doc\zoitechat" />
<Copy SourceFiles="$(WinSparklePath)\COPYING" DestinationFolder="$(ZoiteChatRel)\share\doc\WinSparkle" />
<Copy SourceFiles="@(EnchantProviders)" DestinationFolder="$(ZoiteChatRel)\lib\enchant" />
<Copy SourceFiles="@(Locale)" DestinationFiles="@(Locale->'$(ZoiteChatRel)\share\locale\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(MSWindowsTheme)" DestinationFiles="@(MSWindowsTheme->'$(ZoiteChatRel)\share\themes\MS-Windows\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(HicolorIcons)" DestinationFiles="@(HicolorIcons->'$(ZoiteChatRel)\share\icons\hicolor\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(LuaShare)" DestinationFiles="@(LuaShare->'$(ZoiteChatRel)\share\lua\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(LuaLib)" DestinationFiles="@(LuaLib->'$(ZoiteChatRel)\lib\lua\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(Typelib)" DestinationFiles="@(Typelib->'$(ZoiteChatRel)\lib\girepository-1.0\%(Filename)%(Extension)')" />
<Copy SourceFiles="..\..\plugins\python\xchat.py" DestinationFolder="$(ZoiteChatRel)\python" Condition="'$(Python3Enabled)'=='true'" />
<Copy SourceFiles="..\..\plugins\python\hexchat.py" DestinationFolder="$(ZoiteChatRel)\python" Condition="'$(Python3Enabled)'=='true'" />
<Copy SourceFiles="..\..\plugins\python\zoitechat.py" DestinationFolder="$(ZoiteChatRel)\python" Condition="'$(Python3Enabled)'=='true'" />
<Copy SourceFiles="..\..\plugins\python\_zoitechat.py" DestinationFolder="$(ZoiteChatRel)\python" Condition="'$(Python3Enabled)'=='true'" />
<Copy SourceFiles="..\..\plugins\python\xchat.py" DestinationFolder="$(ZoiteChatRel)\python" />
<Copy SourceFiles="..\..\plugins\python\hexchat.py" DestinationFolder="$(ZoiteChatRel)\python" />
<Copy SourceFiles="..\..\plugins\python\zoitechat.py" DestinationFolder="$(ZoiteChatRel)\python" />
<Copy SourceFiles="..\..\plugins\python\_zoitechat.py" DestinationFolder="$(ZoiteChatRel)\python" />
<WriteLinesToFile File="$(ZoiteChatRel)portable-mode" Lines="2" Overwrite="true" />
<Copy SourceFiles="@(DepsRootDocs)" DestinationFiles="@(DepsRootDocs->'$(ZoiteChatRel)\share\doc\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>
</Project>

View File

@@ -5,6 +5,10 @@
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>

View File

@@ -22,21 +22,35 @@ AppUpdatesURL=http://zoitechat.zoite.net/downloads.html
LicenseFile=share\doc\zoitechat\COPYING
UninstallDisplayIcon={app}\zoitechat.exe
UninstallDisplayName=ZoiteChat
#if APPARCH == "x64"
DefaultDirName={pf64}\ZoiteChat
#else
DefaultDirName={pf32}\ZoiteChat
#endif
DisableDirPage=no
UsePreviousAppDir=no
DefaultGroupName=ZoiteChat
AllowNoIcons=yes
SolidCompression=yes
Compression=lzma2/ultra64
SourceDir=..\rel
OutputDir=..
#if APPARCH == "x64"
OutputBaseFilename={#APPNAM}-{#APPVER}_x64
#else
OutputBaseFilename={#APPNAM}-{#APPVER}_x86
#endif
FlatComponentsList=no
PrivilegesRequired=none
ShowComponentSizes=no
CreateUninstallRegKey=not IsTaskSelected('portable')
Uninstallable=not IsTaskSelected('portable')
#if APPARCH == "x64"
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
#else
ArchitecturesAllowed=x86 x64
#endif
MinVersion=6.1
WizardImageFile={#PROJECTDIR}wizardimage.bmp
WizardSmallImageFile={#PROJECTDIR}wizardsmallimage.bmp
@@ -87,6 +101,7 @@ Root: HKCR; Subkey: "ZoiteChat.Theme\shell\open\command"; ValueType: string; Val
Filename: "{app}\zoitechat.exe"; Description: "Run ZoiteChat after closing the Wizard"; Flags: nowait postinstall skipifsilent
Filename: "http://docs.zoitechat.org/en/latest/changelog.html"; Description: "See what's changed"; Flags: shellexec runasoriginaluser postinstall skipifsilent unchecked
Filename: "{tmp}\vcredist.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Flags: skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\vcredist2013.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Flags: skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\perl.msi"; StatusMsg: "Installing Perl"; Components: langs\perl; Flags: shellexec skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\python.msi"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\python.exe"; Parameters: "InstallAllUsers=1 PrependPath=1"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable
@@ -99,11 +114,10 @@ Source: "changelog.url"; DestDir: "{app}"; Flags: ignoreversion; Components: lib
Source: "readme.url"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "cert.pem"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "share\xml\*"; DestDir: "{app}\share\xml"; Flags: ignoreversion createallsubdirs recursesubdirs; Components: libs
Source: "share\doc\zoitechat\*"; DestDir: "{app}\share\doc\zoitechat"; Flags: ignoreversion createallsubdirs recursesubdirs; Components: libs
Source: "share\doc\WinSparkle\*"; DestDir: "{app}\share\doc\WinSparkle"; Flags: ignoreversion createallsubdirs recursesubdirs; Components: libs
Source: "share\doc\*"; DestDir: "{app}\share\doc"; Flags: ignoreversion createallsubdirs recursesubdirs; Components: libs
Source: "share\icons\*"; DestDir: "{app}\share\icons"; Flags: ignoreversion createallsubdirs recursesubdirs; Components: libs
Source: "share\themes\MS-Windows\*"; DestDir: "{app}\share\themes\MS-Windows"; Flags: ignoreversion createallsubdirs recursesubdirs skipifsourcedoesntexist; Components: libs
Source: "share\glib-2.0\schemas\*"; DestDir: "{app}\share\glib-2.0\schemas"; Flags: ignoreversion createallsubdirs recursesubdirs skipifsourcedoesntexist; Components: libs
Source: "share\icons\hicolor\*"; DestDir: "{app}\share\icons\hicolor"; Flags: ignoreversion createallsubdirs recursesubdirs skipifsourcedoesntexist; Components: libs
Source: "share\locale\*"; DestDir: "{app}\share\locale"; Flags: ignoreversion createallsubdirs recursesubdirs; Components: translations
Source: "etc\fonts\*"; DestDir: "{app}\etc\fonts"; Flags: ignoreversion createallsubdirs recursesubdirs; Components: libs
@@ -148,26 +162,18 @@ Source: "plugins\hcnotifications-winrt.dll"; DestDir: "{app}\plugins"; Flags: ig
Source: "lib\enchant\*"; DestDir: "{app}\lib\enchant"; Flags: ignoreversion; Components: libs
Source: "girepository-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "lua51.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "luajit-5.1.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "luajit.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "lib\lua\2.1\lgi\*.dll"; DestDir: "{app}\lib\lua\lgi"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "lib\lua\5.1\lgi\*.dll"; DestDir: "{app}\lib\lua\lgi"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "lib\girepository-1.0\*.typelib"; DestDir: "{app}\lib\girepository-1.0"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "share\lua\2.1\*.lua"; DestDir: "{app}\share\lua"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "share\lua\2.1\lgi\*.lua"; DestDir: "{app}\share\lua\lgi"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "share\lua\2.1\lgi\override\*.lua"; DestDir: "{app}\share\lua\lgi\override"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "share\lua\5.1\*.lua"; DestDir: "{app}\share\lua"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "share\lua\5.1\lgi\*.lua"; DestDir: "{app}\share\lua\lgi"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "share\lua\5.1\lgi\override\*.lua"; DestDir: "{app}\share\lua\lgi\override"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "girepository-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\lua
Source: "lua51.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\lua
Source: "lib\lua\2.1\lgi\*.dll"; DestDir: "{app}\lib\lua\lgi"; Flags: ignoreversion; Components: langs\lua
Source: "lib\girepository-1.0\*.typelib"; DestDir: "{app}\lib\girepository-1.0"; Flags: ignoreversion; Components: langs\lua
Source: "share\lua\2.1\*.lua"; DestDir: "{app}\share\lua"; Flags: ignoreversion; Components: langs\lua
Source: "share\lua\2.1\lgi\*.lua"; DestDir: "{app}\share\lua\lgi"; Flags: ignoreversion; Components: langs\lua
Source: "share\lua\2.1\lgi\override\*.lua"; DestDir: "{app}\share\lua\lgi\override"; Flags: ignoreversion; Components: langs\lua
Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
Source: "plugins\hcchecksum.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\checksum
Source: "plugins\hcexec.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\exec
Source: "plugins\hcfishlim.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\fishlim
Source: "share\gtkpref.png"; DestDir: "{app}\share"; Flags: ignoreversion; Components: libs
Source: "share\adwaita-icons-attribution.txt"; DestDir: "{app}\share"; Flags: ignoreversion; Components: libs
Source: "share\music.png"; DestDir: "{app}\share"; Flags: ignoreversion; Components: plugins\winamp
Source: "share\download.png"; DestDir: "{app}\share"; Flags: ignoreversion; Components: plugins\upd
Source: "plugins\hcupd.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\upd
@@ -227,7 +233,14 @@ end;
/////////////////////////////////////////////////////////////////////
function GetSysDir(): String;
begin
#if APPARCH != "x64"
if IsWin64 then
Result := ExpandConstant('{syswow64}\')
else
Result := ExpandConstant('{sys}\');
#else
Result := ExpandConstant('{sys}\');
#endif
end;
/////////////////////////////////////////////////////////////////////
@@ -248,6 +261,11 @@ begin
Result := FileExists(GetSysDir() + 'vcruntime140.dll');;
end;
function CheckVC2013Install(): Boolean;
begin
Result := FileExists(GetSysDir() + 'msvcr120.dll');;
end;
/////////////////////////////////////////////////////////////////////
function CheckSpellInstall(): Boolean;
@@ -269,7 +287,9 @@ end;
procedure CurPageChanged(CurPageID: Integer);
var
REDIST: String;
REDIST_2013: String;
PERL: String;
PY2: String;
PY3: String;
SPELL: String;
begin
@@ -280,9 +300,17 @@ begin
if not IsTaskSelected('portable') then
begin
#if APPARCH == "x64"
REDIST := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/vcredist_2015_x64.exe';
REDIST_2013 := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/vcredist_2013_x64.exe';
PERL := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/Perl.5.20.0.x64.msi';
PY3 := 'https://www.python.org/ftp/python/3.14.2/python-3.14.2-amd64.exe';
#else
REDIST := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/vcredist_2015_x86.exe';
REDIST_2013 := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/vcredist_2013_x86.exe';
PERL := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/Perl.5.20.0.x86.msi';
PY3 := 'https://www.python.org/ftp/python/3.14.2/python-3.14.2.exe';
#endif
SPELL := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/ZoiteChat.Spelling.Dictionaries.r2.exe';
if not CheckVCInstall() then
@@ -295,6 +323,9 @@ begin
begin
if IsComponentSelected('langs\perl') and not CheckDLL('perl520.dll') then
begin
if not CheckVC2013Install() then
idpAddFile(REDIST_2013, ExpandConstant('{tmp}\vcredist2013.exe'));
idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi'))
end;
@@ -312,7 +343,11 @@ function NextButtonClick(CurPageID: Integer): Boolean;
begin
if (CurPageID = wpSelectTasks) then
if (WizardForm.TasksList.Checked[1] = True) then
#if APPARCH == "x64"
if (WizardDirValue() = ExpandConstant('{pf64}\ZoiteChat')) then
#else
if (WizardDirValue() = ExpandConstant('{pf32}\ZoiteChat')) then
#endif
begin
WizardForm.TasksList.Checked[1] := False
MsgBox('Portable mode is only intended for use on portable drives and has been disabled.', mbInformation, MB_OK)
@@ -329,7 +364,11 @@ var
sUnInstPath: String;
sUnInstallString: String;
begin
#if APPARCH == "x64"
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\ZoiteChat (x64)_is1');
#else
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\ZoiteChat (x86)_is1');
#endif
sUnInstallString := '';
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);

View File

@@ -5,6 +5,10 @@
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>

View File

@@ -113,22 +113,7 @@
<PngLib Condition="'$(PngLib)'=='' and Exists('$(DepsRoot)\lib\libpng16_static.lib')">libpng16_static.lib</PngLib>
<PngLib Condition="'$(PngLib)'=='' and Exists('$(DepsRoot)\lib\libpng.lib')">libpng.lib</PngLib>
<ArchiveLibDir Condition="'$(ArchiveLibDir)'==''">$(DepsRoot)\lib</ArchiveLibDir>
<ArchiveInclude Condition="Exists('$(DepsRoot)\include\archive.h') and Exists('$(DepsRoot)\include\archive_entry.h')">$(DepsRoot)\include</ArchiveInclude>
<ArchiveInclude Condition="'$(ArchiveInclude)'=='' and Exists('$(DepsRoot)\include\archive\archive.h')">$(DepsRoot)\include\archive</ArchiveInclude>
<ArchiveInclude Condition="'$(ArchiveInclude)'=='' and Exists('$(DepsRoot)\include\libarchive\archive.h')">$(DepsRoot)\include\libarchive</ArchiveInclude>
<ArchiveLib Condition="Exists('$(ArchiveLibDir)\archive.lib')">archive.lib</ArchiveLib>
<ArchiveLib Condition="'$(ArchiveLib)'=='' and Exists('$(ArchiveLibDir)\libarchive.lib')">libarchive.lib</ArchiveLib>
<ArchiveLib Condition="'$(ArchiveLib)'=='' and Exists('$(ArchiveLibDir)\archive-13.lib')">archive-13.lib</ArchiveLib>
<ArchiveLib Condition="'$(ArchiveLib)'=='' and Exists('$(ArchiveLibDir)\libarchive-13.lib')">libarchive-13.lib</ArchiveLib>
<ArchiveLib Condition="'$(ArchiveLib)'=='' and Exists('$(ArchiveLibDir)\archive_static.lib')">archive_static.lib</ArchiveLib>
<ArchiveLib Condition="'$(ArchiveLib)'=='' and Exists('$(ArchiveLibDir)\libarchive_static.lib')">libarchive_static.lib</ArchiveLib>
<ArchiveDefs Condition="'$(ArchiveLib)'=='archive_static.lib' or '$(ArchiveLib)'=='libarchive_static.lib'">LIBARCHIVE_STATIC</ArchiveDefs>
<DepLibs>$(Gtk3Lib);$(Gdk3Lib);wininet.lib;winmm.lib;ws2_32.lib;atk-1.0.lib;gio-2.0.lib;gdk_pixbuf-2.0.lib;pangowin32-1.0.lib;pangocairo-1.0.lib;pango-1.0.lib;cairo.lib;gobject-2.0.lib;gmodule-2.0.lib;glib-2.0.lib;$(IntlLib);$(IconvLib);$(ZlibLib);$(Xml2Lib);$(JpegLib);$(PngLib);$(ArchiveLib);$(OpenSslLibs)</DepLibs>
<DepLibs>$(Gtk3Lib);$(Gdk3Lib);wininet.lib;winmm.lib;ws2_32.lib;atk-1.0.lib;gio-2.0.lib;gdk_pixbuf-2.0.lib;pangowin32-1.0.lib;pangocairo-1.0.lib;pango-1.0.lib;cairo.lib;gobject-2.0.lib;gmodule-2.0.lib;glib-2.0.lib;$(IntlLib);$(IconvLib);$(ZlibLib);$(Xml2Lib);$(JpegLib);$(PngLib);$(OpenSslLibs)</DepLibs>
<DataDir>$(SolutionDir)..\data\\</DataDir>
<ZoiteChatBuild>$(SolutionDir)..\..\zoitechat-build</ZoiteChatBuild>
@@ -175,10 +160,10 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PreProcessorDefinitions>NTDDI_VERSION=NTDDI_WIN8;_WIN32_WINNT=_WIN32_WINNT_WIN8;$(ArchiveDefs);%(PreProcessorDefinitions)</PreProcessorDefinitions>
<PreProcessorDefinitions>NTDDI_VERSION=NTDDI_WIN8;_WIN32_WINNT=_WIN32_WINNT_WIN8;%(PreProcessorDefinitions)</PreProcessorDefinitions>
</ClCompile>
<Lib>
<LinkTimeCodeGeneration>false</LinkTimeCodeGeneration>
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
</Lib>
<Link>
<ImportLibrary>$(ZoiteChatLib)$(TargetName).lib</ImportLibrary>
@@ -187,7 +172,7 @@
<GenerateDebugInformation>Debug</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>

View File

@@ -104,41 +104,76 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lua", "..\plugins\lua\lua.v
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{87554B59-006C-4D94-9714-897B27067BA3}.Release|Win32.ActiveCfg = Release|Win32
{87554B59-006C-4D94-9714-897B27067BA3}.Release|Win32.Build.0 = Release|Win32
{87554B59-006C-4D94-9714-897B27067BA3}.Release|x64.ActiveCfg = Release|x64
{87554B59-006C-4D94-9714-897B27067BA3}.Release|x64.Build.0 = Release|x64
{E4BDB4C8-2335-415A-ACEE-BA88B19BFE82}.Release|Win32.ActiveCfg = Release|Win32
{E4BDB4C8-2335-415A-ACEE-BA88B19BFE82}.Release|Win32.Build.0 = Release|Win32
{E4BDB4C8-2335-415A-ACEE-BA88B19BFE82}.Release|x64.ActiveCfg = Release|x64
{E4BDB4C8-2335-415A-ACEE-BA88B19BFE82}.Release|x64.Build.0 = Release|x64
{E93E1255-95D1-4B08-8FDF-B53CC6A21280}.Release|Win32.ActiveCfg = Release|Win32
{E93E1255-95D1-4B08-8FDF-B53CC6A21280}.Release|Win32.Build.0 = Release|Win32
{E93E1255-95D1-4B08-8FDF-B53CC6A21280}.Release|x64.ActiveCfg = Release|x64
{E93E1255-95D1-4B08-8FDF-B53CC6A21280}.Release|x64.Build.0 = Release|x64
{5EF7F47D-D09C-43C4-BF64-B28B11A0FF91}.Release|Win32.ActiveCfg = Release|Win32
{5EF7F47D-D09C-43C4-BF64-B28B11A0FF91}.Release|Win32.Build.0 = Release|Win32
{5EF7F47D-D09C-43C4-BF64-B28B11A0FF91}.Release|x64.ActiveCfg = Release|x64
{5EF7F47D-D09C-43C4-BF64-B28B11A0FF91}.Release|x64.Build.0 = Release|x64
{17E4BE39-76F7-4A06-AD21-EFD0C5091F76}.Release|Win32.ActiveCfg = Release|Win32
{17E4BE39-76F7-4A06-AD21-EFD0C5091F76}.Release|Win32.Build.0 = Release|Win32
{17E4BE39-76F7-4A06-AD21-EFD0C5091F76}.Release|x64.ActiveCfg = Release|x64
{17E4BE39-76F7-4A06-AD21-EFD0C5091F76}.Release|x64.Build.0 = Release|x64
{3C4F42FC-292A-420B-B63D-C03DFBDD8E4E}.Release|Win32.ActiveCfg = Release|Win32
{3C4F42FC-292A-420B-B63D-C03DFBDD8E4E}.Release|Win32.Build.0 = Release|Win32
{3C4F42FC-292A-420B-B63D-C03DFBDD8E4E}.Release|x64.ActiveCfg = Release|x64
{3C4F42FC-292A-420B-B63D-C03DFBDD8E4E}.Release|x64.Build.0 = Release|x64
{461DC24A-A410-4171-8C02-CCDBF3702C2A}.Release|Win32.ActiveCfg = Release|Win32
{461DC24A-A410-4171-8C02-CCDBF3702C2A}.Release|Win32.Build.0 = Release|Win32
{461DC24A-A410-4171-8C02-CCDBF3702C2A}.Release|x64.ActiveCfg = Release|x64
{461DC24A-A410-4171-8C02-CCDBF3702C2A}.Release|x64.Build.0 = Release|x64
{E78C0D9A-798E-4BF6-B0CC-6FECB8CA2FCE}.Release|Win32.ActiveCfg = Release|Win32
{E78C0D9A-798E-4BF6-B0CC-6FECB8CA2FCE}.Release|Win32.Build.0 = Release|Win32
{E78C0D9A-798E-4BF6-B0CC-6FECB8CA2FCE}.Release|x64.ActiveCfg = Release|x64
{E78C0D9A-798E-4BF6-B0CC-6FECB8CA2FCE}.Release|x64.Build.0 = Release|x64
{6C0CA980-97C5-427A-BE61-5BCECAFABBDA}.Release|Win32.ActiveCfg = Release|Win32
{6C0CA980-97C5-427A-BE61-5BCECAFABBDA}.Release|Win32.Build.0 = Release|Win32
{6C0CA980-97C5-427A-BE61-5BCECAFABBDA}.Release|x64.ActiveCfg = Release|x64
{6C0CA980-97C5-427A-BE61-5BCECAFABBDA}.Release|x64.Build.0 = Release|x64
{B10A2C41-344C-43E0-A32D-B9587C198D8B}.Release|Win32.ActiveCfg = Release|Win32
{B10A2C41-344C-43E0-A32D-B9587C198D8B}.Release|Win32.Build.0 = Release|Win32
{B10A2C41-344C-43E0-A32D-B9587C198D8B}.Release|x64.ActiveCfg = Release|x64
{B10A2C41-344C-43E0-A32D-B9587C198D8B}.Release|x64.Build.0 = Release|x64
{C9B735E4-75BC-45AC-A5E3-39A6D076F912}.Release|Win32.ActiveCfg = Release|Win32
{C9B735E4-75BC-45AC-A5E3-39A6D076F912}.Release|Win32.Build.0 = Release|Win32
{C9B735E4-75BC-45AC-A5E3-39A6D076F912}.Release|x64.ActiveCfg = Release|x64
{C9B735E4-75BC-45AC-A5E3-39A6D076F912}.Release|x64.Build.0 = Release|x64
{5A0F4962-E670-4DA2-9E45-52CC47F26E2F}.Release|Win32.ActiveCfg = Release|Win32
{5A0F4962-E670-4DA2-9E45-52CC47F26E2F}.Release|Win32.Build.0 = Release|Win32
{5A0F4962-E670-4DA2-9E45-52CC47F26E2F}.Release|x64.ActiveCfg = Release|x64
{5A0F4962-E670-4DA2-9E45-52CC47F26E2F}.Release|x64.Build.0 = Release|x64
{D90BC3E3-1341-4849-9354-5F40489D39D1}.Release|Win32.ActiveCfg = Release|Win32
{D90BC3E3-1341-4849-9354-5F40489D39D1}.Release|Win32.Build.0 = Release|Win32
{D90BC3E3-1341-4849-9354-5F40489D39D1}.Release|x64.ActiveCfg = Release|x64
{D90BC3E3-1341-4849-9354-5F40489D39D1}.Release|x64.Build.0 = Release|x64
{C2321A03-0BA7-45B3-8740-ABD82B36B0BF}.Release|Win32.ActiveCfg = Release|Win32
{C2321A03-0BA7-45B3-8740-ABD82B36B0BF}.Release|Win32.Build.0 = Release|Win32
{C2321A03-0BA7-45B3-8740-ABD82B36B0BF}.Release|x64.ActiveCfg = Release|x64
{C2321A03-0BA7-45B3-8740-ABD82B36B0BF}.Release|x64.Build.0 = Release|x64
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|Win32.ActiveCfg = Release|Win32
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|Win32.Build.0 = Release|Win32
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|x64.ActiveCfg = Release|x64
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|x64.Build.0 = Release|x64
{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|Win32.ActiveCfg = Release|Win32
{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|Win32.Build.0 = Release|Win32
{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|x64.ActiveCfg = Release|x64
{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|x64.Build.0 = Release|x64
{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4}.Release|Win32.ActiveCfg = Release|Win32
{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4}.Release|Win32.Build.0 = Release|Win32
{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4}.Release|x64.ActiveCfg = Release|x64
{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4}.Release|x64.Build.0 = Release|x64
EndGlobalSection