mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-03-10 07:50:19 +00:00
Compare commits
26 Commits
gtk3-themi
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f676aa56e6 | ||
| a53802cfef | |||
| b47c45d4cc | |||
| f2354a7fa2 | |||
| ca043be197 | |||
| 50346683a1 | |||
|
|
43374f4fae | ||
|
|
5e86f363ed | ||
| d9be0a7b1c | |||
|
|
f3086fa389 | ||
|
|
0523b0639b | ||
| 1a32bff274 | |||
| 349d7f4c55 | |||
| 734d194cb0 | |||
| 5983befd5b | |||
| e63e68dfde | |||
| 1ef81951b7 | |||
| 395ccb0706 | |||
|
|
b3f692f00c | ||
|
|
64c2fd4e51 | ||
|
|
dd698ecab4 | ||
| b93bad9859 | |||
| 733f932b78 | |||
| 50d99e1314 | |||
|
|
90b7d7c5a1 | ||
|
|
7e422e10a6 |
1
.github/workflows/appimage-build.yml
vendored
1
.github/workflows/appimage-build.yml
vendored
@@ -29,6 +29,7 @@ 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
79
.github/workflows/solus-eopkg-build.yml
vendored
@@ -1,79 +0,0 @@
|
||||
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
|
||||
69
.github/workflows/windows-build.yml
vendored
69
.github/workflows/windows-build.yml
vendored
@@ -36,22 +36,74 @@ jobs:
|
||||
run: |
|
||||
New-Item -Name "deps" -ItemType "Directory" -Force | Out-Null
|
||||
|
||||
Invoke-WebRequest https://files.jrsoftware.org/is/6/innosetup-6.7.0.exe -OutFile deps\innosetup-unicode.exe
|
||||
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
|
||||
& deps\innosetup-unicode.exe /VERYSILENT | Out-Null
|
||||
|
||||
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/idpsetup-1.5.1.exe -OutFile deps\idpsetup.exe
|
||||
Download-WithRetry -Url https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/idpsetup-1.5.1.exe -OutFile deps\idpsetup.exe
|
||||
& deps\idpsetup.exe /VERYSILENT
|
||||
|
||||
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
|
||||
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
|
||||
& 7z.exe x deps\gtk-${{ matrix.arch }}.7z -oC:\gtk-build\gtk\x64\release
|
||||
|
||||
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/gendef-20111031.7z -OutFile deps\gendef.7z
|
||||
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
|
||||
& 7z.exe x deps\gendef.7z -oC:\gtk-build
|
||||
|
||||
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/WinSparkle-20151011.7z -OutFile deps\WinSparkle.7z
|
||||
Download-WithRetry -Url 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
|
||||
|
||||
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
|
||||
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
|
||||
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.20\${{ matrix.platform }}
|
||||
|
||||
$pyRoot = $env:pythonLocation
|
||||
@@ -64,9 +116,6 @@ 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"
|
||||
@@ -81,6 +130,8 @@ 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
|
||||
|
||||
|
||||
@@ -43,10 +43,6 @@ 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]
|
||||
|
||||
@@ -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;application/x-zoitechat-theme;application/x-hexchat-theme;
|
||||
MimeType=x-scheme-handler/irc;x-scheme-handler/ircs;
|
||||
Actions=SafeMode;
|
||||
|
||||
[Desktop Action SafeMode]
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
<?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>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
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
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -27,21 +23,10 @@
|
||||
<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>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -27,15 +23,6 @@
|
||||
<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>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -27,21 +23,10 @@
|
||||
<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>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -27,16 +23,6 @@
|
||||
<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>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -27,26 +23,6 @@
|
||||
<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>
|
||||
|
||||
@@ -27,23 +27,6 @@
|
||||
<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>
|
||||
|
||||
@@ -28,29 +28,15 @@
|
||||
<TargetName>hcsysinfo</TargetName>
|
||||
<OutDir>$(ZoiteChatRel)plugins\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<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;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>
|
||||
<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>
|
||||
<AdditionalDependencies>$(ZoiteChatLib)common.lib;wbemuuid.lib;$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>sysinfo.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
@@ -68,4 +54,4 @@
|
||||
<ClInclude Include="sysinfo.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -27,17 +27,6 @@
|
||||
<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>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -27,11 +23,6 @@
|
||||
<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>
|
||||
@@ -39,7 +30,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>
|
||||
|
||||
@@ -258,12 +258,13 @@ 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;
|
||||
|
||||
sscanf (str, "%04hx %04hx %04hx", r, g, b);
|
||||
return 1;
|
||||
matched = sscanf (str, "%04hx %04hx %04hx", r, g, b);
|
||||
return matched == 3;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -438,6 +439,7 @@ 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},
|
||||
@@ -567,6 +569,7 @@ 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},
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -49,6 +45,8 @@
|
||||
<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" />
|
||||
@@ -79,6 +77,8 @@
|
||||
<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,16 +96,10 @@
|
||||
<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;$(OpenSslInclude);$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ZoiteChatLib);$(DepsRoot)\include;$(ArchiveInclude);$(OpenSslInclude);$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
@@ -119,6 +119,12 @@
|
||||
<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">
|
||||
@@ -202,6 +208,12 @@
|
||||
<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" />
|
||||
|
||||
1196
src/common/gtk3-theme-service.c
Normal file
1196
src/common/gtk3-theme-service.c
Normal file
File diff suppressed because it is too large
Load Diff
32
src/common/gtk3-theme-service.h
Normal file
32
src/common/gtk3-theme-service.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#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
|
||||
@@ -3,6 +3,7 @@ common_sources = [
|
||||
'chanopt.c',
|
||||
'ctcp.c',
|
||||
'dcc.c',
|
||||
'gtk3-theme-service.c',
|
||||
'zoitechat.c',
|
||||
'history.c',
|
||||
'ignore.c',
|
||||
@@ -27,12 +28,17 @@ 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('.')
|
||||
@@ -133,3 +139,18 @@ 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,
|
||||
)
|
||||
|
||||
@@ -3770,45 +3770,6 @@ 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 *basename = g_path_get_basename (theme_path);
|
||||
char *dot = strrchr (basename, '.');
|
||||
char *message;
|
||||
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
|
||||
if (zoitechat_import_theme (theme_path, &error))
|
||||
{
|
||||
if (zoitechat_apply_theme (basename, &error))
|
||||
{
|
||||
message = g_strdup_printf (_("Theme \"%s\" imported and applied."), basename);
|
||||
fe_message (message, FE_MSG_INFO);
|
||||
handle_command (sess, "gui apply", FALSE);
|
||||
g_free (message);
|
||||
}
|
||||
else
|
||||
{
|
||||
fe_message (error ? error->message : _("Theme imported, but failed to apply."),
|
||||
FE_MSG_ERROR);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fe_message (error ? error->message : _("Failed to import theme."),
|
||||
FE_MSG_ERROR);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
g_free (basename);
|
||||
g_free (theme_path);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *server_name = NULL;
|
||||
char *port = NULL;
|
||||
char *channel = NULL;
|
||||
|
||||
@@ -338,8 +338,8 @@ static const struct defaultserver def[] =
|
||||
|
||||
{"Zoite", 0, 0, 0, LOGIN_SASL, 0, TRUE},
|
||||
{0, "irc.zoite.net"},
|
||||
{0, "penumbra.newnet.net"},
|
||||
{0, "hedy.newnet.net"},
|
||||
{0, "penumbra.zoite.net"},
|
||||
{0, "hedy.zoite.net"},
|
||||
|
||||
{0,0}
|
||||
};
|
||||
|
||||
684
src/common/tests/test-gtk3-theme-service.c
Normal file
684
src/common/tests/test-gtk3-theme-service.c
Normal file
@@ -0,0 +1,684 @@
|
||||
#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 ();
|
||||
}
|
||||
176
src/common/theme-service.c
Normal file
176
src/common/theme-service.c
Normal file
@@ -0,0 +1,176 @@
|
||||
#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 ();
|
||||
}
|
||||
12
src/common/theme-service.h
Normal file
12
src/common/theme-service.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#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
|
||||
@@ -112,36 +112,9 @@ struct zoitechatprefs prefs;
|
||||
gboolean
|
||||
zoitechat_theme_path_from_arg (const char *arg, char **path_out)
|
||||
{
|
||||
char *path = NULL;
|
||||
const char *ext;
|
||||
|
||||
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;
|
||||
|
||||
ext = strrchr (path, '.');
|
||||
if (!g_file_test (path, G_FILE_TEST_IS_REGULAR) ||
|
||||
!ext ||
|
||||
(g_ascii_strcasecmp (ext, ".zct") != 0 &&
|
||||
g_ascii_strcasecmp (ext, ".hct") != 0))
|
||||
{
|
||||
g_free (path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (path_out)
|
||||
*path_out = path;
|
||||
else
|
||||
g_free (path);
|
||||
|
||||
return TRUE;
|
||||
(void) arg;
|
||||
(void) path_out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -251,237 +224,19 @@ zoitechat_remote_win32 (void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static zoitechat_theme_post_apply_callback zoitechat_theme_post_apply_cb;
|
||||
|
||||
static gboolean
|
||||
zoitechat_copy_theme_file (const char *src, const char *dest, GError **error)
|
||||
void
|
||||
zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback)
|
||||
{
|
||||
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;
|
||||
zoitechat_theme_post_apply_cb = callback;
|
||||
}
|
||||
|
||||
gboolean
|
||||
zoitechat_apply_theme (const char *theme_name, GError **error)
|
||||
void
|
||||
zoitechat_run_theme_post_apply_callback (void)
|
||||
{
|
||||
char *theme_dir;
|
||||
char *colors_src;
|
||||
char *colors_dest;
|
||||
char *events_src;
|
||||
char *events_dest;
|
||||
gboolean ok = FALSE;
|
||||
|
||||
if (!theme_name || !*theme_name)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
_("No theme name specified."));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
theme_dir = g_build_filename (get_xdir (), "themes", theme_name, NULL);
|
||||
colors_src = g_build_filename (theme_dir, "colors.conf", NULL);
|
||||
colors_dest = g_build_filename (get_xdir (), "colors.conf", NULL);
|
||||
events_src = g_build_filename (theme_dir, "pevents.conf", NULL);
|
||||
events_dest = g_build_filename (get_xdir (), "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."));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!zoitechat_copy_theme_file (colors_src, colors_dest, error))
|
||||
goto cleanup;
|
||||
|
||||
if (g_file_test (events_src, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
if (!zoitechat_copy_theme_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;
|
||||
}
|
||||
}
|
||||
|
||||
ok = TRUE;
|
||||
|
||||
cleanup:
|
||||
g_free (events_dest);
|
||||
g_free (events_src);
|
||||
g_free (colors_dest);
|
||||
g_free (colors_src);
|
||||
g_free (theme_dir);
|
||||
return ok;
|
||||
}
|
||||
|
||||
gboolean
|
||||
zoitechat_import_theme (const char *path, GError **error)
|
||||
{
|
||||
char *themes_dir;
|
||||
char *basename;
|
||||
char *dot;
|
||||
char *theme_dir;
|
||||
char *argv[] = {"unzip", "-o", (char *)path, "-d", NULL, NULL};
|
||||
int status = 0;
|
||||
gboolean ok;
|
||||
#ifdef WIN32
|
||||
char *command = NULL;
|
||||
char *powershell = NULL;
|
||||
#endif
|
||||
|
||||
if (!path)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
_("No theme file specified."));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
themes_dir = g_build_filename (get_xdir (), "themes", NULL);
|
||||
basename = g_path_get_basename (path);
|
||||
if (!basename || basename[0] == '\0')
|
||||
{
|
||||
g_free (themes_dir);
|
||||
g_free (basename);
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
_("Failed to determine theme name."));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dot = strrchr (basename, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
|
||||
theme_dir = g_build_filename (themes_dir, basename, NULL);
|
||||
if (g_mkdir_with_parents (theme_dir, 0700) != 0)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Failed to create theme directory."));
|
||||
g_free (theme_dir);
|
||||
g_free (basename);
|
||||
g_free (themes_dir);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
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."));
|
||||
ok = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
GString *escaped_path = g_string_new ("'");
|
||||
GString *escaped_dir = g_string_new ("'");
|
||||
const char *cursor;
|
||||
|
||||
for (cursor = 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 = theme_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 WindowsBase; "
|
||||
"$ErrorActionPreference='Stop'; "
|
||||
"$package=[System.IO.Packaging.Package]::Open(%s); "
|
||||
"try { "
|
||||
"foreach ($part in $package.GetParts()) { "
|
||||
"$relative=$part.Uri.OriginalString.TrimStart('/'); "
|
||||
"if ([string]::IsNullOrEmpty($relative)) { continue }; "
|
||||
"$destPath=[System.IO.Path]::Combine(%s, $relative); "
|
||||
"$destDir=[System.IO.Path]::GetDirectoryName($destPath); "
|
||||
"if ($destDir -and -not (Test-Path -LiteralPath $destDir)) { "
|
||||
"[System.IO.Directory]::CreateDirectory($destDir) | Out-Null "
|
||||
"}; "
|
||||
"$partStream=$part.GetStream(); "
|
||||
"$fileStream=[System.IO.File]::Open($destPath,[System.IO.FileMode]::Create,[System.IO.FileAccess]::Write); "
|
||||
"$partStream.CopyTo($fileStream); "
|
||||
"$fileStream.Dispose(); "
|
||||
"$partStream.Dispose(); "
|
||||
"} "
|
||||
"} finally { $package.Close(); }",
|
||||
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);
|
||||
}
|
||||
}
|
||||
#else
|
||||
argv[4] = theme_dir;
|
||||
ok = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
|
||||
NULL, NULL, &status, error);
|
||||
#endif
|
||||
if (!ok)
|
||||
{
|
||||
#ifdef WIN32
|
||||
g_free (command);
|
||||
g_free (powershell);
|
||||
#endif
|
||||
g_free (theme_dir);
|
||||
g_free (basename);
|
||||
g_free (themes_dir);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!g_spawn_check_exit_status (status, error))
|
||||
{
|
||||
#ifdef WIN32
|
||||
g_free (command);
|
||||
g_free (powershell);
|
||||
#endif
|
||||
g_free (theme_dir);
|
||||
g_free (basename);
|
||||
g_free (themes_dir);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
g_free (command);
|
||||
g_free (powershell);
|
||||
#endif
|
||||
|
||||
g_free (theme_dir);
|
||||
g_free (basename);
|
||||
g_free (themes_dir);
|
||||
return TRUE;
|
||||
if (zoitechat_theme_post_apply_cb)
|
||||
zoitechat_theme_post_apply_cb ();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -785,7 +540,6 @@ irc_init (session *sess)
|
||||
{
|
||||
static int done_init = FALSE;
|
||||
char *buf;
|
||||
char *theme_path;
|
||||
|
||||
if (done_init)
|
||||
return;
|
||||
@@ -808,51 +562,10 @@ irc_init (session *sess)
|
||||
|
||||
if (arg_url != NULL)
|
||||
{
|
||||
theme_path = NULL;
|
||||
if (zoitechat_theme_path_from_arg (arg_url, &theme_path))
|
||||
{
|
||||
GError *error = NULL;
|
||||
char *basename = g_path_get_basename (theme_path);
|
||||
char *dot = strrchr (basename, '.');
|
||||
char *message;
|
||||
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
|
||||
if (zoitechat_import_theme (theme_path, &error))
|
||||
{
|
||||
if (zoitechat_apply_theme (basename, &error))
|
||||
{
|
||||
message = g_strdup_printf (_("Theme \"%s\" imported and applied."), basename);
|
||||
fe_message (message, FE_MSG_INFO);
|
||||
handle_command (sess, "gui apply", FALSE);
|
||||
g_free (message);
|
||||
}
|
||||
else
|
||||
{
|
||||
fe_message (error ? error->message : _("Theme imported, but failed to apply."),
|
||||
FE_MSG_ERROR);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fe_message (error ? error->message : _("Failed to import theme."),
|
||||
FE_MSG_ERROR);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
g_free (basename);
|
||||
}
|
||||
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 */
|
||||
buf = g_strdup_printf ("server %s", arg_url);
|
||||
handle_command (sess, buf, FALSE);
|
||||
g_free (buf);
|
||||
g_free (arg_url);
|
||||
}
|
||||
|
||||
if (arg_urls != NULL)
|
||||
@@ -860,50 +573,9 @@ irc_init (session *sess)
|
||||
guint i;
|
||||
for (i = 0; i < g_strv_length (arg_urls); i++)
|
||||
{
|
||||
theme_path = NULL;
|
||||
if (zoitechat_theme_path_from_arg (arg_urls[i], &theme_path))
|
||||
{
|
||||
GError *error = NULL;
|
||||
char *basename = g_path_get_basename (theme_path);
|
||||
char *dot = strrchr (basename, '.');
|
||||
char *message;
|
||||
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
|
||||
if (zoitechat_import_theme (theme_path, &error))
|
||||
{
|
||||
if (zoitechat_apply_theme (basename, &error))
|
||||
{
|
||||
message = g_strdup_printf (_("Theme \"%s\" imported and applied."), basename);
|
||||
fe_message (message, FE_MSG_INFO);
|
||||
handle_command (sess, "gui apply", FALSE);
|
||||
g_free (message);
|
||||
}
|
||||
else
|
||||
{
|
||||
fe_message (error ? error->message : _("Theme imported, but failed to apply."),
|
||||
FE_MSG_ERROR);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fe_message (error ? error->message : _("Failed to import theme."),
|
||||
FE_MSG_ERROR);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
g_free (basename);
|
||||
}
|
||||
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);
|
||||
buf = g_strdup_printf ("%s %s", i == 0 ? "server" : "newserver", arg_urls[i]);
|
||||
handle_command (sess, buf, FALSE);
|
||||
g_free (buf);
|
||||
}
|
||||
g_strfreev (arg_urls);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
#define ZOITECHAT_H
|
||||
|
||||
gboolean zoitechat_theme_path_from_arg (const char *arg, char **path_out);
|
||||
gboolean zoitechat_import_theme (const char *path, GError **error);
|
||||
gboolean zoitechat_apply_theme (const char *theme_name, GError **error);
|
||||
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);
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#ifdef __APPLE__
|
||||
@@ -140,6 +141,7 @@ 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;
|
||||
@@ -320,6 +322,7 @@ 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 */
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#endif
|
||||
|
||||
#include "fe-gtk.h"
|
||||
#include "theme/theme-manager.h"
|
||||
|
||||
#include "../common/zoitechat.h"
|
||||
#include "../common/fe.h"
|
||||
@@ -563,6 +564,7 @@ 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);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (banlist_clear_cb), banl);
|
||||
|
||||
@@ -36,6 +36,21 @@ 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):
|
||||
@@ -149,6 +164,92 @@ 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)
|
||||
{
|
||||
@@ -157,7 +258,6 @@ 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));
|
||||
@@ -177,25 +277,15 @@ 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_left_is_moving = 1;
|
||||
|
||||
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;
|
||||
tab_scroll_animation_cancel (&tab_left_animation);
|
||||
return;
|
||||
}
|
||||
|
||||
tab_scroll_animation_start (&tab_left_animation, adj,
|
||||
gtk_adjustment_get_value (adj), new_value,
|
||||
-1, &tab_left_is_moving, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -206,7 +296,6 @@ 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));
|
||||
@@ -226,25 +315,15 @@ 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_right_is_moving = 1;
|
||||
|
||||
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;
|
||||
tab_scroll_animation_cancel (&tab_right_animation);
|
||||
return;
|
||||
}
|
||||
|
||||
tab_scroll_animation_start (&tab_right_animation, adj,
|
||||
gtk_adjustment_get_value (adj), new_value,
|
||||
1, &tab_right_is_moving, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
||||
@@ -27,6 +27,8 @@ 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)
|
||||
@@ -136,14 +138,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
|
||||
)
|
||||
{
|
||||
gtkutil_apply_palette (view, &colors[COL_BG], &colors[COL_FG],
|
||||
ThemeWidgetStyleValues style_values;
|
||||
|
||||
theme_get_widget_style_values_for_widget (view, &style_values);
|
||||
gtkutil_apply_palette (view, &style_values.background, &style_values.foreground,
|
||||
cv->font_desc);
|
||||
}
|
||||
/*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[COL_BG]);*/
|
||||
/*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[THEME_LEGACY_TEXT_BACKGROUND]);*/
|
||||
gtk_widget_set_can_focus (view, FALSE);
|
||||
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
#include "maingui.h"
|
||||
#include "gtkutil.h"
|
||||
#include "chanview.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-manager.h"
|
||||
#include "theme/theme-access.h"
|
||||
|
||||
/* treeStore columns */
|
||||
#define COL_NAME 0 /* (char *) */
|
||||
@@ -75,6 +76,7 @@ struct _chanview
|
||||
unsigned int sorted:1;
|
||||
unsigned int vertical:1;
|
||||
unsigned int use_icons:1;
|
||||
guint theme_listener_id;
|
||||
};
|
||||
|
||||
struct _chan
|
||||
@@ -91,6 +93,7 @@ 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 ======= */
|
||||
@@ -127,15 +130,17 @@ chanview_apply_theme (chanview *cv)
|
||||
if (input_style)
|
||||
font = input_style->font_desc;
|
||||
|
||||
if (fe_dark_mode_is_enabled () || prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_LIGHT)
|
||||
{
|
||||
gtkutil_apply_palette (w, &colors[COL_BG], &colors[COL_FG], font);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Keep list font in sync while reverting colors to theme defaults. */
|
||||
gtkutil_apply_palette (w, NULL, NULL, font);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
static char *
|
||||
@@ -278,6 +283,12 @@ 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);
|
||||
|
||||
@@ -312,6 +323,7 @@ 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);
|
||||
@@ -608,6 +620,45 @@ 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)
|
||||
{
|
||||
@@ -638,7 +689,7 @@ gboolean
|
||||
chan_remove (chan *ch, gboolean force)
|
||||
{
|
||||
chan *new_ch;
|
||||
int i, num;
|
||||
chan *first_ch;
|
||||
extern int zoitechat_is_quitting;
|
||||
|
||||
if (zoitechat_is_quitting) /* avoid lots of looping on exit */
|
||||
@@ -658,25 +709,14 @@ chan_remove (chan *ch, gboolean force)
|
||||
{
|
||||
ch->cv->focused = NULL;
|
||||
|
||||
/* 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);
|
||||
cv_find_neighbors_for_removal (ch->cv, ch, &new_ch, &first_ch);
|
||||
if (new_ch && new_ch != ch)
|
||||
{
|
||||
chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */
|
||||
} else
|
||||
chan_focus (new_ch);
|
||||
}
|
||||
else if (first_ch && first_ch != 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;
|
||||
}
|
||||
}
|
||||
chan_focus (first_ch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,9 @@
|
||||
#include "../common/util.h"
|
||||
#include "../common/network.h"
|
||||
#include "gtkutil.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-gtk.h"
|
||||
#include "maingui.h"
|
||||
#include "theme/theme-access.h"
|
||||
|
||||
#define ICON_DCC_CANCEL "dialog-cancel"
|
||||
#define ICON_DCC_ACCEPT "dialog-apply"
|
||||
@@ -55,7 +56,7 @@ enum /* DCC SEND/RECV */
|
||||
COL_ETA,
|
||||
COL_NICK,
|
||||
COL_DCC, /* struct DCC * */
|
||||
COL_COLOR, /* PaletteColor */
|
||||
COL_COLOR, /* GdkRGBA */
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
@@ -67,7 +68,7 @@ enum /* DCC CHAT */
|
||||
CCOL_SENT,
|
||||
CCOL_START,
|
||||
CCOL_DCC, /* struct DCC * */
|
||||
CCOL_COLOR, /* PaletteColor * */
|
||||
CCOL_COLOR, /* GdkRGBA * */
|
||||
CN_COLUMNS
|
||||
};
|
||||
|
||||
@@ -177,15 +178,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)
|
||||
{
|
||||
const PaletteColor *color = NULL;
|
||||
GdkRGBA rgba;
|
||||
const GdkRGBA *color = NULL;
|
||||
|
||||
if (color_index != 1)
|
||||
color = &colors[color_index];
|
||||
if (color_index != 1 && theme_get_mirc_color ((unsigned int) color_index, &rgba))
|
||||
color = &rgba;
|
||||
|
||||
if (color)
|
||||
{
|
||||
GdkRGBA rgba = *color;
|
||||
gtk_list_store_set (store, iter, column, &rgba, -1);
|
||||
gtk_list_store_set (store, iter, column, color, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -623,15 +624,7 @@ 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
|
||||
@@ -753,7 +746,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, PALETTE_FOREGROUND_PROPERTY, colorcol,
|
||||
"text", textcol, THEME_GTK_FOREGROUND_PROPERTY, colorcol,
|
||||
NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
}
|
||||
@@ -839,7 +832,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, PALETTE_GDK_TYPE);
|
||||
G_TYPE_STRING, G_TYPE_POINTER, THEME_GTK_COLOR_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);
|
||||
@@ -1111,7 +1104,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, PALETTE_GDK_TYPE);
|
||||
G_TYPE_POINTER, THEME_GTK_COLOR_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);
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#include "chanlist.h"
|
||||
#include "joind.h"
|
||||
#include "xtext.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-gtk.h"
|
||||
#include "menu.h"
|
||||
#include "notifygui.h"
|
||||
#include "textgui.h"
|
||||
@@ -57,6 +57,8 @@
|
||||
#include "urlgrab.h"
|
||||
#include "setup.h"
|
||||
#include "plugin-notification.h"
|
||||
#include "theme/theme-manager.h"
|
||||
#include "theme/theme-application.h"
|
||||
|
||||
#ifdef USE_LIBCANBERRA
|
||||
#include <canberra.h>
|
||||
@@ -105,6 +107,7 @@ create_msg_dialog (gchar *title, gchar *message)
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", message);
|
||||
theme_manager_attach_window (dialog);
|
||||
gtk_window_set_title (GTK_WINDOW (dialog), title);
|
||||
|
||||
/* On Win32 we automatically have the icon. If we try to load it explicitly, it will look ugly for some reason. */
|
||||
@@ -429,37 +432,12 @@ fe_args (int argc, char *argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char cursor_color_rc[] =
|
||||
"style \"xc-ib-st\""
|
||||
"{"
|
||||
"GtkEntry::cursor-color=\"#%02x%02x%02x\""
|
||||
"}"
|
||||
"widget \"*.zoitechat-inputbox\" style : application \"xc-ib-st\"";
|
||||
|
||||
InputStyle *create_input_style (InputStyle *style);
|
||||
|
||||
static const char adwaita_workaround_rc[] =
|
||||
"style \"zoitechat-input-workaround\""
|
||||
"{"
|
||||
"engine \"pixmap\" {"
|
||||
"image {"
|
||||
"function = FLAT_BOX\n"
|
||||
"state = NORMAL\n"
|
||||
"}"
|
||||
"image {"
|
||||
"function = FLAT_BOX\n"
|
||||
"state = ACTIVE\n"
|
||||
"}"
|
||||
"}"
|
||||
"}"
|
||||
"widget \"*.zoitechat-inputbox\" style \"zoitechat-input-workaround\"";
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
gboolean
|
||||
fe_win32_high_contrast_is_enabled (void)
|
||||
{
|
||||
HIGHCONTRASTW hc;
|
||||
@@ -472,7 +450,7 @@ fe_win32_high_contrast_is_enabled (void)
|
||||
return (hc.dwFlags & HCF_HIGHCONTRASTON) != 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gboolean
|
||||
fe_win32_try_get_system_dark (gboolean *prefer_dark)
|
||||
{
|
||||
DWORD value = 1;
|
||||
@@ -502,7 +480,7 @@ fe_win32_try_get_system_dark (gboolean *prefer_dark)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark_mode)
|
||||
{
|
||||
HWND hwnd;
|
||||
@@ -525,7 +503,7 @@ fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark_mode)
|
||||
sizeof (use_dark));
|
||||
}
|
||||
#else
|
||||
static void
|
||||
void
|
||||
fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark_mode)
|
||||
{
|
||||
(void) window;
|
||||
@@ -533,163 +511,14 @@ fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark_mode)
|
||||
}
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
fe_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;
|
||||
#endif
|
||||
|
||||
if (!settings)
|
||||
return FALSE;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
have_win_pref = fe_win32_try_get_system_dark (&prefer_dark);
|
||||
if (!have_win_pref)
|
||||
#endif
|
||||
if (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 (!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;
|
||||
}
|
||||
|
||||
static gboolean auto_dark_mode_enabled = FALSE;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
static void
|
||||
fe_apply_windows_theme (gboolean dark)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
|
||||
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", dark, NULL);
|
||||
}
|
||||
|
||||
{
|
||||
static GtkCssProvider *win_theme_provider = NULL;
|
||||
GdkScreen *screen = gdk_screen_get_default ();
|
||||
const char *css =
|
||||
"window.zoitechat-dark, .zoitechat-dark {"
|
||||
"background-color: #202020;"
|
||||
"color: #f0f0f0;"
|
||||
"}"
|
||||
"window.zoitechat-light, .zoitechat-light {"
|
||||
"background-color: #f6f6f6;"
|
||||
"color: #101010;"
|
||||
"}";
|
||||
|
||||
if (!win_theme_provider)
|
||||
win_theme_provider = gtk_css_provider_new ();
|
||||
|
||||
gtk_css_provider_load_from_data (win_theme_provider, css, -1, NULL);
|
||||
if (screen)
|
||||
gtk_style_context_add_provider_for_screen (
|
||||
screen,
|
||||
GTK_STYLE_PROVIDER (win_theme_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
fe_auto_dark_mode_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data)
|
||||
{
|
||||
gboolean enabled;
|
||||
|
||||
(void) settings;
|
||||
(void) pspec;
|
||||
(void) data;
|
||||
|
||||
if (prefs.hex_gui_dark_mode != ZOITECHAT_DARK_MODE_AUTO)
|
||||
return;
|
||||
|
||||
enabled = fe_system_prefers_dark ();
|
||||
if (enabled == auto_dark_mode_enabled)
|
||||
return;
|
||||
|
||||
auto_dark_mode_enabled = enabled;
|
||||
fe_apply_theme_for_mode (ZOITECHAT_DARK_MODE_AUTO, NULL);
|
||||
setup_apply_real (0, TRUE, FALSE, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
fe_set_auto_dark_mode_state (gboolean enabled)
|
||||
{
|
||||
auto_dark_mode_enabled = enabled;
|
||||
}
|
||||
|
||||
void
|
||||
fe_refresh_auto_dark_mode (void)
|
||||
{
|
||||
fe_auto_dark_mode_changed (NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
fe_apply_theme_for_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
gboolean enabled = fe_dark_mode_is_enabled_for (mode);
|
||||
gboolean changed = palette_apply_dark_mode (enabled);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
fe_apply_windows_theme (enabled);
|
||||
#endif
|
||||
|
||||
if (palette_changed)
|
||||
*palette_changed = changed;
|
||||
|
||||
if (input_style)
|
||||
create_input_style (input_style);
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void
|
||||
fe_apply_theme_to_toplevel (GtkWidget *window)
|
||||
{
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
{
|
||||
GtkStyleContext *context = gtk_widget_get_style_context (window);
|
||||
gboolean dark = fe_dark_mode_is_enabled ();
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
fe_win32_apply_native_titlebar (window, fe_dark_mode_is_enabled ());
|
||||
}
|
||||
|
||||
gboolean
|
||||
fe_dark_mode_is_enabled_for (unsigned int mode)
|
||||
{
|
||||
@@ -711,174 +540,10 @@ fe_dark_mode_is_enabled (void)
|
||||
return fe_dark_mode_is_enabled_for (prefs.hex_gui_dark_mode);
|
||||
}
|
||||
|
||||
InputStyle *
|
||||
create_input_style (InputStyle *style)
|
||||
{
|
||||
char buf[256];
|
||||
static int done_rc = FALSE;
|
||||
static GtkCssProvider *input_css_provider = NULL;
|
||||
static char *last_theme_name = NULL;
|
||||
static gboolean last_dark_mode = FALSE;
|
||||
static gboolean last_input_style = FALSE;
|
||||
static gboolean last_colors_set = FALSE;
|
||||
static guint16 last_fg_red;
|
||||
static guint16 last_fg_green;
|
||||
static guint16 last_fg_blue;
|
||||
static guint16 last_bg_red;
|
||||
static guint16 last_bg_green;
|
||||
static guint16 last_bg_blue;
|
||||
|
||||
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);
|
||||
|
||||
/* fall back */
|
||||
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");
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_input_style)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
GdkScreen *screen = gdk_screen_get_default ();
|
||||
char *theme_name;
|
||||
char cursor_rc[sizeof (cursor_color_rc)];
|
||||
char cursor_color[8];
|
||||
const char *cursor_color_start = NULL;
|
||||
guint16 fg_red;
|
||||
guint16 fg_green;
|
||||
guint16 fg_blue;
|
||||
guint16 bg_red;
|
||||
guint16 bg_green;
|
||||
guint16 bg_blue;
|
||||
gboolean dark_mode = fe_dark_mode_is_enabled ();
|
||||
gboolean needs_reload;
|
||||
|
||||
g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
|
||||
|
||||
palette_color_get_rgb16 (&colors[COL_FG], &fg_red, &fg_green, &fg_blue);
|
||||
palette_color_get_rgb16 (&colors[COL_BG], &bg_red, &bg_green, &bg_blue);
|
||||
needs_reload = !done_rc
|
||||
|| !last_input_style
|
||||
|| last_dark_mode != dark_mode
|
||||
|| g_strcmp0 (last_theme_name, theme_name) != 0
|
||||
|| !last_colors_set
|
||||
|| last_fg_red != fg_red
|
||||
|| last_fg_green != fg_green
|
||||
|| last_fg_blue != fg_blue
|
||||
|| last_bg_red != bg_red
|
||||
|| last_bg_green != bg_green
|
||||
|| last_bg_blue != bg_blue;
|
||||
|
||||
if (needs_reload)
|
||||
{
|
||||
if (!input_css_provider)
|
||||
input_css_provider = gtk_css_provider_new ();
|
||||
|
||||
g_snprintf (buf, sizeof (buf), "#%02x%02x%02x",
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8));
|
||||
g_snprintf (cursor_rc, sizeof (cursor_rc), cursor_color_rc,
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8));
|
||||
cursor_color_start = g_strstr_len (cursor_rc, -1, "cursor-color=\"");
|
||||
if (cursor_color_start)
|
||||
{
|
||||
cursor_color_start += strlen ("cursor-color=\"");
|
||||
g_strlcpy (cursor_color, cursor_color_start, sizeof (cursor_color));
|
||||
cursor_color[7] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
g_strlcpy (cursor_color, buf, sizeof (cursor_color));
|
||||
}
|
||||
{
|
||||
GString *css = g_string_new ("#zoitechat-inputbox {");
|
||||
|
||||
/* GTK3 equivalents for adwaita_workaround_rc/cursor_color_rc. */
|
||||
if (adwaita_workaround_rc[0] != '\0'
|
||||
&& (g_str_has_prefix (theme_name, "Adwaita")
|
||||
|| g_str_has_prefix (theme_name, "Yaru")))
|
||||
g_string_append (css, "background-image: none;");
|
||||
|
||||
g_string_append_printf (
|
||||
css,
|
||||
"background-color: #%02x%02x%02x;"
|
||||
"color: #%02x%02x%02x;"
|
||||
"caret-color: %s;"
|
||||
"}"
|
||||
"#zoitechat-inputbox text {"
|
||||
"color: #%02x%02x%02x;"
|
||||
"caret-color: %s;"
|
||||
"}",
|
||||
(bg_red >> 8), (bg_green >> 8), (bg_blue >> 8),
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
|
||||
cursor_color,
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
|
||||
cursor_color);
|
||||
gtk_css_provider_load_from_data (input_css_provider, css->str, -1, NULL);
|
||||
g_string_free (css, TRUE);
|
||||
}
|
||||
|
||||
if (screen)
|
||||
gtk_style_context_add_provider_for_screen (
|
||||
screen,
|
||||
GTK_STYLE_PROVIDER (input_css_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
|
||||
done_rc = TRUE;
|
||||
last_input_style = TRUE;
|
||||
last_dark_mode = dark_mode;
|
||||
last_colors_set = TRUE;
|
||||
last_fg_red = fg_red;
|
||||
last_fg_green = fg_green;
|
||||
last_fg_blue = fg_blue;
|
||||
last_bg_red = bg_red;
|
||||
last_bg_green = bg_green;
|
||||
last_bg_blue = bg_blue;
|
||||
g_free (last_theme_name);
|
||||
last_theme_name = g_strdup (theme_name);
|
||||
}
|
||||
|
||||
g_free (theme_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkScreen *screen = gdk_screen_get_default ();
|
||||
|
||||
if (input_css_provider && screen)
|
||||
{
|
||||
gtk_style_context_remove_provider_for_screen (
|
||||
screen,
|
||||
GTK_STYLE_PROVIDER (input_css_provider));
|
||||
}
|
||||
g_clear_object (&input_css_provider);
|
||||
g_clear_pointer (&last_theme_name, g_free);
|
||||
done_rc = FALSE;
|
||||
last_input_style = FALSE;
|
||||
last_colors_set = FALSE;
|
||||
}
|
||||
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
void
|
||||
fe_init (void)
|
||||
{
|
||||
GtkSettings *settings;
|
||||
|
||||
palette_load ();
|
||||
settings = gtk_settings_get_default ();
|
||||
if (settings)
|
||||
auto_dark_mode_enabled = fe_system_prefers_dark ();
|
||||
|
||||
fe_apply_theme_for_mode (prefs.hex_gui_dark_mode, NULL);
|
||||
theme_manager_init ();
|
||||
key_init ();
|
||||
pixmaps_init ();
|
||||
|
||||
@@ -886,15 +551,8 @@ fe_init (void)
|
||||
gtkosx_application_set_dock_icon_pixbuf (osx_app, pix_zoitechat);
|
||||
#endif
|
||||
channelwin_pix = pixmap_load_from_file (prefs.hex_text_background);
|
||||
input_style = create_input_style (input_style);
|
||||
theme_application_reload_input_style ();
|
||||
|
||||
if (settings)
|
||||
{
|
||||
g_signal_connect (settings, "notify::gtk-application-prefer-dark-theme",
|
||||
G_CALLBACK (fe_auto_dark_mode_changed), NULL);
|
||||
g_signal_connect (settings, "notify::gtk-theme-name",
|
||||
G_CALLBACK (fe_auto_dark_mode_changed), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_GTK_MAC
|
||||
@@ -1040,6 +698,7 @@ fe_message (char *msg, int flags)
|
||||
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (parent_window), 0, type,
|
||||
GTK_BUTTONS_OK, "%s", msg);
|
||||
theme_manager_attach_window (dialog);
|
||||
if (flags & FE_MSG_MARKUP)
|
||||
gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), msg);
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
@@ -1440,7 +1099,7 @@ fe_ctrl_gui (session *sess, fe_gui_action action, int arg)
|
||||
mg_detach (sess, arg); /* arg: 0=toggle 1=detach 2=attach */
|
||||
break;
|
||||
case FE_GUI_APPLY:
|
||||
setup_apply_real (TRUE, TRUE, TRUE, FALSE);
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PIXMAP | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_LAYOUT | THEME_CHANGED_REASON_WIDGET_STYLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1564,61 +1223,6 @@ fe_set_inputbox_contents (session *sess, char *text)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static char *
|
||||
url_escape_hostname (const char *url)
|
||||
{
|
||||
char *host_start, *host_end, *ret, *hostname;
|
||||
|
||||
host_start = strstr (url, "://");
|
||||
if (host_start != NULL)
|
||||
{
|
||||
*host_start = '\0';
|
||||
host_start += 3;
|
||||
host_end = strchr (host_start, '/');
|
||||
|
||||
if (host_end != NULL)
|
||||
{
|
||||
*host_end = '\0';
|
||||
host_end++;
|
||||
}
|
||||
|
||||
hostname = g_hostname_to_ascii (host_start);
|
||||
if (host_end != NULL)
|
||||
ret = g_strdup_printf ("%s://%s/%s", url, hostname, host_end);
|
||||
else
|
||||
ret = g_strdup_printf ("%s://%s", url, hostname);
|
||||
|
||||
g_free (hostname);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return g_strdup (url);
|
||||
}
|
||||
|
||||
static void
|
||||
osx_show_uri (const char *url)
|
||||
{
|
||||
char *escaped_url, *encoded_url, *open, *cmd;
|
||||
|
||||
escaped_url = url_escape_hostname (url);
|
||||
encoded_url = g_filename_from_utf8 (escaped_url, -1, NULL, NULL, NULL);
|
||||
if (encoded_url)
|
||||
{
|
||||
open = g_find_program_in_path ("open");
|
||||
cmd = g_strjoin (" ", open, encoded_url, NULL);
|
||||
|
||||
zoitechat_exec (cmd);
|
||||
|
||||
g_free (encoded_url);
|
||||
g_free (cmd);
|
||||
}
|
||||
|
||||
g_free (escaped_url);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline char *
|
||||
escape_uri (const char *uri)
|
||||
{
|
||||
@@ -1630,7 +1234,7 @@ uri_contains_forbidden_characters (const char *uri)
|
||||
{
|
||||
while (*uri)
|
||||
{
|
||||
if (!g_ascii_isalnum (*uri) && !strchr ("-._~:/?#[]@!$&'()*+,;=", *uri))
|
||||
if (!g_ascii_isalnum (*uri) && !strchr ("-._~%:/?#[]@!$&'()*+,;=", *uri))
|
||||
return TRUE;
|
||||
uri++;
|
||||
}
|
||||
@@ -1661,48 +1265,39 @@ maybe_escape_uri (const char *uri)
|
||||
static void
|
||||
fe_open_url_inner (const char *url)
|
||||
{
|
||||
#ifdef WIN32
|
||||
gunichar2 *url_utf16 = g_utf8_to_utf16 (url, -1, NULL, NULL, NULL);
|
||||
|
||||
if (url_utf16 == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ShellExecuteW (0, L"open", url_utf16, NULL, NULL, SW_SHOWNORMAL);
|
||||
|
||||
g_free (url_utf16);
|
||||
#elif defined(__APPLE__)
|
||||
osx_show_uri (url);
|
||||
#else
|
||||
GError *error = NULL;
|
||||
char *escaped_url = maybe_escape_uri (url);
|
||||
gchar *xdg_open_argv[] = {(gchar *) "xdg-open", escaped_url, NULL};
|
||||
gchar **spawn_env = NULL;
|
||||
gboolean opened = FALSE;
|
||||
g_debug ("Opening URL \"%s\" (%s)", escaped_url, url);
|
||||
gboolean opened = g_app_info_launch_default_for_uri (escaped_url, NULL, &error);
|
||||
|
||||
/* AppImage runtime variables can point host binaries like /bin/sh at
|
||||
* bundled libraries, which may not be ABI-compatible with system tools. */
|
||||
spawn_env = g_get_environ ();
|
||||
if (!opened)
|
||||
{
|
||||
gchar **tmp_env = spawn_env;
|
||||
spawn_env = g_environ_unsetenv (tmp_env, "LD_LIBRARY_PATH");
|
||||
if (spawn_env != tmp_env)
|
||||
g_strfreev (tmp_env);
|
||||
g_clear_error (&error);
|
||||
#ifdef WIN32
|
||||
gunichar2 *url_utf16 = g_utf8_to_utf16 (escaped_url, -1, NULL, NULL, NULL);
|
||||
|
||||
tmp_env = spawn_env;
|
||||
spawn_env = g_environ_unsetenv (tmp_env, "LD_PRELOAD");
|
||||
if (spawn_env != tmp_env)
|
||||
g_strfreev (tmp_env);
|
||||
}
|
||||
if (url_utf16 != NULL)
|
||||
{
|
||||
opened = ((INT_PTR) ShellExecuteW (0, L"open", url_utf16, NULL, NULL, SW_SHOWNORMAL)) > 32;
|
||||
g_free (url_utf16);
|
||||
}
|
||||
#else
|
||||
gchar *xdg_open_argv[] = {(gchar *) "xdg-open", escaped_url, NULL};
|
||||
gchar **spawn_env = NULL;
|
||||
|
||||
/* Prefer xdg-open when available because gtk_show_uri can inherit
|
||||
* AppImage runtime state and fail before we can control the environment. */
|
||||
{
|
||||
gchar *xdg_open_path = g_find_program_in_path ("xdg-open");
|
||||
if (xdg_open_path &&
|
||||
g_spawn_async (NULL, xdg_open_argv, spawn_env,
|
||||
spawn_env = g_get_environ ();
|
||||
{
|
||||
gchar **tmp_env = spawn_env;
|
||||
spawn_env = g_environ_unsetenv (tmp_env, "LD_LIBRARY_PATH");
|
||||
if (spawn_env != tmp_env)
|
||||
g_strfreev (tmp_env);
|
||||
|
||||
tmp_env = spawn_env;
|
||||
spawn_env = g_environ_unsetenv (tmp_env, "LD_PRELOAD");
|
||||
if (spawn_env != tmp_env)
|
||||
g_strfreev (tmp_env);
|
||||
}
|
||||
|
||||
if (g_spawn_async (NULL, xdg_open_argv, spawn_env,
|
||||
G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
|
||||
NULL, NULL, NULL, &error))
|
||||
{
|
||||
@@ -1712,27 +1307,26 @@ fe_open_url_inner (const char *url)
|
||||
{
|
||||
g_clear_error (&error);
|
||||
}
|
||||
g_free (xdg_open_path);
|
||||
}
|
||||
|
||||
if (!opened && gtk_show_uri (NULL, escaped_url, GDK_CURRENT_TIME, &error))
|
||||
{
|
||||
opened = TRUE;
|
||||
}
|
||||
else if (!opened)
|
||||
{
|
||||
g_warning ("gtk_show_uri failed for '%s': %s", escaped_url, error ? error->message : "unknown error");
|
||||
g_clear_error (&error);
|
||||
if (!opened && gtk_show_uri (NULL, escaped_url, GDK_CURRENT_TIME, &error))
|
||||
{
|
||||
opened = TRUE;
|
||||
}
|
||||
else if (!opened)
|
||||
{
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
g_strfreev (spawn_env);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!opened)
|
||||
{
|
||||
g_warning ("Unable to open URL '%s' via xdg-open or gtk_show_uri", escaped_url);
|
||||
g_warning ("Unable to open URL '%s' using system default application", escaped_url);
|
||||
}
|
||||
|
||||
g_strfreev (spawn_env);
|
||||
g_free (escaped_url);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -114,6 +114,7 @@ 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 */
|
||||
@@ -178,6 +179,8 @@ 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? */
|
||||
@@ -190,9 +193,12 @@ extern cairo_surface_t *dialogwin_pix;
|
||||
gboolean fe_dark_mode_is_enabled (void);
|
||||
gboolean fe_dark_mode_is_enabled_for (unsigned int mode);
|
||||
void fe_set_auto_dark_mode_state (gboolean enabled);
|
||||
void fe_refresh_auto_dark_mode (void);
|
||||
gboolean fe_apply_theme_for_mode (unsigned int mode, gboolean *palette_changed);
|
||||
void fe_apply_theme_to_toplevel (GtkWidget *window);
|
||||
|
||||
#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);
|
||||
|
||||
#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)
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -27,18 +23,6 @@
|
||||
<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>
|
||||
@@ -46,8 +30,8 @@
|
||||
<DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;comsupp.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(ArchiveLibDir);$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
@@ -74,6 +58,7 @@ 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" />
|
||||
@@ -92,6 +77,16 @@ 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" />
|
||||
@@ -104,13 +99,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" />
|
||||
@@ -125,6 +120,15 @@ 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" />
|
||||
@@ -135,6 +139,12 @@ 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" />
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
<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>
|
||||
@@ -96,6 +99,36 @@
|
||||
<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">
|
||||
@@ -128,6 +161,9 @@
|
||||
<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>
|
||||
@@ -143,9 +179,6 @@
|
||||
<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>
|
||||
@@ -191,6 +224,33 @@
|
||||
<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">
|
||||
@@ -206,9 +266,27 @@
|
||||
<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>
|
||||
|
||||
@@ -48,7 +48,10 @@
|
||||
#include "gtkutil.h"
|
||||
#include "menu.h"
|
||||
#include "xtext.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-access.h"
|
||||
#include "theme/theme-manager.h"
|
||||
#include "theme/theme-css.h"
|
||||
#include "theme/theme-gtk3.h"
|
||||
#include "maingui.h"
|
||||
#include "textgui.h"
|
||||
#include "fkeys.h"
|
||||
@@ -142,6 +145,53 @@ 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)
|
||||
{
|
||||
@@ -746,9 +796,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,
|
||||
@@ -760,8 +810,7 @@ key_dialog_treeview_new (GtkWidget *box)
|
||||
"}",
|
||||
-1,
|
||||
NULL);
|
||||
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
theme_css_apply_widget_provider (view, GTK_STYLE_PROVIDER (provider));
|
||||
g_object_unref (provider);
|
||||
}
|
||||
|
||||
@@ -904,13 +953,16 @@ key_dialog_show ()
|
||||
NULL, 600, 360, &vbox, 0);
|
||||
|
||||
view = key_dialog_treeview_new (vbox);
|
||||
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
|
||||
theme_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);
|
||||
|
||||
@@ -42,7 +42,9 @@
|
||||
#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>
|
||||
@@ -62,124 +64,28 @@ struct file_req
|
||||
int flags; /* FRF_* flags */
|
||||
};
|
||||
|
||||
static const char *
|
||||
gtkutil_menu_custom_icon_from_stock (const char *stock_name)
|
||||
{
|
||||
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 (!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].custom_icon;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static GdkPixbuf *
|
||||
gtkutil_menu_icon_pixbuf_new (const char *icon_name)
|
||||
{
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
char *resource_path;
|
||||
const char *system_icon_name = NULL;
|
||||
int action;
|
||||
|
||||
if (!icon_name || !g_str_has_prefix (icon_name, "zc-menu-"))
|
||||
if (!icon_name || !icon_resolver_menu_action_from_name (icon_name, &action))
|
||||
return NULL;
|
||||
|
||||
resource_path = g_strdup_printf ("/icons/menu/light/%s.png", icon_name + strlen ("zc-menu-"));
|
||||
pixbuf = gdk_pixbuf_new_from_resource (resource_path, NULL);
|
||||
if (!pixbuf)
|
||||
{
|
||||
g_free (resource_path);
|
||||
resource_path = g_strdup_printf ("/icons/menu/light/%s.svg", icon_name + strlen ("zc-menu-"));
|
||||
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;
|
||||
|
||||
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;
|
||||
@@ -188,136 +94,26 @@ gtkutil_menu_icon_pixbuf_new (const char *icon_name)
|
||||
const char *
|
||||
gtkutil_icon_name_from_stock (const char *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";
|
||||
|
||||
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;
|
||||
return icon_resolver_icon_name_from_stock (stock_name);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
gtkutil_menu_icon_image_new (const char *icon_name, GtkIconSize size)
|
||||
{
|
||||
GtkWidget *image = NULL;
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
char *resource_path;
|
||||
const char *variant;
|
||||
GdkPixbuf *pixbuf;
|
||||
gint width;
|
||||
gint height;
|
||||
|
||||
if (!icon_name || !g_str_has_prefix (icon_name, "zc-menu-"))
|
||||
pixbuf = gtkutil_menu_icon_pixbuf_new (icon_name);
|
||||
if (!pixbuf)
|
||||
return NULL;
|
||||
|
||||
variant = gtkutil_menu_icon_theme_variant ();
|
||||
resource_path = g_strdup_printf ("/icons/menu/%s/%s.png", variant, icon_name + strlen ("zc-menu-"));
|
||||
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.png", icon_name + strlen ("zc-menu-"));
|
||||
}
|
||||
image = gtk_image_new_from_pixbuf (pixbuf);
|
||||
g_object_unref (pixbuf);
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_resource (resource_path, NULL);
|
||||
if (!pixbuf)
|
||||
{
|
||||
g_free (resource_path);
|
||||
resource_path = g_strdup_printf ("/icons/menu/%s/%s.svg", variant, icon_name + strlen ("zc-menu-"));
|
||||
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.svg", icon_name + strlen ("zc-menu-"));
|
||||
}
|
||||
pixbuf = gdk_pixbuf_new_from_resource (resource_path, NULL);
|
||||
}
|
||||
if (pixbuf)
|
||||
{
|
||||
image = gtk_image_new_from_pixbuf (pixbuf);
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
if (gtk_icon_size_lookup (size, &width, &height))
|
||||
gtk_image_set_pixel_size (GTK_IMAGE (image), MAX (width, height));
|
||||
|
||||
return image;
|
||||
}
|
||||
@@ -327,26 +123,24 @@ gtkutil_image_new_from_stock (const char *stock, GtkIconSize size)
|
||||
{
|
||||
GtkWidget *image;
|
||||
const char *icon_name;
|
||||
const char *resolved_icon_name = NULL;
|
||||
int action;
|
||||
|
||||
icon_name = gtkutil_icon_name_from_stock (stock);
|
||||
if (!icon_name && stock && g_str_has_prefix (stock, "zc-menu-"))
|
||||
icon_name = stock;
|
||||
if (size == GTK_ICON_SIZE_MENU)
|
||||
{
|
||||
const char *menu_icon_name = gtkutil_menu_custom_icon_from_stock (stock);
|
||||
|
||||
if (!menu_icon_name)
|
||||
menu_icon_name = gtkutil_menu_custom_icon_from_icon_name (icon_name);
|
||||
|
||||
if (menu_icon_name)
|
||||
icon_name = menu_icon_name;
|
||||
}
|
||||
|
||||
image = gtkutil_menu_icon_image_new (icon_name, size);
|
||||
if (image)
|
||||
return image;
|
||||
|
||||
return gtk_image_new_from_icon_name (icon_name, size);
|
||||
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);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
@@ -484,66 +278,7 @@ void
|
||||
gtkutil_apply_palette (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
|
||||
const PangoFontDescription *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);
|
||||
}
|
||||
theme_manager_apply_palette_widget (widget, bg, fg, font_desc);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -819,6 +554,8 @@ gtkutil_file_req (GtkWindow *parent, const char *title, void *callback, void *us
|
||||
_("_Open"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
|
||||
theme_manager_attach_window (dialog);
|
||||
|
||||
if (filter && filter[0] && (flags & FRF_FILTERISINITIAL))
|
||||
{
|
||||
if (flags & FRF_WRITE)
|
||||
@@ -977,6 +714,7 @@ 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);
|
||||
|
||||
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);
|
||||
@@ -1072,6 +810,7 @@ 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);
|
||||
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));
|
||||
@@ -1112,6 +851,7 @@ 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);
|
||||
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));
|
||||
@@ -1228,6 +968,7 @@ 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");
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "../common/fe.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-gtk.h"
|
||||
|
||||
typedef void (*filereqcallback) (void *, char *file);
|
||||
|
||||
|
||||
380
src/fe-gtk/icon-resolver.c
Normal file
380
src/fe-gtk/icon-resolver.c
Normal file
@@ -0,0 +1,380 @@
|
||||
#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);
|
||||
}
|
||||
89
src/fe-gtk/icon-resolver.h
Normal file
89
src/fe-gtk/icon-resolver.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#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
|
||||
@@ -23,6 +23,7 @@
|
||||
#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,6 +296,7 @@ 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);
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (ignore_clear_cb), NULL);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "fe-gtk.h"
|
||||
#include "chanlist.h"
|
||||
#include "gtkutil.h"
|
||||
#include "theme/theme-manager.h"
|
||||
|
||||
#define ICON_JOIND_NETWORK "network-workgroup"
|
||||
|
||||
@@ -129,6 +130,7 @@ joind_show_dialog (server *serv)
|
||||
char buf2[256];
|
||||
|
||||
serv->gui->joind_win = dialog1 = gtk_dialog_new ();
|
||||
theme_manager_attach_window (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);
|
||||
|
||||
@@ -41,10 +41,13 @@
|
||||
#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 "palette.h"
|
||||
#include "theme/theme-access.h"
|
||||
#include "theme/theme-palette.h"
|
||||
#include "maingui.h"
|
||||
#include "menu.h"
|
||||
#include "fkeys.h"
|
||||
@@ -216,8 +219,7 @@ mg_apply_font_css (GtkWidget *widget, const PangoFontDescription *desc,
|
||||
g_string_free (css, TRUE);
|
||||
|
||||
gtk_style_context_add_class (context, class_name);
|
||||
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
theme_css_apply_widget_provider (widget, GTK_STYLE_PROVIDER (provider));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -405,7 +407,7 @@ mg_attr_list_create (const XTextColor *col, int size)
|
||||
static void
|
||||
mg_create_tab_colors (void)
|
||||
{
|
||||
XTextColor gui_palette[MAX_COL + 1];
|
||||
XTextColor gui_palette[XTEXT_COLS];
|
||||
|
||||
if (plain_list)
|
||||
{
|
||||
@@ -416,12 +418,12 @@ mg_create_tab_colors (void)
|
||||
pango_attr_list_unref (away_list);
|
||||
}
|
||||
|
||||
palette_get_xtext_colors (gui_palette, G_N_ELEMENTS (gui_palette));
|
||||
theme_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[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);
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -430,12 +432,47 @@ 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);
|
||||
}
|
||||
|
||||
@@ -1362,6 +1399,7 @@ 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);
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (mg_tab_close_cb), sess);
|
||||
if (prefs.hex_gui_tab_layout)
|
||||
@@ -1459,6 +1497,7 @@ mg_open_quit_dialog (gboolean minimize_button)
|
||||
}
|
||||
|
||||
dialog = gtk_dialog_new ();
|
||||
theme_manager_attach_window (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));
|
||||
@@ -1751,7 +1790,13 @@ mg_create_color_menu (GtkWidget *menu, session *sess)
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
|
||||
palette_color_get_rgb16 (&colors[i], &red, &green, &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);
|
||||
sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
|
||||
" </span></tt>",
|
||||
i, red >> 8, green >> 8, blue >> 8);
|
||||
@@ -1766,7 +1811,13 @@ mg_create_color_menu (GtkWidget *menu, session *sess)
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
|
||||
palette_color_get_rgb16 (&colors[i], &red, &green, &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);
|
||||
sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
|
||||
" </span></tt>",
|
||||
i, red >> 8, green >> 8, blue >> 8);
|
||||
@@ -2359,8 +2410,7 @@ mg_limit_entry_cb (GtkWidget * igad, gpointer userdata)
|
||||
static void
|
||||
mg_apply_entry_style (GtkWidget *entry)
|
||||
{
|
||||
gtkutil_apply_palette (entry, &colors[COL_BG], &colors[COL_FG],
|
||||
input_style->font_desc);
|
||||
theme_manager_apply_entry_palette (entry, input_style->font_desc);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2604,7 +2654,7 @@ mg_update_xtext (GtkWidget *wid)
|
||||
const gchar *font_name;
|
||||
XTextColor xtext_palette[XTEXT_COLS];
|
||||
|
||||
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
|
||||
theme_get_xtext_colors_for_widget (wid, 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);
|
||||
@@ -2651,7 +2701,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);
|
||||
|
||||
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
|
||||
theme_get_xtext_colors_for_widget (frame, 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);
|
||||
@@ -2770,6 +2820,81 @@ 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)
|
||||
{
|
||||
@@ -2787,19 +2912,10 @@ mg_create_userlist (session_gui *gui, GtkWidget *box)
|
||||
|
||||
gui->user_tree = ulist = userlist_create (vbox);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
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);
|
||||
|
||||
mg_create_meters (gui, vbox);
|
||||
|
||||
@@ -3626,7 +3742,6 @@ 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 */
|
||||
@@ -3681,7 +3796,10 @@ mg_create_topwindow (session *sess)
|
||||
mg_place_userlist_and_chanview (sess->gui);
|
||||
|
||||
gtk_widget_show (win);
|
||||
fe_apply_theme_to_toplevel (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);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
parent_win = gtk_widget_get_window (win);
|
||||
@@ -3730,7 +3848,7 @@ mg_win32_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
|
||||
|
||||
if (msg->message == WM_SETTINGCHANGE || msg->message == WM_THEMECHANGED)
|
||||
{
|
||||
fe_refresh_auto_dark_mode ();
|
||||
theme_manager_refresh_auto_mode ();
|
||||
return GDK_FILTER_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -3818,7 +3936,6 @@ 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 */
|
||||
@@ -3857,7 +3974,10 @@ mg_create_tabwindow (session *sess)
|
||||
mg_place_userlist_and_chanview (sess->gui);
|
||||
|
||||
gtk_widget_show (win);
|
||||
fe_apply_theme_to_toplevel (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);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
parent_win = gtk_widget_get_window (win);
|
||||
@@ -3881,8 +4001,6 @@ 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;
|
||||
@@ -4218,6 +4336,8 @@ 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);
|
||||
|
||||
@@ -54,7 +54,8 @@
|
||||
#include "notifygui.h"
|
||||
#include "pixmaps.h"
|
||||
#include "rawlog.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-gtk.h"
|
||||
#include "theme/theme-manager.h"
|
||||
#include "plugingui.h"
|
||||
#include "search.h"
|
||||
#include "textgui.h"
|
||||
@@ -1520,6 +1521,7 @@ menu_join (GtkWidget * wid, gpointer none)
|
||||
_("_Cancel"), GTK_RESPONSE_REJECT,
|
||||
_("_OK"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
theme_manager_attach_window (dialog);
|
||||
{
|
||||
GtkWidget *button;
|
||||
|
||||
@@ -1856,6 +1858,7 @@ static void
|
||||
menu_about (GtkWidget *wid, gpointer sess)
|
||||
{
|
||||
GtkAboutDialog *dialog = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
|
||||
theme_manager_attach_window (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" \
|
||||
@@ -2483,6 +2486,7 @@ 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
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
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',
|
||||
@@ -9,12 +21,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',
|
||||
@@ -37,6 +49,8 @@ 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')
|
||||
|
||||
@@ -76,11 +90,19 @@ 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'
|
||||
@@ -108,9 +130,124 @@ 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,
|
||||
)
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<?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>
|
||||
@@ -34,19 +30,6 @@
|
||||
<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>
|
||||
|
||||
@@ -34,8 +34,10 @@
|
||||
#include "../common/outbound.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-gtk.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"
|
||||
@@ -69,7 +71,7 @@ notify_closegui (void)
|
||||
}
|
||||
|
||||
/* Need this to be able to set the foreground colour property of a row
|
||||
* from a PaletteColor * in the model -Vince
|
||||
* from a GdkRGBA * in the model -Vince
|
||||
*/
|
||||
static void
|
||||
notify_treecell_property_mapper (GtkTreeViewColumn *col, GtkCellRenderer *cell,
|
||||
@@ -77,21 +79,21 @@ notify_treecell_property_mapper (GtkTreeViewColumn *col, GtkCellRenderer *cell,
|
||||
gpointer data)
|
||||
{
|
||||
gchar *text;
|
||||
PaletteColor *colour;
|
||||
GdkRGBA *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,
|
||||
PALETTE_FOREGROUND_PROPERTY, colour, NULL);
|
||||
THEME_GTK_FOREGROUND_PROPERTY, colour, NULL);
|
||||
if (colour)
|
||||
gdk_rgba_free (colour);
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_store_color (GtkListStore *store, GtkTreeIter *iter, const PaletteColor *color)
|
||||
notify_store_color (GtkListStore *store, GtkTreeIter *iter, const GdkRGBA *color)
|
||||
{
|
||||
if (color)
|
||||
{
|
||||
@@ -135,7 +137,7 @@ notify_treeview_new (GtkWidget *box)
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
PALETTE_GDK_TYPE,
|
||||
THEME_GTK_COLOR_TYPE,
|
||||
G_TYPE_POINTER
|
||||
);
|
||||
g_return_val_if_fail (store != NULL, NULL);
|
||||
@@ -170,6 +172,7 @@ notify_gui_update (void)
|
||||
GSList *slist;
|
||||
gchar *name, *status, *server, *seen;
|
||||
int online, servcount, lastseenminutes;
|
||||
GdkRGBA color;
|
||||
time_t lastseen;
|
||||
char agobuf[128];
|
||||
|
||||
@@ -225,7 +228,10 @@ 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);
|
||||
notify_store_color (store, &iter, &colors[4]);
|
||||
if (theme_get_color (THEME_TOKEN_MIRC_4, &color))
|
||||
notify_store_color (store, &iter, &color);
|
||||
else
|
||||
notify_store_color (store, &iter, NULL);
|
||||
if (valid)
|
||||
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
|
||||
|
||||
@@ -251,7 +257,10 @@ 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);
|
||||
notify_store_color (store, &iter, &colors[3]);
|
||||
if (theme_get_color (THEME_TOKEN_MIRC_3, &color))
|
||||
notify_store_color (store, &iter, &color);
|
||||
else
|
||||
notify_store_color (store, &iter, NULL);
|
||||
if (valid)
|
||||
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
|
||||
|
||||
@@ -373,6 +382,7 @@ 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);
|
||||
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);
|
||||
|
||||
@@ -1,453 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -20,61 +20,12 @@
|
||||
#ifndef ZOITECHAT_PALETTE_H
|
||||
#define ZOITECHAT_PALETTE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "xtext-color.h"
|
||||
#include "theme/theme-gtk.h"
|
||||
#include "theme/theme-palette.h"
|
||||
|
||||
typedef GdkRGBA PaletteColor;
|
||||
#define PALETTE_GDK_TYPE GDK_TYPE_RGBA
|
||||
#define PALETTE_FOREGROUND_PROPERTY "foreground-rgba"
|
||||
#define PALETTE_GDK_TYPE THEME_GTK_COLOR_TYPE
|
||||
#define PALETTE_FOREGROUND_PROPERTY THEME_GTK_FOREGROUND_PROPERTY
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#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>
|
||||
@@ -117,11 +118,43 @@ 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);
|
||||
|
||||
@@ -151,25 +184,36 @@ pixmap_load_from_file (char *filename)
|
||||
|
||||
/* load custom icons from <config>/icons, don't mess in system folders */
|
||||
static GdkPixbuf *
|
||||
load_pixmap (const char *filename)
|
||||
load_pixmap (IconResolverRole role, int item)
|
||||
{
|
||||
GdkPixbuf *pixbuf, *scaledpixbuf;
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
GdkPixbuf *scaledpixbuf;
|
||||
const char *scale;
|
||||
int iscale;
|
||||
char *path;
|
||||
const char *system_icon_name = NULL;
|
||||
|
||||
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)
|
||||
path = icon_resolver_resolve_path (role, item, GTK_ICON_SIZE_MENU, "pixmap",
|
||||
ICON_RESOLVER_THEME_SYSTEM, &system_icon_name);
|
||||
if (path)
|
||||
{
|
||||
path = g_strdup_printf ("/icons/%s.png", filename);
|
||||
pixbuf = gdk_pixbuf_new_from_resource (path, NULL);
|
||||
if (g_str_has_prefix (path, "/icons/"))
|
||||
pixbuf = gdk_pixbuf_new_from_resource (path, NULL);
|
||||
else
|
||||
pixbuf = gdk_pixbuf_new_from_file (path, 0);
|
||||
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)
|
||||
if (scale && pixbuf)
|
||||
{
|
||||
iscale = atoi (scale);
|
||||
if (iscale > 0)
|
||||
@@ -195,26 +239,26 @@ pixmaps_init (void)
|
||||
{
|
||||
zoitechat_register_resource();
|
||||
|
||||
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_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_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_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_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");
|
||||
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);
|
||||
|
||||
/* 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 = load_pixmap ("zoitechat");
|
||||
pix_zoitechat = gdk_pixbuf_new_from_resource ("/icons/zoitechat.png", NULL);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/server.h"
|
||||
#include "gtkutil.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-access.h"
|
||||
#include "theme/theme-manager.h"
|
||||
#include "maingui.h"
|
||||
#include "rawlog.h"
|
||||
#include "xtext.h"
|
||||
@@ -45,6 +46,53 @@
|
||||
#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)
|
||||
{
|
||||
@@ -126,11 +174,12 @@ open_rawlog (struct server *serv)
|
||||
gtk_widget_set_vexpand (scrolledwindow, TRUE);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
|
||||
|
||||
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
|
||||
theme_get_xtext_colors_for_widget (scrolledwindow, 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);
|
||||
@@ -144,8 +193,12 @@ 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
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#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 */
|
||||
@@ -787,6 +788,7 @@ servlist_deletenet_cb (GtkWidget *item, ircnet *net)
|
||||
GTK_BUTTONS_OK_CANCEL,
|
||||
_("Really remove network \"%s\" and all its servers?"),
|
||||
net->name);
|
||||
theme_manager_attach_window (dialog);
|
||||
g_signal_connect (dialog, "response",
|
||||
G_CALLBACK (servlist_deletenetdialog_cb), net);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
@@ -1791,6 +1793,7 @@ 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);
|
||||
@@ -2072,6 +2075,7 @@ 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);
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
#include "../common/zoitechatc.h"
|
||||
#include "../common/outbound.h"
|
||||
#include "fe-gtk.h"
|
||||
#include "theme/theme-manager.h"
|
||||
#include "theme/theme-preferences.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
#include "chanview.h"
|
||||
#include "palette.h"
|
||||
#include "pixmaps.h"
|
||||
#include "menu.h"
|
||||
#include "plugin-tray.h"
|
||||
@@ -48,8 +48,6 @@
|
||||
#endif
|
||||
#include "sexy-spell-entry.h"
|
||||
|
||||
InputStyle *create_input_style (InputStyle *);
|
||||
|
||||
#define LABEL_INDENT 12
|
||||
|
||||
static GtkWidget *setup_window = NULL;
|
||||
@@ -57,17 +55,8 @@ static int last_selected_page = 0;
|
||||
static int last_selected_row = 0; /* sound row */
|
||||
static gboolean color_change;
|
||||
static struct zoitechatprefs setup_prefs;
|
||||
static GSList *color_selector_widgets;
|
||||
static GtkWidget *cancel_button;
|
||||
static GtkWidget *font_dialog = NULL;
|
||||
void setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWidget *combo;
|
||||
GtkWidget *apply_button;
|
||||
GtkWidget *status_label;
|
||||
} setup_theme_ui;
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -172,10 +161,6 @@ static const setting appearance_settings[] =
|
||||
{ST_TOGGLE, N_("Colored nick names"), P_OFFINTNL(hex_text_color_nicks), N_("Give each person on IRC a different color"),0,0},
|
||||
{ST_TOGGLR, N_("Indent nick names"), P_OFFINTNL(hex_text_indent), N_("Make nick names right-justified"),0,0},
|
||||
{ST_TOGGLE, N_ ("Show marker line"), P_OFFINTNL (hex_text_show_marker), N_ ("Insert a red line after the last read text."), 0, 0},
|
||||
{ST_EFILE, N_ ("Background image:"), P_OFFSETNL (hex_text_background), 0, 0, sizeof prefs.hex_text_background},
|
||||
|
||||
{ST_HEADER, N_("Transparency Settings"), 0,0,0},
|
||||
{ST_HSCALE, N_("Window opacity:"), P_OFFINTNL(hex_gui_transparency),0,0,0},
|
||||
|
||||
{ST_HEADER, N_("Timestamps"),0,0,0},
|
||||
{ST_TOGGLE, N_("Enable timestamps"), P_OFFINTNL(hex_stamp_text),0,0,1},
|
||||
@@ -194,6 +179,15 @@ static const setting appearance_settings[] =
|
||||
{ST_END, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static const setting appearance_advanced_settings[] =
|
||||
{
|
||||
{ST_HEADER, N_("Advanced"),0,0,0},
|
||||
{ST_EFILE, N_ ("Background image:"), P_OFFSETNL (hex_text_background), 0, 0, sizeof prefs.hex_text_background},
|
||||
{ST_HSCALE, N_("Window opacity:"), P_OFFINTNL(hex_gui_transparency),0,0,0},
|
||||
|
||||
{ST_END, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static const char *const tabcompmenu[] =
|
||||
{
|
||||
N_("A-Z"),
|
||||
@@ -204,7 +198,6 @@ static const char *const tabcompmenu[] =
|
||||
static const setting inputbox_settings[] =
|
||||
{
|
||||
{ST_HEADER, N_("Input Box"),0,0,0},
|
||||
{ST_TOGGLE, N_("Use the text box font and colors"), P_OFFINTNL(hex_gui_input_style),0,0,0},
|
||||
{ST_TOGGLE, N_("Render colors and attributes"), P_OFFINTNL (hex_gui_input_attr),0,0,0},
|
||||
{ST_TOGGLE, N_("Show nick box"), P_OFFINTNL(hex_gui_input_nick),0,0,1},
|
||||
{ST_TOGGLE, N_("Show user mode icon in nick box"), P_OFFINTNL(hex_gui_input_icon),0,0,0},
|
||||
@@ -268,7 +261,6 @@ static const setting userlist_settings[] =
|
||||
{
|
||||
{ST_HEADER, N_("User List"),0,0,0},
|
||||
{ST_TOGGLE, N_("Show hostnames in user list"), P_OFFINTNL(hex_gui_ulist_show_hosts), 0, 0, 0},
|
||||
{ST_TOGGLE, N_("Use the Text box font and colors"), P_OFFINTNL(hex_gui_ulist_style),0,0,0},
|
||||
{ST_TOGGLE, N_("Show icons for user modes"), P_OFFINTNL(hex_gui_ulist_icons), N_("Use graphical icons instead of text symbols in the user list."), 0, 0},
|
||||
{ST_TOGGLE, N_("Color nicknames in userlist"), P_OFFINTNL(hex_gui_ulist_color), N_("Will color nicknames the same as in chat."), 0, 0},
|
||||
{ST_TOGGLE, N_("Show user count in channels"), P_OFFINTNL(hex_gui_ulist_count), 0, 0, 0},
|
||||
@@ -344,34 +336,6 @@ static const setting tabs_settings[] =
|
||||
{ST_END, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static const setting color_settings[] =
|
||||
{
|
||||
{ST_TOGGLE, N_("Messages"), P_OFFINTNL(hex_text_stripcolor_msg), 0, 0, 0},
|
||||
{ST_TOGGLE, N_("Scrollback"), P_OFFINTNL(hex_text_stripcolor_replay), 0, 0, 0},
|
||||
{ST_TOGGLE, N_("Topic"), P_OFFINTNL(hex_text_stripcolor_topic), 0, 0, 0},
|
||||
|
||||
{ST_END, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static const char *const dark_mode_modes[] =
|
||||
{
|
||||
N_("Auto (system)"),
|
||||
N_("Dark"),
|
||||
N_("Light"),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const setting dark_mode_setting =
|
||||
{
|
||||
ST_MENU,
|
||||
N_("Dark mode:"),
|
||||
P_OFFINTNL(hex_gui_dark_mode),
|
||||
N_("Choose how ZoiteChat selects its color palette for the chat buffer, channel list, and user list.\n"
|
||||
"This includes message colors, selection colors, and interface highlights.\n"),
|
||||
dark_mode_modes,
|
||||
0
|
||||
};
|
||||
|
||||
static const char *const dccaccept[] =
|
||||
{
|
||||
N_("Ask for confirmation"),
|
||||
@@ -1182,22 +1146,64 @@ setup_filereq_cb (GtkWidget *entry, char *file)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setup_browsefile_response_cb (GtkNativeDialog *dialog, gint response, gpointer user_data)
|
||||
{
|
||||
GtkWidget *entry = user_data;
|
||||
|
||||
if (response == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
char *file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
|
||||
setup_filereq_cb (entry, file);
|
||||
g_free (file);
|
||||
}
|
||||
|
||||
g_object_unref (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_browsefile_cb (GtkWidget *button, GtkWidget *entry)
|
||||
{
|
||||
/* used for background image only */
|
||||
char *filter;
|
||||
int filter_type;
|
||||
GtkFileChooserNative *dialog;
|
||||
GtkFileFilter *filefilter;
|
||||
const char *current;
|
||||
char *dirname;
|
||||
|
||||
(void)button;
|
||||
dialog = gtk_file_chooser_native_new (_("Select an Image File"),
|
||||
GTK_WINDOW (setup_window),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
_("_Open"),
|
||||
_("_Cancel"));
|
||||
gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (dialog), TRUE);
|
||||
|
||||
current = gtk_entry_get_text (GTK_ENTRY (entry));
|
||||
if (current && current[0])
|
||||
{
|
||||
dirname = g_path_get_dirname (current);
|
||||
if (dirname && dirname[0] && g_file_test (dirname, G_FILE_TEST_IS_DIR))
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), dirname);
|
||||
else if (g_file_test (current, G_FILE_TEST_IS_DIR))
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), current);
|
||||
g_free (dirname);
|
||||
}
|
||||
|
||||
filefilter = gtk_file_filter_new ();
|
||||
#ifdef WIN32
|
||||
filter = "*png;*.tiff;*.gif;*.jpeg;*.jpg";
|
||||
filter_type = FRF_EXTENSIONS;
|
||||
gtk_file_filter_add_pattern (filefilter, "*.png");
|
||||
gtk_file_filter_add_pattern (filefilter, "*.tiff");
|
||||
gtk_file_filter_add_pattern (filefilter, "*.gif");
|
||||
gtk_file_filter_add_pattern (filefilter, "*.jpeg");
|
||||
gtk_file_filter_add_pattern (filefilter, "*.jpg");
|
||||
#else
|
||||
filter = "image/*";
|
||||
filter_type = FRF_MIMETYPES;
|
||||
gtk_file_filter_add_mime_type (filefilter, "image/*");
|
||||
#endif
|
||||
gtkutil_file_req (GTK_WINDOW (setup_window), _("Select an Image File"), setup_filereq_cb,
|
||||
entry, NULL, filter, filter_type|FRF_RECENTLYUSED|FRF_MODAL);
|
||||
gtk_file_filter_set_name (filefilter, _("Images"));
|
||||
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filefilter);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (setup_browsefile_response_cb), entry);
|
||||
gtk_native_dialog_show (GTK_NATIVE_DIALOG (dialog));
|
||||
}
|
||||
|
||||
|
||||
@@ -1233,6 +1239,7 @@ setup_browsefont_cb (GtkWidget *button, GtkWidget *entry)
|
||||
const char *font_name;
|
||||
|
||||
dialog = gtk_font_chooser_dialog_new (_("Select font"), GTK_WINDOW (setup_window));
|
||||
theme_manager_attach_window (dialog);
|
||||
font_dialog = dialog; /* global var */
|
||||
|
||||
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||
@@ -1474,469 +1481,45 @@ setup_create_page (const setting *set)
|
||||
return tab;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_color_selectors_set_sensitive (gboolean sensitive)
|
||||
{
|
||||
GSList *l = color_selector_widgets;
|
||||
while (l)
|
||||
{
|
||||
GtkWidget *w = (GtkWidget *) l->data;
|
||||
if (GTK_IS_WIDGET (w))
|
||||
gtk_widget_set_sensitive (w, sensitive);
|
||||
l = l->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setup_dark_mode_menu_cb (GtkWidget *cbox, const setting *set)
|
||||
{
|
||||
setup_menu_cb (cbox, set);
|
||||
/* Keep color selectors usable even when dark mode is enabled. */
|
||||
setup_color_selectors_set_sensitive (TRUE);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
setup_create_dark_mode_menu (GtkWidget *table, int row, const setting *set)
|
||||
{
|
||||
GtkWidget *wid, *cbox, *box;
|
||||
const char **text = (const char **)set->list;
|
||||
int i;
|
||||
|
||||
wid = gtk_label_new (_(set->label));
|
||||
gtk_widget_set_halign (wid, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (wid, GTK_ALIGN_CENTER);
|
||||
setup_table_attach (table, wid, 2, 3, row, row + 1, FALSE, FALSE,
|
||||
SETUP_ALIGN_START, SETUP_ALIGN_CENTER,
|
||||
LABEL_INDENT, 0);
|
||||
|
||||
cbox = gtk_combo_box_text_new ();
|
||||
|
||||
for (i = 0; text[i]; i++)
|
||||
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cbox), _(text[i]));
|
||||
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX (cbox),
|
||||
setup_get_int (&setup_prefs, set) - set->extra);
|
||||
g_signal_connect (G_OBJECT (cbox), "changed",
|
||||
G_CALLBACK (setup_dark_mode_menu_cb), (gpointer)set);
|
||||
|
||||
box = gtkutil_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (box), cbox, 0, 0, 0);
|
||||
setup_table_attach (table, box, 3, 4, row, row + 1, TRUE, FALSE,
|
||||
SETUP_ALIGN_FILL, SETUP_ALIGN_FILL, 0, 0);
|
||||
|
||||
return cbox;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_color_button_apply (GtkWidget *button, const PaletteColor *color)
|
||||
{
|
||||
GtkWidget *target = g_object_get_data (G_OBJECT (button), "zoitechat-color-box");
|
||||
GtkWidget *apply_widget = GTK_IS_WIDGET (target) ? target : button;
|
||||
|
||||
gtkutil_apply_palette (apply_widget, color, NULL, NULL);
|
||||
|
||||
if (apply_widget != button)
|
||||
gtkutil_apply_palette (button, color, NULL, NULL);
|
||||
|
||||
gtk_widget_queue_draw (button);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWidget *button;
|
||||
PaletteColor *color;
|
||||
} setup_color_dialog_data;
|
||||
|
||||
static void
|
||||
setup_rgba_from_palette (const PaletteColor *color, GdkRGBA *rgba)
|
||||
{
|
||||
guint16 red, green, blue;
|
||||
char color_string[16];
|
||||
|
||||
palette_color_get_rgb16 (color, &red, &green, &blue);
|
||||
g_snprintf (color_string, sizeof (color_string), "#%04x%04x%04x",
|
||||
red, green, blue);
|
||||
if (!gdk_rgba_parse (rgba, color_string))
|
||||
{
|
||||
rgba->red = red / 65535.0;
|
||||
rgba->green = green / 65535.0;
|
||||
rgba->blue = blue / 65535.0;
|
||||
rgba->alpha = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setup_color_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
|
||||
{
|
||||
setup_color_dialog_data *data = user_data;
|
||||
|
||||
if (response_id == GTK_RESPONSE_OK)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
|
||||
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog), &rgba);
|
||||
*data->color = rgba;
|
||||
color_change = TRUE;
|
||||
setup_color_button_apply (data->button, data->color);
|
||||
|
||||
if (fe_dark_mode_is_enabled_for (setup_prefs.hex_gui_dark_mode))
|
||||
palette_dark_set_color ((int)(data->color - colors), data->color);
|
||||
else
|
||||
palette_user_set_color ((int)(data->color - colors), data->color);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_color_cb (GtkWidget *button, gpointer userdata)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
PaletteColor *color;
|
||||
GdkRGBA rgba;
|
||||
setup_color_dialog_data *data;
|
||||
|
||||
color = &colors[GPOINTER_TO_INT (userdata)];
|
||||
|
||||
dialog = gtk_color_chooser_dialog_new (_("Select color"), GTK_WINDOW (setup_window));
|
||||
setup_rgba_from_palette (color, &rgba);
|
||||
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (dialog), &rgba);
|
||||
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||
|
||||
data = g_new0 (setup_color_dialog_data, 1);
|
||||
data->button = button;
|
||||
data->color = color;
|
||||
g_signal_connect (dialog, "response", G_CALLBACK (setup_color_response_cb), data);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_create_color_button (GtkWidget *table, int num, int row, int col)
|
||||
{
|
||||
GtkWidget *but;
|
||||
GtkWidget *label;
|
||||
GtkWidget *box;
|
||||
char buf[64];
|
||||
|
||||
if (num > 31)
|
||||
strcpy (buf, "<span size=\"x-small\">  </span>");
|
||||
else if (num < 10)
|
||||
sprintf (buf, "<span size=\"x-small\"> %d</span>", num);
|
||||
else
|
||||
/* 12345678901 23456789 01 23456789 */
|
||||
sprintf (buf, "<span size=\"x-small\">%d</span>", num);
|
||||
but = gtk_button_new ();
|
||||
label = gtk_label_new (" ");
|
||||
gtk_label_set_markup (GTK_LABEL (label), buf);
|
||||
box = gtk_event_box_new ();
|
||||
gtk_event_box_set_visible_window (GTK_EVENT_BOX (box), TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (box), label);
|
||||
gtk_container_add (GTK_CONTAINER (but), box);
|
||||
gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
|
||||
gtk_widget_show (label);
|
||||
gtk_widget_show (box);
|
||||
/* win32 build uses this to turn off themeing */
|
||||
g_object_set_data (G_OBJECT (but), "zoitechat-color", (gpointer)1);
|
||||
g_object_set_data (G_OBJECT (but), "zoitechat-color-box", box);
|
||||
setup_table_attach (table, but, col, col + 1, row, row + 1, FALSE, FALSE,
|
||||
SETUP_ALIGN_CENTER, SETUP_ALIGN_CENTER, 0, 0);
|
||||
g_signal_connect (G_OBJECT (but), "clicked",
|
||||
G_CALLBACK (setup_color_cb), GINT_TO_POINTER (num));
|
||||
setup_color_button_apply (but, &colors[num]);
|
||||
|
||||
/* Track all color selector widgets (used for dark mode UI behavior). */
|
||||
color_selector_widgets = g_slist_prepend (color_selector_widgets, but);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_create_other_colorR (char *text, int num, int row, GtkWidget *tab)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (text);
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
setup_table_attach (tab, label, 5, 9, row, row + 1, FALSE, FALSE,
|
||||
SETUP_ALIGN_START, SETUP_ALIGN_CENTER,
|
||||
LABEL_INDENT, 0);
|
||||
setup_create_color_button (tab, num, row, 9);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_create_other_color (char *text, int num, int row, GtkWidget *tab)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (text);
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
setup_table_attach (tab, label, 2, 3, row, row + 1, FALSE, FALSE,
|
||||
SETUP_ALIGN_START, SETUP_ALIGN_CENTER,
|
||||
LABEL_INDENT, 0);
|
||||
setup_create_color_button (tab, num, row, 3);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
setup_create_color_page (void)
|
||||
{
|
||||
color_selector_widgets = NULL;
|
||||
|
||||
GtkWidget *tab, *box, *label;
|
||||
int i;
|
||||
|
||||
box = gtkutil_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (box), 6);
|
||||
|
||||
tab = gtkutil_grid_new (9, 2, FALSE);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (tab), 6);
|
||||
gtk_grid_set_row_spacing (GTK_GRID (tab), 2);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (tab), 3);
|
||||
gtk_container_add (GTK_CONTAINER (box), tab);
|
||||
|
||||
setup_create_header (tab, 0, N_("Text Colors"));
|
||||
|
||||
label = gtk_label_new (_("mIRC colors:"));
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
setup_table_attach (tab, label, 2, 3, 1, 2, FALSE, FALSE,
|
||||
SETUP_ALIGN_START, SETUP_ALIGN_CENTER,
|
||||
LABEL_INDENT, 0);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
setup_create_color_button (tab, i, 1, i+3);
|
||||
|
||||
label = gtk_label_new (_("Local colors:"));
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
setup_table_attach (tab, label, 2, 3, 2, 3, FALSE, FALSE,
|
||||
SETUP_ALIGN_START, SETUP_ALIGN_CENTER,
|
||||
LABEL_INDENT, 0);
|
||||
|
||||
for (i = 16; i < 32; i++)
|
||||
setup_create_color_button (tab, i, 2, (i+3) - 16);
|
||||
|
||||
setup_create_other_color (_("Foreground:"), COL_FG, 3, tab);
|
||||
setup_create_other_colorR (_("Background:"), COL_BG, 3, tab);
|
||||
|
||||
setup_create_header (tab, 5, N_("Selected Text"));
|
||||
|
||||
setup_create_other_color (_("Foreground:"), COL_MARK_FG, 6, tab);
|
||||
setup_create_other_colorR (_("Background:"), COL_MARK_BG, 6, tab);
|
||||
|
||||
setup_create_header (tab, 8, N_("Interface Colors"));
|
||||
|
||||
setup_create_other_color (_("New data:"), COL_NEW_DATA, 9, tab);
|
||||
setup_create_other_colorR (_("Marker line:"), COL_MARKER, 9, tab);
|
||||
setup_create_other_color (_("New message:"), COL_NEW_MSG, 10, tab);
|
||||
setup_create_other_colorR (_("Away user:"), COL_AWAY, 10, tab);
|
||||
setup_create_other_color (_("Highlight:"), COL_HILIGHT, 11, tab);
|
||||
setup_create_other_colorR (_("Spell checker:"), COL_SPELL, 11, tab);
|
||||
setup_create_dark_mode_menu (tab, 13, &dark_mode_setting);
|
||||
setup_color_selectors_set_sensitive (TRUE);
|
||||
setup_create_header (tab, 15, N_("Color Stripping"));
|
||||
|
||||
/* label = gtk_label_new (_("Strip colors from:"));
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
setup_table_attach (tab, label, 2, 3, 16, 17, FALSE, FALSE,
|
||||
SETUP_ALIGN_FILL, SETUP_ALIGN_FILL,
|
||||
LABEL_INDENT, 0); */
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
setup_create_toggleL (tab, i + 16, &color_settings[i]);
|
||||
}
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_theme_show_message (GtkMessageType message_type, const char *primary)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (setup_window), GTK_DIALOG_MODAL,
|
||||
message_type, GTK_BUTTONS_CLOSE, "%s", primary);
|
||||
gtk_dialog_run (GTK_DIALOG (dialog));
|
||||
gtk_widget_destroy (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_theme_populate (setup_theme_ui *ui)
|
||||
{
|
||||
char *themes_dir;
|
||||
GDir *dir;
|
||||
const char *name;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
int count;
|
||||
|
||||
model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->combo));
|
||||
while (gtk_tree_model_get_iter_first (model, &iter))
|
||||
gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (ui->combo), 0);
|
||||
|
||||
themes_dir = g_build_filename (get_xdir (), "themes", NULL);
|
||||
if (!g_file_test (themes_dir, G_FILE_TEST_IS_DIR))
|
||||
g_mkdir_with_parents (themes_dir, 0700);
|
||||
|
||||
dir = g_dir_open (themes_dir, 0, NULL);
|
||||
if (dir)
|
||||
{
|
||||
while ((name = g_dir_read_name (dir)))
|
||||
{
|
||||
char *path = g_build_filename (themes_dir, name, NULL);
|
||||
if (g_file_test (path, G_FILE_TEST_IS_DIR))
|
||||
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (ui->combo), name);
|
||||
g_free (path);
|
||||
}
|
||||
g_dir_close (dir);
|
||||
}
|
||||
|
||||
count = gtk_tree_model_iter_n_children (gtk_combo_box_get_model (GTK_COMBO_BOX (ui->combo)), NULL);
|
||||
if (count > 0)
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX (ui->combo), 0);
|
||||
|
||||
gtk_widget_set_sensitive (ui->apply_button, count > 0);
|
||||
gtk_label_set_text (GTK_LABEL (ui->status_label),
|
||||
count > 0 ? _("Select a theme to apply.") : _("No themes found."));
|
||||
|
||||
g_free (themes_dir);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_theme_refresh_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
setup_theme_ui *ui = user_data;
|
||||
|
||||
setup_theme_populate (ui);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_theme_open_folder_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
char *themes_dir;
|
||||
|
||||
themes_dir = g_build_filename (get_xdir (), "themes", NULL);
|
||||
g_mkdir_with_parents (themes_dir, 0700);
|
||||
fe_open_url (themes_dir);
|
||||
g_free (themes_dir);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_theme_selection_changed (GtkComboBox *combo, gpointer user_data)
|
||||
{
|
||||
setup_theme_ui *ui = user_data;
|
||||
gboolean has_selection = gtk_combo_box_get_active (combo) >= 0;
|
||||
|
||||
gtk_widget_set_sensitive (ui->apply_button, has_selection);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_theme_apply_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
setup_theme_ui *ui = user_data;
|
||||
GtkWidget *dialog;
|
||||
gint response;
|
||||
char *theme;
|
||||
GError *error = NULL;
|
||||
|
||||
theme = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (ui->combo));
|
||||
if (!theme)
|
||||
return;
|
||||
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (setup_window), GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
|
||||
"%s", _("Applying a theme will overwrite your current colors and event settings.\nContinue?"));
|
||||
response = gtk_dialog_run (GTK_DIALOG (dialog));
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
if (response != GTK_RESPONSE_OK)
|
||||
{
|
||||
g_free (theme);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!zoitechat_apply_theme (theme, &error))
|
||||
{
|
||||
setup_theme_show_message (GTK_MESSAGE_ERROR, error ? error->message : _("Failed to apply theme."));
|
||||
g_clear_error (&error);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
palette_load ();
|
||||
palette_apply_dark_mode (fe_dark_mode_is_enabled ());
|
||||
color_change = TRUE;
|
||||
setup_apply_real (0, TRUE, FALSE, FALSE);
|
||||
|
||||
setup_theme_show_message (GTK_MESSAGE_INFO, _("Theme applied. Some changes may require a restart to take full effect."));
|
||||
|
||||
cleanup:
|
||||
g_free (theme);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
setup_create_theme_page (void)
|
||||
{
|
||||
setup_theme_ui *ui;
|
||||
return theme_preferences_create_page (GTK_WINDOW (setup_window),
|
||||
&setup_prefs,
|
||||
&color_change);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
setup_create_appearance_page (void)
|
||||
{
|
||||
GtkWidget *box;
|
||||
GtkWidget *label;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *button_box;
|
||||
char *themes_dir;
|
||||
char *markup;
|
||||
GtkWidget *appearance_page;
|
||||
GtkWidget *theme_label;
|
||||
GtkWidget *theme_page;
|
||||
GtkWidget *advanced_page;
|
||||
|
||||
ui = g_new0 (setup_theme_ui, 1);
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
||||
appearance_page = setup_create_page (appearance_settings);
|
||||
theme_label = gtk_label_new (NULL);
|
||||
theme_page = setup_create_theme_page ();
|
||||
advanced_page = setup_create_page (appearance_advanced_settings);
|
||||
|
||||
box = gtkutil_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 6);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (box), 6);
|
||||
{
|
||||
char *markup = g_markup_printf_escaped ("<b>%s</b>", _("GTK3 Theme"));
|
||||
gtk_label_set_markup (GTK_LABEL (theme_label), markup);
|
||||
g_free (markup);
|
||||
}
|
||||
gtk_widget_set_halign (theme_label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (theme_label, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_margin_start (theme_label, 2);
|
||||
gtk_widget_set_margin_end (theme_label, 2);
|
||||
gtk_widget_set_margin_top (theme_label, 1);
|
||||
gtk_widget_set_margin_bottom (theme_label, 1);
|
||||
|
||||
themes_dir = g_build_filename (get_xdir (), "themes", NULL);
|
||||
markup = g_markup_printf_escaped (_("Theme files are loaded from <tt>%s</tt>."), themes_dir);
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_markup (GTK_LABEL (label), markup);
|
||||
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
|
||||
g_free (markup);
|
||||
g_free (themes_dir);
|
||||
|
||||
hbox = gtkutil_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 6);
|
||||
gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
|
||||
|
||||
ui->combo = gtk_combo_box_text_new ();
|
||||
gtk_box_pack_start (GTK_BOX (hbox), ui->combo, TRUE, TRUE, 0);
|
||||
g_signal_connect (G_OBJECT (ui->combo), "changed",
|
||||
G_CALLBACK (setup_theme_selection_changed), ui);
|
||||
|
||||
button_box = gtkutil_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 6);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), button_box, FALSE, FALSE, 0);
|
||||
|
||||
ui->apply_button = gtk_button_new_with_mnemonic (_("_Apply Theme"));
|
||||
gtk_box_pack_start (GTK_BOX (button_box), ui->apply_button, FALSE, FALSE, 0);
|
||||
g_signal_connect (G_OBJECT (ui->apply_button), "clicked",
|
||||
G_CALLBACK (setup_theme_apply_cb), ui);
|
||||
|
||||
label = gtk_button_new_with_mnemonic (_("_Refresh"));
|
||||
gtk_box_pack_start (GTK_BOX (button_box), label, FALSE, FALSE, 0);
|
||||
g_signal_connect (G_OBJECT (label), "clicked",
|
||||
G_CALLBACK (setup_theme_refresh_cb), ui);
|
||||
|
||||
label = gtk_button_new_with_mnemonic (_("_Open Folder"));
|
||||
gtk_box_pack_start (GTK_BOX (button_box), label, FALSE, FALSE, 0);
|
||||
g_signal_connect (G_OBJECT (label), "clicked",
|
||||
G_CALLBACK (setup_theme_open_folder_cb), ui);
|
||||
|
||||
ui->status_label = gtk_label_new (NULL);
|
||||
gtk_widget_set_halign (ui->status_label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (ui->status_label, GTK_ALIGN_CENTER);
|
||||
gtk_box_pack_start (GTK_BOX (box), ui->status_label, FALSE, FALSE, 0);
|
||||
|
||||
setup_theme_populate (ui);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (box), "setup-theme-ui", ui, g_free);
|
||||
gtk_box_pack_start (GTK_BOX (box), appearance_page, FALSE, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (box), theme_label, FALSE, FALSE, 2);
|
||||
gtk_box_pack_start (GTK_BOX (box), theme_page, FALSE, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (box), advanced_page, FALSE, FALSE, 0);
|
||||
|
||||
return box;
|
||||
}
|
||||
@@ -2228,8 +1811,6 @@ static const char *const cata_interface[] =
|
||||
N_("Input box"),
|
||||
N_("User list"),
|
||||
N_("Channel switcher"),
|
||||
N_("Themes"),
|
||||
N_("Colors"),
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -2255,16 +1836,15 @@ static GtkWidget *
|
||||
setup_create_pages (GtkWidget *box)
|
||||
{
|
||||
GtkWidget *book;
|
||||
GtkWindow *win = GTK_WINDOW(gtk_widget_get_toplevel (box));
|
||||
GtkWindow *win = GTK_WINDOW (setup_window);
|
||||
|
||||
(void)box;
|
||||
book = gtk_notebook_new ();
|
||||
|
||||
setup_add_page (cata_interface[0], book, setup_create_page (appearance_settings));
|
||||
setup_add_page (cata_interface[0], book, setup_create_appearance_page ());
|
||||
setup_add_page (cata_interface[1], book, setup_create_page (inputbox_settings));
|
||||
setup_add_page (cata_interface[2], book, setup_create_page (userlist_settings));
|
||||
setup_add_page (cata_interface[3], book, setup_create_page (tabs_settings));
|
||||
setup_add_page (cata_interface[4], book, setup_create_theme_page ());
|
||||
setup_add_page (cata_interface[5], book, setup_create_color_page ());
|
||||
|
||||
setup_add_page (cata_chatting[0], book, setup_create_page (general_settings));
|
||||
|
||||
@@ -2405,67 +1985,12 @@ setup_create_tree (GtkWidget *box, GtkWidget *book)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setup_apply_entry_style (GtkWidget *entry)
|
||||
{
|
||||
gtkutil_apply_palette (entry, &colors[COL_BG], &colors[COL_FG],
|
||||
input_style->font_desc);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_apply_to_sess (session_gui *gui)
|
||||
{
|
||||
mg_update_xtext (gui->xtext);
|
||||
chanview_apply_theme ((chanview *) gui->chanview);
|
||||
|
||||
|
||||
{
|
||||
const PaletteColor *bg = NULL;
|
||||
const PaletteColor *fg = NULL;
|
||||
const PangoFontDescription *font = NULL;
|
||||
|
||||
if (prefs.hex_gui_ulist_style || fe_dark_mode_is_enabled ())
|
||||
bg = &colors[COL_BG];
|
||||
if (fe_dark_mode_is_enabled ())
|
||||
fg = &colors[COL_FG];
|
||||
if (input_style)
|
||||
font = input_style->font_desc;
|
||||
|
||||
gtkutil_apply_palette (gui->user_tree, bg, fg, font);
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_input_style)
|
||||
{
|
||||
char buf[128];
|
||||
GtkCssProvider *provider = gtk_css_provider_new ();
|
||||
GtkStyleContext *context;
|
||||
char *color_string = gdk_rgba_to_string (&colors[COL_FG]);
|
||||
|
||||
g_snprintf (buf, sizeof (buf), ".zoitechat-inputbox { caret-color: %s; }",
|
||||
color_string);
|
||||
gtk_css_provider_load_from_data (provider, buf, -1, NULL);
|
||||
g_free (color_string);
|
||||
|
||||
context = gtk_widget_get_style_context (gui->input_box);
|
||||
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
context = gtk_widget_get_style_context (gui->limit_entry);
|
||||
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
context = gtk_widget_get_style_context (gui->key_entry);
|
||||
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
context = gtk_widget_get_style_context (gui->topic_entry);
|
||||
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
|
||||
g_object_unref (provider);
|
||||
|
||||
setup_apply_entry_style (gui->input_box);
|
||||
setup_apply_entry_style (gui->limit_entry);
|
||||
setup_apply_entry_style (gui->key_entry);
|
||||
setup_apply_entry_style (gui->topic_entry);
|
||||
}
|
||||
theme_preferences_apply_to_session (gui, input_style);
|
||||
|
||||
if (prefs.hex_gui_ulist_buttons)
|
||||
gtk_widget_show (gui->button_box);
|
||||
@@ -2496,7 +2021,7 @@ unslash (char *dir)
|
||||
}
|
||||
|
||||
void
|
||||
setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd)
|
||||
setup_apply_real (const ThemeChangedEvent *event)
|
||||
{
|
||||
GSList *list;
|
||||
session *sess;
|
||||
@@ -2509,14 +2034,14 @@ setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd)
|
||||
g_mkdir (prefs.hex_dcc_dir, 0700);
|
||||
g_mkdir (prefs.hex_dcc_completed_dir, 0700);
|
||||
|
||||
if (new_pix)
|
||||
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PIXMAP))
|
||||
{
|
||||
if (channelwin_pix)
|
||||
cairo_surface_destroy (channelwin_pix);
|
||||
channelwin_pix = pixmap_load_from_file (prefs.hex_text_background);
|
||||
}
|
||||
|
||||
input_style = create_input_style (input_style);
|
||||
theme_manager_reload_input_style ();
|
||||
|
||||
list = sess_list;
|
||||
while (list)
|
||||
@@ -2537,7 +2062,7 @@ setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd)
|
||||
|
||||
log_open_or_close (sess);
|
||||
|
||||
if (do_ulist)
|
||||
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_USERLIST))
|
||||
userlist_rehash (sess);
|
||||
|
||||
list = list->next;
|
||||
@@ -2547,10 +2072,10 @@ setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd)
|
||||
tray_apply_setup ();
|
||||
zoitechat_reinit_timers ();
|
||||
|
||||
if (do_layout)
|
||||
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_LAYOUT))
|
||||
menu_change_layout ();
|
||||
|
||||
if (do_identd)
|
||||
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_IDENTD))
|
||||
handle_command (current_sess, "IDENTD reload", FALSE);
|
||||
}
|
||||
|
||||
@@ -2562,15 +2087,11 @@ setup_apply (struct zoitechatprefs *pr)
|
||||
PangoFontDescription *new_desc;
|
||||
char buffer[4 * FONTNAMELEN + 1];
|
||||
#endif
|
||||
int new_pix = FALSE;
|
||||
int noapply = FALSE;
|
||||
int do_ulist = FALSE;
|
||||
int do_layout = FALSE;
|
||||
int do_identd = FALSE;
|
||||
int old_dark_mode = prefs.hex_gui_dark_mode;
|
||||
int noapply = FALSE;
|
||||
ThemeChangedEvent event;
|
||||
struct zoitechatprefs old_prefs = prefs;
|
||||
int old_dark_mode = prefs.hex_gui_dark_mode;
|
||||
|
||||
if (strcmp (pr->hex_text_background, prefs.hex_text_background) != 0)
|
||||
new_pix = TRUE;
|
||||
|
||||
#define DIFF(a) (pr->a != prefs.a)
|
||||
|
||||
@@ -2604,23 +2125,8 @@ setup_apply (struct zoitechatprefs *pr)
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_ulist_show_hosts))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_ulist_style))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_ulist_sort))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_input_style) && prefs.hex_gui_input_style == TRUE)
|
||||
noapply = TRUE; /* Requires restart to *disable* */
|
||||
|
||||
if (DIFF (hex_gui_tab_dots))
|
||||
do_layout = TRUE;
|
||||
if (DIFF (hex_gui_tab_layout))
|
||||
do_layout = TRUE;
|
||||
|
||||
if (DIFF (hex_identd_server) || DIFF (hex_identd_port))
|
||||
do_identd = TRUE;
|
||||
|
||||
if (color_change || (DIFF (hex_gui_ulist_color)) || (DIFF (hex_away_size_max)) || (DIFF (hex_away_track)))
|
||||
do_ulist = TRUE;
|
||||
|
||||
if ((pr->hex_gui_tab_pos == 5 || pr->hex_gui_tab_pos == 6) &&
|
||||
pr->hex_gui_tab_layout == 2 && pr->hex_gui_tab_pos != prefs.hex_gui_tab_pos)
|
||||
@@ -2638,23 +2144,7 @@ setup_apply (struct zoitechatprefs *pr)
|
||||
|
||||
memcpy (&prefs, pr, sizeof (prefs));
|
||||
|
||||
/*
|
||||
* "Dark mode" applies ZoiteChat's built-in dark palette to the chat views.
|
||||
*
|
||||
* IMPORTANT: don't short-circuit this call.
|
||||
* We MUST run palette_apply_dark_mode() when the setting changes, otherwise
|
||||
* the preference flips but the palette stays the same (aka: "nothing happens").
|
||||
*/
|
||||
{
|
||||
gboolean pal_changed = FALSE;
|
||||
|
||||
fe_apply_theme_for_mode (prefs.hex_gui_dark_mode, &pal_changed);
|
||||
if (prefs.hex_gui_dark_mode != old_dark_mode || pal_changed)
|
||||
color_change = TRUE;
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO)
|
||||
fe_set_auto_dark_mode_state (fe_dark_mode_is_enabled_for (ZOITECHAT_DARK_MODE_AUTO));
|
||||
event = theme_manager_on_preferences_changed (&old_prefs, &prefs, old_dark_mode, &color_change);
|
||||
|
||||
#ifdef WIN32
|
||||
/* merge hex_font_main and hex_font_alternative into hex_font_normal */
|
||||
@@ -2678,7 +2168,7 @@ setup_apply (struct zoitechatprefs *pr)
|
||||
strcpy (prefs.hex_irc_real_name, "realname");
|
||||
}
|
||||
|
||||
setup_apply_real (new_pix, do_ulist, do_layout, do_identd);
|
||||
theme_manager_dispatch_setup_apply (&event);
|
||||
|
||||
if (noapply)
|
||||
fe_message (_("Some settings were changed that require a"
|
||||
@@ -2704,7 +2194,7 @@ setup_ok_cb (GtkWidget *but, GtkWidget *win)
|
||||
gtk_widget_destroy (win);
|
||||
setup_apply (&setup_prefs);
|
||||
save_config ();
|
||||
palette_save ();
|
||||
theme_manager_save_preferences ();
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
@@ -2715,6 +2205,7 @@ setup_window_open (void)
|
||||
|
||||
g_snprintf(buf, sizeof(buf), _("Preferences - %s"), _(DISPLAY_NAME));
|
||||
win = gtkutil_window_new (buf, "prefs", 0, 600, 2);
|
||||
setup_window = win;
|
||||
|
||||
vbox = gtkutil_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 5);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
|
||||
@@ -2751,11 +2242,6 @@ setup_close_cb (GtkWidget *win, GtkWidget **swin)
|
||||
{
|
||||
*swin = NULL;
|
||||
|
||||
if (color_selector_widgets)
|
||||
{
|
||||
g_slist_free (color_selector_widgets);
|
||||
color_selector_widgets = NULL;
|
||||
}
|
||||
|
||||
if (font_dialog)
|
||||
{
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#ifndef ZOITECHAT_SETUP_H
|
||||
#define ZOITECHAT_SETUP_H
|
||||
|
||||
void setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd);
|
||||
#include "theme/theme-manager.h"
|
||||
|
||||
void setup_apply_real (const ThemeChangedEvent *event);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/zoitechatc.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-access.h"
|
||||
#include "theme/theme-palette.h"
|
||||
#include "xtext.h"
|
||||
#include "gtkutil.h"
|
||||
|
||||
@@ -56,6 +57,34 @@
|
||||
#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
|
||||
@@ -353,7 +382,7 @@ insert_underline_error (SexySpellEntry *entry, guint start, guint end)
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
|
||||
palette_color_get_rgb16 (&colors[COL_SPELL], &red, &green, &blue);
|
||||
theme_token_color_rgb16 (THEME_TOKEN_SPELL, &red, &green, &blue);
|
||||
ucolor = pango_attr_underline_color_new (red, green, blue);
|
||||
unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR);
|
||||
|
||||
@@ -421,27 +450,27 @@ insert_color (SexySpellEntry *entry, guint start, int fgcolor, int bgcolor)
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
|
||||
if (fgcolor < 0 || fgcolor > MAX_COL)
|
||||
if (fgcolor < 0)
|
||||
{
|
||||
palette_color_get_rgb16 (&colors[COL_FG], &red, &green, &blue);
|
||||
theme_token_color_rgb16 (THEME_TOKEN_TEXT_FOREGROUND, &red, &green, &blue);
|
||||
fgattr = pango_attr_foreground_new (red, green, blue);
|
||||
ulattr = pango_attr_underline_color_new (red, green, blue);
|
||||
}
|
||||
else
|
||||
{
|
||||
palette_color_get_rgb16 (&colors[fgcolor], &red, &green, &blue);
|
||||
theme_mirc_color_rgb16 (fgcolor, &red, &green, &blue);
|
||||
fgattr = pango_attr_foreground_new (red, green, blue);
|
||||
ulattr = pango_attr_underline_color_new (red, green, blue);
|
||||
}
|
||||
|
||||
if (bgcolor < 0 || bgcolor > MAX_COL)
|
||||
if (bgcolor < 0)
|
||||
{
|
||||
palette_color_get_rgb16 (&colors[COL_BG], &red, &green, &blue);
|
||||
theme_token_color_rgb16 (THEME_TOKEN_TEXT_BACKGROUND, &red, &green, &blue);
|
||||
bgattr = pango_attr_background_new (red, green, blue);
|
||||
}
|
||||
else
|
||||
{
|
||||
palette_color_get_rgb16 (&colors[bgcolor], &red, &green, &blue);
|
||||
theme_mirc_color_rgb16 (bgcolor, &red, &green, &blue);
|
||||
bgattr = pango_attr_background_new (red, green, blue);
|
||||
}
|
||||
|
||||
@@ -1053,7 +1082,7 @@ check_attributes (SexySpellEntry *entry, const char *text, int len)
|
||||
|
||||
case ATTR_REVERSE:
|
||||
insert_hiddenchar (entry, i, i + 1);
|
||||
insert_color (entry, i, COL_BG, COL_FG);
|
||||
insert_color (entry, i, THEME_TOKEN_TEXT_BACKGROUND, THEME_TOKEN_TEXT_FOREGROUND);
|
||||
goto check_color;
|
||||
|
||||
case '\n':
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
#include "gtkutil.h"
|
||||
#include "xtext.h"
|
||||
#include "maingui.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-access.h"
|
||||
#include "theme/theme-manager.h"
|
||||
#include "textgui.h"
|
||||
|
||||
#define ICON_TEXTEVENT_SAVE_AS "document-save-as"
|
||||
@@ -49,6 +50,8 @@ 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,
|
||||
@@ -151,6 +154,51 @@ 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)
|
||||
{
|
||||
@@ -467,12 +515,16 @@ 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);
|
||||
|
||||
palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
|
||||
theme_get_xtext_colors_for_widget (wid, 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);
|
||||
@@ -487,4 +539,5 @@ pevent_dialog_show ()
|
||||
NULL, _("OK"));
|
||||
|
||||
gtk_widget_show_all (pevent_dialog);
|
||||
pevent_dialog_theme_apply (pevent_dialog);
|
||||
}
|
||||
|
||||
349
src/fe-gtk/theme/tests/test-theme-access-routing.c
Normal file
349
src/fe-gtk/theme/tests/test-theme-access-routing.c
Normal file
@@ -0,0 +1,349 @@
|
||||
#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 ();
|
||||
}
|
||||
122
src/fe-gtk/theme/tests/test-theme-application-input-style.c
Normal file
122
src/fe-gtk/theme/tests/test-theme-application-input-style.c
Normal file
@@ -0,0 +1,122 @@
|
||||
#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 ();
|
||||
}
|
||||
325
src/fe-gtk/theme/tests/test-theme-gtk3-settings.c
Normal file
325
src/fe-gtk/theme/tests/test-theme-gtk3-settings.c
Normal file
@@ -0,0 +1,325 @@
|
||||
#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;
|
||||
}
|
||||
71
src/fe-gtk/theme/tests/test-theme-gtk3-stub.c
Normal file
71
src/fe-gtk/theme/tests/test-theme-gtk3-stub.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#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;
|
||||
}
|
||||
272
src/fe-gtk/theme/tests/test-theme-manager-auto-refresh.c
Normal file
272
src/fe-gtk/theme/tests/test-theme-manager-auto-refresh.c
Normal file
@@ -0,0 +1,272 @@
|
||||
#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 ();
|
||||
}
|
||||
284
src/fe-gtk/theme/tests/test-theme-manager-dispatch-routing.c
Normal file
284
src/fe-gtk/theme/tests/test-theme-manager-dispatch-routing.c
Normal file
@@ -0,0 +1,284 @@
|
||||
#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 ();
|
||||
}
|
||||
379
src/fe-gtk/theme/tests/test-theme-manager-policy.c
Normal file
379
src/fe-gtk/theme/tests/test-theme-manager-policy.c
Normal file
@@ -0,0 +1,379 @@
|
||||
#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 ();
|
||||
}
|
||||
280
src/fe-gtk/theme/tests/test-theme-preferences-gtk3-populate.c
Normal file
280
src/fe-gtk/theme/tests/test-theme-preferences-gtk3-populate.c
Normal file
@@ -0,0 +1,280 @@
|
||||
#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 ();
|
||||
}
|
||||
373
src/fe-gtk/theme/tests/test-theme-runtime-persistence.c
Normal file
373
src/fe-gtk/theme/tests/test-theme-runtime-persistence.c
Normal file
@@ -0,0 +1,373 @@
|
||||
#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 ();
|
||||
}
|
||||
146
src/fe-gtk/theme/theme-access.c
Normal file
146
src/fe-gtk/theme/theme-access.c
Normal file
@@ -0,0 +1,146 @@
|
||||
#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, >k_map))
|
||||
{
|
||||
theme_runtime_get_widget_style_values_mapped (>k_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;
|
||||
}
|
||||
}
|
||||
22
src/fe-gtk/theme/theme-access.h
Normal file
22
src/fe-gtk/theme/theme-access.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#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
|
||||
113
src/fe-gtk/theme/theme-application.c
Normal file
113
src/fe-gtk/theme/theme-application.c
Normal file
@@ -0,0 +1,113 @@
|
||||
#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;
|
||||
}
|
||||
11
src/fe-gtk/theme/theme-application.h
Normal file
11
src/fe-gtk/theme/theme-application.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#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
|
||||
340
src/fe-gtk/theme/theme-css.c
Normal file
340
src/fe-gtk/theme/theme-css.c
Normal file
@@ -0,0 +1,340 @@
|
||||
#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);
|
||||
}
|
||||
21
src/fe-gtk/theme/theme-css.h
Normal file
21
src/fe-gtk/theme/theme-css.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#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
|
||||
9
src/fe-gtk/theme/theme-gtk.h
Normal file
9
src/fe-gtk/theme/theme-gtk.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#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
|
||||
944
src/fe-gtk/theme/theme-gtk3.c
Normal file
944
src/fe-gtk/theme/theme-gtk3.c
Normal file
@@ -0,0 +1,944 @@
|
||||
#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 (¤t, G_PARAM_SPEC_VALUE_TYPE (property));
|
||||
g_object_get_property (G_OBJECT (settings), "gtk-application-prefer-dark-theme", ¤t);
|
||||
if (g_value_get_boolean (¤t) != dark)
|
||||
g_object_set (settings, "gtk-application-prefer-dark-theme", dark, NULL);
|
||||
g_value_unset (¤t);
|
||||
}
|
||||
|
||||
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 (¤t, G_PARAM_SPEC_VALUE_TYPE (property));
|
||||
g_object_get_property (G_OBJECT (settings), property_name, ¤t);
|
||||
g_hash_table_insert (defaults, g_strdup (property_name), settings_value_dup (¤t));
|
||||
g_value_unset (¤t);
|
||||
}
|
||||
|
||||
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 (¤t, property_type);
|
||||
g_object_get_property (G_OBJECT (settings), property_name, ¤t);
|
||||
changed = !settings_value_equal_typed (¤t, &value, property_type);
|
||||
g_value_unset (¤t);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
22
src/fe-gtk/theme/theme-gtk3.h
Normal file
22
src/fe-gtk/theme/theme-gtk3.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#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
|
||||
681
src/fe-gtk/theme/theme-manager.c
Normal file
681
src/fe-gtk/theme/theme-manager.c
Normal file
@@ -0,0 +1,681 @@
|
||||
#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;
|
||||
}
|
||||
75
src/fe-gtk/theme/theme-manager.h
Normal file
75
src/fe-gtk/theme/theme-manager.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#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
|
||||
286
src/fe-gtk/theme/theme-palette.c
Normal file
286
src/fe-gtk/theme/theme-palette.c
Normal file
@@ -0,0 +1,286 @@
|
||||
#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);
|
||||
}
|
||||
143
src/fe-gtk/theme/theme-palette.h
Normal file
143
src/fe-gtk/theme/theme-palette.h
Normal file
@@ -0,0 +1,143 @@
|
||||
#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
|
||||
59
src/fe-gtk/theme/theme-policy.c
Normal file
59
src/fe-gtk/theme/theme-policy.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#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);
|
||||
}
|
||||
10
src/fe-gtk/theme/theme-policy.h
Normal file
10
src/fe-gtk/theme/theme-policy.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#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
|
||||
1376
src/fe-gtk/theme/theme-preferences.c
Normal file
1376
src/fe-gtk/theme/theme-preferences.c
Normal file
File diff suppressed because it is too large
Load Diff
18
src/fe-gtk/theme/theme-preferences.h
Normal file
18
src/fe-gtk/theme/theme-preferences.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#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
|
||||
592
src/fe-gtk/theme/theme-runtime.c
Normal file
592
src/fe-gtk/theme/theme-runtime.c
Normal file
@@ -0,0 +1,592 @@
|
||||
#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 (>k_map->text_foreground, &fallback, &text_foreground);
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_TEXT_BACKGROUND, &fallback));
|
||||
theme_runtime_resolve_color (>k_map->text_background, &fallback, &text_background);
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_SELECTION_FOREGROUND, &fallback));
|
||||
theme_runtime_resolve_color (>k_map->selection_foreground, &fallback, &selection_foreground);
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_SELECTION_BACKGROUND, &fallback));
|
||||
theme_runtime_resolve_color (>k_map->selection_background, &fallback, &selection_background);
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_MARKER, &fallback));
|
||||
theme_runtime_resolve_color (>k_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;
|
||||
}
|
||||
35
src/fe-gtk/theme/theme-runtime.h
Normal file
35
src/fe-gtk/theme/theme-runtime.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#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
|
||||
@@ -33,10 +33,11 @@
|
||||
#include "../common/zoitechatc.h"
|
||||
#include "../common/fe.h"
|
||||
#include "gtkutil.h"
|
||||
#include "palette.h"
|
||||
#include "theme/theme-gtk.h"
|
||||
#include "maingui.h"
|
||||
#include "menu.h"
|
||||
#include "pixmaps.h"
|
||||
#include "theme/theme-access.h"
|
||||
#include "userlistgui.h"
|
||||
#include "fkeys.h"
|
||||
|
||||
@@ -46,10 +47,10 @@ enum
|
||||
COL_NICK=1, /* char * */
|
||||
COL_HOST=2, /* char * */
|
||||
COL_USER=3, /* struct User * */
|
||||
COL_GDKCOLOR=4 /* PaletteColor */
|
||||
COL_GDKCOLOR=4 /* GdkRGBA */
|
||||
};
|
||||
|
||||
static void userlist_store_color (GtkListStore *store, GtkTreeIter *iter, int color_index);
|
||||
static void userlist_store_color (GtkListStore *store, GtkTreeIter *iter, ThemeSemanticToken token, gboolean has_token);
|
||||
|
||||
GdkPixbuf *
|
||||
get_user_icon (server *serv, struct User *user)
|
||||
@@ -130,6 +131,81 @@ 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
|
||||
@@ -139,8 +215,20 @@ 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
|
||||
@@ -148,6 +236,7 @@ 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
|
||||
@@ -237,13 +326,23 @@ fe_userlist_set_selected (struct session *sess)
|
||||
}
|
||||
|
||||
static GtkTreeIter *
|
||||
find_row (GtkTreeView *treeview, GtkTreeModel *model, struct User *user,
|
||||
find_row (session *sess, 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
|
||||
@@ -251,6 +350,7 @@ find_row (GtkTreeView *treeview, GtkTreeModel *model, struct User *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))
|
||||
@@ -286,10 +386,11 @@ fe_userlist_remove (session *sess, struct User *user)
|
||||
gfloat val, end;*/
|
||||
int sel;
|
||||
|
||||
iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
|
||||
GTK_TREE_MODEL(sess->res->user_model), user, &sel);
|
||||
iter = find_row (sess, 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;*/
|
||||
@@ -314,22 +415,35 @@ fe_userlist_rehash (session *sess, struct User *user)
|
||||
{
|
||||
GtkTreeIter *iter;
|
||||
int sel;
|
||||
int nick_color = 0;
|
||||
ThemeSemanticToken nick_token = THEME_TOKEN_TEXT_FOREGROUND;
|
||||
gboolean have_nick_token = FALSE;
|
||||
|
||||
iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
|
||||
GTK_TREE_MODEL(sess->res->user_model), user, &sel);
|
||||
iter = find_row (sess, 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_color = COL_AWAY;
|
||||
{
|
||||
nick_token = THEME_TOKEN_TAB_AWAY;
|
||||
have_nick_token = TRUE;
|
||||
}
|
||||
else if (prefs.hex_gui_ulist_color)
|
||||
nick_color = text_color_of(user->nick);
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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_color);
|
||||
userlist_store_color (GTK_LIST_STORE (sess->res->user_model), iter, nick_token, have_nick_token);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -339,12 +453,24 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
|
||||
GdkPixbuf *pix = get_user_icon (sess->server, newuser);
|
||||
GtkTreeIter iter;
|
||||
char *nick;
|
||||
int nick_color = 0;
|
||||
ThemeSemanticToken nick_token = THEME_TOKEN_TEXT_FOREGROUND;
|
||||
gboolean have_nick_token = FALSE;
|
||||
|
||||
if (prefs.hex_away_track && newuser->away)
|
||||
nick_color = COL_AWAY;
|
||||
{
|
||||
nick_token = THEME_TOKEN_TAB_AWAY;
|
||||
have_nick_token = TRUE;
|
||||
}
|
||||
else if (prefs.hex_gui_ulist_color)
|
||||
nick_color = text_color_of(newuser->nick);
|
||||
{
|
||||
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 = newuser->nick;
|
||||
if (!prefs.hex_gui_ulist_icons)
|
||||
@@ -364,13 +490,15 @@ 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_color);
|
||||
userlist_store_color (GTK_LIST_STORE (model), &iter, nick_token, have_nick_token);
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -391,6 +519,8 @@ 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);
|
||||
}
|
||||
|
||||
@@ -469,14 +599,17 @@ userlist_ops_cmp (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b,
|
||||
}
|
||||
|
||||
static void
|
||||
userlist_store_color (GtkListStore *store, GtkTreeIter *iter, int color_index)
|
||||
userlist_store_color (GtkListStore *store, GtkTreeIter *iter, ThemeSemanticToken token, gboolean has_token)
|
||||
{
|
||||
const PaletteColor *color = color_index ? &colors[color_index] : NULL;
|
||||
GdkRGBA rgba;
|
||||
const GdkRGBA *color = NULL;
|
||||
|
||||
if (has_token && theme_get_color (token, &rgba))
|
||||
color = &rgba;
|
||||
|
||||
if (color)
|
||||
{
|
||||
GdkRGBA rgba = *color;
|
||||
gtk_list_store_set (store, iter, COL_GDKCOLOR, &rgba, -1);
|
||||
gtk_list_store_set (store, iter, COL_GDKCOLOR, color, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -492,7 +625,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, PALETTE_GDK_TYPE);
|
||||
G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
|
||||
|
||||
switch (prefs.hex_gui_ulist_sort)
|
||||
{
|
||||
@@ -545,7 +678,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, PALETTE_FOREGROUND_PROPERTY, 4, NULL);
|
||||
"text", 1, THEME_GTK_FOREGROUND_PROPERTY, 4, NULL);
|
||||
|
||||
if (prefs.hex_gui_ulist_show_hosts)
|
||||
{
|
||||
@@ -682,6 +815,7 @@ 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)),
|
||||
@@ -733,38 +867,48 @@ userlist_show (session *sess)
|
||||
void
|
||||
fe_uselect (session *sess, char *word[], int do_clear, int scroll_to)
|
||||
{
|
||||
int thisname;
|
||||
char *name;
|
||||
int thisname;
|
||||
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 (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
if (do_clear)
|
||||
gtk_tree_selection_unselect_all (selection);
|
||||
if (do_clear)
|
||||
gtk_tree_selection_unselect_all (selection);
|
||||
|
||||
do
|
||||
thisname = 0;
|
||||
while (*(name = word[thisname++]))
|
||||
{
|
||||
user = userlist_find (sess, name);
|
||||
if (!user)
|
||||
continue;
|
||||
|
||||
if (userlist_row_map_get_iter (sess, model, user, &iter))
|
||||
{
|
||||
if (*word[0])
|
||||
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
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
|
||||
thisname = 0;
|
||||
while ( *(name = word[thisname++]) )
|
||||
if (row_user == user)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "fe-gtk.h"
|
||||
#include "xtext.h"
|
||||
#include "fkeys.h"
|
||||
#include "theme/theme-access.h"
|
||||
|
||||
#define charlen(str) g_utf8_skip[*(guchar *)(str)]
|
||||
|
||||
@@ -86,9 +87,10 @@ struct textentry
|
||||
gint16 left_len;
|
||||
GSList *slp;
|
||||
GSList *sublines;
|
||||
int subline_count;
|
||||
guchar tag;
|
||||
guchar pad1;
|
||||
guchar pad2; /* 32-bit align : 44 bytes total */
|
||||
guchar pad2;
|
||||
GList *marks; /* List of found strings */
|
||||
};
|
||||
|
||||
@@ -362,8 +364,107 @@ xtext_draw_bg_offset (GtkXText *xtext, int x, int y, int width, int height, int
|
||||
|
||||
if (xtext->background_surface)
|
||||
{
|
||||
cairo_set_source_surface (cr, xtext->background_surface, tile_x, tile_y);
|
||||
cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
|
||||
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_rectangle (cr, (double)x, (double)y, (double)width, (double)height);
|
||||
cairo_fill (cr);
|
||||
}
|
||||
@@ -627,13 +728,36 @@ 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;
|
||||
@@ -667,6 +791,8 @@ 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
|
||||
@@ -703,7 +829,7 @@ gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal)
|
||||
|
||||
if (fire_signal)
|
||||
{
|
||||
gtk_adjustment_value_changed (adj);
|
||||
g_signal_emit_by_name (adj, "value-changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -767,6 +893,7 @@ 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);
|
||||
}
|
||||
@@ -798,6 +925,12 @@ 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);
|
||||
@@ -1327,7 +1460,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 * g_slist_length (ent->sublines);
|
||||
render_y = y + xtext->font->descent + xtext->fontsize * ent->subline_count;
|
||||
}
|
||||
else return;
|
||||
|
||||
@@ -1355,6 +1488,12 @@ 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);
|
||||
|
||||
@@ -1417,6 +1556,11 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1800,7 +1944,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);
|
||||
gtk_adjustment_value_changed (adj);
|
||||
g_signal_emit_by_name (adj, "value-changed");
|
||||
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),
|
||||
@@ -1844,7 +1988,7 @@ gtk_xtext_scrollup_timeout (GtkXText * xtext)
|
||||
}
|
||||
xtext->select_start_y += delta_y;
|
||||
xtext->select_start_adj = xtext_adj_get_value (adj);
|
||||
gtk_adjustment_value_changed (adj);
|
||||
g_signal_emit_by_name (adj, "value-changed");
|
||||
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),
|
||||
@@ -2969,19 +3113,15 @@ 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 len, int *emphasis, int str_width)
|
||||
{
|
||||
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));
|
||||
int dofill;
|
||||
|
||||
if (xtext->dont_render || len < 1 || xtext->hidden)
|
||||
return 0;
|
||||
|
||||
str_width = backend_get_text_width_emph (xtext, str, len, *emphasis);
|
||||
if (str_width < 0)
|
||||
str_width = backend_get_text_width_emph (xtext, str, len, *emphasis);
|
||||
|
||||
if (xtext->dont_render2)
|
||||
return str_width;
|
||||
@@ -3000,74 +3140,17 @@ gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
|
||||
goto dounder;
|
||||
}
|
||||
|
||||
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 */
|
||||
}
|
||||
dofill = !xtext->background_surface || xtext->backcolor;
|
||||
|
||||
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;
|
||||
/* pango_attr_strikethrough_new does not render in the custom widget so we need to reinvent the wheel */
|
||||
y = dest_y + (xtext->fontsize / 2);
|
||||
int strike_y = y - xtext->font->ascent + (xtext->fontsize / 2);
|
||||
|
||||
cr = xtext_create_context (xtext);
|
||||
xtext_draw_line (xtext, cr, &xtext->fgc, dest_x, y, dest_x + str_width - 1, y);
|
||||
xtext_draw_line (xtext, cr, &xtext->fgc, x, strike_y, x + str_width - 1, strike_y);
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
|
||||
@@ -3076,17 +3159,10 @@ 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, dest_x, y, dest_x + str_width - 1, y);
|
||||
xtext_draw_line (xtext, cr, &xtext->fgc, x, underline_y, x + str_width - 1, underline_y);
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
}
|
||||
@@ -3094,6 +3170,46 @@ 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)
|
||||
{
|
||||
@@ -3177,7 +3293,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)
|
||||
#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))
|
||||
|
||||
static int
|
||||
gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
|
||||
@@ -3193,6 +3309,7 @@ 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;
|
||||
|
||||
@@ -3476,7 +3593,8 @@ 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_render_flush (xtext, x, y, pstr, j, emphasis,
|
||||
gtk_xtext_lookup_run_width (ent, &slp_cache, pstr - ent->str, j, *emphasis));
|
||||
ret = 0; /* skip the rest of the lines, we're done. */
|
||||
j = 0;
|
||||
break;
|
||||
@@ -3807,18 +3925,6 @@ 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
|
||||
{
|
||||
@@ -3832,12 +3938,34 @@ 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 g_slist_length (ent->sublines) - subline;
|
||||
return ent->subline_count - subline;
|
||||
}
|
||||
} else
|
||||
{
|
||||
@@ -3910,6 +4038,7 @@ 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;
|
||||
@@ -3921,10 +4050,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 -
|
||||
gtk_xtext_text_width (buf->xtext, ent->str,
|
||||
ent->left_len)) - buf->xtext->space_width;
|
||||
(buf->indent - prefix_width);
|
||||
if (ent->indent < MARGIN)
|
||||
ent->indent = MARGIN;
|
||||
}
|
||||
@@ -3973,6 +4102,12 @@ 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)
|
||||
{
|
||||
@@ -4015,12 +4150,14 @@ 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));
|
||||
return 1;
|
||||
ent->subline_count = 1;
|
||||
return ent->subline_count;
|
||||
}
|
||||
|
||||
indent = ent->indent;
|
||||
@@ -4035,7 +4172,8 @@ gtk_xtext_lines_taken (xtext_buffer *buf, textentry * ent)
|
||||
}
|
||||
while (str < ent->str + ent->str_len);
|
||||
|
||||
return g_slist_length (ent->sublines);
|
||||
ent->subline_count = g_slist_length (ent->sublines);
|
||||
return ent->subline_count;
|
||||
}
|
||||
|
||||
/* Calculate number of actual lines (with wraps), to set adj->lower. *
|
||||
@@ -4112,7 +4250,7 @@ gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
|
||||
ent = ent->prev;
|
||||
if (!ent)
|
||||
break;
|
||||
lines -= g_slist_length (ent->sublines);
|
||||
lines -= ent->subline_count;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -4121,10 +4259,10 @@ gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
|
||||
|
||||
while (ent)
|
||||
{
|
||||
lines += g_slist_length (ent->sublines);
|
||||
lines += ent->subline_count;
|
||||
if (lines > line)
|
||||
{
|
||||
*subline = g_slist_length (ent->sublines) - (lines - line);
|
||||
*subline = ent->subline_count - (lines - line);
|
||||
return ent;
|
||||
}
|
||||
ent = ent->next;
|
||||
@@ -4218,7 +4356,7 @@ gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb)
|
||||
line -= subline;
|
||||
subline = 0;
|
||||
}
|
||||
line += g_slist_length (ent->sublines);
|
||||
line += ent->subline_count;
|
||||
}
|
||||
|
||||
if (ent == entb)
|
||||
@@ -4303,7 +4441,7 @@ gtk_xtext_render_page (GtkXText * xtext)
|
||||
xtext->buffer->last_pixel_pos = pos;
|
||||
|
||||
#ifndef __APPLE__
|
||||
if (!xtext->background_surface && abs (overlap) < height)
|
||||
if (abs (overlap) < height)
|
||||
{
|
||||
GdkRectangle area;
|
||||
cairo_t *cr;
|
||||
@@ -4452,6 +4590,8 @@ 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;
|
||||
@@ -4467,22 +4607,22 @@ gtk_xtext_remove_top (xtext_buffer *buffer)
|
||||
ent = buffer->text_first;
|
||||
if (!ent)
|
||||
return;
|
||||
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->num_lines -= ent->subline_count;
|
||||
buffer->pagetop_line -= ent->subline_count;
|
||||
buffer->last_pixel_pos -= (ent->subline_count * 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 -= g_slist_length (ent->sublines);
|
||||
buffer->old_value -= ent->subline_count;
|
||||
if (buffer->xtext->buffer == buffer) /* is it the current buffer? */
|
||||
{
|
||||
xtext_adj_set_value (buffer->xtext->adj,
|
||||
xtext_adj_get_value (buffer->xtext->adj) -
|
||||
g_slist_length (ent->sublines));
|
||||
buffer->xtext->select_start_adj -= g_slist_length (ent->sublines);
|
||||
ent->subline_count);
|
||||
buffer->xtext->select_start_adj -= ent->subline_count;
|
||||
}
|
||||
|
||||
if (gtk_xtext_kill_ent (buffer, ent))
|
||||
@@ -4512,7 +4652,7 @@ gtk_xtext_remove_bottom (xtext_buffer *buffer)
|
||||
ent = buffer->text_last;
|
||||
if (!ent)
|
||||
return;
|
||||
buffer->num_lines -= g_slist_length (ent->sublines);
|
||||
buffer->num_lines -= ent->subline_count;
|
||||
buffer->text_last = ent->prev;
|
||||
if (buffer->text_last)
|
||||
buffer->text_last->next = NULL;
|
||||
@@ -4638,7 +4778,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 -= g_slist_length (ent->sublines);
|
||||
lines -= ent->subline_count;
|
||||
if (lines <= 0)
|
||||
{
|
||||
return FALSE;
|
||||
@@ -5025,7 +5165,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 += g_slist_length (ent->sublines);
|
||||
value += ent->subline_count;
|
||||
}
|
||||
if (value > xtext_adj_get_upper (adj) - xtext_adj_get_page_size (adj))
|
||||
{
|
||||
@@ -5034,7 +5174,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) -
|
||||
g_slist_length (ent->sublines);
|
||||
ent->subline_count;
|
||||
if (value < 0)
|
||||
{
|
||||
value = 0;
|
||||
@@ -5111,6 +5251,7 @@ 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 */
|
||||
@@ -5194,7 +5335,7 @@ gtk_xtext_append_indent (xtext_buffer *buf,
|
||||
unsigned char *str;
|
||||
int space;
|
||||
int tempindent;
|
||||
int left_width;
|
||||
int prefix_width;
|
||||
int min_indent;
|
||||
|
||||
if (left_len == -1)
|
||||
@@ -5219,12 +5360,12 @@ gtk_xtext_append_indent (xtext_buffer *buf,
|
||||
memcpy (str + left_len + 1, right_text, right_len);
|
||||
str[left_len + 1 + right_len] = 0;
|
||||
|
||||
left_width = gtk_xtext_text_width (buf->xtext, left_text, left_len);
|
||||
prefix_width = gtk_xtext_text_width (buf->xtext, str, left_len + 1);
|
||||
|
||||
ent->left_len = left_len;
|
||||
ent->str = str;
|
||||
ent->str_len = left_len + 1 + right_len;
|
||||
ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
|
||||
ent->indent = buf->indent - prefix_width;
|
||||
|
||||
/* This is copied into the scratch buffer later, double check math */
|
||||
g_assert (ent->str_len < sizeof (buf->xtext->scratch_buffer));
|
||||
@@ -5241,7 +5382,7 @@ gtk_xtext_append_indent (xtext_buffer *buf,
|
||||
buf->indent < buf->xtext->max_auto_indent &&
|
||||
ent->indent < min_indent)
|
||||
{
|
||||
tempindent = min_indent + buf->xtext->space_width + left_width;
|
||||
tempindent = min_indent + prefix_width;
|
||||
|
||||
/* Ignore tiny one-pixel style nudges.
|
||||
* They can trigger expensive full-width recalculations and are
|
||||
@@ -5252,12 +5393,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 + left_width + buf->xtext->space_width)
|
||||
if (buf->indent > ent->indent + prefix_width)
|
||||
{
|
||||
gtk_xtext_fix_indent (buf);
|
||||
gtk_xtext_recalc_widths (buf, FALSE);
|
||||
|
||||
ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
|
||||
ent->indent = buf->indent - prefix_width;
|
||||
buf->xtext->force_render = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -5457,7 +5598,7 @@ gtk_xtext_moveto_marker_pos (GtkXText *xtext)
|
||||
{
|
||||
if (ent == buf->marker_pos)
|
||||
break;
|
||||
value += g_slist_length (ent->sublines);
|
||||
value += ent->subline_count;
|
||||
ent = ent->next;
|
||||
}
|
||||
if (value >= xtext_adj_get_value (adj) &&
|
||||
@@ -5595,7 +5736,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);
|
||||
gtk_adjustment_value_changed (xtext->adj);
|
||||
g_signal_emit_by_name (xtext->adj, "value-changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,9 +135,16 @@ 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;
|
||||
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -27,31 +23,17 @@
|
||||
<TargetName>zoitechat-text</TargetName>
|
||||
<OutDir>$(ZoiteChatRel)</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<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;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>
|
||||
<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>
|
||||
<AdditionalDependencies>$(DepLibs);$(ZoiteChatLib)common.lib;wbemuuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<?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>
|
||||
@@ -32,7 +28,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>
|
||||
|
||||
@@ -39,3 +39,16 @@ 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.
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
@@ -30,7 +26,7 @@
|
||||
<None Include="changelog.url" />
|
||||
<None Include="readme.url" />
|
||||
|
||||
<None Include="$(DepsRoot)\bin\lua51.dll" />
|
||||
<None Include="$(DepsRoot)\bin\$(LuaRuntimeDll)" Condition="'$(LuaEnabled)'=='true' and '$(LuaRuntimeDll)'!=''" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Target Name="Build">
|
||||
@@ -67,17 +63,15 @@
|
||||
<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\luajit*.dll" />
|
||||
<None Include="$(DepsRoot)\bin\*girepository*.dll" />
|
||||
<None Include="$(Python3Path)\Lib\site-packages\_cffi_backend.*.pyd" />
|
||||
<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)'!=''" />
|
||||
|
||||
<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\**\*" />
|
||||
@@ -88,11 +82,10 @@
|
||||
|
||||
<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)" />
|
||||
@@ -103,23 +96,21 @@
|
||||
<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" />
|
||||
<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" />
|
||||
<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'" />
|
||||
|
||||
<WriteLinesToFile File="$(ZoiteChatRel)portable-mode" Lines="2" Overwrite="true" />
|
||||
|
||||
<Copy SourceFiles="@(DepsRootDocs)" DestinationFiles="@(DepsRootDocs->'$(ZoiteChatRel)\share\doc\%(RecursiveDir)%(Filename)%(Extension)')" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
|
||||
@@ -22,33 +22,21 @@ 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
|
||||
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
|
||||
@@ -99,7 +87,6 @@ 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
|
||||
@@ -112,10 +99,11 @@ 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\*"; 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\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\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
|
||||
|
||||
@@ -160,18 +148,26 @@ 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; 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: "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: "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
|
||||
@@ -231,14 +227,7 @@ end;
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function GetSysDir(): String;
|
||||
begin
|
||||
#if APPARCH != "x64"
|
||||
if IsWin64 then
|
||||
Result := ExpandConstant('{syswow64}\')
|
||||
else
|
||||
Result := ExpandConstant('{sys}\');
|
||||
#else
|
||||
Result := ExpandConstant('{sys}\');
|
||||
#endif
|
||||
end;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@@ -259,11 +248,6 @@ begin
|
||||
Result := FileExists(GetSysDir() + 'vcruntime140.dll');;
|
||||
end;
|
||||
|
||||
function CheckVC2013Install(): Boolean;
|
||||
begin
|
||||
Result := FileExists(GetSysDir() + 'msvcr120.dll');;
|
||||
end;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function CheckSpellInstall(): Boolean;
|
||||
@@ -285,9 +269,7 @@ end;
|
||||
procedure CurPageChanged(CurPageID: Integer);
|
||||
var
|
||||
REDIST: String;
|
||||
REDIST_2013: String;
|
||||
PERL: String;
|
||||
PY2: String;
|
||||
PY3: String;
|
||||
SPELL: String;
|
||||
begin
|
||||
@@ -298,17 +280,9 @@ 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
|
||||
@@ -321,9 +295,6 @@ 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;
|
||||
|
||||
@@ -341,11 +312,7 @@ 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)
|
||||
@@ -362,11 +329,7 @@ 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);
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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>
|
||||
|
||||
@@ -113,7 +113,22 @@
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<DataDir>$(SolutionDir)..\data\\</DataDir>
|
||||
<ZoiteChatBuild>$(SolutionDir)..\..\zoitechat-build</ZoiteChatBuild>
|
||||
@@ -160,10 +175,10 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<PreProcessorDefinitions>NTDDI_VERSION=NTDDI_WIN8;_WIN32_WINNT=_WIN32_WINNT_WIN8;%(PreProcessorDefinitions)</PreProcessorDefinitions>
|
||||
<PreProcessorDefinitions>NTDDI_VERSION=NTDDI_WIN8;_WIN32_WINNT=_WIN32_WINNT_WIN8;$(ArchiveDefs);%(PreProcessorDefinitions)</PreProcessorDefinitions>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
|
||||
<LinkTimeCodeGeneration>false</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
<Link>
|
||||
<ImportLibrary>$(ZoiteChatLib)$(TargetName).lib</ImportLibrary>
|
||||
@@ -172,7 +187,7 @@
|
||||
<GenerateDebugInformation>Debug</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
|
||||
@@ -104,76 +104,41 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user