Compare commits
104 Commits
zoitechat-
...
99-colors-
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c23a48ef8 | |||
| f3fd7fd2b3 | |||
| 10940a37dd | |||
| 8249be816a | |||
|
|
05f8b4d4b3 | ||
|
|
935c35fd43 | ||
|
|
33b18cb48d | ||
|
|
d9442d6e94 | ||
|
|
b9ac55fe7f | ||
|
|
ca558e7fa2 | ||
|
|
08f367fef0 | ||
|
|
ca5c94f75e | ||
|
|
6f4e5f95a6 | ||
| 2defd0ed42 | |||
| 86aca30744 | |||
|
|
d5f7299a8e | ||
| e73a5d4c8b | |||
|
|
ec18c95d32 | ||
| d9557c7da1 | |||
| fc90fd41be | |||
| ec5e38d1f9 | |||
| 0eb4d08daa | |||
|
|
4b365132ce | ||
| a5ff902ae2 | |||
| bd874540bb | |||
| f6e1af5701 | |||
|
|
e2bfc9b7c9 | ||
|
|
5f3f91bf1c | ||
| 6527c08e4d | |||
| a57104469d | |||
| 94f450ba67 | |||
| b9e4113c81 | |||
| 65707f53b7 | |||
| 21c73e699b | |||
| 63226a7267 | |||
| fb37810367 | |||
|
|
2b66734fa6 | ||
| 380aae139f | |||
|
|
e4e823c4d6 | ||
| d4b16d5866 | |||
| b9cfd9a9f9 | |||
| 664aa150fb | |||
| aead92d9e4 | |||
|
|
7cc5d14045 | ||
| 33fce2af2a | |||
| d26fbb6e89 | |||
| cd93fe3b0e | |||
|
|
1208449322 | ||
| a11ec3a05b | |||
|
|
e39dfcf00c | ||
| 5f1e0fe7d8 | |||
|
|
57d0d92ecb | ||
| cf89d80765 | |||
|
|
758722ddeb | ||
| 182adba83c | |||
|
|
3c880216cf | ||
| e854153b88 | |||
| 416b8449b9 | |||
|
|
394de09cb1 | ||
| 5213669fbc | |||
| 8471e2feac | |||
|
|
dd53a92ecf | ||
| 0ff4c3608e | |||
|
|
909854ae12 | ||
| 725da0065b | |||
| c8801dd5d3 | |||
| 80c59795d7 | |||
| b1f74094f1 | |||
|
|
255089a0a5 | ||
| 7c24c1137d | |||
| 7138a8f397 | |||
| b32f6522ac | |||
| b7e4548d2a | |||
| 0cfb63f301 | |||
| e90b68967c | |||
| 0155b07c9d | |||
| 592d74e788 | |||
| 854a913911 | |||
| 48b551b188 | |||
| 9e808c57b4 | |||
| 896a761e24 | |||
| 96af9bdde6 | |||
| e90f0188c1 | |||
| 0ab7eb7207 | |||
| 81dcdbe648 | |||
| f4090cb0e5 | |||
| 0f5dfb147e | |||
| 0bcd369426 | |||
|
|
b316d4a281 | ||
| 86ab0135ce | |||
| 2d42d4e181 | |||
| 23e6313e2f | |||
| b5c5810913 | |||
| b5ebba4521 | |||
| 4fe8e273e4 | |||
| 12d8f5e69c | |||
| 8a1cf3080e | |||
|
|
b9bd0ed61c | ||
| 1f33ed8034 | |||
|
|
38f38cd2c4 | ||
| 71478a0262 | |||
|
|
f76f2cd94c | ||
| 183b134817 | |||
| 6a7d2012a5 |
21
.github/workflows/windows-build.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.14.2'
|
||||
python-version: '3.14.3'
|
||||
architecture: ${{ matrix.arch }}
|
||||
|
||||
- name: Install Dependencies
|
||||
@@ -62,19 +62,16 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
Download-WithRetry -Url https://files.jrsoftware.org/is/6/innosetup-6.7.0.exe -OutFile deps\innosetup-unicode.exe
|
||||
Download-WithRetry -Url https://github.com/jrsoftware/issrc/releases/download/is-6_7_1/innosetup-6.7.1.exe -OutFile deps\innosetup-unicode.exe
|
||||
& deps\innosetup-unicode.exe /VERYSILENT | Out-Null
|
||||
|
||||
Download-WithRetry -Url https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/idpsetup-1.5.1.exe -OutFile deps\idpsetup.exe
|
||||
& deps\idpsetup.exe /VERYSILENT
|
||||
|
||||
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre1/GTK3_Gvsbuild_zoitechat-2.18.0-pre1_${{ matrix.platform }}.7z -OutFile deps\gtk-${{ matrix.arch }}.7z
|
||||
& 7z.exe x deps\gtk-${{ matrix.arch }}.7z -oC:\gtk-build\gtk\x64\release
|
||||
|
||||
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-hicolor-icon-theme-0.18-1-any.pkg.tar.zst -OutFile deps\hicolor-icon-theme.pkg.tar.zst
|
||||
python -c "import tarfile,zstandard,pathlib;archive=pathlib.Path(r'deps\\hicolor-icon-theme.pkg.tar.zst');target=pathlib.Path(r'C:\\gtk-build\\gtk\\x64\\release');dctx=zstandard.ZstdDecompressor();f=archive.open('rb');reader=dctx.stream_reader(f);tf=tarfile.open(fileobj=reader,mode='r|');[tf.extract(m,path=target) for m in tf if m.name.startswith('mingw64/share/icons/hicolor/')];tf.close();reader.close();f.close()"
|
||||
|
||||
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-libarchive-3.8.1-1-any.pkg.tar.zst -OutFile deps\libarchive.pkg.tar.zst
|
||||
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-libarchive-3.8.6-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) {
|
||||
@@ -97,19 +94,19 @@ jobs:
|
||||
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
|
||||
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/gendef20260315.7z -OutFile deps\gendef.7z
|
||||
& 7z.exe x deps\gendef.7z -oC:\gtk-build
|
||||
|
||||
Download-WithRetry -Url https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/WinSparkle-20151011.7z -OutFile deps\WinSparkle.7z
|
||||
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/WinSparkle-20260315.7z -OutFile deps\WinSparkle.7z
|
||||
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle
|
||||
|
||||
Download-WithRetry -Url https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/perl-5.20.0-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
|
||||
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.20\${{ matrix.platform }}
|
||||
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/perl-5.42.0.1-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
|
||||
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.42.0.1\${{ matrix.platform }}
|
||||
|
||||
$pyRoot = $env:pythonLocation
|
||||
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
|
||||
|
||||
foreach ($pyDir in @("C:\gtk-build\python-3.14.2", "C:\gtk-build\python-3.14")) {
|
||||
foreach ($pyDir in @("C:\gtk-build\python-3.14.3", "C:\gtk-build\python-3.14")) {
|
||||
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
|
||||
$target = Join-Path $pyDir "${{ matrix.platform }}"
|
||||
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
|
||||
@@ -120,7 +117,7 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
|
||||
|
||||
set "PYTHON_DIR=C:\gtk-build\python-3.14.2\${{ matrix.platform }}"
|
||||
set "PYTHON_DIR=C:\gtk-build\python-3.14.3\${{ matrix.platform }}"
|
||||
if not exist "%PYTHON_DIR%\libs\python314.lib" (
|
||||
echo Missing %PYTHON_DIR%\libs\python314.lib
|
||||
dir "%PYTHON_DIR%\libs"
|
||||
|
||||
@@ -1,6 +1,37 @@
|
||||
ZoiteChat ChangeLog
|
||||
=================
|
||||
|
||||
2.18.0~pre5 (2026-03-22)
|
||||
------------------------
|
||||
- Overhauled preferences/config saving: fully staged and transactional, debounced
|
||||
with flush on close, write failures now surfaced.
|
||||
- Overhauled GTK theme handling: live preview, correct colour reset persistence,
|
||||
proper file:// import paths, consistent menubar CSS.
|
||||
- Fixed GTK entry scroll artifact in the input box.
|
||||
- Hardened tray menu lifetime; fixed stale pointer crash on menu destruction.
|
||||
- Fixed AppIndicator tray init for Wayland/source builds; skip redundant X11
|
||||
tray probe for AppIndicator builds.
|
||||
- Split topic/mode rows in channel bar, tighten spacing, persist userlist column widths.
|
||||
- Preserve saved right-pane size on first layout.
|
||||
- Fixed sounds prefs section.
|
||||
- Fixed chanview tree layout and header alignment; tighten topic URL hit-testing.
|
||||
- Dropped realpath() in favour of GLib-only absolute path build.
|
||||
- Made About dialog links explicit; added GPL licence URL.
|
||||
- Added licence headers to new source files.
|
||||
- Windows installer: fixed VC++ redist URL, added non-plugin download fallback,
|
||||
switched to registry-based runtime detection.
|
||||
- Made libayatana-appindicator a required dep in PKGBUILD.
|
||||
- Cleaned up Meson libperl detection.
|
||||
|
||||
2.18.0~pre4 (2026-03-15)
|
||||
------------------------
|
||||
|
||||
- Fixed a regression where Ctrl+A could incorrectly mark you away instead of selecting all text.
|
||||
- Fixed a crash when toggling the GUI with F9 or the menu toggle action.
|
||||
- Fixed sidebar collapse behavior on fresh installs.
|
||||
- Added multiline topic bar support with clickable URLs.
|
||||
- Improved GTK selection styling so text selection is shown visually on topic and chat text box.
|
||||
|
||||
2.18.0~pre3 (2026-03-13)
|
||||
------------------------
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 250 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 250 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 250 KiB After Width: | Height: | Size: 4.8 KiB |
@@ -29,6 +29,57 @@
|
||||
<id>zoitechat.desktop</id>
|
||||
</provides>
|
||||
<releases>
|
||||
<release date="2026-03-22" version="2.18.0~pre5">
|
||||
<description>
|
||||
<p>Preferences and config saving:</p>
|
||||
<ul>
|
||||
<li>Overhauled preferences/config saving: fully staged and transactional, debounced with flush on close, write failures now surfaced.</li>
|
||||
</ul>
|
||||
<p>GTK theme and UI:</p>
|
||||
<ul>
|
||||
<li>Overhauled GTK theme handling: live preview, correct colour reset persistence, proper <code>file://</code> import paths, consistent menubar CSS.</li>
|
||||
<li>Fixed GTK entry scroll artifact in the input box.</li>
|
||||
<li>Split topic/mode rows in channel bar, tighten spacing, persist userlist column widths.</li>
|
||||
<li>Preserve saved right-pane size on first layout.</li>
|
||||
<li>Fixed sounds prefs section.</li>
|
||||
<li>Fixed chanview tree layout and header alignment; tighten topic URL hit-testing.</li>
|
||||
</ul>
|
||||
<p>Tray:</p>
|
||||
<ul>
|
||||
<li>Hardened tray menu lifetime; fixed stale pointer crash on menu destruction.</li>
|
||||
<li>Fixed AppIndicator tray init for Wayland/source builds; skip redundant X11 tray probe for AppIndicator builds.</li>
|
||||
</ul>
|
||||
<p>Build and packaging:</p>
|
||||
<ul>
|
||||
<li>Dropped <code>realpath()</code> in favour of GLib-only absolute path build.</li>
|
||||
<li>Made About dialog links explicit; added GPL licence URL.</li>
|
||||
<li>Added licence headers to new source files.</li>
|
||||
<li>Windows installer: fixed VC++ redist URL, added non-plugin download fallback, switched to registry-based runtime detection.</li>
|
||||
<li>Made <code>libayatana-appindicator</code> a required dep in PKGBUILD.</li>
|
||||
<li>Cleaned up Meson libperl detection.</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release date="2026-03-22" version="2.18.0~pre5">
|
||||
<description>
|
||||
<p>Version metadata update:</p>
|
||||
<ul>
|
||||
<li>Bumped release version references to <code>2.18.0~pre5</code> across build and packaging files.</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release date="2026-03-14" version="2.18.0~pre4">
|
||||
<description>
|
||||
<p>UI fixes, topic bar improvements, and selection styling updates:</p>
|
||||
<ul>
|
||||
<li>Fixed a regression where <code>Ctrl+A</code> could incorrectly mark you away instead of selecting all text.</li>
|
||||
<li>Fixed a crash when toggling the GUI with <code>F9</code> or the menu toggle action.</li>
|
||||
<li>Fixed sidebar collapse behavior on fresh installs.</li>
|
||||
<li>Added multiline topic bar support with clickable URLs.</li>
|
||||
<li>Improved GTK selection styling so text selection is shown visually in the topic bar and chat input box.</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release date="2026-03-13" version="2.18.0~pre3">
|
||||
<description>
|
||||
<p>GTK3 theming, UI, and platform improvements:</p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
project('zoitechat', 'c',
|
||||
version: '2.18.0~pre3',
|
||||
version: '2.18.0~pre5',
|
||||
meson_version: '>= 0.55.0',
|
||||
default_options: [
|
||||
'c_std=c17',
|
||||
|
||||
@@ -12,6 +12,7 @@ depends=(
|
||||
'glib2'
|
||||
'gtk3'
|
||||
'iso-codes'
|
||||
'libayatana-appindicator'
|
||||
'libcanberra'
|
||||
'lua'
|
||||
'openssl'
|
||||
@@ -26,7 +27,6 @@ makedepends=(
|
||||
'python'
|
||||
)
|
||||
optdepends=(
|
||||
'libayatana-appindicator: Ayatana/AppIndicator tray backend'
|
||||
'pciutils: sysinfo plugin hardware detection details'
|
||||
)
|
||||
provides=('zoitechat')
|
||||
|
||||
@@ -45,6 +45,22 @@ endif
|
||||
perl_ldflags = []
|
||||
perl_rpath = ''
|
||||
foreach flag : ret.stdout().strip().split(' ')
|
||||
if flag.startswith('/') and '/libperl.' in flag
|
||||
split = flag.split('/')
|
||||
libperl_dir = ''
|
||||
foreach part : split
|
||||
if part != '' and not part.startswith('libperl.')
|
||||
if libperl_dir == ''
|
||||
libperl_dir = '/' + part
|
||||
else
|
||||
libperl_dir += '/' + part
|
||||
endif
|
||||
endif
|
||||
endforeach
|
||||
perl_ldflags += '-L' + libperl_dir
|
||||
perl_ldflags += '-lperl'
|
||||
continue
|
||||
endif
|
||||
if flag.startswith('-L') or flag.startswith('-l')
|
||||
perl_ldflags += flag
|
||||
endif
|
||||
|
||||
@@ -19,7 +19,7 @@ else:
|
||||
if not hasattr(sys, 'argv'):
|
||||
sys.argv = ['<zoitechat>']
|
||||
|
||||
VERSION = b'2.18.0~pre3'
|
||||
VERSION = b'2.18.0~pre5'
|
||||
PLUGIN_NAME = ffi.new('char[]', b'Python')
|
||||
PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1]))
|
||||
PLUGIN_VERSION = ffi.new('char[]', VERSION)
|
||||
|
||||
@@ -26,6 +26,7 @@ char *sysinfo_backend_get_disk(void);
|
||||
char *sysinfo_backend_get_memory(void);
|
||||
char *sysinfo_backend_get_cpu(void);
|
||||
char *sysinfo_backend_get_gpu(void);
|
||||
char *sysinfo_backend_get_chipset(void);
|
||||
char *sysinfo_backend_get_sound(void);
|
||||
char *sysinfo_backend_get_uptime(void);
|
||||
char *sysinfo_backend_get_network(void);
|
||||
|
||||
@@ -41,7 +41,7 @@ static zoitechat_plugin *ph;
|
||||
static char name[] = "Sysinfo";
|
||||
static char desc[] = "Display info about your hardware and OS";
|
||||
static char version[] = "1.0";
|
||||
static char sysinfo_help[] = "SysInfo Usage:\n /SYSINFO [-e|-o] [CLIENT|UI|OS|CPU|RAM|DISK|VGA|SOUND|ETHERNET|UPTIME], print various details about your system or print a summary without arguments\n /SYSINFO SET <variable>\n";
|
||||
static char sysinfo_help[] = "SysInfo Usage:\n /SYSINFO [-e|-o] [CLIENT|UI|OS|CPU|RAM|DISK|GPU|CHIPSET|SOUND|ETHERNET|UPTIME], print various details about your system or print a summary without arguments\n /SYSINFO SET <variable>\n";
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -54,16 +54,11 @@ typedef struct
|
||||
static char *
|
||||
get_client (void)
|
||||
{
|
||||
char *ui = sysinfo_backend_get_ui();
|
||||
const char *ver = zoitechat_get_info(ph, "version");
|
||||
const char *ver = zoitechat_get_info(ph, "version");
|
||||
char *out;
|
||||
|
||||
if (ui != NULL && *ui != '\0')
|
||||
out = g_strdup_printf ("ZoiteChat %s (%s)", ver, ui);
|
||||
else
|
||||
out = g_strdup_printf ("ZoiteChat %s", ver);
|
||||
|
||||
g_free (ui);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -74,7 +69,8 @@ static hwinfo hwinfos[] = {
|
||||
{"cpu", "CPU", sysinfo_backend_get_cpu},
|
||||
{"memory", "Memory", sysinfo_backend_get_memory},
|
||||
{"storage", "Storage", sysinfo_backend_get_disk},
|
||||
{"vga", "VGA", sysinfo_backend_get_gpu},
|
||||
{"gpu", "GPU", sysinfo_backend_get_gpu},
|
||||
{"chipset", "CHIPSET", sysinfo_backend_get_chipset, TRUE},
|
||||
{"sound", "Sound", sysinfo_backend_get_sound, TRUE},
|
||||
{"ethernet", "Ethernet", sysinfo_backend_get_network, TRUE},
|
||||
{"uptime", "Uptime", sysinfo_backend_get_uptime},
|
||||
|
||||
@@ -121,7 +121,6 @@ char *sysinfo_backend_get_cpu(void)
|
||||
char *sysinfo_backend_get_gpu(void)
|
||||
{
|
||||
char vid_card[bsize];
|
||||
char agp_bridge[bsize];
|
||||
char buffer[bsize];
|
||||
int ret;
|
||||
|
||||
@@ -130,18 +129,29 @@ char *sysinfo_backend_get_gpu(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (xs_parse_agpbridge (agp_bridge) != 0)
|
||||
{
|
||||
g_snprintf (buffer, bsize, "%s", vid_card);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_snprintf (buffer, bsize, "%s @ %s", vid_card, agp_bridge);
|
||||
}
|
||||
g_snprintf (buffer, bsize, "%s", vid_card);
|
||||
|
||||
return g_strdup (buffer);
|
||||
}
|
||||
|
||||
char *sysinfo_backend_get_chipset(void)
|
||||
{
|
||||
char agp_bridge[bsize];
|
||||
char buffer[bsize];
|
||||
int ret;
|
||||
|
||||
if ((ret = xs_parse_agpbridge (agp_bridge)) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_snprintf (buffer, bsize, "%s", agp_bridge);
|
||||
|
||||
return g_strdup (buffer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
char *sysinfo_backend_get_sound(void)
|
||||
{
|
||||
char sound[bsize];
|
||||
|
||||
@@ -89,6 +89,12 @@ sysinfo_backend_get_gpu (void)
|
||||
return sysinfo_get_gpu ();
|
||||
}
|
||||
|
||||
char *
|
||||
sysinfo_backend_get_chipset (void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
sysinfo_backend_get_os (void)
|
||||
{
|
||||
|
||||
2
po/de.po
@@ -1325,7 +1325,7 @@ msgstr "%C29*%O$t%C29MOTD übersprungen%O"
|
||||
|
||||
#: src/common/textevents.h:277
|
||||
msgid "%C23*%O$t%C28$1%C is already in use. Retrying with %C18$2%O..."
|
||||
msgstr "%C23*%O$t%C28$1%C wird bereits verwendet. Erneurter Versuch mit %C18$2%O …"
|
||||
msgstr "%C23*%O$t%C28$1%C wird bereits verwendet. Erneuter Versuch mit %C18$2%O …"
|
||||
|
||||
#: src/common/textevents.h:280
|
||||
msgid "%C23*%O$t%C28$1%C is erroneous. Retrying with %C18$2%O..."
|
||||
|
||||
@@ -44,6 +44,7 @@ For more information on ZoiteChat please read our [documentation](https://docs.z
|
||||
<sub>
|
||||
X-Chat ("xchat") Copyright (c) 1998-2010 By Peter Zelezny.
|
||||
HexChat ("hexchat") Copyright (c) 2009-2014 By Berke Viktor.
|
||||
Hexchat ("hexchat") Copyright (c) 2015-2025 By Patrick Griffis.
|
||||
ZoiteChat ("zoitechat") Copyright (c) 2026 By deepend.
|
||||
</sub>
|
||||
|
||||
|
||||
@@ -407,7 +407,11 @@ const struct prefs vars[] =
|
||||
{"gui_autoopen_send", P_OFFINT (hex_gui_autoopen_send), TYPE_BOOL},
|
||||
{"gui_chanlist_maxusers", P_OFFINT (hex_gui_chanlist_maxusers), TYPE_INT},
|
||||
{"gui_chanlist_minusers", P_OFFINT (hex_gui_chanlist_minusers), TYPE_INT},
|
||||
{"gui_chanlist_width_channel", P_OFFINT (hex_gui_chanlist_width_channel), TYPE_INT},
|
||||
{"gui_chanlist_width_topic", P_OFFINT (hex_gui_chanlist_width_topic), TYPE_INT},
|
||||
{"gui_chanlist_width_users", P_OFFINT (hex_gui_chanlist_width_users), TYPE_INT},
|
||||
{"gui_compact", P_OFFINT (hex_gui_compact), TYPE_BOOL},
|
||||
{"gui_ctrlq_quit", P_OFFINT (hex_gui_ctrlq_quit), TYPE_BOOL},
|
||||
{"gui_dialog_height", P_OFFINT (hex_gui_dialog_height), TYPE_INT},
|
||||
{"gui_dialog_left", P_OFFINT (hex_gui_dialog_left), TYPE_INT},
|
||||
{"gui_dialog_top", P_OFFINT (hex_gui_dialog_top), TYPE_INT},
|
||||
@@ -466,6 +470,8 @@ const struct prefs vars[] =
|
||||
{"gui_ulist_hide", P_OFFINT (hex_gui_ulist_hide), TYPE_BOOL},
|
||||
{"gui_ulist_icons", P_OFFINT (hex_gui_ulist_icons), TYPE_BOOL},
|
||||
{"gui_ulist_pos", P_OFFINT (hex_gui_ulist_pos), TYPE_INT},
|
||||
{"gui_ulist_nick_width", P_OFFINT (hex_gui_ulist_nick_width), TYPE_INT},
|
||||
{"gui_ulist_host_width", P_OFFINT (hex_gui_ulist_host_width), TYPE_INT},
|
||||
{"gui_ulist_show_hosts", P_OFFINT(hex_gui_ulist_show_hosts), TYPE_BOOL},
|
||||
{"gui_ulist_sort", P_OFFINT (hex_gui_ulist_sort), TYPE_INT},
|
||||
{"gui_ulist_style", P_OFFINT (hex_gui_ulist_style), TYPE_BOOL},
|
||||
@@ -760,6 +766,7 @@ load_default_config(void)
|
||||
#ifdef HAVE_GTK_MAC
|
||||
prefs.hex_gui_hide_menu = 1;
|
||||
#endif
|
||||
prefs.hex_gui_ctrlq_quit = 1;
|
||||
prefs.hex_gui_input_attr = 1;
|
||||
prefs.hex_gui_input_icon = 1;
|
||||
prefs.hex_gui_input_nick = 1;
|
||||
@@ -824,6 +831,9 @@ load_default_config(void)
|
||||
prefs.hex_flood_msg_time = 30;
|
||||
prefs.hex_gui_chanlist_maxusers = 9999;
|
||||
prefs.hex_gui_chanlist_minusers = 5;
|
||||
prefs.hex_gui_chanlist_width_channel = 0;
|
||||
prefs.hex_gui_chanlist_width_topic = 0;
|
||||
prefs.hex_gui_chanlist_width_users = 0;
|
||||
prefs.hex_gui_dialog_height = 256;
|
||||
prefs.hex_gui_dialog_width = 500;
|
||||
prefs.hex_gui_lagometer = 1;
|
||||
@@ -837,6 +847,8 @@ load_default_config(void)
|
||||
prefs.hex_gui_tab_trunc = 20;
|
||||
prefs.hex_gui_throttlemeter = 1;
|
||||
prefs.hex_gui_ulist_pos = 3;
|
||||
prefs.hex_gui_ulist_nick_width = 0;
|
||||
prefs.hex_gui_ulist_host_width = 0;
|
||||
prefs.hex_gui_win_height = 400;
|
||||
prefs.hex_gui_win_width = 640;
|
||||
prefs.hex_irc_ban_type = 1;
|
||||
@@ -1009,15 +1021,51 @@ load_config (void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
save_config (void)
|
||||
static int
|
||||
save_config_write_to_fd (int fh)
|
||||
{
|
||||
int fh, i;
|
||||
char *config, *new_config;
|
||||
int i;
|
||||
|
||||
if (!cfg_put_str (fh, "version", PACKAGE_VERSION))
|
||||
return 0;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
switch (vars[i].type)
|
||||
{
|
||||
case TYPE_STR:
|
||||
if (!cfg_put_str (fh, vars[i].name, (char *) &prefs + vars[i].offset))
|
||||
return 0;
|
||||
break;
|
||||
case TYPE_INT:
|
||||
case TYPE_BOOL:
|
||||
if (!cfg_put_int (fh, *((int *) &prefs + vars[i].offset), vars[i].name))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vars[i].after_update != NULL)
|
||||
vars[i].after_update();
|
||||
i++;
|
||||
}
|
||||
while (vars[i].name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
save_config_prepare (char **temp_path)
|
||||
{
|
||||
int fh;
|
||||
char *config;
|
||||
char *new_config;
|
||||
|
||||
if (check_config_dir () != 0)
|
||||
make_config_dirs ();
|
||||
|
||||
if (!temp_path)
|
||||
return 0;
|
||||
|
||||
config = default_file ();
|
||||
new_config = g_strconcat (config, ".new", NULL);
|
||||
|
||||
@@ -1028,63 +1076,67 @@ save_config (void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cfg_put_str (fh, "version", PACKAGE_VERSION))
|
||||
if (!save_config_write_to_fd (fh))
|
||||
{
|
||||
close (fh);
|
||||
g_free (new_config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
switch (vars[i].type)
|
||||
{
|
||||
case TYPE_STR:
|
||||
if (!cfg_put_str (fh, vars[i].name, (char *) &prefs + vars[i].offset))
|
||||
{
|
||||
close (fh);
|
||||
g_free (new_config);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case TYPE_INT:
|
||||
case TYPE_BOOL:
|
||||
if (!cfg_put_int (fh, *((int *) &prefs + vars[i].offset), vars[i].name))
|
||||
{
|
||||
close (fh);
|
||||
g_free (new_config);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (vars[i].after_update != NULL)
|
||||
{
|
||||
vars[i].after_update();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while (vars[i].name);
|
||||
|
||||
if (close (fh) == -1)
|
||||
{
|
||||
g_free (new_config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
g_unlink (config); /* win32 can't rename to an existing file */
|
||||
#endif
|
||||
if (g_rename (new_config, config) == -1)
|
||||
{
|
||||
g_free (new_config);
|
||||
*temp_path = new_config;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
save_config_finalize (const char *temp_path)
|
||||
{
|
||||
char *config;
|
||||
|
||||
if (!temp_path)
|
||||
return 0;
|
||||
|
||||
config = default_file ();
|
||||
#ifdef WIN32
|
||||
g_unlink (config);
|
||||
#endif
|
||||
if (g_rename (temp_path, config) == -1)
|
||||
return 0;
|
||||
}
|
||||
g_free (new_config);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
save_config_discard (const char *temp_path)
|
||||
{
|
||||
if (!temp_path)
|
||||
return;
|
||||
|
||||
g_unlink (temp_path);
|
||||
}
|
||||
|
||||
int
|
||||
save_config (void)
|
||||
{
|
||||
char *temp_path = NULL;
|
||||
int result;
|
||||
|
||||
if (!save_config_prepare (&temp_path))
|
||||
return 0;
|
||||
|
||||
result = save_config_finalize (temp_path);
|
||||
if (!result)
|
||||
save_config_discard (temp_path);
|
||||
g_free (temp_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
set_showval (session *sess, const struct prefs *var, char *tbuf)
|
||||
{
|
||||
|
||||
@@ -43,6 +43,9 @@ int make_config_dirs (void);
|
||||
int make_dcc_dirs (void);
|
||||
int load_config (void);
|
||||
int save_config (void);
|
||||
int save_config_prepare (char **temp_path);
|
||||
int save_config_finalize (const char *temp_path);
|
||||
void save_config_discard (const char *temp_path);
|
||||
void list_free (GSList ** list);
|
||||
void list_loadconf (char *file, GSList ** list, char *defaultconf);
|
||||
int list_delentry (GSList ** list, char *name);
|
||||
|
||||
@@ -44,11 +44,20 @@ typedef struct
|
||||
char *icon; /* filename */
|
||||
} menu_entry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean success;
|
||||
gboolean partial_failure;
|
||||
gboolean config_failed;
|
||||
gboolean theme_failed;
|
||||
} fe_preferences_save_result;
|
||||
|
||||
int fe_args (int argc, char *argv[]);
|
||||
void fe_init (void);
|
||||
void fe_main (void);
|
||||
void fe_cleanup (void);
|
||||
void fe_exit (void);
|
||||
fe_preferences_save_result fe_preferences_persistence_save_all (void);
|
||||
int fe_timeout_add (int interval, void *callback, void *userdata);
|
||||
int fe_timeout_add_seconds (int interval, void *callback, void *userdata);
|
||||
void fe_timeout_remove (int tag);
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "gtk3-theme-service.h"
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_GTK3_THEME_SERVICE_H
|
||||
#define ZOITECHAT_GTK3_THEME_SERVICE_H
|
||||
|
||||
|
||||
@@ -1796,6 +1796,36 @@ inbound_cap_ack (server *serv, char *nick, char *extensions,
|
||||
inbound_toggle_caps (serv, extensions, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
inbound_cap_new (server *serv, char *nick, char *extensions,
|
||||
const message_tags_data *tags_data)
|
||||
{
|
||||
if (extensions)
|
||||
{
|
||||
char **tokens = g_strsplit (extensions, " ", 0);
|
||||
int i;
|
||||
|
||||
for (i = 0; tokens[i]; i++)
|
||||
{
|
||||
char **parts = g_strsplit (tokens[i], "=", 2);
|
||||
|
||||
if (!g_strcmp0 (parts[0], "sts") && parts[1] && parts[1][0])
|
||||
{
|
||||
sts_handle_capability (serv, parts[1]);
|
||||
}
|
||||
|
||||
g_strfreev (parts);
|
||||
}
|
||||
|
||||
g_strfreev (tokens);
|
||||
}
|
||||
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions,
|
||||
NULL, NULL, 0, tags_data->timestamp);
|
||||
|
||||
inbound_toggle_caps (serv, extensions, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
inbound_cap_del (server *serv, char *nick, char *extensions,
|
||||
const message_tags_data *tags_data)
|
||||
|
||||
@@ -95,6 +95,8 @@ void inbound_cap_ls (server *serv, char *nick, char *extensions,
|
||||
void inbound_cap_nak (server *serv, char *extensions, const message_tags_data *tags_data);
|
||||
void inbound_cap_list (server *serv, char *nick, char *extensions,
|
||||
const message_tags_data *tags_data);
|
||||
void inbound_cap_new (server *serv, char *nick, char *extensions,
|
||||
const message_tags_data *tags_data);
|
||||
void inbound_cap_del (server *serv, char *nick, char *extensions,
|
||||
const message_tags_data *tags_data);
|
||||
void inbound_sasl_authenticate (server *serv, char *data);
|
||||
|
||||
@@ -1359,12 +1359,18 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
|
||||
word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
|
||||
tags_data);
|
||||
}
|
||||
else if (g_ascii_strncasecmp(word[4], "LS", 2) == 0 || g_ascii_strncasecmp(word[4], "NEW", 3) == 0)
|
||||
else if (g_ascii_strncasecmp(word[4], "LS", 2) == 0)
|
||||
{
|
||||
inbound_cap_ls (serv, word[1],
|
||||
word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
|
||||
tags_data);
|
||||
}
|
||||
else if (g_ascii_strncasecmp(word[4], "NEW", 3) == 0)
|
||||
{
|
||||
inbound_cap_new (serv, word[1],
|
||||
word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
|
||||
tags_data);
|
||||
}
|
||||
else if (g_ascii_strncasecmp(word[4], "NAK", 3) == 0)
|
||||
{
|
||||
inbound_cap_nak (serv, word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data);
|
||||
|
||||
@@ -305,6 +305,7 @@ sts_parse_value (const char *value, guint16 *port, guint64 *duration, gboolean *
|
||||
{
|
||||
char **tokens;
|
||||
gsize i;
|
||||
char *end;
|
||||
|
||||
if (!value || !*value)
|
||||
{
|
||||
@@ -349,8 +350,9 @@ sts_parse_value (const char *value, guint16 *port, guint64 *duration, gboolean *
|
||||
continue;
|
||||
}
|
||||
|
||||
port_value = g_ascii_strtoll (val, NULL, 10);
|
||||
if (port_value > 0 && port_value <= G_MAXUINT16)
|
||||
end = NULL;
|
||||
port_value = g_ascii_strtoll (val, &end, 10);
|
||||
if (end && *end == '\0' && port_value > 0 && port_value <= G_MAXUINT16)
|
||||
{
|
||||
*port = (guint16) port_value;
|
||||
*has_port = TRUE;
|
||||
@@ -371,7 +373,12 @@ sts_parse_value (const char *value, guint16 *port, guint64 *duration, gboolean *
|
||||
continue;
|
||||
}
|
||||
|
||||
duration_value = g_ascii_strtoull (val, NULL, 10);
|
||||
end = NULL;
|
||||
duration_value = g_ascii_strtoull (val, &end, 10);
|
||||
if (!end || *end != '\0')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
*duration = duration_value;
|
||||
*has_duration = TRUE;
|
||||
}
|
||||
@@ -612,7 +619,7 @@ sts_handle_capability (struct server *serv, const char *value)
|
||||
{
|
||||
time_t now = time (NULL);
|
||||
time_t expires_at = now + (time_t) duration;
|
||||
guint16 effective_port = 0;
|
||||
guint16 effective_port = (guint16) serv->port;
|
||||
sts_profile *existing_profile;
|
||||
sts_profile *profile;
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../zoitechat.h"
|
||||
#include "../gtk3-theme-service.h"
|
||||
#include "../cfgfiles.h"
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 <errno.h>
|
||||
|
||||
#include "zoitechat.h"
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_SERVICE_H
|
||||
#define ZOITECHAT_THEME_SERVICE_H
|
||||
|
||||
|
||||
275
src/common/url.c
@@ -35,20 +35,13 @@ GTree *url_btree = NULL;
|
||||
static gboolean regex_match (const GRegex *re, const char *word,
|
||||
int *start, int *end);
|
||||
static const GRegex *re_url (void);
|
||||
static const GRegex *re_url_no_scheme (void);
|
||||
static const GRegex *re_host (void);
|
||||
static const GRegex *re_host6 (void);
|
||||
static const GRegex *re_email (void);
|
||||
static const GRegex *re_nick (void);
|
||||
static const GRegex *re_channel (void);
|
||||
static const GRegex *re_path (void);
|
||||
static gboolean match_nick (const char *word, int *start, int *end);
|
||||
static gboolean match_channel (const char *word, int *start, int *end);
|
||||
static gboolean match_email (const char *word, int *start, int *end);
|
||||
static gboolean match_url (const char *word, int *start, int *end);
|
||||
static gboolean match_host (const char *word, int *start, int *end);
|
||||
static gboolean match_host6 (const char *word, int *start, int *end);
|
||||
static gboolean match_path (const char *word, int *start, int *end);
|
||||
static gboolean match_email (const char *word, int *start, int *end);
|
||||
|
||||
static int
|
||||
url_free (char *url, void *data)
|
||||
@@ -122,6 +115,11 @@ url_add (char *urltext, int len)
|
||||
return;
|
||||
}
|
||||
|
||||
if (len <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_strndup (urltext, len);
|
||||
|
||||
if (data[len - 1] == '.')
|
||||
@@ -193,9 +191,6 @@ url_check_word (const char *word)
|
||||
{ match_url, WORD_URL },
|
||||
{ match_email, WORD_EMAIL },
|
||||
{ match_channel, WORD_CHANNEL },
|
||||
{ match_host6, WORD_HOST6 },
|
||||
{ match_host, WORD_HOST },
|
||||
{ match_path, WORD_PATH },
|
||||
{ match_nick, WORD_NICK },
|
||||
{ NULL, 0}
|
||||
};
|
||||
@@ -268,39 +263,18 @@ match_channel (const char *word, int *start, int *end)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_url (const char *word, int *start, int *end)
|
||||
{
|
||||
return regex_match (re_url (), word, start, end);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_email (const char *word, int *start, int *end)
|
||||
{
|
||||
return regex_match (re_email (), word, start, end);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_url (const char *word, int *start, int *end)
|
||||
{
|
||||
if (regex_match (re_url (), word, start, end))
|
||||
return TRUE;
|
||||
|
||||
return regex_match (re_url_no_scheme (), word, start, end);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_host (const char *word, int *start, int *end)
|
||||
{
|
||||
return regex_match (re_host (), word, start, end);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_host6 (const char *word, int *start, int *end)
|
||||
{
|
||||
return regex_match (re_host6 (), word, start, end);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_path (const char *word, int *start, int *end)
|
||||
{
|
||||
return regex_match (re_path (), word, start, end);
|
||||
}
|
||||
|
||||
/* List of IRC commands for which contents (and thus possible URLs)
|
||||
* are visible to the user. NOTE: Trailing blank required in each. */
|
||||
static char *commands[] = {
|
||||
@@ -373,40 +347,64 @@ url_last (int *lstart, int *lend)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
regex_match (const GRegex *re, const char *word, int *start, int *end)
|
||||
match_has_valid_context (const char *word, int start, int end)
|
||||
{
|
||||
GMatchInfo *gmi;
|
||||
if (start > 0)
|
||||
{
|
||||
char prev = word[start - 1];
|
||||
if (g_ascii_isalnum ((guchar)prev) || prev == '_' || prev == '-')
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_regex_match (re, word, 0, &gmi);
|
||||
|
||||
if (!g_match_info_matches (gmi))
|
||||
if (word[end] != '\0')
|
||||
{
|
||||
g_match_info_free (gmi);
|
||||
return FALSE;
|
||||
char next = word[end];
|
||||
if (g_ascii_isalnum ((guchar)next) || next == '_')
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (g_match_info_matches (gmi))
|
||||
{
|
||||
g_match_info_fetch_pos (gmi, 0, start, end);
|
||||
g_match_info_next (gmi, NULL);
|
||||
}
|
||||
|
||||
g_match_info_free (gmi);
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
regex_match (const GRegex *re, const char *word, int *start, int *end)
|
||||
{
|
||||
GMatchInfo *gmi;
|
||||
gboolean found = FALSE;
|
||||
int mstart;
|
||||
int mend;
|
||||
|
||||
g_regex_match (re, word, 0, &gmi);
|
||||
|
||||
while (g_match_info_matches (gmi))
|
||||
{
|
||||
g_match_info_fetch_pos (gmi, 0, &mstart, &mend);
|
||||
if (match_has_valid_context (word, mstart, mend))
|
||||
{
|
||||
*start = mstart;
|
||||
*end = mend;
|
||||
found = TRUE;
|
||||
}
|
||||
g_match_info_next (gmi, NULL);
|
||||
}
|
||||
|
||||
g_match_info_free (gmi);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Miscellaneous description --- */
|
||||
#define DOMAIN "[_\\pL\\pN\\pS][-_\\pL\\pN\\pS]*(\\.[-_\\pL\\pN\\pS]+)*"
|
||||
#define TLD "\\.[\\pL][-\\pL\\pN]*[\\pL]"
|
||||
#define IPADDR "[0-9]{1,3}(\\.[0-9]{1,3}){3}"
|
||||
#define DOMAIN_LABEL "[\\pL\\pN](?:[-\\pL\\pN]{0,61}[\\pL\\pN])?"
|
||||
#define DOMAIN DOMAIN_LABEL "(\\." DOMAIN_LABEL ")*"
|
||||
#define TLD "\\.[\\pL](?:[-\\pL\\pN]*[\\pL\\pN])?"
|
||||
#define IPADDR "(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}"
|
||||
#define IPV6GROUP "([0-9a-f]{0,4})"
|
||||
#define IPV6ADDR "((" IPV6GROUP "(:" IPV6GROUP "){7})" \
|
||||
"|(" IPV6GROUP "(:" IPV6GROUP ")*:(:" IPV6GROUP ")+))" /* with :: compression */
|
||||
#define HOST "(" DOMAIN TLD "|" IPADDR "|" IPV6ADDR ")"
|
||||
/* In urls the IPv6 must be enclosed in square brackets */
|
||||
#define HOST_URL "(" DOMAIN TLD "|" IPADDR "|" "\\[" IPV6ADDR "\\]" ")"
|
||||
#define HOST_URL_OPT_TLD "(" DOMAIN "|" HOST_URL ")"
|
||||
#define HOST_URL_OPT_TLD HOST_URL
|
||||
#define PORT "(:[1-9][0-9]{0,4})"
|
||||
#define OPT_PORT "(" PORT ")?"
|
||||
|
||||
@@ -421,34 +419,7 @@ make_re (const char *grist)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* HOST description --- */
|
||||
/* (see miscellaneous above) */
|
||||
static const GRegex *
|
||||
re_host (void)
|
||||
{
|
||||
static GRegex *host_ret;
|
||||
|
||||
if (host_ret) return host_ret;
|
||||
|
||||
host_ret = make_re ("(" "(" HOST_URL PORT ")|(" HOST ")" ")");
|
||||
|
||||
return host_ret;
|
||||
}
|
||||
|
||||
static const GRegex *
|
||||
re_host6 (void)
|
||||
{
|
||||
static GRegex *host6_ret;
|
||||
|
||||
if (host6_ret) return host6_ret;
|
||||
|
||||
host6_ret = make_re ("(" "(" IPV6ADDR ")|(" "\\[" IPV6ADDR "\\]" PORT ")" ")");
|
||||
|
||||
return host6_ret;
|
||||
}
|
||||
|
||||
/* URL description --- */
|
||||
#define SCHEME "(%s)"
|
||||
#define LPAR "\\("
|
||||
#define RPAR "\\)"
|
||||
#define NOPARENS "[^() \t]*"
|
||||
@@ -459,86 +430,20 @@ re_host6 (void)
|
||||
"(" NOPARENS ")" \
|
||||
")*" /* Zero or more occurrences of either of these */ \
|
||||
"(?<![.,?!\\]])" /* Not allowed to end with these */
|
||||
#define USERINFO "([-a-z0-9._~%]+(:[-a-z0-9._~%]*)?@)"
|
||||
|
||||
/* Flags used to describe URIs (RFC 3986)
|
||||
*
|
||||
* Bellow is an example of what the flags match.
|
||||
*
|
||||
* URI_AUTHORITY - http://example.org:80/foo/bar
|
||||
* ^^^^^^^^^^^^^^^^
|
||||
* URI_USERINFO/URI_OPT_USERINFO - http://user@example.org:80/foo/bar
|
||||
* ^^^^^
|
||||
* URI_PATH - http://example.org:80/foo/bar
|
||||
* ^^^^^^^^
|
||||
*/
|
||||
#define URI_AUTHORITY (1 << 0)
|
||||
#define URI_OPT_USERINFO (1 << 1)
|
||||
#define URI_USERINFO (1 << 2)
|
||||
#define URI_PATH (1 << 3)
|
||||
|
||||
struct
|
||||
{
|
||||
const char *scheme; /* scheme name. e.g. http */
|
||||
const char *path_sep; /* string that begins the path */
|
||||
int flags; /* see above (flag macros) */
|
||||
const char *scheme;
|
||||
} uri[] = {
|
||||
{ "irc", "/", URI_PATH },
|
||||
{ "ircs", "/", URI_PATH },
|
||||
{ "rtsp", "/", URI_AUTHORITY | URI_PATH },
|
||||
{ "feed", "/", URI_AUTHORITY | URI_PATH },
|
||||
{ "teamspeak", "?", URI_AUTHORITY | URI_PATH },
|
||||
{ "ftp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "sftp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "ftps", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "http", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "https", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "cvs", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "svn", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "git", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "bzr", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "rsync", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "mumble", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "ventrilo", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "xmpp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "h323", ";", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "imap", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "pop", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "nfs", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "smb", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
|
||||
{ "gopher", "/", URI_AUTHORITY | URI_PATH },
|
||||
{ "gemini", "/", URI_AUTHORITY | URI_PATH },
|
||||
{ "ssh", "", URI_AUTHORITY | URI_OPT_USERINFO },
|
||||
{ "sip", "", URI_AUTHORITY | URI_USERINFO },
|
||||
{ "sips", "", URI_AUTHORITY | URI_USERINFO },
|
||||
{ "magnet", "?", URI_PATH },
|
||||
{ "mailto", "", URI_PATH },
|
||||
{ "bitcoin", "", URI_PATH },
|
||||
{ "gtalk", "", URI_PATH },
|
||||
{ "steam", "", URI_PATH },
|
||||
{ "file", "/", URI_PATH },
|
||||
{ "callto", "", URI_PATH },
|
||||
{ "skype", "", URI_PATH },
|
||||
{ "geo", "", URI_PATH },
|
||||
{ "spotify", "", URI_PATH },
|
||||
{ "lastfm", "/", URI_PATH },
|
||||
{ "xfire", "", URI_PATH },
|
||||
{ "ts3server", "", URI_PATH },
|
||||
{ NULL, "", 0}
|
||||
{ "http" },
|
||||
{ "https" },
|
||||
{ "ftp" },
|
||||
{ "gopher" },
|
||||
{ "gemini" },
|
||||
{ "irc" },
|
||||
{ "ircs" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static const GRegex *
|
||||
re_url_no_scheme (void)
|
||||
{
|
||||
static GRegex *url_ret = NULL;
|
||||
|
||||
if (url_ret) return url_ret;
|
||||
|
||||
url_ret = make_re ("(" HOST_URL OPT_PORT "/" "(" PATH ")?" ")");
|
||||
|
||||
return url_ret;
|
||||
}
|
||||
|
||||
static const GRegex *
|
||||
re_url (void)
|
||||
{
|
||||
@@ -557,27 +462,9 @@ re_url (void)
|
||||
g_string_append (grist_gstr, "|");
|
||||
|
||||
g_string_append (grist_gstr, "(");
|
||||
g_string_append_printf (grist_gstr, "%s:", uri[i].scheme);
|
||||
|
||||
if (uri[i].flags & URI_AUTHORITY)
|
||||
g_string_append (grist_gstr, "//");
|
||||
|
||||
if (uri[i].flags & URI_USERINFO)
|
||||
g_string_append (grist_gstr, USERINFO);
|
||||
else if (uri[i].flags & URI_OPT_USERINFO)
|
||||
g_string_append (grist_gstr, USERINFO "?");
|
||||
|
||||
if (uri[i].flags & URI_AUTHORITY)
|
||||
g_string_append (grist_gstr, HOST_URL_OPT_TLD OPT_PORT);
|
||||
|
||||
if (uri[i].flags & URI_PATH)
|
||||
{
|
||||
char *sep_escaped = g_regex_escape_string (uri[i].path_sep, strlen(uri[i].path_sep));
|
||||
|
||||
g_string_append_printf (grist_gstr, "(" "%s" PATH ")?", sep_escaped);
|
||||
|
||||
g_free (sep_escaped);
|
||||
}
|
||||
g_string_append_printf (grist_gstr, "%s://", uri[i].scheme);
|
||||
g_string_append (grist_gstr, HOST_URL_OPT_TLD OPT_PORT);
|
||||
g_string_append_printf (grist_gstr, "(/" PATH ")?");
|
||||
|
||||
g_string_append (grist_gstr, ")");
|
||||
}
|
||||
@@ -590,8 +477,9 @@ re_url (void)
|
||||
return url_ret;
|
||||
}
|
||||
|
||||
/* EMAIL description --- */
|
||||
#define EMAIL "[a-z0-9][._%+-a-z0-9]+@" "(" HOST_URL ")"
|
||||
#define EMAIL_LOCAL_ATOM "[\\pL\\pN!#$%&'*+/=?^_`{|}~-]+"
|
||||
#define EMAIL_LOCAL EMAIL_LOCAL_ATOM "(\\." EMAIL_LOCAL_ATOM ")*"
|
||||
#define EMAIL EMAIL_LOCAL "@" DOMAIN TLD
|
||||
|
||||
static const GRegex *
|
||||
re_email (void)
|
||||
@@ -650,24 +538,3 @@ re_channel (void)
|
||||
|
||||
return channel_ret;
|
||||
}
|
||||
|
||||
/* PATH description --- */
|
||||
#ifdef WIN32
|
||||
/* Windows path can be .\ ..\ or C: D: etc */
|
||||
#define FS_PATH "^(\\.{1,2}\\\\|[a-z]:).*"
|
||||
#else
|
||||
/* Linux path can be / or ./ or ../ etc */
|
||||
#define FS_PATH "^(/|\\./|\\.\\./).*"
|
||||
#endif
|
||||
|
||||
static const GRegex *
|
||||
re_path (void)
|
||||
{
|
||||
static GRegex *path_ret;
|
||||
|
||||
if (path_ret) return path_ret;
|
||||
|
||||
path_ret = make_re ("(" FS_PATH ")");
|
||||
|
||||
return path_ret;
|
||||
}
|
||||
|
||||
@@ -1104,7 +1104,18 @@ zoitechat_exit (void)
|
||||
plugin_kill_all ();
|
||||
fe_cleanup ();
|
||||
|
||||
save_config ();
|
||||
{
|
||||
fe_preferences_save_result save_result;
|
||||
|
||||
save_result = fe_preferences_persistence_save_all ();
|
||||
if (!save_result.success)
|
||||
{
|
||||
if (save_result.partial_failure || (!save_result.config_failed && save_result.theme_failed))
|
||||
g_printerr ("Could not fully save preferences. zoitechat.conf was written, but colors.conf failed.\n");
|
||||
else
|
||||
g_printerr ("Could not save zoitechat.conf.\n");
|
||||
}
|
||||
}
|
||||
if (prefs.save_pevents)
|
||||
{
|
||||
pevent_save (NULL);
|
||||
|
||||
@@ -122,6 +122,7 @@ struct zoitechatprefs
|
||||
unsigned int hex_gui_autoopen_recv;
|
||||
unsigned int hex_gui_autoopen_send;
|
||||
unsigned int hex_gui_compact;
|
||||
unsigned int hex_gui_ctrlq_quit;
|
||||
unsigned int hex_gui_filesize_iec;
|
||||
unsigned int hex_gui_focus_omitalerts;
|
||||
unsigned int hex_gui_hide_menu;
|
||||
@@ -248,6 +249,9 @@ struct zoitechatprefs
|
||||
int hex_flood_msg_time;
|
||||
int hex_gui_chanlist_maxusers;
|
||||
int hex_gui_chanlist_minusers;
|
||||
int hex_gui_chanlist_width_channel;
|
||||
int hex_gui_chanlist_width_topic;
|
||||
int hex_gui_chanlist_width_users;
|
||||
int hex_gui_dialog_height;
|
||||
int hex_gui_dialog_left;
|
||||
int hex_gui_dialog_top;
|
||||
@@ -269,6 +273,8 @@ struct zoitechatprefs
|
||||
int hex_gui_transparency;
|
||||
int hex_gui_throttlemeter;
|
||||
int hex_gui_ulist_pos;
|
||||
int hex_gui_ulist_nick_width;
|
||||
int hex_gui_ulist_host_width;
|
||||
int hex_gui_ulist_sort;
|
||||
int hex_gui_url_mod;
|
||||
int hex_gui_win_height;
|
||||
|
||||
@@ -153,7 +153,7 @@ ascii_open (void)
|
||||
gtk_widget_set_size_request (but, 28, -1);
|
||||
g_signal_connect (G_OBJECT (but), "clicked",
|
||||
G_CALLBACK (ascii_click), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "enter_notify_event",
|
||||
g_signal_connect (G_OBJECT (but), "enter-notify-event",
|
||||
G_CALLBACK (ascii_enter), label);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), but, 0, 0, 0);
|
||||
gtk_widget_show (but);
|
||||
|
||||
@@ -74,6 +74,15 @@ chanlistrow;
|
||||
|
||||
#define GET_MODEL(xserv) (gtk_tree_view_get_model(GTK_TREE_VIEW(xserv->gui->chanlist_list)))
|
||||
|
||||
static int
|
||||
chanlist_normalize_width (int width)
|
||||
{
|
||||
if (width < 1)
|
||||
return 1;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_set_label_alignment (GtkWidget *widget)
|
||||
{
|
||||
@@ -508,36 +517,72 @@ chanlist_match_topic_button_toggled (GtkWidget * wid, server *serv)
|
||||
serv->gui->chanlist_match_wants_topic = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid));
|
||||
}
|
||||
|
||||
static GSList *
|
||||
chanlist_get_selection (server *serv, int column)
|
||||
{
|
||||
GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (serv->gui->chanlist_list));
|
||||
GtkTreeModel *model = GET_MODEL (serv);
|
||||
GList *rows;
|
||||
GList *cur;
|
||||
GSList *result = NULL;
|
||||
|
||||
rows = gtk_tree_selection_get_selected_rows (sel, &model);
|
||||
for (cur = rows; cur != NULL; cur = cur->next)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
char *value;
|
||||
|
||||
if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *)cur->data))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, column, &value, -1);
|
||||
result = g_slist_prepend (result, value);
|
||||
}
|
||||
}
|
||||
|
||||
g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
|
||||
return g_slist_reverse (result);
|
||||
}
|
||||
|
||||
static char *
|
||||
chanlist_get_selected (server *serv, gboolean get_topic)
|
||||
{
|
||||
char *chan;
|
||||
GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (serv->gui->chanlist_list));
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GSList *selection = chanlist_get_selection (serv, get_topic ? COL_TOPIC : COL_CHANNEL);
|
||||
char *value;
|
||||
|
||||
if (!gtk_tree_selection_get_selected (sel, &model, &iter))
|
||||
if (!selection)
|
||||
return NULL;
|
||||
|
||||
gtk_tree_model_get (model, &iter, get_topic ? COL_TOPIC : COL_CHANNEL, &chan, -1);
|
||||
return chan;
|
||||
value = selection->data;
|
||||
selection->data = NULL;
|
||||
g_slist_free_full (selection, g_free);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_join (GtkWidget * wid, server *serv)
|
||||
{
|
||||
char tbuf[CHANLEN + 6];
|
||||
char *chan = chanlist_get_selected (serv, FALSE);
|
||||
if (chan)
|
||||
GSList *selection;
|
||||
GSList *item;
|
||||
gboolean joined = FALSE;
|
||||
|
||||
selection = chanlist_get_selection (serv, COL_CHANNEL);
|
||||
for (item = selection; item != NULL; item = item->next)
|
||||
{
|
||||
if (serv->connected && (strcmp (chan, "*") != 0))
|
||||
char *chan = item->data;
|
||||
|
||||
if (serv->connected && strcmp (chan, "*") != 0)
|
||||
{
|
||||
g_snprintf (tbuf, sizeof (tbuf), "join %s", chan);
|
||||
handle_command (serv->server_session, tbuf, FALSE);
|
||||
} else
|
||||
gdk_display_beep (gdk_display_get_default ());
|
||||
g_free (chan);
|
||||
joined = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!joined && selection)
|
||||
gdk_display_beep (gdk_display_get_default ());
|
||||
|
||||
g_slist_free_full (selection, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -614,7 +659,8 @@ chanlist_minusers (GtkSpinButton *wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_minusers = gtk_spin_button_get_value_as_int (wid);
|
||||
prefs.hex_gui_chanlist_minusers = serv->gui->chanlist_minusers;
|
||||
save_config();
|
||||
if (!save_config ())
|
||||
fe_message (_("Could not save zoitechat.conf."), FE_MSG_WARN);
|
||||
|
||||
if (serv->gui->chanlist_minusers < serv->gui->chanlist_minusers_downloaded)
|
||||
{
|
||||
@@ -636,7 +682,8 @@ chanlist_maxusers (GtkSpinButton *wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_maxusers = gtk_spin_button_get_value_as_int (wid);
|
||||
prefs.hex_gui_chanlist_maxusers = serv->gui->chanlist_maxusers;
|
||||
save_config();
|
||||
if (!save_config ())
|
||||
fe_message (_("Could not save zoitechat.conf."), FE_MSG_WARN);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -656,23 +703,47 @@ chanlist_menu_destroy (GtkWidget *menu, gpointer userdata)
|
||||
static void
|
||||
chanlist_copychannel (GtkWidget *item, server *serv)
|
||||
{
|
||||
char *chan = chanlist_get_selected (serv, FALSE);
|
||||
if (chan)
|
||||
GSList *selection = chanlist_get_selection (serv, COL_CHANNEL);
|
||||
GSList *cur;
|
||||
GString *text;
|
||||
|
||||
if (!selection)
|
||||
return;
|
||||
|
||||
text = g_string_new ("");
|
||||
for (cur = selection; cur != NULL; cur = cur->next)
|
||||
{
|
||||
gtkutil_copy_to_clipboard (item, NULL, chan);
|
||||
g_free (chan);
|
||||
if (text->len)
|
||||
g_string_append_c (text, '\n');
|
||||
g_string_append (text, (char *)cur->data);
|
||||
}
|
||||
|
||||
gtkutil_copy_to_clipboard (item, NULL, text->str);
|
||||
g_string_free (text, TRUE);
|
||||
g_slist_free_full (selection, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_copytopic (GtkWidget *item, server *serv)
|
||||
{
|
||||
char *topic = chanlist_get_selected (serv, TRUE);
|
||||
if (topic)
|
||||
GSList *selection = chanlist_get_selection (serv, COL_TOPIC);
|
||||
GSList *cur;
|
||||
GString *text;
|
||||
|
||||
if (!selection)
|
||||
return;
|
||||
|
||||
text = g_string_new ("");
|
||||
for (cur = selection; cur != NULL; cur = cur->next)
|
||||
{
|
||||
gtkutil_copy_to_clipboard (item, NULL, topic);
|
||||
g_free (topic);
|
||||
if (text->len)
|
||||
g_string_append_c (text, '\n');
|
||||
g_string_append (text, (char *)cur->data);
|
||||
}
|
||||
|
||||
gtkutil_copy_to_clipboard (item, NULL, text->str);
|
||||
g_string_free (text, TRUE);
|
||||
g_slist_free_full (selection, g_free);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -689,10 +760,12 @@ chanlist_button_cb (GtkTreeView *tree, GdkEventButton *event, server *serv)
|
||||
if (!gtk_tree_view_get_path_at_pos (tree, event->x, event->y, &path, 0, 0, 0))
|
||||
return FALSE;
|
||||
|
||||
/* select what they right-clicked on */
|
||||
sel = gtk_tree_view_get_selection (tree);
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
gtk_tree_selection_select_path (sel, path);
|
||||
if (!gtk_tree_selection_path_is_selected (sel, path))
|
||||
{
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
gtk_tree_selection_select_path (sel, path);
|
||||
}
|
||||
gtk_tree_path_free (path);
|
||||
|
||||
menu = gtk_menu_new ();
|
||||
@@ -741,6 +814,26 @@ chanlist_button_cb (GtkTreeView *tree, GdkEventButton *event, server *serv)
|
||||
static void
|
||||
chanlist_destroy_widget (GtkWidget *wid, server *serv)
|
||||
{
|
||||
GtkTreeViewColumn *column;
|
||||
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (serv->gui->chanlist_list), COL_CHANNEL);
|
||||
if (column)
|
||||
prefs.hex_gui_chanlist_width_channel =
|
||||
chanlist_normalize_width (gtk_tree_view_column_get_width (column));
|
||||
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (serv->gui->chanlist_list), COL_USERS);
|
||||
if (column)
|
||||
prefs.hex_gui_chanlist_width_users =
|
||||
chanlist_normalize_width (gtk_tree_view_column_get_width (column));
|
||||
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (serv->gui->chanlist_list), COL_TOPIC);
|
||||
if (column)
|
||||
prefs.hex_gui_chanlist_width_topic =
|
||||
chanlist_normalize_width (gtk_tree_view_column_get_width (column));
|
||||
|
||||
if (!save_config ())
|
||||
fe_message (_("Could not save zoitechat.conf."), FE_MSG_WARN);
|
||||
|
||||
custom_list_clear ((CustomList *)GET_MODEL (serv));
|
||||
chanlist_data_free (serv);
|
||||
|
||||
@@ -831,7 +924,8 @@ chanlist_opengui (server *serv, int do_refresh)
|
||||
if (prefs.hex_gui_chanlist_minusers < 1 || prefs.hex_gui_chanlist_minusers > 999999)
|
||||
{
|
||||
prefs.hex_gui_chanlist_minusers = 5;
|
||||
save_config();
|
||||
if (!save_config ())
|
||||
fe_message (_("Could not save zoitechat.conf."), FE_MSG_WARN);
|
||||
}
|
||||
|
||||
serv->gui->chanlist_minusers = prefs.hex_gui_chanlist_minusers;
|
||||
@@ -842,7 +936,8 @@ chanlist_opengui (server *serv, int do_refresh)
|
||||
if (prefs.hex_gui_chanlist_maxusers < 1 || prefs.hex_gui_chanlist_maxusers > 999999)
|
||||
{
|
||||
prefs.hex_gui_chanlist_maxusers = 9999;
|
||||
save_config();
|
||||
if (!save_config ())
|
||||
fe_message (_("Could not save zoitechat.conf."), FE_MSG_WARN);
|
||||
}
|
||||
|
||||
serv->gui->chanlist_maxusers = prefs.hex_gui_chanlist_maxusers;
|
||||
@@ -870,7 +965,7 @@ chanlist_opengui (server *serv, int do_refresh)
|
||||
GTK_SHADOW_IN);
|
||||
serv->gui->chanlist_list = view;
|
||||
|
||||
g_signal_connect (G_OBJECT (view), "row_activated",
|
||||
g_signal_connect (G_OBJECT (view), "row-activated",
|
||||
G_CALLBACK (chanlist_dclick_cb), serv);
|
||||
g_signal_connect (G_OBJECT (view), "button-press-event",
|
||||
G_CALLBACK (chanlist_button_cb), serv);
|
||||
@@ -878,7 +973,39 @@ chanlist_opengui (server *serv, int do_refresh)
|
||||
chanlist_add_column (view, COL_CHANNEL, 96, _("Channel"), FALSE);
|
||||
chanlist_add_column (view, COL_USERS, 50, _("Users"), TRUE);
|
||||
chanlist_add_column (view, COL_TOPIC, 50, _("Topic"), FALSE);
|
||||
|
||||
if (prefs.hex_gui_chanlist_width_channel > 0)
|
||||
{
|
||||
GtkTreeViewColumn *column = gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_CHANNEL);
|
||||
if (column)
|
||||
gtk_tree_view_column_set_fixed_width (column,
|
||||
chanlist_normalize_width (prefs.hex_gui_chanlist_width_channel));
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_chanlist_width_users > 0)
|
||||
{
|
||||
GtkTreeViewColumn *column = gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_USERS);
|
||||
if (column)
|
||||
{
|
||||
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_fixed_width (column,
|
||||
chanlist_normalize_width (prefs.hex_gui_chanlist_width_users));
|
||||
gtk_tree_view_column_set_resizable (column, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_chanlist_width_topic > 0)
|
||||
{
|
||||
GtkTreeViewColumn *column = gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_TOPIC);
|
||||
if (column)
|
||||
{
|
||||
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_fixed_width (column,
|
||||
chanlist_normalize_width (prefs.hex_gui_chanlist_width_topic));
|
||||
}
|
||||
}
|
||||
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_HORIZONTAL);
|
||||
gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), GTK_SELECTION_MULTIPLE);
|
||||
/* this is a speed up, but no horizontal scrollbar :( */
|
||||
/*gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);*/
|
||||
gtk_widget_show (view);
|
||||
@@ -936,7 +1063,7 @@ chanlist_opengui (server *serv, int do_refresh)
|
||||
wid = gtk_spin_button_new_with_range (1, 999999, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
serv->gui->chanlist_minusers);
|
||||
g_signal_connect (G_OBJECT (wid), "value_changed",
|
||||
g_signal_connect (G_OBJECT (wid), "value-changed",
|
||||
G_CALLBACK (chanlist_minusers), serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
@@ -949,7 +1076,7 @@ chanlist_opengui (server *serv, int do_refresh)
|
||||
wid = gtk_spin_button_new_with_range (1, 999999, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
serv->gui->chanlist_maxusers);
|
||||
g_signal_connect (G_OBJECT (wid), "value_changed",
|
||||
g_signal_connect (G_OBJECT (wid), "value-changed",
|
||||
G_CALLBACK (chanlist_maxusers), serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
@@ -371,7 +371,7 @@ make_sbutton (GtkArrowType type, void *click_cb, void *userdata)
|
||||
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
|
||||
g_signal_connect (G_OBJECT (button), "clicked",
|
||||
G_CALLBACK (click_cb), userdata);
|
||||
g_signal_connect (G_OBJECT (button), "scroll_event",
|
||||
g_signal_connect (G_OBJECT (button), "scroll-event",
|
||||
G_CALLBACK (tab_scroll_cb), userdata);
|
||||
gtk_widget_show (arrow);
|
||||
|
||||
@@ -395,7 +395,7 @@ cv_tabs_init (chanview *cv)
|
||||
outer = gtkutil_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
}
|
||||
((tabview *)cv)->outer = outer;
|
||||
g_signal_connect (G_OBJECT (outer), "size_allocate",
|
||||
g_signal_connect (G_OBJECT (outer), "size-allocate",
|
||||
G_CALLBACK (cv_tabs_sizealloc), cv);
|
||||
gtk_widget_show (outer);
|
||||
|
||||
@@ -405,7 +405,7 @@ cv_tabs_init (chanview *cv)
|
||||
gtk_widget_set_size_request (viewport, -1, 1);
|
||||
else
|
||||
gtk_widget_set_size_request (viewport, 1, -1);
|
||||
g_signal_connect (G_OBJECT (viewport), "scroll_event",
|
||||
g_signal_connect (G_OBJECT (viewport), "scroll-event",
|
||||
G_CALLBACK (tab_scroll_cb), cv);
|
||||
gtk_box_pack_start (GTK_BOX (outer), viewport, 1, 1, 0);
|
||||
gtk_widget_show (viewport);
|
||||
@@ -671,12 +671,12 @@ cv_tabs_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
|
||||
gtk_widget_set_name (but, "zoitechat-tab");
|
||||
g_object_set_data (G_OBJECT (but), "c", ch);
|
||||
/* used to trap right-clicks */
|
||||
g_signal_connect (G_OBJECT (but), "button_press_event",
|
||||
g_signal_connect (G_OBJECT (but), "button-press-event",
|
||||
G_CALLBACK (tab_click_cb), ch);
|
||||
/* avoid prelights */
|
||||
g_signal_connect (G_OBJECT (but), "enter_notify_event",
|
||||
g_signal_connect (G_OBJECT (but), "enter-notify-event",
|
||||
G_CALLBACK (tab_ignore_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "leave_notify_event",
|
||||
g_signal_connect (G_OBJECT (but), "leave-notify-event",
|
||||
G_CALLBACK (tab_ignore_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "pressed",
|
||||
G_CALLBACK (tab_pressed_cb), ch);
|
||||
|
||||
@@ -131,6 +131,7 @@ cv_tree_init (chanview *cv)
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_scrolled_window_set_min_content_width (GTK_SCROLLED_WINDOW (win), 1);
|
||||
gtk_container_add (GTK_CONTAINER (cv->box), win);
|
||||
gtk_widget_show (win);
|
||||
|
||||
@@ -165,30 +166,32 @@ cv_tree_init (chanview *cv)
|
||||
gtk_container_add (GTK_CONTAINER (win), view);
|
||||
col = gtk_tree_view_column_new();
|
||||
|
||||
/* icon column */
|
||||
if (cv->use_icons)
|
||||
{
|
||||
renderer = gtk_cell_renderer_pixbuf_new ();
|
||||
if (prefs.hex_gui_compact)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
|
||||
gtk_tree_view_column_pack_start(col, renderer, FALSE);
|
||||
gtk_tree_view_column_pack_start (col, renderer, FALSE);
|
||||
gtk_tree_view_column_set_attributes (col, renderer, "pixbuf", COL_PIXBUF, NULL);
|
||||
}
|
||||
|
||||
/* main column */
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (prefs.hex_gui_compact)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
gtk_tree_view_column_pack_start(col, renderer, TRUE);
|
||||
gtk_tree_view_column_pack_start (col, renderer, TRUE);
|
||||
gtk_tree_view_column_set_attributes (col, renderer,
|
||||
"text", COL_NAME,
|
||||
"attributes", COL_ATTR,
|
||||
"underline", COL_UNDERLINE,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_expand (col, TRUE);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
||||
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
|
||||
gtk_tree_view_column_set_min_width (col, 1);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
|
||||
gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view), col);
|
||||
|
||||
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (view))),
|
||||
"changed", G_CALLBACK (cv_tree_sel_cb), cv);
|
||||
@@ -196,20 +199,20 @@ cv_tree_init (chanview *cv)
|
||||
G_CALLBACK (cv_tree_click_cb), cv);
|
||||
g_signal_connect (G_OBJECT (view), "row-activated",
|
||||
G_CALLBACK (cv_tree_activated_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "scroll_event",
|
||||
g_signal_connect (G_OBJECT (view), "scroll-event",
|
||||
G_CALLBACK (cv_tree_scroll_event_cb), NULL);
|
||||
|
||||
gtk_drag_dest_set (view, GTK_DEST_DEFAULT_ALL, dnd_dest_target, 1,
|
||||
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
|
||||
gtk_drag_source_set (view, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_COPY);
|
||||
|
||||
g_signal_connect (G_OBJECT (view), "drag_begin",
|
||||
g_signal_connect (G_OBJECT (view), "drag-begin",
|
||||
G_CALLBACK (mg_drag_begin_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_drop",
|
||||
g_signal_connect (G_OBJECT (view), "drag-drop",
|
||||
G_CALLBACK (mg_drag_drop_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_motion",
|
||||
g_signal_connect (G_OBJECT (view), "drag-motion",
|
||||
G_CALLBACK (mg_drag_motion_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_end",
|
||||
g_signal_connect (G_OBJECT (view), "drag-end",
|
||||
G_CALLBACK (mg_drag_end_cb), NULL);
|
||||
|
||||
((treeview *)cv)->tree = GTK_TREE_VIEW (view);
|
||||
|
||||
@@ -863,7 +863,7 @@ fe_dcc_open_recv_win (int passive)
|
||||
gtk_tree_selection_set_mode (dccfwin.sel, GTK_SELECTION_MULTIPLE);
|
||||
|
||||
if (!prefs.hex_gui_tab_utils)
|
||||
g_signal_connect (G_OBJECT (dccfwin.window), "configure_event",
|
||||
g_signal_connect (G_OBJECT (dccfwin.window), "configure-event",
|
||||
G_CALLBACK (dcc_configure_cb), 0);
|
||||
g_signal_connect (G_OBJECT (dccfwin.sel), "changed",
|
||||
G_CALLBACK (dcc_row_cb), NULL);
|
||||
|
||||
@@ -290,7 +290,7 @@ editlist_treeview_new (GtkWidget *box, char *title1, char *title2)
|
||||
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
|
||||
gtk_tree_view_set_reorderable (GTK_TREE_VIEW (view), TRUE);
|
||||
|
||||
g_signal_connect (G_OBJECT (view), "key_press_event",
|
||||
g_signal_connect (G_OBJECT (view), "key-press-event",
|
||||
G_CALLBACK (editlist_keypress), NULL);
|
||||
|
||||
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_HORIZONTAL);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <dwmapi.h>
|
||||
#include <glib/gwin32.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -59,6 +60,7 @@
|
||||
#include "plugin-notification.h"
|
||||
#include "theme/theme-manager.h"
|
||||
#include "theme/theme-application.h"
|
||||
#include "preferences-persistence.h"
|
||||
|
||||
#ifdef USE_LIBCANBERRA
|
||||
#include <canberra.h>
|
||||
@@ -300,9 +302,28 @@ fe_args (int argc, char *argv[])
|
||||
GOptionContext *context;
|
||||
char *buffer;
|
||||
const char *desktop_id = "net.zoite.Zoitechat";
|
||||
#ifdef WIN32
|
||||
char *base_path = NULL;
|
||||
char *locale_path = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NLS
|
||||
#ifdef WIN32
|
||||
base_path = g_win32_get_package_installation_directory_of_module (NULL);
|
||||
if (base_path)
|
||||
{
|
||||
locale_path = g_build_filename (base_path, "share", "locale", NULL);
|
||||
bindtextdomain (GETTEXT_PACKAGE, locale_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
||||
}
|
||||
g_free (locale_path);
|
||||
g_free (base_path);
|
||||
#else
|
||||
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
||||
#endif
|
||||
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
||||
textdomain (GETTEXT_PACKAGE);
|
||||
#endif
|
||||
@@ -585,6 +606,21 @@ fe_cleanup (void)
|
||||
{
|
||||
}
|
||||
|
||||
fe_preferences_save_result
|
||||
fe_preferences_persistence_save_all (void)
|
||||
{
|
||||
PreferencesPersistenceResult save_result;
|
||||
fe_preferences_save_result result;
|
||||
|
||||
save_result = preferences_persistence_save_all ();
|
||||
result.success = save_result.success;
|
||||
result.partial_failure = save_result.partial_failure;
|
||||
result.config_failed = save_result.config_failed;
|
||||
result.theme_failed = save_result.theme_failed;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
fe_exit (void)
|
||||
{
|
||||
@@ -758,11 +794,15 @@ fe_set_topic (session *sess, char *topic, char *stripped_topic)
|
||||
{
|
||||
if (prefs.hex_text_stripcolor_topic)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (sess->gui->topic_entry), stripped_topic);
|
||||
gtk_text_buffer_set_text (
|
||||
gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry)),
|
||||
stripped_topic, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (sess->gui->topic_entry), topic);
|
||||
gtk_text_buffer_set_text (
|
||||
gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry)),
|
||||
topic, -1);
|
||||
}
|
||||
mg_set_topic_tip (sess);
|
||||
}
|
||||
@@ -1335,22 +1375,79 @@ fe_open_url_inner (const char *url)
|
||||
g_free (escaped_url);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fe_open_url_is_local_path (const char *url)
|
||||
{
|
||||
if (g_path_is_absolute (url) || g_file_test (url, G_FILE_TEST_EXISTS))
|
||||
return TRUE;
|
||||
|
||||
#ifdef WIN32
|
||||
if (g_ascii_isalpha (url[0]) && url[1] == ':' &&
|
||||
(url[2] == '\\' || url[2] == '/'))
|
||||
return TRUE;
|
||||
|
||||
if (url[0] == '\\' && url[1] == '\\')
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static char *
|
||||
fe_open_url_canonicalize_path (const char *path)
|
||||
{
|
||||
char *absolute_path;
|
||||
char *cwd;
|
||||
|
||||
if (!path || path[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
if (g_path_is_absolute (path))
|
||||
return g_strdup (path);
|
||||
|
||||
cwd = g_get_current_dir ();
|
||||
absolute_path = g_build_filename (cwd, path, NULL);
|
||||
g_free (cwd);
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
void
|
||||
fe_open_url (const char *url)
|
||||
{
|
||||
int url_type = url_check_word (url);
|
||||
char *uri;
|
||||
char *path;
|
||||
char *path_uri;
|
||||
|
||||
if (fe_open_url_is_local_path (url))
|
||||
{
|
||||
path = fe_open_url_canonicalize_path (url);
|
||||
path_uri = g_filename_to_uri (path, NULL, NULL);
|
||||
g_free (path);
|
||||
|
||||
if (path_uri)
|
||||
{
|
||||
fe_open_url_inner (path_uri);
|
||||
g_free (path_uri);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* gvfs likes file:// */
|
||||
if (url_type == WORD_PATH)
|
||||
{
|
||||
#ifndef WIN32
|
||||
uri = g_strconcat ("file://", url, NULL);
|
||||
fe_open_url_inner (uri);
|
||||
g_free (uri);
|
||||
#else
|
||||
fe_open_url_inner (url);
|
||||
#endif
|
||||
path = fe_open_url_canonicalize_path (url);
|
||||
path_uri = g_filename_to_uri (path, NULL, NULL);
|
||||
g_free (path);
|
||||
if (path_uri)
|
||||
{
|
||||
fe_open_url_inner (path_uri);
|
||||
g_free (path_uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
fe_open_url_inner (url);
|
||||
}
|
||||
}
|
||||
/* IPv6 addr. Add http:// */
|
||||
else if (url_type == WORD_HOST6)
|
||||
|
||||
@@ -170,7 +170,7 @@ typedef struct session_gui
|
||||
GtkWidget *shbox, *shentry; /* search bar hbox */
|
||||
gulong search_changed_signal; /* hook for search change event so blanking the box doesn't suck */
|
||||
|
||||
#define MENU_ID_NUM 14
|
||||
#define MENU_ID_NUM 15
|
||||
GtkWidget *menu_item[MENU_ID_NUM+1]; /* some items we may change state of */
|
||||
|
||||
void *chanview; /* chanview.h */
|
||||
|
||||
@@ -61,6 +61,7 @@ powershell "Get-Content -Encoding UTF8 '$(ZoiteChatLib)zoitechat.rc.utf8' | Out-
|
||||
<ClInclude Include="icon-resolver.h" />
|
||||
<ClInclude Include="joind.h" />
|
||||
<ClInclude Include="maingui.h" />
|
||||
<ClInclude Include="preferences-persistence.h" />
|
||||
<ClInclude Include="menu.h" />
|
||||
<ClInclude Include="notifications\notification-backend.h" />
|
||||
<ClInclude Include="notifygui.h" />
|
||||
@@ -107,6 +108,7 @@ powershell "Get-Content -Encoding UTF8 '$(ZoiteChatLib)zoitechat.rc.utf8' | Out-
|
||||
<ClCompile Include="notifications\notification-windows.c" />
|
||||
<ClCompile Include="notifygui.c" />
|
||||
<ClCompile Include="pixmaps.c" />
|
||||
<ClCompile Include="preferences-persistence.c" />
|
||||
<ClCompile Include="plugin-notification.c" />
|
||||
<ClCompile Include="plugin-tray.c" />
|
||||
<ClCompile Include="plugingui.c" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
@@ -51,6 +51,9 @@
|
||||
<ClInclude Include="maingui.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="preferences-persistence.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="menu.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -182,6 +185,9 @@
|
||||
<ClCompile Include="pixmaps.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="preferences-persistence.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="plugingui.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -470,7 +470,7 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
|
||||
return 1;
|
||||
case 2:
|
||||
g_signal_stop_emission_by_name (G_OBJECT (wid),
|
||||
"key_press_event");
|
||||
"key-press-event");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1888,6 +1888,20 @@ key_action_put_history (GtkWidget * wid, GdkEventKey * ent, char *d1,
|
||||
return 2;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
replace_set_pos_idle (gpointer data)
|
||||
{
|
||||
GtkWidget *t = GTK_WIDGET (data);
|
||||
gpointer pos_data = g_object_get_data (G_OBJECT (t), "zoitechat-replace-pos");
|
||||
|
||||
if (pos_data)
|
||||
SPELL_ENTRY_SET_POS (t, GPOINTER_TO_INT (pos_data));
|
||||
|
||||
g_object_set_data (G_OBJECT (t), "zoitechat-replace-pos", NULL);
|
||||
g_object_unref (t);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
replace_handle (GtkWidget *t)
|
||||
{
|
||||
@@ -1903,11 +1917,12 @@ replace_handle (GtkWidget *t)
|
||||
size_t match_len;
|
||||
ptrdiff_t cursor_byte_offset;
|
||||
ptrdiff_t match_start_offset;
|
||||
ptrdiff_t match_end_offset;
|
||||
ptrdiff_t new_cursor_offset;
|
||||
const char *best_match;
|
||||
size_t best_len;
|
||||
struct popup *best_pop;
|
||||
int best_rank;
|
||||
ptrdiff_t best_distance;
|
||||
|
||||
text = SPELL_ENTRY_GET_TEXT (t);
|
||||
|
||||
@@ -1922,19 +1937,50 @@ replace_handle (GtkWidget *t)
|
||||
best_match = NULL;
|
||||
best_len = 0;
|
||||
best_pop = NULL;
|
||||
best_rank = 3;
|
||||
best_distance = 0;
|
||||
cursor_byte_offset = cursor_ptr - text;
|
||||
while (list)
|
||||
{
|
||||
pop = (struct popup *) list->data;
|
||||
if (pop->name[0] != '\0')
|
||||
{
|
||||
size_t pop_len = strlen (pop->name);
|
||||
const char *found = strstr (text, pop->name);
|
||||
const char *found = text;
|
||||
|
||||
if (found && (!best_match || found < best_match))
|
||||
while ((found = strstr (found, pop->name)) != NULL)
|
||||
{
|
||||
best_match = found;
|
||||
best_len = pop_len;
|
||||
best_pop = pop;
|
||||
ptrdiff_t found_offset = found - text;
|
||||
ptrdiff_t found_end_offset = found_offset + (ptrdiff_t) pop_len;
|
||||
int rank;
|
||||
ptrdiff_t distance;
|
||||
|
||||
if (cursor_byte_offset >= found_offset && cursor_byte_offset <= found_end_offset)
|
||||
{
|
||||
rank = 0;
|
||||
distance = found_end_offset - cursor_byte_offset;
|
||||
}
|
||||
else if (found_end_offset <= cursor_byte_offset)
|
||||
{
|
||||
rank = 1;
|
||||
distance = cursor_byte_offset - found_end_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
rank = 2;
|
||||
distance = found_offset - cursor_byte_offset;
|
||||
}
|
||||
|
||||
if (rank < best_rank || (rank == best_rank && distance < best_distance))
|
||||
{
|
||||
best_rank = rank;
|
||||
best_distance = distance;
|
||||
best_match = found;
|
||||
best_len = pop_len;
|
||||
best_pop = pop;
|
||||
}
|
||||
|
||||
found++;
|
||||
}
|
||||
}
|
||||
list = list->next;
|
||||
@@ -1952,21 +1998,17 @@ replace_handle (GtkWidget *t)
|
||||
return;
|
||||
|
||||
replacement_len = strlen (pop->cmd);
|
||||
cursor_byte_offset = cursor_ptr - text;
|
||||
match_start_offset = match_start - text;
|
||||
match_end_offset = match_start_offset + (ptrdiff_t) match_len;
|
||||
if (cursor_byte_offset <= match_start_offset)
|
||||
new_cursor_offset = cursor_byte_offset;
|
||||
else if (cursor_byte_offset >= match_end_offset)
|
||||
new_cursor_offset = cursor_byte_offset + ((ptrdiff_t) replacement_len - (ptrdiff_t) match_len);
|
||||
else
|
||||
new_cursor_offset = match_start_offset + (ptrdiff_t) replacement_len;
|
||||
new_cursor_offset = match_start_offset + (ptrdiff_t) replacement_len;
|
||||
buf = g_string_sized_new (strlen (text) + 32);
|
||||
g_string_append_len (buf, text, match_start - text);
|
||||
g_string_append (buf, pop->cmd);
|
||||
g_string_append (buf, match_start + match_len);
|
||||
SPELL_ENTRY_SET_TEXT (t, buf->str);
|
||||
SPELL_ENTRY_SET_POS (t, len_to_offset (buf->str, new_cursor_offset));
|
||||
new_cursor_offset = len_to_offset (buf->str, new_cursor_offset);
|
||||
SPELL_ENTRY_SET_POS (t, new_cursor_offset);
|
||||
g_object_set_data (G_OBJECT (t), "zoitechat-replace-pos", GINT_TO_POINTER ((gint) new_cursor_offset));
|
||||
g_idle_add (replace_set_pos_idle, g_object_ref (t));
|
||||
g_string_free (buf, TRUE);
|
||||
}
|
||||
|
||||
|
||||
@@ -662,7 +662,7 @@ gtkutil_esc_destroy (GtkWidget * win, GdkEventKey * key, gpointer userdata)
|
||||
void
|
||||
gtkutil_destroy_on_esc (GtkWidget *win)
|
||||
{
|
||||
g_signal_connect (G_OBJECT (win), "key_press_event", G_CALLBACK (gtkutil_esc_destroy), win);
|
||||
g_signal_connect (G_OBJECT (win), "key-press-event", G_CALLBACK (gtkutil_esc_destroy), win);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1120,7 +1120,9 @@ gtkutil_treeview_get_selected (GtkTreeView *view, GtkTreeIter *iter_ret, ...)
|
||||
gboolean
|
||||
gtkutil_tray_icon_supported (GtkWindow *window)
|
||||
{
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#if defined(HAVE_AYATANA_APPINDICATOR) || defined(HAVE_APPINDICATOR)
|
||||
return TRUE;
|
||||
#elif defined(GDK_WINDOWING_X11)
|
||||
GdkScreen *screen = gtk_window_get_screen (window);
|
||||
GdkDisplay *display = gdk_screen_get_display (screen);
|
||||
if (!GDK_IS_X11_DISPLAY (display))
|
||||
|
||||
@@ -241,7 +241,7 @@ joind_show_dialog (server *serv)
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog1), "destroy",
|
||||
G_CALLBACK (joind_destroy_cb), serv);
|
||||
g_signal_connect (G_OBJECT (entry1), "focus_in_event",
|
||||
g_signal_connect (G_OBJECT (entry1), "focus-in-event",
|
||||
G_CALLBACK (joind_entryfocus_cb), serv);
|
||||
g_signal_connect (G_OBJECT (entry1), "activate",
|
||||
G_CALLBACK (joind_entryenter_cb), okbutton1);
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "theme/theme-palette.h"
|
||||
#include "maingui.h"
|
||||
#include "menu.h"
|
||||
#include "preferences-persistence.h"
|
||||
#include "fkeys.h"
|
||||
#include "userlistgui.h"
|
||||
#include "chanview.h"
|
||||
@@ -173,6 +174,81 @@ enum
|
||||
|
||||
static void mg_apply_emoji_fallback_widget (GtkWidget *widget);
|
||||
|
||||
#define MG_CONFIG_SAVE_DEBOUNCE_MS 250
|
||||
|
||||
static guint mg_config_save_source_id = 0;
|
||||
static gboolean mg_config_prefs_dirty = FALSE;
|
||||
|
||||
static void
|
||||
mg_show_save_failure (const PreferencesPersistenceResult *save_result)
|
||||
{
|
||||
char buffer[192];
|
||||
|
||||
if (!save_result || save_result->success)
|
||||
return;
|
||||
|
||||
if (save_result->partial_failure)
|
||||
{
|
||||
fe_message (_("Could not fully save preferences. zoitechat.conf was written, but colors.conf failed. Retry is possible."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
g_snprintf (buffer, sizeof (buffer), _("Could not save preferences (%s). Retry is possible."), save_result->failed_file ? save_result->failed_file : _("unknown file"));
|
||||
fe_message (buffer, FE_MSG_ERROR);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_config_save_timeout_cb (gpointer userdata)
|
||||
{
|
||||
PreferencesPersistenceResult save_result;
|
||||
|
||||
mg_config_save_source_id = 0;
|
||||
|
||||
if (!mg_config_prefs_dirty)
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
save_result = preferences_persistence_save_all ();
|
||||
if (!save_result.success)
|
||||
mg_show_save_failure (&save_result);
|
||||
mg_config_prefs_dirty = FALSE;
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
mg_schedule_config_save (void)
|
||||
{
|
||||
if (!mg_config_prefs_dirty)
|
||||
return;
|
||||
|
||||
if (mg_config_save_source_id != 0)
|
||||
g_source_remove (mg_config_save_source_id);
|
||||
|
||||
mg_config_save_source_id = g_timeout_add (MG_CONFIG_SAVE_DEBOUNCE_MS,
|
||||
mg_config_save_timeout_cb,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
mg_flush_config_save (void)
|
||||
{
|
||||
PreferencesPersistenceResult save_result;
|
||||
|
||||
if (mg_config_save_source_id != 0)
|
||||
{
|
||||
g_source_remove (mg_config_save_source_id);
|
||||
mg_config_save_source_id = 0;
|
||||
}
|
||||
|
||||
if (mg_config_prefs_dirty)
|
||||
{
|
||||
save_result = preferences_persistence_save_all ();
|
||||
if (!save_result.success)
|
||||
mg_show_save_failure (&save_result);
|
||||
mg_config_prefs_dirty = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
mg_set_source_color (cairo_t *cr, const XTextColor *color)
|
||||
{
|
||||
@@ -231,6 +307,34 @@ mg_set_label_alignment_start (GtkWidget *widget)
|
||||
gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
|
||||
}
|
||||
|
||||
static void
|
||||
mg_apply_compact_mode_css (GtkWidget *widget)
|
||||
{
|
||||
GtkStyleContext *context;
|
||||
GtkCssProvider *provider;
|
||||
|
||||
if (!widget)
|
||||
return;
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
provider = g_object_get_data (G_OBJECT (widget), "mg-mode-css-provider");
|
||||
if (!provider)
|
||||
{
|
||||
provider = gtk_css_provider_new ();
|
||||
g_object_set_data_full (G_OBJECT (widget), "mg-mode-css-provider", provider, g_object_unref);
|
||||
}
|
||||
|
||||
gtk_css_provider_load_from_data (provider,
|
||||
".zoitechat-mode-control { min-height: 11px; padding-top: 0; padding-bottom: 0; }"
|
||||
".zoitechat-mode-control label { padding-top: 0; padding-bottom: 0; }",
|
||||
-1, NULL);
|
||||
gtk_style_context_add_class (context, "zoitechat-mode-control");
|
||||
theme_css_apply_widget_provider (widget, GTK_STYLE_PROVIDER (provider));
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
mg_box_new (GtkOrientation orientation, gboolean homogeneous, gint spacing)
|
||||
{
|
||||
@@ -670,7 +774,7 @@ mg_spellcheck_cb (SexySpellEntry *entry, gchar *word, gpointer data)
|
||||
{
|
||||
/* This can cause freezes on long words, nicks arn't very long anyway. */
|
||||
if (strlen (word) > 20)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
|
||||
/* Ignore anything we think is a valid url */
|
||||
if (url_check_word (word) != 0)
|
||||
@@ -761,6 +865,10 @@ fe_set_title (session *sess)
|
||||
static gboolean
|
||||
mg_windowstate_cb (GtkWindow *wid, GdkEventWindowState *event, gpointer userdata)
|
||||
{
|
||||
guint win_state;
|
||||
guint win_fullscreen;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
if ((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) &&
|
||||
(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) &&
|
||||
prefs.hex_gui_tray_minimize && prefs.hex_gui_tray &&
|
||||
@@ -769,13 +877,31 @@ mg_windowstate_cb (GtkWindow *wid, GdkEventWindowState *event, gpointer userdata
|
||||
tray_toggle_visibility (TRUE);
|
||||
}
|
||||
|
||||
prefs.hex_gui_win_state = 0;
|
||||
if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
|
||||
prefs.hex_gui_win_state = 1;
|
||||
win_state = 0;
|
||||
if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
|
||||
win_state = 1;
|
||||
|
||||
prefs.hex_gui_win_fullscreen = 0;
|
||||
if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
|
||||
prefs.hex_gui_win_fullscreen = 1;
|
||||
win_fullscreen = 0;
|
||||
if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
|
||||
win_fullscreen = 1;
|
||||
|
||||
if (prefs.hex_gui_win_state != win_state)
|
||||
{
|
||||
prefs.hex_gui_win_state = win_state;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_win_fullscreen != win_fullscreen)
|
||||
{
|
||||
prefs.hex_gui_win_fullscreen = win_fullscreen;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
mg_config_prefs_dirty = TRUE;
|
||||
mg_schedule_config_save ();
|
||||
}
|
||||
|
||||
menu_set_fullscreen (current_sess->gui, prefs.hex_gui_win_fullscreen);
|
||||
|
||||
@@ -789,30 +915,87 @@ mg_windowstate_cb (GtkWindow *wid, GdkEventWindowState *event, gpointer userdata
|
||||
static gboolean
|
||||
mg_configure_cb (GtkWidget *wid, GdkEventConfigure *event, session *sess)
|
||||
{
|
||||
if (sess == NULL) /* for the main_window */
|
||||
gboolean changed = FALSE;
|
||||
|
||||
if (sess == NULL)
|
||||
{
|
||||
if (mg_gui)
|
||||
{
|
||||
if (prefs.hex_gui_win_save && !prefs.hex_gui_win_state && !prefs.hex_gui_win_fullscreen)
|
||||
{
|
||||
sess = current_sess;
|
||||
gtk_window_get_position (GTK_WINDOW (wid), &prefs.hex_gui_win_left,
|
||||
&prefs.hex_gui_win_top);
|
||||
gtk_window_get_size (GTK_WINDOW (wid), &prefs.hex_gui_win_width,
|
||||
&prefs.hex_gui_win_height);
|
||||
int win_left;
|
||||
int win_top;
|
||||
int win_width;
|
||||
int win_height;
|
||||
|
||||
gtk_window_get_position (GTK_WINDOW (wid), &win_left, &win_top);
|
||||
gtk_window_get_size (GTK_WINDOW (wid), &win_width, &win_height);
|
||||
|
||||
if (prefs.hex_gui_win_left != win_left)
|
||||
{
|
||||
prefs.hex_gui_win_left = win_left;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_win_top != win_top)
|
||||
{
|
||||
prefs.hex_gui_win_top = win_top;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_win_width != win_width)
|
||||
{
|
||||
prefs.hex_gui_win_width = win_width;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_win_height != win_height)
|
||||
{
|
||||
prefs.hex_gui_win_height = win_height;
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sess)
|
||||
else if (sess->type == SESS_DIALOG && prefs.hex_gui_win_save)
|
||||
{
|
||||
if (sess->type == SESS_DIALOG && prefs.hex_gui_win_save)
|
||||
int dialog_left;
|
||||
int dialog_top;
|
||||
int dialog_width;
|
||||
int dialog_height;
|
||||
|
||||
gtk_window_get_position (GTK_WINDOW (wid), &dialog_left, &dialog_top);
|
||||
gtk_window_get_size (GTK_WINDOW (wid), &dialog_width, &dialog_height);
|
||||
|
||||
if (prefs.hex_gui_dialog_left != dialog_left)
|
||||
{
|
||||
gtk_window_get_position (GTK_WINDOW (wid), &prefs.hex_gui_dialog_left,
|
||||
&prefs.hex_gui_dialog_top);
|
||||
gtk_window_get_size (GTK_WINDOW (wid), &prefs.hex_gui_dialog_width,
|
||||
&prefs.hex_gui_dialog_height);
|
||||
prefs.hex_gui_dialog_left = dialog_left;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_dialog_top != dialog_top)
|
||||
{
|
||||
prefs.hex_gui_dialog_top = dialog_top;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_dialog_width != dialog_width)
|
||||
{
|
||||
prefs.hex_gui_dialog_width = dialog_width;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (prefs.hex_gui_dialog_height != dialog_height)
|
||||
{
|
||||
prefs.hex_gui_dialog_height = dialog_height;
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
mg_config_prefs_dirty = TRUE;
|
||||
mg_schedule_config_save ();
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@@ -920,13 +1103,18 @@ mg_unpopulate (session *sess)
|
||||
{
|
||||
restore_gui *res;
|
||||
session_gui *gui;
|
||||
GtkTextBuffer *topic_buffer;
|
||||
GtkTextIter start;
|
||||
GtkTextIter end;
|
||||
int i;
|
||||
|
||||
gui = sess->gui;
|
||||
res = sess->res;
|
||||
|
||||
res->input_text = g_strdup (SPELL_ENTRY_GET_TEXT (gui->input_box));
|
||||
res->topic_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->topic_entry)));
|
||||
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gui->topic_entry));
|
||||
gtk_text_buffer_get_bounds (topic_buffer, &start, &end);
|
||||
res->topic_text = gtk_text_buffer_get_text (topic_buffer, &start, &end, FALSE);
|
||||
res->limit_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->limit_entry)));
|
||||
res->key_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->key_entry)));
|
||||
if (gui->laginfo)
|
||||
@@ -1003,6 +1191,9 @@ void
|
||||
mg_set_topic_tip (session *sess)
|
||||
{
|
||||
char *text;
|
||||
GtkTextBuffer *topic_buffer;
|
||||
GtkTextIter start;
|
||||
GtkTextIter end;
|
||||
|
||||
switch (sess->type)
|
||||
{
|
||||
@@ -1017,11 +1208,14 @@ mg_set_topic_tip (session *sess)
|
||||
gtk_widget_set_tooltip_text (sess->gui->topic_entry, _("No topic is set"));
|
||||
break;
|
||||
default:
|
||||
if (gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry)) &&
|
||||
gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry))[0])
|
||||
gtk_widget_set_tooltip_text (sess->gui->topic_entry, (char *)gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry)));
|
||||
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry));
|
||||
gtk_text_buffer_get_bounds (topic_buffer, &start, &end);
|
||||
text = gtk_text_buffer_get_text (topic_buffer, &start, &end, FALSE);
|
||||
if (text[0])
|
||||
gtk_widget_set_tooltip_text (sess->gui->topic_entry, text);
|
||||
else
|
||||
gtk_widget_set_tooltip_text (sess->gui->topic_entry, NULL);
|
||||
g_free (text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1052,9 +1246,14 @@ mg_userlist_showhide (session *sess, int show)
|
||||
session_gui *gui = sess->gui;
|
||||
int handle_size;
|
||||
int right_size;
|
||||
int min_right_size;
|
||||
GtkAllocation allocation;
|
||||
|
||||
right_size = MAX (prefs.hex_gui_pane_right_size, prefs.hex_gui_pane_right_size_min);
|
||||
gtk_widget_get_size_request (gui->user_box, &min_right_size, NULL);
|
||||
if (min_right_size < 1)
|
||||
min_right_size = 1;
|
||||
|
||||
right_size = MAX (prefs.hex_gui_pane_right_size, min_right_size);
|
||||
|
||||
if (show)
|
||||
{
|
||||
@@ -1164,7 +1363,7 @@ mg_populate (session *sess)
|
||||
/* hide the userlist */
|
||||
mg_decide_userlist (sess, FALSE);
|
||||
/* shouldn't edit the topic */
|
||||
gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), FALSE);
|
||||
gtk_text_view_set_editable (GTK_TEXT_VIEW (gui->topic_entry), FALSE);
|
||||
/* might be hidden from server tab */
|
||||
if (prefs.hex_gui_topicbar)
|
||||
gtk_widget_show (gui->topic_bar);
|
||||
@@ -1186,8 +1385,8 @@ mg_populate (session *sess)
|
||||
gtk_widget_show (gui->topicbutton_box);
|
||||
/* show the userlist */
|
||||
mg_decide_userlist (sess, FALSE);
|
||||
/* let the topic be editted */
|
||||
gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), TRUE);
|
||||
/* let the topic be edited */
|
||||
gtk_text_view_set_editable (GTK_TEXT_VIEW (gui->topic_entry), TRUE);
|
||||
if (prefs.hex_gui_topicbar)
|
||||
gtk_widget_show (gui->topic_bar);
|
||||
}
|
||||
@@ -1207,8 +1406,21 @@ mg_populate (session *sess)
|
||||
if (gui->is_tab)
|
||||
gtk_widget_set_sensitive (gui->menu, TRUE);
|
||||
|
||||
/* restore all the GtkEntry's */
|
||||
mg_restore_entry (gui->topic_entry, &res->topic_text);
|
||||
if (res->topic_text)
|
||||
{
|
||||
GtkTextBuffer *topic_buffer;
|
||||
|
||||
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gui->topic_entry));
|
||||
gtk_text_buffer_set_text (topic_buffer, res->topic_text, -1);
|
||||
g_free (res->topic_text);
|
||||
res->topic_text = NULL;
|
||||
} else
|
||||
{
|
||||
GtkTextBuffer *topic_buffer;
|
||||
|
||||
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gui->topic_entry));
|
||||
gtk_text_buffer_set_text (topic_buffer, "", -1);
|
||||
}
|
||||
mg_restore_speller (gui->input_box, &res->input_text);
|
||||
mg_restore_entry (gui->key_entry, &res->key_text);
|
||||
mg_restore_entry (gui->limit_entry, &res->limit_text);
|
||||
@@ -2114,30 +2326,204 @@ mg_create_userlistbuttons (GtkWidget *box)
|
||||
}
|
||||
|
||||
static void
|
||||
mg_topic_cb (GtkWidget *entry, gpointer userdata)
|
||||
mg_topic_cb (GtkWidget *entry)
|
||||
{
|
||||
session *sess = current_sess;
|
||||
GtkTextBuffer *topic_buffer;
|
||||
GtkTextIter start;
|
||||
GtkTextIter end;
|
||||
char *text;
|
||||
|
||||
if (sess->channel[0] && sess->server->connected && sess->type == SESS_CHANNEL)
|
||||
{
|
||||
text = (char *)gtk_entry_get_text (GTK_ENTRY (entry));
|
||||
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
|
||||
gtk_text_buffer_get_bounds (topic_buffer, &start, &end);
|
||||
text = gtk_text_buffer_get_text (topic_buffer, &start, &end, FALSE);
|
||||
if (text[0] == 0)
|
||||
text = NULL;
|
||||
sess->server->p_topic (sess->server, sess->channel, text);
|
||||
sess->server->p_topic (sess->server, sess->channel, NULL);
|
||||
else
|
||||
sess->server->p_topic (sess->server, sess->channel, text);
|
||||
g_free (text);
|
||||
} else
|
||||
gtk_entry_set_text (GTK_ENTRY (entry), "");
|
||||
{
|
||||
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
|
||||
gtk_text_buffer_set_text (topic_buffer, "", -1);
|
||||
}
|
||||
/* restore focus to the input widget, where the next input will most
|
||||
likely be */
|
||||
gtk_widget_grab_focus (sess->gui->input_box);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_topic_key_press_cb (GtkWidget *entry, GdkEventKey *event, gpointer userdata)
|
||||
{
|
||||
if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter)
|
||||
{
|
||||
mg_topic_cb (entry);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static char *
|
||||
mg_topic_get_word_at_pos (GtkWidget *entry, gdouble event_x, gdouble event_y, int *word_pos)
|
||||
{
|
||||
GtkTextBuffer *buffer;
|
||||
GtkTextIter iter;
|
||||
GtkTextIter start;
|
||||
GtkTextIter end;
|
||||
GtkTextIter cursor;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
x = (int)event_x;
|
||||
y = (int)event_y;
|
||||
gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (entry), GTK_TEXT_WINDOW_TEXT,
|
||||
x, y, &x, &y);
|
||||
gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (entry), &iter, x, y);
|
||||
|
||||
cursor = iter;
|
||||
start = iter;
|
||||
while (!gtk_text_iter_starts_line (&start))
|
||||
{
|
||||
GtkTextIter prev = start;
|
||||
gunichar ch;
|
||||
|
||||
gtk_text_iter_backward_char (&prev);
|
||||
ch = gtk_text_iter_get_char (&prev);
|
||||
if (g_unichar_isspace (ch))
|
||||
break;
|
||||
start = prev;
|
||||
}
|
||||
|
||||
end = iter;
|
||||
while (!gtk_text_iter_ends_line (&end))
|
||||
{
|
||||
gunichar ch;
|
||||
|
||||
ch = gtk_text_iter_get_char (&end);
|
||||
if (ch == 0 || g_unichar_isspace (ch))
|
||||
break;
|
||||
gtk_text_iter_forward_char (&end);
|
||||
}
|
||||
|
||||
if (gtk_text_iter_equal (&start, &end))
|
||||
return NULL;
|
||||
|
||||
if (word_pos)
|
||||
{
|
||||
char *prefix;
|
||||
|
||||
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
|
||||
prefix = gtk_text_buffer_get_text (buffer, &start, &cursor, FALSE);
|
||||
*word_pos = (int)strlen (prefix);
|
||||
g_free (prefix);
|
||||
}
|
||||
|
||||
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
|
||||
return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
mg_topic_set_cursor (GtkWidget *entry, GdkCursorType cursor_type)
|
||||
{
|
||||
GdkWindow *text_window;
|
||||
GdkDisplay *display;
|
||||
GdkCursor *cursor;
|
||||
|
||||
text_window = gtk_text_view_get_window (GTK_TEXT_VIEW (entry), GTK_TEXT_WINDOW_TEXT);
|
||||
if (!text_window)
|
||||
return;
|
||||
|
||||
display = gdk_window_get_display (text_window);
|
||||
cursor = gdk_cursor_new_for_display (display, cursor_type);
|
||||
gdk_window_set_cursor (text_window, cursor);
|
||||
g_object_unref (cursor);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_topic_word_is_clickable (const char *word, int word_pos)
|
||||
{
|
||||
int start;
|
||||
int end;
|
||||
|
||||
if (!word || word[0] == 0)
|
||||
return FALSE;
|
||||
|
||||
if (strcmp (word, "/") == 0)
|
||||
return FALSE;
|
||||
|
||||
if (url_check_word (word) == 0)
|
||||
return FALSE;
|
||||
|
||||
url_last (&start, &end);
|
||||
return word_pos >= start && word_pos < end;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_topic_motion_cb (GtkWidget *entry, GdkEventMotion *event, gpointer userdata)
|
||||
{
|
||||
char *word;
|
||||
int word_pos;
|
||||
gboolean word_is_clickable;
|
||||
|
||||
word_pos = 0;
|
||||
word = mg_topic_get_word_at_pos (entry, event->x, event->y, &word_pos);
|
||||
word_is_clickable = mg_topic_word_is_clickable (word, word_pos);
|
||||
if (word_is_clickable)
|
||||
mg_topic_set_cursor (entry, GDK_HAND2);
|
||||
else
|
||||
mg_topic_set_cursor (entry, GDK_XTERM);
|
||||
g_free (word);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_topic_leave_cb (GtkWidget *entry, GdkEventCrossing *event, gpointer userdata)
|
||||
{
|
||||
mg_topic_set_cursor (entry, GDK_XTERM);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_topic_button_release_cb (GtkWidget *entry, GdkEventButton *event, gpointer userdata)
|
||||
{
|
||||
char *word;
|
||||
int word_pos;
|
||||
int start;
|
||||
int end;
|
||||
|
||||
if (event->button != 1)
|
||||
return FALSE;
|
||||
|
||||
word_pos = 0;
|
||||
word = mg_topic_get_word_at_pos (entry, event->x, event->y, &word_pos);
|
||||
if (!word)
|
||||
return FALSE;
|
||||
|
||||
if (mg_topic_word_is_clickable (word, word_pos))
|
||||
{
|
||||
url_last (&start, &end);
|
||||
word[end] = 0;
|
||||
fe_open_url (word + start);
|
||||
g_free (word);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_free (word);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
mg_tabwindow_kill_cb (GtkWidget *win, gpointer userdata)
|
||||
{
|
||||
GSList *list, *next;
|
||||
session *sess;
|
||||
|
||||
mg_flush_config_save ();
|
||||
|
||||
zoitechat_is_quitting = TRUE;
|
||||
|
||||
/* see if there's any non-tab windows left */
|
||||
@@ -2358,8 +2744,10 @@ mg_create_flagbutton (char *tip, GtkWidget *box, char *face)
|
||||
gtk_label_set_markup (GTK_LABEL(lbl), label_markup);
|
||||
|
||||
btn = gtk_toggle_button_new ();
|
||||
gtk_widget_set_size_request (btn, -1, 0);
|
||||
gtk_widget_set_size_request (btn, -1, 11);
|
||||
gtk_widget_set_tooltip_text (btn, tip);
|
||||
gtk_button_set_relief (GTK_BUTTON (btn), GTK_RELIEF_NONE);
|
||||
mg_apply_compact_mode_css (btn);
|
||||
gtk_container_add (GTK_CONTAINER(btn), lbl);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (box), btn, 0, 0, 0);
|
||||
@@ -2415,6 +2803,57 @@ mg_apply_entry_style (GtkWidget *entry)
|
||||
theme_manager_apply_entry_palette (entry, input_style->font_desc);
|
||||
}
|
||||
|
||||
static void
|
||||
mg_apply_entry_scroll_artifact_fix (GtkWidget *entry)
|
||||
{
|
||||
GtkStyleContext *context;
|
||||
GtkCssProvider *provider;
|
||||
|
||||
if (!entry || !GTK_IS_ENTRY (entry))
|
||||
return;
|
||||
|
||||
context = gtk_widget_get_style_context (entry);
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
provider = g_object_get_data (G_OBJECT (entry), "mg-entry-scroll-artifact-provider");
|
||||
if (!provider)
|
||||
{
|
||||
provider = gtk_css_provider_new ();
|
||||
g_object_set_data_full (G_OBJECT (entry), "mg-entry-scroll-artifact-provider", provider, g_object_unref);
|
||||
gtk_css_provider_load_from_data (provider,
|
||||
"entry.zoitechat-no-undershoot undershoot,\n"
|
||||
"entry.zoitechat-no-undershoot undershoot.left,\n"
|
||||
"entry.zoitechat-no-undershoot undershoot.right,\n"
|
||||
".zoitechat-no-undershoot undershoot,\n"
|
||||
".zoitechat-no-undershoot undershoot.left,\n"
|
||||
".zoitechat-no-undershoot undershoot.right {\n"
|
||||
" background-image: none;\n"
|
||||
" background-color: transparent;\n"
|
||||
" border: none;\n"
|
||||
" box-shadow: none;\n"
|
||||
"}\n",
|
||||
-1, NULL);
|
||||
}
|
||||
|
||||
gtk_style_context_add_class (context, "zoitechat-no-undershoot");
|
||||
theme_css_apply_widget_provider (entry, GTK_STYLE_PROVIDER (provider));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_entry_select_all (GtkWidget *entry, GdkEventKey *event, gpointer userdata)
|
||||
{
|
||||
if ((event->state & GDK_CONTROL_MASK) &&
|
||||
!(event->state & (GDK_SHIFT_MASK | GDK_MOD1_MASK | GDK_META_MASK)) &&
|
||||
(event->keyval == GDK_KEY_a || event->keyval == GDK_KEY_A))
|
||||
{
|
||||
gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
mg_create_chanmodebuttons (session_gui *gui, GtkWidget *box)
|
||||
{
|
||||
@@ -2429,27 +2868,35 @@ mg_create_chanmodebuttons (session_gui *gui, GtkWidget *box)
|
||||
gui->key_entry = gtk_entry_new ();
|
||||
gtk_widget_set_name (gui->key_entry, "zoitechat-inputbox");
|
||||
gtk_entry_set_max_length (GTK_ENTRY (gui->key_entry), 23);
|
||||
gtk_widget_set_size_request (gui->key_entry, 115, -1);
|
||||
gtk_widget_set_size_request (gui->key_entry, 115, 11);
|
||||
gtk_box_pack_start (GTK_BOX (box), gui->key_entry, 0, 0, 0);
|
||||
mg_apply_emoji_fallback_widget (gui->key_entry);
|
||||
mg_apply_compact_mode_css (gui->key_entry);
|
||||
g_signal_connect (G_OBJECT (gui->key_entry), "activate",
|
||||
G_CALLBACK (mg_key_entry_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (gui->key_entry), "key-press-event",
|
||||
G_CALLBACK (mg_entry_select_all), NULL);
|
||||
|
||||
if (prefs.hex_gui_input_style)
|
||||
mg_apply_entry_style (gui->key_entry);
|
||||
mg_apply_entry_scroll_artifact_fix (gui->key_entry);
|
||||
|
||||
gui->flag_l = mg_create_flagbutton (_("User Limit"), box, "l");
|
||||
gui->limit_entry = gtk_entry_new ();
|
||||
gtk_widget_set_name (gui->limit_entry, "zoitechat-inputbox");
|
||||
gtk_entry_set_max_length (GTK_ENTRY (gui->limit_entry), 10);
|
||||
gtk_widget_set_size_request (gui->limit_entry, 30, -1);
|
||||
gtk_widget_set_size_request (gui->limit_entry, 30, 11);
|
||||
gtk_box_pack_start (GTK_BOX (box), gui->limit_entry, 0, 0, 0);
|
||||
mg_apply_emoji_fallback_widget (gui->limit_entry);
|
||||
mg_apply_compact_mode_css (gui->limit_entry);
|
||||
g_signal_connect (G_OBJECT (gui->limit_entry), "activate",
|
||||
G_CALLBACK (mg_limit_entry_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (gui->limit_entry), "key-press-event",
|
||||
G_CALLBACK (mg_entry_select_all), NULL);
|
||||
|
||||
if (prefs.hex_gui_input_style)
|
||||
mg_apply_entry_style (gui->limit_entry);
|
||||
mg_apply_entry_scroll_artifact_fix (gui->limit_entry);
|
||||
}
|
||||
|
||||
/*static void
|
||||
@@ -2470,12 +2917,18 @@ mg_dialog_button_cb (GtkWidget *wid, char *cmd)
|
||||
char buf[128];
|
||||
char *host = "";
|
||||
char *topic;
|
||||
char *topic_text;
|
||||
GtkTextBuffer *topic_buffer;
|
||||
GtkTextIter start;
|
||||
GtkTextIter end;
|
||||
|
||||
if (!current_sess)
|
||||
return;
|
||||
|
||||
topic = (char *)(gtk_entry_get_text (GTK_ENTRY (current_sess->gui->topic_entry)));
|
||||
topic = strrchr (topic, '@');
|
||||
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (current_sess->gui->topic_entry));
|
||||
gtk_text_buffer_get_bounds (topic_buffer, &start, &end);
|
||||
topic_text = gtk_text_buffer_get_text (topic_buffer, &start, &end, FALSE);
|
||||
topic = strrchr (topic_text, '@');
|
||||
if (topic)
|
||||
host = topic + 1;
|
||||
|
||||
@@ -2484,6 +2937,7 @@ mg_dialog_button_cb (GtkWidget *wid, char *cmd)
|
||||
current_sess->channel, "");
|
||||
|
||||
handle_command (current_sess, buf, TRUE);
|
||||
g_free (topic_text);
|
||||
|
||||
/* dirty trick to avoid auto-selection */
|
||||
SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE);
|
||||
@@ -2521,33 +2975,46 @@ mg_create_dialogbuttons (GtkWidget *box)
|
||||
static void
|
||||
mg_create_topicbar (session *sess, GtkWidget *box)
|
||||
{
|
||||
GtkWidget *hbox, *topic, *bbox;
|
||||
session_gui *gui = sess->gui;
|
||||
GtkWidget *vbox, *hbox, *mode_hbox, *topic, *bbox;
|
||||
session_gui *gui = sess->gui;
|
||||
|
||||
gui->topic_bar = hbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
|
||||
gui->topic_bar = vbox = mg_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (box), vbox, 0, 0, 0);
|
||||
|
||||
hbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
|
||||
if (!gui->is_tab)
|
||||
sess->res->tab = NULL;
|
||||
|
||||
gui->topic_entry = topic = sexy_spell_entry_new ();
|
||||
gui->topic_entry = topic = gtk_text_view_new ();
|
||||
gtk_widget_set_name (topic, "zoitechat-inputbox");
|
||||
sexy_spell_entry_set_checked (SEXY_SPELL_ENTRY (topic), FALSE);
|
||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (topic), GTK_WRAP_WORD_CHAR);
|
||||
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (topic), 4);
|
||||
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (topic), 4);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), topic, TRUE, TRUE, 0);
|
||||
mg_apply_emoji_fallback_widget (topic);
|
||||
g_signal_connect (G_OBJECT (topic), "activate",
|
||||
G_CALLBACK (mg_topic_cb), 0);
|
||||
gtk_widget_add_events (topic, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
|
||||
g_signal_connect (G_OBJECT (topic), "key-press-event",
|
||||
G_CALLBACK (mg_topic_key_press_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (topic), "button-release-event",
|
||||
G_CALLBACK (mg_topic_button_release_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (topic), "motion-notify-event",
|
||||
G_CALLBACK (mg_topic_motion_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (topic), "leave-notify-event",
|
||||
G_CALLBACK (mg_topic_leave_cb), NULL);
|
||||
|
||||
if (prefs.hex_gui_input_style)
|
||||
mg_apply_entry_style (topic);
|
||||
gui->dialogbutton_box = bbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
|
||||
mg_create_dialogbuttons (bbox);
|
||||
|
||||
gui->topicbutton_box = bbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
|
||||
mg_create_chanmodebuttons (gui, bbox);
|
||||
mode_hbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), mode_hbox, 0, 0, 0);
|
||||
|
||||
gui->dialogbutton_box = bbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
|
||||
mg_create_dialogbuttons (bbox);
|
||||
gui->topicbutton_box = bbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_end (GTK_BOX (mode_hbox), bbox, 0, 0, 0);
|
||||
mg_create_chanmodebuttons (gui, bbox);
|
||||
}
|
||||
|
||||
/* check if a word is clickable */
|
||||
@@ -2723,18 +3190,18 @@ mg_create_textarea (session *sess, GtkWidget *box)
|
||||
|
||||
gtk_drag_dest_set (gui->vscrollbar, 5, dnd_dest_targets, 2,
|
||||
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
|
||||
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_begin",
|
||||
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag-begin",
|
||||
G_CALLBACK (mg_drag_begin_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_drop",
|
||||
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag-drop",
|
||||
G_CALLBACK (mg_drag_drop_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_motion",
|
||||
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag-motion",
|
||||
G_CALLBACK (mg_drag_motion_cb), gui->vscrollbar);
|
||||
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_end",
|
||||
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag-end",
|
||||
G_CALLBACK (mg_drag_end_cb), NULL);
|
||||
|
||||
gtk_drag_dest_set (gui->xtext, GTK_DEST_DEFAULT_ALL, dnd_targets, 1,
|
||||
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
|
||||
g_signal_connect (G_OBJECT (gui->xtext), "drag_data_received",
|
||||
g_signal_connect (G_OBJECT (gui->xtext), "drag-data-received",
|
||||
G_CALLBACK (mg_dialog_dnd_drop), NULL);
|
||||
}
|
||||
|
||||
@@ -2900,17 +3367,16 @@ mg_theme_window_destroy_cb (GtkWidget *widget, gpointer userdata)
|
||||
static void
|
||||
mg_create_userlist (session_gui *gui, GtkWidget *box)
|
||||
{
|
||||
GtkWidget *frame, *ulist, *vbox;
|
||||
GtkWidget *ulist, *vbox;
|
||||
|
||||
vbox = mg_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 1);
|
||||
gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 0);
|
||||
|
||||
frame = gtk_frame_new (NULL);
|
||||
if (prefs.hex_gui_ulist_count)
|
||||
gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, GUI_SPACING);
|
||||
|
||||
gui->namelistinfo = gtk_label_new (NULL);
|
||||
gtk_container_add (GTK_CONTAINER (frame), gui->namelistinfo);
|
||||
gtk_label_set_xalign (GTK_LABEL (gui->namelistinfo), 0.0f);
|
||||
gtk_widget_set_halign (gui->namelistinfo, GTK_ALIGN_START);
|
||||
if (prefs.hex_gui_ulist_count)
|
||||
gtk_box_pack_start (GTK_BOX (vbox), gui->namelistinfo, 0, 0, 0);
|
||||
|
||||
gui->user_tree = ulist = userlist_create (vbox);
|
||||
|
||||
@@ -2949,6 +3415,23 @@ mg_rightpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
|
||||
prefs.hex_gui_pane_right_size = allocation.width - gtk_paned_get_position (pane) - handle_size;
|
||||
}
|
||||
|
||||
static void
|
||||
mg_restore_rightpane_cb (GtkWidget *widget, GtkAllocation *allocation, gpointer data)
|
||||
{
|
||||
int handle_size;
|
||||
int saved_size;
|
||||
/* only restore once, then disconnect */
|
||||
g_signal_handlers_disconnect_by_func (widget, mg_restore_rightpane_cb, data);
|
||||
/* use the value captured at connect time, since notify::position may
|
||||
* have already overwritten prefs.hex_gui_pane_right_size during the
|
||||
* initial layout pass */
|
||||
saved_size = GPOINTER_TO_INT (data);
|
||||
if (saved_size < 1)
|
||||
return;
|
||||
gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
|
||||
gtk_paned_set_position (GTK_PANED (widget), allocation->width - saved_size - handle_size);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_add_pane_signals (session_gui *gui)
|
||||
{
|
||||
@@ -2982,14 +3465,21 @@ mg_create_center (session *sess, session_gui *gui, GtkWidget *box)
|
||||
/* sep between xtext and right side */
|
||||
gui->hpane_right = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
/* restore right pane position after first allocation (needs widget width).
|
||||
* capture the saved size now because notify::position will overwrite the
|
||||
* pref during the initial layout before size-allocate fires. */
|
||||
g_signal_connect (gui->hpane_right, "size-allocate",
|
||||
G_CALLBACK (mg_restore_rightpane_cb),
|
||||
GINT_TO_POINTER (prefs.hex_gui_pane_right_size));
|
||||
|
||||
if (prefs.hex_gui_win_swap)
|
||||
{
|
||||
gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, TRUE);
|
||||
gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, FALSE);
|
||||
gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->hpane_right, TRUE, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, TRUE);
|
||||
gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, FALSE);
|
||||
gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->hpane_right, TRUE, TRUE);
|
||||
}
|
||||
gtk_paned_pack2 (GTK_PANED (gui->hpane_right), gui->vpane_right, FALSE, TRUE);
|
||||
@@ -3448,7 +3938,8 @@ static void
|
||||
search_set_option (GtkToggleButton *but, guint *pref)
|
||||
{
|
||||
*pref = gtk_toggle_button_get_active(but);
|
||||
save_config();
|
||||
if (!save_config ())
|
||||
fe_message (_("Could not save zoitechat.conf."), FE_MSG_WARN);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3503,8 +3994,9 @@ mg_create_search(session *sess, GtkWidget *box)
|
||||
gtk_box_pack_start(GTK_BOX(gui->shbox), entry, FALSE, FALSE, 0);
|
||||
gtk_widget_set_size_request (gui->shentry, 180, -1);
|
||||
mg_apply_emoji_fallback_widget (entry);
|
||||
mg_apply_entry_scroll_artifact_fix (entry);
|
||||
gui->search_changed_signal = g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(search_handle_change), sess);
|
||||
g_signal_connect (G_OBJECT (entry), "key_press_event", G_CALLBACK (search_handle_esc), sess);
|
||||
g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK (search_handle_esc), sess);
|
||||
g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(mg_search_handle_next), sess);
|
||||
gtk_entry_set_icon_activatable (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, FALSE);
|
||||
gtk_entry_set_icon_tooltip_text (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, _("Search hit end or not found."));
|
||||
@@ -3586,11 +4078,11 @@ mg_create_entry (session *sess, GtkWidget *box)
|
||||
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
|
||||
|
||||
gtk_widget_set_name (entry, "zoitechat-inputbox");
|
||||
g_signal_connect (G_OBJECT (entry), "key_press_event",
|
||||
g_signal_connect (G_OBJECT (entry), "key-press-event",
|
||||
G_CALLBACK (key_handle_key_press), NULL);
|
||||
g_signal_connect (G_OBJECT (entry), "focus_in_event",
|
||||
g_signal_connect (G_OBJECT (entry), "focus-in-event",
|
||||
G_CALLBACK (mg_inputbox_focus), gui);
|
||||
g_signal_connect (G_OBJECT (entry), "populate_popup",
|
||||
g_signal_connect (G_OBJECT (entry), "populate-popup",
|
||||
G_CALLBACK (mg_inputbox_rightclick), NULL);
|
||||
g_signal_connect (G_OBJECT (entry), "word-check",
|
||||
G_CALLBACK (mg_spellcheck_cb), NULL);
|
||||
@@ -3598,6 +4090,7 @@ mg_create_entry (session *sess, GtkWidget *box)
|
||||
|
||||
if (prefs.hex_gui_input_style)
|
||||
mg_apply_entry_style (entry);
|
||||
mg_apply_entry_scroll_artifact_fix (entry);
|
||||
|
||||
g_object_set (G_OBJECT (entry), "show-emoji-icon", TRUE, NULL);
|
||||
|
||||
@@ -3713,7 +4206,7 @@ mg_create_menu (session_gui *gui, GtkWidget *table, int away_state)
|
||||
gui->menu_item);
|
||||
gtk_widget_set_hexpand (gui->menu, TRUE);
|
||||
gtk_widget_set_vexpand (gui->menu, FALSE);
|
||||
gtk_widget_set_halign (gui->menu, GTK_ALIGN_FILL);
|
||||
gtk_widget_set_halign (gui->menu, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (gui->menu, GTK_ALIGN_FILL);
|
||||
gtk_grid_attach (GTK_GRID (table), gui->menu, 0, 0, 3, 1);
|
||||
}
|
||||
@@ -3753,11 +4246,11 @@ mg_create_topwindow (session *sess)
|
||||
gtk_container_set_border_width (GTK_CONTAINER (win), GUI_BORDER);
|
||||
gtk_widget_set_opacity (win, (prefs.hex_gui_transparency / 255.));
|
||||
|
||||
g_signal_connect (G_OBJECT (win), "focus_in_event",
|
||||
g_signal_connect (G_OBJECT (win), "focus-in-event",
|
||||
G_CALLBACK (mg_topwin_focus_cb), sess);
|
||||
g_signal_connect (G_OBJECT (win), "destroy",
|
||||
G_CALLBACK (mg_topdestroy_cb), sess);
|
||||
g_signal_connect (G_OBJECT (win), "configure_event",
|
||||
g_signal_connect (G_OBJECT (win), "configure-event",
|
||||
G_CALLBACK (mg_configure_cb), sess);
|
||||
|
||||
|
||||
@@ -3943,15 +4436,15 @@ mg_create_tabwindow (session *sess)
|
||||
gtk_widget_set_opacity (win, (prefs.hex_gui_transparency / 255.));
|
||||
gtk_container_set_border_width (GTK_CONTAINER (win), GUI_BORDER);
|
||||
|
||||
g_signal_connect (G_OBJECT (win), "delete_event",
|
||||
g_signal_connect (G_OBJECT (win), "delete-event",
|
||||
G_CALLBACK (mg_tabwindow_de_cb), 0);
|
||||
g_signal_connect (G_OBJECT (win), "destroy",
|
||||
G_CALLBACK (mg_tabwindow_kill_cb), 0);
|
||||
g_signal_connect (G_OBJECT (win), "focus_in_event",
|
||||
g_signal_connect (G_OBJECT (win), "focus-in-event",
|
||||
G_CALLBACK (mg_tabwin_focus_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (win), "configure_event",
|
||||
g_signal_connect (G_OBJECT (win), "configure-event",
|
||||
G_CALLBACK (mg_configure_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (win), "window_state_event",
|
||||
g_signal_connect (G_OBJECT (win), "window-state-event",
|
||||
G_CALLBACK (mg_windowstate_cb), NULL);
|
||||
|
||||
|
||||
@@ -4088,7 +4581,8 @@ fe_clear_channel (session *sess)
|
||||
|
||||
if (!sess->gui->is_tab || sess == current_tab)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (gui->topic_entry), "");
|
||||
gtk_text_buffer_set_text (
|
||||
gtk_text_view_get_buffer (GTK_TEXT_VIEW (gui->topic_entry)), "", -1);
|
||||
|
||||
if (gui->op_xpm)
|
||||
{
|
||||
|
||||
@@ -869,6 +869,9 @@ menu_nickmenu (session *sess, GdkEventButton *event, char *nick, int num_sel)
|
||||
static void
|
||||
menu_showhide_cb (session *sess)
|
||||
{
|
||||
if (!sess->gui->menu || !GTK_IS_WIDGET (sess->gui->menu))
|
||||
return;
|
||||
|
||||
if (prefs.hex_gui_hide_menu)
|
||||
gtk_widget_hide (sess->gui->menu);
|
||||
else
|
||||
@@ -926,6 +929,11 @@ menu_setting_foreach (void (*callback) (session *), int id, guint state)
|
||||
while (list)
|
||||
{
|
||||
sess = list->data;
|
||||
if (!sess || !sess->gui)
|
||||
{
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sess->gui->is_tab || !maindone)
|
||||
{
|
||||
@@ -935,9 +943,20 @@ menu_setting_foreach (void (*callback) (session *), int id, guint state)
|
||||
{
|
||||
GtkWidget *menu_item = sess->gui->menu_item[id];
|
||||
|
||||
if (menu_item != NULL)
|
||||
if (menu_item != NULL && GTK_IS_CHECK_MENU_ITEM (menu_item))
|
||||
{
|
||||
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), state);
|
||||
guint toggled_signal = g_signal_lookup ("toggled", G_OBJECT_TYPE (menu_item));
|
||||
|
||||
if (toggled_signal != 0)
|
||||
{
|
||||
g_signal_handlers_block_matched (menu_item, G_SIGNAL_MATCH_ID, toggled_signal, 0, NULL, NULL, NULL);
|
||||
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), state);
|
||||
g_signal_handlers_unblock_matched (menu_item, G_SIGNAL_MATCH_ID, toggled_signal, 0, NULL, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callback)
|
||||
@@ -952,7 +971,7 @@ void
|
||||
menu_bar_toggle (void)
|
||||
{
|
||||
prefs.hex_gui_hide_menu = !prefs.hex_gui_hide_menu;
|
||||
menu_setting_foreach (menu_showhide_cb, MENU_ID_MENUBAR, !prefs.hex_gui_hide_menu);
|
||||
menu_setting_foreach (menu_showhide_cb, -1, !prefs.hex_gui_hide_menu);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1783,6 +1802,44 @@ menu_change_layout (void)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
menu_update_quit_accel (void)
|
||||
{
|
||||
GSList *list;
|
||||
|
||||
list = sess_list;
|
||||
while (list)
|
||||
{
|
||||
session *sess = list->data;
|
||||
session_gui *gui = sess->gui;
|
||||
GtkWidget *item;
|
||||
GtkAccelGroup *accel_group;
|
||||
int enabled;
|
||||
|
||||
list = list->next;
|
||||
if (!gui)
|
||||
continue;
|
||||
|
||||
item = gui->menu_item[MENU_ID_QUIT];
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
enabled = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "zc-ctrlq-enabled"));
|
||||
if (enabled == (int)prefs.hex_gui_ctrlq_quit)
|
||||
continue;
|
||||
|
||||
accel_group = g_object_get_data (G_OBJECT (item), "zc-quit-accel-group");
|
||||
if (!accel_group)
|
||||
continue;
|
||||
|
||||
if (prefs.hex_gui_ctrlq_quit)
|
||||
gtk_widget_add_accelerator (item, "activate", accel_group, GDK_KEY_q, STATE_CTRL, GTK_ACCEL_VISIBLE);
|
||||
else
|
||||
gtk_widget_remove_accelerator (item, accel_group, GDK_KEY_q, STATE_CTRL);
|
||||
g_object_set_data (G_OBJECT (item), "zc-ctrlq-enabled", GINT_TO_POINTER (prefs.hex_gui_ctrlq_quit));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
menu_layout_cb (GtkWidget *item, gpointer none)
|
||||
{
|
||||
@@ -1848,8 +1905,18 @@ menu_metres_both (GtkWidget *item, gpointer none)
|
||||
}
|
||||
|
||||
static void
|
||||
about_dialog_close (GtkDialog *dialog, int response, gpointer data)
|
||||
about_dialog_response (GtkDialog *dialog, int response, gpointer data)
|
||||
{
|
||||
if (response == GTK_RESPONSE_HELP)
|
||||
{
|
||||
fe_open_url ("http://zoitechat.zoite.net");
|
||||
return;
|
||||
}
|
||||
if (response == GTK_RESPONSE_APPLY)
|
||||
{
|
||||
fe_open_url ("https://www.gnu.org/licenses/old-licenses/gpl-2.0.html");
|
||||
return;
|
||||
}
|
||||
gtk_widget_destroy (GTK_WIDGET(dialog));
|
||||
}
|
||||
|
||||
@@ -1863,7 +1930,14 @@ about_dialog_openurl (GtkAboutDialog *dialog, char *uri, gpointer data)
|
||||
static void
|
||||
menu_about (GtkWidget *wid, gpointer sess)
|
||||
{
|
||||
GtkAboutDialog *dialog = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
|
||||
GtkAboutDialog *dialog = GTK_ABOUT_DIALOG (g_object_new (GTK_TYPE_ABOUT_DIALOG, "use-header-bar", FALSE, NULL));
|
||||
GtkWidget *website;
|
||||
GtkWidget *license;
|
||||
GtkWidget *close;
|
||||
GtkWidget *actions;
|
||||
GList *children;
|
||||
GList *child;
|
||||
static const gchar *empty_people[] = { NULL };
|
||||
theme_manager_attach_window (GTK_WIDGET (dialog));
|
||||
char comment[512];
|
||||
g_snprintf (comment, sizeof(comment), ""
|
||||
@@ -1880,15 +1954,31 @@ menu_about (GtkWidget *wid, gpointer sess)
|
||||
|
||||
gtk_about_dialog_set_program_name (dialog, _(DISPLAY_NAME));
|
||||
gtk_about_dialog_set_version (dialog, PACKAGE_VERSION);
|
||||
gtk_about_dialog_set_license_type (GTK_ABOUT_DIALOG (dialog), GTK_LICENSE_GPL_2_0);
|
||||
gtk_about_dialog_set_website (dialog, "http://zoitechat.zoite.net");
|
||||
gtk_about_dialog_set_website_label (dialog, "Website");
|
||||
gtk_about_dialog_set_authors (dialog, empty_people);
|
||||
gtk_about_dialog_set_documenters (dialog, empty_people);
|
||||
gtk_about_dialog_set_artists (dialog, empty_people);
|
||||
gtk_about_dialog_set_translator_credits (dialog, "");
|
||||
gtk_about_dialog_set_website (dialog, NULL);
|
||||
gtk_about_dialog_set_website_label (dialog, NULL);
|
||||
gtk_about_dialog_set_license (dialog, NULL);
|
||||
gtk_about_dialog_set_wrap_license (dialog, FALSE);
|
||||
gtk_about_dialog_set_logo (dialog, pix_zoitechat);
|
||||
gtk_about_dialog_set_copyright (dialog, "\302\251 1998-2010 Peter \305\275elezn\303\275\n\302\251 2009-2014 Berke Viktor\n\302\251 2026 deepend");
|
||||
gtk_about_dialog_set_copyright (dialog, "\302\251 1998-2010 Peter \305\275elezn\303\275\n\302\251 2009-2014 Berke Viktor\n\302\251 2015-2025 Patrick Griffis\n\302\251 2026 deepend");
|
||||
gtk_about_dialog_set_comments (dialog, comment);
|
||||
actions = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
|
||||
children = gtk_container_get_children (GTK_CONTAINER (actions));
|
||||
for (child = children; child; child = child->next)
|
||||
gtk_widget_destroy (GTK_WIDGET (child->data));
|
||||
g_list_free (children);
|
||||
website = gtk_dialog_add_button (GTK_DIALOG (dialog), "Website", GTK_RESPONSE_HELP);
|
||||
license = gtk_dialog_add_button (GTK_DIALOG (dialog), "License", GTK_RESPONSE_APPLY);
|
||||
close = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Close"), GTK_RESPONSE_CLOSE);
|
||||
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (actions), website, TRUE);
|
||||
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (actions), license, TRUE);
|
||||
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (actions), close, FALSE);
|
||||
|
||||
gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent_window));
|
||||
g_signal_connect (G_OBJECT(dialog), "response", G_CALLBACK(about_dialog_close), NULL);
|
||||
g_signal_connect (G_OBJECT(dialog), "response", G_CALLBACK(about_dialog_response), NULL);
|
||||
g_signal_connect (G_OBJECT(dialog), "activate-link", G_CALLBACK(about_dialog_openurl), NULL);
|
||||
|
||||
gtk_widget_show_all (GTK_WIDGET(dialog));
|
||||
@@ -1914,7 +2004,7 @@ static struct mymenu mymenu[] = {
|
||||
#define CLOSE_OFFSET (13)
|
||||
{0, menu_close, 0, M_MENUITEM, 0, 0, 1},
|
||||
{0, 0, 0, M_SEP, 0, 0, 0},
|
||||
{N_("_Quit"), menu_quit, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_q}, /* 15 */
|
||||
{N_("_Quit"), menu_quit, 0, M_MENUITEM, MENU_ID_QUIT, 0, 1, GDK_KEY_q}, /* 15 */
|
||||
|
||||
{N_("_View"), 0, 0, M_NEWMENU, 0, 0, 1},
|
||||
#define MENUBAR_OFFSET (17)
|
||||
@@ -2170,6 +2260,11 @@ menu_foreach_gui (menu_entry *me, void (*callback) (GtkWidget *, menu_entry *, c
|
||||
while (list)
|
||||
{
|
||||
sess = list->data;
|
||||
if (!sess || !sess->gui)
|
||||
{
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
/* do it only once for tab sessions, since they share a GUI */
|
||||
if (!sess->gui->is_tab || !tabdone)
|
||||
{
|
||||
@@ -2593,15 +2688,21 @@ menu_create_main (void *accel_group, int bar, int away, int toplevel,
|
||||
case M_MENUITEM:
|
||||
item = gtk_menu_item_new_with_mnemonic (_(mymenu[i].text));
|
||||
normalitem:
|
||||
if (mymenu[i].key != 0)
|
||||
if (mymenu[i].key != 0 && !(mymenu[i].id == MENU_ID_QUIT && !prefs.hex_gui_ctrlq_quit))
|
||||
gtk_widget_add_accelerator (item, "activate", accel_group,
|
||||
mymenu[i].key,
|
||||
mymenu[i].key == GDK_KEY_F1 ? 0 :
|
||||
mymenu[i].key == GDK_KEY_w ? close_mask :
|
||||
(g_ascii_isupper (mymenu[i].key)) ?
|
||||
STATE_SHIFT | STATE_CTRL :
|
||||
STATE_CTRL,
|
||||
GTK_ACCEL_VISIBLE);
|
||||
mymenu[i].key,
|
||||
mymenu[i].key == GDK_KEY_F1 ? 0 :
|
||||
mymenu[i].key == GDK_KEY_w ? close_mask :
|
||||
mymenu[i].id == MENU_ID_AWAY ? away_mask :
|
||||
(g_ascii_isupper (mymenu[i].key)) ?
|
||||
STATE_SHIFT | STATE_CTRL :
|
||||
STATE_CTRL,
|
||||
GTK_ACCEL_VISIBLE);
|
||||
if (mymenu[i].id == MENU_ID_QUIT)
|
||||
{
|
||||
g_object_set_data (G_OBJECT (item), "zc-quit-accel-group", accel_group);
|
||||
g_object_set_data (G_OBJECT (item), "zc-ctrlq-enabled", GINT_TO_POINTER (prefs.hex_gui_ctrlq_quit));
|
||||
}
|
||||
if (mymenu[i].callback)
|
||||
g_signal_connect (G_OBJECT (item), "activate",
|
||||
G_CALLBACK (mymenu[i].callback), 0);
|
||||
@@ -2643,9 +2744,11 @@ togitem:
|
||||
goto togitem;
|
||||
|
||||
case M_SEP:
|
||||
item = gtk_menu_item_new ();
|
||||
gtk_widget_set_sensitive (item, FALSE);
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
||||
item = gtk_separator_menu_item_new ();
|
||||
if (submenu)
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item);
|
||||
else
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
||||
gtk_widget_show (item);
|
||||
break;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ void menu_create (GtkWidget *menu, GSList *list, char *target, int check_path);
|
||||
void menu_bar_toggle (void);
|
||||
void menu_add_plugin_items (GtkWidget *menu, char *root, char *target);
|
||||
void menu_change_layout (void);
|
||||
void menu_update_quit_accel (void);
|
||||
|
||||
void menu_set_away (session_gui *gui, int away);
|
||||
void menu_set_fullscreen (session_gui *gui, int fullscreen);
|
||||
@@ -63,8 +64,9 @@ void menu_set_fullscreen (session_gui *gui, int fullscreen);
|
||||
#define MENU_ID_USERMENU 12
|
||||
#define MENU_ID_FULLSCREEN 13
|
||||
#define MENU_ID_ZOITECHAT 14
|
||||
#define MENU_ID_QUIT 15
|
||||
|
||||
#if (MENU_ID_NUM < MENU_ID_ZOITECHAT)
|
||||
#if (MENU_ID_NUM < MENU_ID_QUIT)
|
||||
#error MENU_ID_NUM is set wrong
|
||||
#endif
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ zoitechat_gtk_sources = [
|
||||
'maingui.c',
|
||||
'notifygui.c',
|
||||
'pixmaps.c',
|
||||
'preferences-persistence.c',
|
||||
'plugin-tray.c',
|
||||
'plugin-notification.c',
|
||||
'rawlog.c',
|
||||
@@ -64,8 +65,8 @@ if host_machine.system() != 'windows'
|
||||
if appindicator_dep.found()
|
||||
zoitechat_gtk_deps += appindicator_dep
|
||||
zoitechat_gtk_cflags += '-DHAVE_APPINDICATOR'
|
||||
elif appindicator_opt.enabled()
|
||||
error('appindicator=enabled, but neither ayatana-appindicator3-0.1 nor appindicator3-0.1 was found')
|
||||
elif appindicator_opt.enabled() or (appindicator_opt.auto() and host_machine.system() == 'linux')
|
||||
error('tray support requires ayatana-appindicator3-0.1 or appindicator3-0.1 on Linux (use -Dappindicator=disabled to override)')
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -125,6 +125,7 @@ static void tray_menu_notify_cb (GObject *tray, GParamSpec *pspec, gpointer user
|
||||
static void tray_update_toggle_item_label (void);
|
||||
static gboolean tray_window_state_cb (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata);
|
||||
static void tray_window_visibility_cb (GtkWidget *widget, gpointer userdata);
|
||||
static void tray_toggle_item_destroy_cb (GtkWidget *widget, gpointer userdata);
|
||||
#if HAVE_APPINDICATOR_BACKEND
|
||||
static void tray_menu_show_cb (GtkWidget *menu, gpointer userdata) G_GNUC_UNUSED;
|
||||
#endif
|
||||
@@ -364,8 +365,8 @@ tray_app_indicator_set_icon (TrayIcon icon)
|
||||
if (!icon_name)
|
||||
return;
|
||||
|
||||
app_indicator_set_icon_full (tray_indicator, icon_name, _(DISPLAY_NAME));
|
||||
app_indicator_set_status (tray_indicator, APP_INDICATOR_STATUS_ACTIVE);
|
||||
app_indicator_set_icon_full (tray_indicator, icon_name, _(DISPLAY_NAME));
|
||||
|
||||
g_free (icon_name_alloc);
|
||||
}
|
||||
@@ -408,7 +409,8 @@ tray_app_indicator_cleanup (void)
|
||||
|
||||
if (tray_menu)
|
||||
{
|
||||
gtk_widget_destroy (tray_menu);
|
||||
if (GTK_IS_WIDGET (tray_menu))
|
||||
gtk_widget_destroy (tray_menu);
|
||||
tray_menu = NULL;
|
||||
}
|
||||
}
|
||||
@@ -431,7 +433,6 @@ tray_app_indicator_init (void)
|
||||
g_signal_connect (G_OBJECT (tray_menu), "map",
|
||||
G_CALLBACK (tray_menu_show_cb), NULL);
|
||||
app_indicator_set_menu (tray_indicator, GTK_MENU (tray_menu));
|
||||
app_indicator_set_status (tray_indicator, APP_INDICATOR_STATUS_ACTIVE);
|
||||
|
||||
klass = G_OBJECT_GET_CLASS (tray_indicator);
|
||||
if (klass && g_object_class_find_property (klass, "connected"))
|
||||
@@ -1027,10 +1028,15 @@ blink_item (unsigned int *setting, GtkWidget *menu, char *label)
|
||||
static void
|
||||
tray_menu_destroy (GtkWidget *menu, gpointer userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
GtkWidget **menu_ptr = userdata;
|
||||
|
||||
gtk_widget_destroy (menu);
|
||||
g_object_unref (menu);
|
||||
if (menu_ptr && *menu_ptr == menu)
|
||||
*menu_ptr = NULL;
|
||||
|
||||
if (GTK_IS_WIDGET (menu))
|
||||
gtk_widget_destroy (menu);
|
||||
if (G_IS_OBJECT (menu))
|
||||
g_object_unref (menu);
|
||||
#ifdef WIN32
|
||||
g_source_remove (tray_menu_timer);
|
||||
#endif
|
||||
@@ -1090,6 +1096,8 @@ tray_menu_populate (GtkWidget *menu)
|
||||
zoitechat_set_context (ph, zoitechat_find_context (ph, NULL, NULL));
|
||||
|
||||
tray_toggle_item = tray_make_item (menu, _("_Hide Window"), tray_menu_restore_cb, NULL);
|
||||
g_signal_connect (G_OBJECT (tray_toggle_item), "destroy",
|
||||
G_CALLBACK (tray_toggle_item_destroy_cb), NULL);
|
||||
tray_update_toggle_item_label ();
|
||||
tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
|
||||
|
||||
@@ -1130,12 +1138,22 @@ tray_menu_clear (GtkWidget *menu)
|
||||
|
||||
children = gtk_container_get_children (GTK_CONTAINER (menu));
|
||||
for (iter = children; iter; iter = iter->next)
|
||||
gtk_widget_destroy (GTK_WIDGET (iter->data));
|
||||
if (GTK_IS_WIDGET (iter->data))
|
||||
gtk_widget_destroy (GTK_WIDGET (iter->data));
|
||||
g_list_free (children);
|
||||
tray_toggle_item = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
tray_toggle_item_destroy_cb (GtkWidget *widget, gpointer userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
|
||||
if (tray_toggle_item == widget)
|
||||
tray_toggle_item = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
tray_update_toggle_item_label (void)
|
||||
{
|
||||
@@ -1143,6 +1161,11 @@ tray_update_toggle_item_label (void)
|
||||
|
||||
if (!tray_toggle_item)
|
||||
return;
|
||||
if (!GTK_IS_MENU_ITEM (tray_toggle_item))
|
||||
{
|
||||
tray_toggle_item = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (tray_get_window_status () == WS_HIDDEN)
|
||||
label = _("_Restore Window");
|
||||
@@ -1195,10 +1218,9 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
|
||||
(void)time;
|
||||
(void)userdata;
|
||||
|
||||
/* close any old menu */
|
||||
if (G_IS_OBJECT (menu))
|
||||
if (menu)
|
||||
{
|
||||
tray_menu_destroy (menu, NULL);
|
||||
tray_menu_destroy (menu, &menu);
|
||||
}
|
||||
|
||||
menu = gtk_menu_new ();
|
||||
@@ -1208,8 +1230,9 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
|
||||
g_object_ref (menu);
|
||||
g_object_ref_sink (menu);
|
||||
g_object_unref (menu);
|
||||
g_object_add_weak_pointer (G_OBJECT (menu), (gpointer *)&menu);
|
||||
g_signal_connect (G_OBJECT (menu), "selection-done",
|
||||
G_CALLBACK (tray_menu_destroy), NULL);
|
||||
G_CALLBACK (tray_menu_destroy), &menu);
|
||||
#ifdef WIN32
|
||||
g_signal_connect (G_OBJECT (menu), "leave-notify-event",
|
||||
G_CALLBACK (tray_menu_left_cb), NULL);
|
||||
@@ -1390,9 +1413,14 @@ tray_apply_setup (void)
|
||||
}
|
||||
else
|
||||
{
|
||||
#if HAVE_APPINDICATOR_BACKEND
|
||||
if (prefs.hex_gui_tray)
|
||||
tray_init ();
|
||||
#else
|
||||
GtkWindow *window = GTK_WINDOW(zoitechat_get_info (ph, "gtkwin_ptr"));
|
||||
if (prefs.hex_gui_tray && gtkutil_tray_icon_supported (window))
|
||||
tray_init ();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1439,7 +1467,11 @@ tray_plugin_init (zoitechat_plugin *plugin_handle, char **plugin_name,
|
||||
G_CALLBACK (tray_window_visibility_cb), NULL);
|
||||
}
|
||||
|
||||
#if HAVE_APPINDICATOR_BACKEND
|
||||
if (prefs.hex_gui_tray)
|
||||
#else
|
||||
if (prefs.hex_gui_tray && gtkutil_tray_icon_supported (window))
|
||||
#endif
|
||||
tray_init ();
|
||||
|
||||
return 1; /* return 1 for success */
|
||||
|
||||
86
src/fe-gtk/preferences-persistence.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "preferences-persistence.h"
|
||||
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "theme/theme-runtime.h"
|
||||
|
||||
PreferencesPersistenceResult
|
||||
preferences_persistence_save_all (void)
|
||||
{
|
||||
PreferencesPersistenceResult result;
|
||||
char *config_temp;
|
||||
char *theme_temp;
|
||||
|
||||
result.success = FALSE;
|
||||
result.retry_possible = TRUE;
|
||||
result.partial_failure = FALSE;
|
||||
result.config_failed = FALSE;
|
||||
result.theme_failed = FALSE;
|
||||
result.failed_file = NULL;
|
||||
config_temp = NULL;
|
||||
theme_temp = NULL;
|
||||
|
||||
if (!save_config_prepare (&config_temp))
|
||||
{
|
||||
result.config_failed = TRUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!theme_runtime_save_prepare (&theme_temp))
|
||||
{
|
||||
result.theme_failed = TRUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!save_config_finalize (config_temp))
|
||||
{
|
||||
result.config_failed = TRUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!theme_runtime_save_finalize (theme_temp))
|
||||
{
|
||||
result.theme_failed = TRUE;
|
||||
result.partial_failure = TRUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
result.success = TRUE;
|
||||
|
||||
done:
|
||||
if (!result.success)
|
||||
{
|
||||
if (result.config_failed && result.theme_failed)
|
||||
result.failed_file = "zoitechat.conf and colors.conf";
|
||||
else if (result.config_failed)
|
||||
result.failed_file = "zoitechat.conf";
|
||||
else if (result.theme_failed)
|
||||
result.failed_file = "colors.conf";
|
||||
}
|
||||
|
||||
save_config_discard (config_temp);
|
||||
theme_runtime_save_discard (theme_temp);
|
||||
g_free (config_temp);
|
||||
g_free (theme_temp);
|
||||
|
||||
return result;
|
||||
}
|
||||
38
src/fe-gtk/preferences-persistence.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_PREFERENCES_PERSISTENCE_H
|
||||
#define ZOITECHAT_PREFERENCES_PERSISTENCE_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean success;
|
||||
gboolean retry_possible;
|
||||
gboolean partial_failure;
|
||||
gboolean config_failed;
|
||||
gboolean theme_failed;
|
||||
const char *failed_file;
|
||||
} PreferencesPersistenceResult;
|
||||
|
||||
PreferencesPersistenceResult preferences_persistence_save_all (void);
|
||||
|
||||
#endif
|
||||
@@ -192,7 +192,7 @@ open_rawlog (struct server *serv)
|
||||
serv, _("Save As..."));
|
||||
|
||||
/* 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_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);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
@@ -89,6 +90,9 @@ static GtkWidget *edit_label_nick2;
|
||||
static GtkWidget *edit_label_real;
|
||||
static GtkWidget *edit_label_user;
|
||||
static GtkWidget *edit_trees[N_TREES];
|
||||
static GtkWidget *edit_button_cert_generate;
|
||||
static GtkWidget *edit_button_cert_info;
|
||||
static GtkWidget *edit_button_cert_delete;
|
||||
|
||||
static ircnet *selected_net = NULL;
|
||||
static ircserver *selected_serv = NULL;
|
||||
@@ -99,6 +103,281 @@ static session *servlist_sess;
|
||||
static void servlist_network_row_cb (GtkTreeSelection *sel, gpointer user_data);
|
||||
static GtkWidget *servlist_open_edit (GtkWidget *parent, ircnet *net);
|
||||
|
||||
static char *
|
||||
servlist_get_cert_file (ircnet *net)
|
||||
{
|
||||
if (!net || !net->name || !net->name[0])
|
||||
return NULL;
|
||||
|
||||
return g_strdup_printf ("%s" G_DIR_SEPARATOR_S "certs" G_DIR_SEPARATOR_S "%s.pem",
|
||||
get_xdir (), net->name);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
servlist_network_cert_exists (ircnet *net)
|
||||
{
|
||||
char *cert_file;
|
||||
gboolean exists;
|
||||
|
||||
cert_file = servlist_get_cert_file (net);
|
||||
if (!cert_file)
|
||||
return FALSE;
|
||||
|
||||
exists = g_file_test (cert_file, G_FILE_TEST_IS_REGULAR);
|
||||
g_free (cert_file);
|
||||
return exists;
|
||||
}
|
||||
|
||||
static void
|
||||
servlist_update_cert_buttons (ircnet *net)
|
||||
{
|
||||
gboolean has_cert = servlist_network_cert_exists (net);
|
||||
|
||||
if (edit_button_cert_generate)
|
||||
gtk_widget_set_visible (edit_button_cert_generate, !has_cert);
|
||||
if (edit_button_cert_info)
|
||||
gtk_widget_set_visible (edit_button_cert_info, has_cert);
|
||||
if (edit_button_cert_delete)
|
||||
gtk_widget_set_visible (edit_button_cert_delete, has_cert);
|
||||
}
|
||||
|
||||
static void
|
||||
servlist_generate_client_cert_cb (GtkWidget *button, gpointer userdata)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
ircnet *net = (ircnet *)userdata;
|
||||
GtkWidget *dialog;
|
||||
char *cert_dir;
|
||||
char *cert_file;
|
||||
char *key_file;
|
||||
char *crt_file;
|
||||
char *subject;
|
||||
char *openssl_conf;
|
||||
const char *conf_data;
|
||||
char *key_data;
|
||||
char *crt_data;
|
||||
char *pem_data;
|
||||
char *stderr_data;
|
||||
char *stdout_data;
|
||||
gsize key_len;
|
||||
gsize crt_len;
|
||||
gboolean spawned;
|
||||
gboolean success;
|
||||
gint status;
|
||||
char *argv[20];
|
||||
|
||||
if (!net || !net->name || !net->name[0])
|
||||
return;
|
||||
|
||||
cert_dir = g_build_filename (get_xdir (), "certs", NULL);
|
||||
cert_file = servlist_get_cert_file (net);
|
||||
key_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.key", cert_dir, net->name);
|
||||
crt_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.crt", cert_dir, net->name);
|
||||
subject = g_strdup_printf ("/CN=%s", net->name);
|
||||
openssl_conf = g_build_filename (cert_dir, "openssl.cnf", NULL);
|
||||
conf_data = "[req]\n"
|
||||
"distinguished_name=req_distinguished_name\n"
|
||||
"[req_distinguished_name]\n";
|
||||
key_data = NULL;
|
||||
crt_data = NULL;
|
||||
pem_data = NULL;
|
||||
stderr_data = NULL;
|
||||
stdout_data = NULL;
|
||||
key_len = 0;
|
||||
crt_len = 0;
|
||||
success = FALSE;
|
||||
status = 0;
|
||||
|
||||
if (g_mkdir_with_parents (cert_dir, 0700) == 0 &&
|
||||
g_file_set_contents (openssl_conf, conf_data, -1, NULL))
|
||||
{
|
||||
argv[0] = "openssl";
|
||||
argv[1] = "req";
|
||||
argv[2] = "-x509";
|
||||
argv[3] = "-newkey";
|
||||
argv[4] = "ec";
|
||||
argv[5] = "-pkeyopt";
|
||||
argv[6] = "ec_paramgen_curve:P-256";
|
||||
argv[7] = "-sha256";
|
||||
argv[8] = "-days";
|
||||
argv[9] = "3650";
|
||||
argv[10] = "-nodes";
|
||||
argv[11] = "-keyout";
|
||||
argv[12] = key_file;
|
||||
argv[13] = "-out";
|
||||
argv[14] = crt_file;
|
||||
argv[15] = "-config";
|
||||
argv[16] = openssl_conf;
|
||||
argv[17] = "-subj";
|
||||
argv[18] = subject;
|
||||
argv[19] = NULL;
|
||||
|
||||
spawned = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
|
||||
&stdout_data, &stderr_data, &status, NULL);
|
||||
if (spawned && g_spawn_check_exit_status (status, NULL) &&
|
||||
g_file_get_contents (key_file, &key_data, &key_len, NULL) &&
|
||||
g_file_get_contents (crt_file, &crt_data, &crt_len, NULL))
|
||||
{
|
||||
pem_data = g_strconcat (key_data, crt_data, NULL);
|
||||
if (pem_data && g_file_set_contents (cert_file, pem_data, -1, NULL))
|
||||
{
|
||||
chmod (cert_file, 0600);
|
||||
success = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_remove (key_file);
|
||||
g_remove (crt_file);
|
||||
g_remove (openssl_conf);
|
||||
|
||||
if (success)
|
||||
{
|
||||
servlist_update_cert_buttons (net);
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_INFO,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
_("Client certificate generated for \"%s\"."),
|
||||
net->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
_("Failed to generate the client certificate for \"%s\"."),
|
||||
net->name);
|
||||
if (stderr_data && stderr_data[0])
|
||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", stderr_data);
|
||||
}
|
||||
theme_manager_attach_window (dialog);
|
||||
g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
g_free (stdout_data);
|
||||
g_free (stderr_data);
|
||||
g_free (pem_data);
|
||||
g_free (key_data);
|
||||
g_free (crt_data);
|
||||
g_free (subject);
|
||||
g_free (crt_file);
|
||||
g_free (key_file);
|
||||
g_free (openssl_conf);
|
||||
g_free (cert_file);
|
||||
g_free (cert_dir);
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
servlist_cert_info_cb (GtkWidget *button, gpointer userdata)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
ircnet *net = (ircnet *)userdata;
|
||||
GtkWidget *dialog;
|
||||
char *cert_file;
|
||||
char *stdout_data;
|
||||
char *stderr_data;
|
||||
gboolean spawned;
|
||||
gint status;
|
||||
char *argv[12];
|
||||
|
||||
cert_file = servlist_get_cert_file (net);
|
||||
if (!cert_file)
|
||||
return;
|
||||
|
||||
stdout_data = NULL;
|
||||
stderr_data = NULL;
|
||||
status = 0;
|
||||
argv[0] = "openssl";
|
||||
argv[1] = "x509";
|
||||
argv[2] = "-in";
|
||||
argv[3] = cert_file;
|
||||
argv[4] = "-noout";
|
||||
argv[5] = "-subject";
|
||||
argv[6] = "-issuer";
|
||||
argv[7] = "-startdate";
|
||||
argv[8] = "-enddate";
|
||||
argv[9] = "-fingerprint";
|
||||
argv[10] = "-sha256";
|
||||
argv[11] = NULL;
|
||||
|
||||
spawned = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
|
||||
&stdout_data, &stderr_data, &status, NULL);
|
||||
|
||||
if (spawned && g_spawn_check_exit_status (status, NULL) && stdout_data && stdout_data[0])
|
||||
{
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_INFO,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
_("Client certificate information for \"%s\"."),
|
||||
net->name);
|
||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", stdout_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
_("Failed to read client certificate information for \"%s\"."),
|
||||
net->name);
|
||||
if (stderr_data && stderr_data[0])
|
||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", stderr_data);
|
||||
}
|
||||
|
||||
theme_manager_attach_window (dialog);
|
||||
g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
|
||||
gtk_widget_show (dialog);
|
||||
g_free (stdout_data);
|
||||
g_free (stderr_data);
|
||||
g_free (cert_file);
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
servlist_delete_client_cert_cb (GtkWidget *button, gpointer userdata)
|
||||
{
|
||||
ircnet *net = (ircnet *)userdata;
|
||||
GtkWidget *dialog;
|
||||
char *cert_file;
|
||||
|
||||
cert_file = servlist_get_cert_file (net);
|
||||
if (!cert_file)
|
||||
return;
|
||||
|
||||
if (g_remove (cert_file) == 0)
|
||||
{
|
||||
servlist_update_cert_buttons (net);
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_INFO,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
_("Client certificate removed for \"%s\"."),
|
||||
net->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
_("Failed to remove client certificate for \"%s\"."),
|
||||
net->name);
|
||||
}
|
||||
|
||||
theme_manager_attach_window (dialog);
|
||||
g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
|
||||
gtk_widget_show (dialog);
|
||||
g_free (cert_file);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
servlist_icon_button_new (const char *label, const char *icon_name)
|
||||
{
|
||||
@@ -763,9 +1042,9 @@ servlist_edit_cb (GtkWidget *but, gpointer none)
|
||||
servlist_servers_populate (selected_net, edit_trees[SERVER_TREE]);
|
||||
servlist_channels_populate (selected_net, edit_trees[CHANNEL_TREE]);
|
||||
servlist_commands_populate (selected_net, edit_trees[CMD_TREE]);
|
||||
g_signal_connect (G_OBJECT (edit_win), "delete_event",
|
||||
g_signal_connect (G_OBJECT (edit_win), "delete-event",
|
||||
G_CALLBACK (servlist_editwin_delete_cb), 0);
|
||||
g_signal_connect (G_OBJECT (edit_win), "configure_event",
|
||||
g_signal_connect (G_OBJECT (edit_win), "configure-event",
|
||||
G_CALLBACK (servlist_edit_configure_cb), 0);
|
||||
gtk_widget_show (edit_win);
|
||||
}
|
||||
@@ -1002,7 +1281,8 @@ servlist_savegui (void)
|
||||
sp[0] = 0; /* spaces will break the login */
|
||||
/* strcpy (prefs.hex_irc_real_name, gtk_entry_get_text (GTK_ENTRY (entry_greal))); */
|
||||
servlist_save ();
|
||||
save_config (); /* For nicks stored in zoitechat.conf */
|
||||
if (!save_config ())
|
||||
fe_message (_("Could not save zoitechat.conf."), FE_MSG_WARN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1782,6 +2062,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
|
||||
GtkWidget *buttonadd;
|
||||
GtkWidget *buttonremove;
|
||||
GtkWidget *buttonedit;
|
||||
GtkWidget *hbox_cert_buttons;
|
||||
GtkWidget *hseparator2;
|
||||
GtkWidget *hbuttonbox4;
|
||||
GtkWidget *button10;
|
||||
@@ -1838,7 +2119,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
|
||||
model = GTK_TREE_MODEL (store);
|
||||
|
||||
edit_trees[SERVER_TREE] = treeview_servers = gtk_tree_view_new_with_model (model);
|
||||
g_signal_connect (G_OBJECT (treeview_servers), "key_press_event",
|
||||
g_signal_connect (G_OBJECT (treeview_servers), "key-press-event",
|
||||
G_CALLBACK (servlist_keypress_cb), notebook);
|
||||
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview_servers))),
|
||||
"changed", G_CALLBACK (servlist_server_row_cb), NULL);
|
||||
@@ -1863,7 +2144,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
|
||||
model = GTK_TREE_MODEL (store);
|
||||
|
||||
edit_trees[CHANNEL_TREE] = treeview_channels = gtk_tree_view_new_with_model (model);
|
||||
g_signal_connect (G_OBJECT (treeview_channels), "key_press_event",
|
||||
g_signal_connect (G_OBJECT (treeview_channels), "key-press-event",
|
||||
G_CALLBACK (servlist_keypress_cb), notebook);
|
||||
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview_channels))),
|
||||
"changed", G_CALLBACK (servlist_channel_row_cb), NULL);
|
||||
@@ -1900,7 +2181,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
|
||||
model = GTK_TREE_MODEL (store);
|
||||
|
||||
edit_trees[CMD_TREE] = treeview_commands = gtk_tree_view_new_with_model (model);
|
||||
g_signal_connect (G_OBJECT (treeview_commands), "key_press_event",
|
||||
g_signal_connect (G_OBJECT (treeview_commands), "key-press-event",
|
||||
G_CALLBACK (servlist_keypress_cb), notebook);
|
||||
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview_commands))),
|
||||
"changed", G_CALLBACK (servlist_command_row_cb), NULL);
|
||||
@@ -1946,7 +2227,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
|
||||
|
||||
|
||||
/* Checkboxes and entries */
|
||||
table3 = gtkutil_grid_new (13, 2, FALSE);
|
||||
table3 = gtkutil_grid_new (14, 2, FALSE);
|
||||
gtk_box_pack_start (GTK_BOX (vbox5), table3, FALSE, FALSE, 0);
|
||||
gtk_grid_set_row_spacing (GTK_GRID (table3), 2);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (table3), 8);
|
||||
@@ -2001,6 +2282,27 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
|
||||
SERVLIST_ALIGN_FILL, SERVLIST_ALIGN_FILL,
|
||||
4, 2);
|
||||
|
||||
hbox_cert_buttons = gtkutil_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 6);
|
||||
edit_button_cert_generate = gtk_button_new_with_mnemonic (_("Generate client SSL cert"));
|
||||
g_signal_connect (G_OBJECT (edit_button_cert_generate), "clicked",
|
||||
G_CALLBACK (servlist_generate_client_cert_cb), net);
|
||||
gtk_box_pack_start (GTK_BOX (hbox_cert_buttons), edit_button_cert_generate, FALSE, FALSE, 0);
|
||||
|
||||
edit_button_cert_info = gtk_button_new_with_mnemonic (_("Client SSL cert info"));
|
||||
g_signal_connect (G_OBJECT (edit_button_cert_info), "clicked",
|
||||
G_CALLBACK (servlist_cert_info_cb), net);
|
||||
gtk_box_pack_start (GTK_BOX (hbox_cert_buttons), edit_button_cert_info, FALSE, FALSE, 0);
|
||||
|
||||
edit_button_cert_delete = gtk_button_new_with_mnemonic (_("Delete cert"));
|
||||
g_signal_connect (G_OBJECT (edit_button_cert_delete), "clicked",
|
||||
G_CALLBACK (servlist_delete_client_cert_cb), net);
|
||||
gtk_box_pack_start (GTK_BOX (hbox_cert_buttons), edit_button_cert_delete, FALSE, FALSE, 0);
|
||||
|
||||
servlist_table_attach (table3, hbox_cert_buttons, 0, 2, 13, 14,
|
||||
FALSE, FALSE,
|
||||
SERVLIST_ALIGN_START, SERVLIST_ALIGN_CENTER,
|
||||
SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
|
||||
|
||||
|
||||
/* Rule and Close button */
|
||||
hseparator2 = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
@@ -2025,6 +2327,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
|
||||
gtk_widget_grab_default (button10);
|
||||
|
||||
gtk_widget_show_all (editwindow);
|
||||
servlist_update_cert_buttons (net);
|
||||
|
||||
/* We can't set the active tab without child elements being shown, so this must be *after* gtk_widget_show()s! */
|
||||
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), netedit_active_tab);
|
||||
@@ -2356,13 +2659,13 @@ fe_serverlist_open (session *sess)
|
||||
|
||||
servlist_networks_populate (networks_tree, network_list);
|
||||
|
||||
g_signal_connect (G_OBJECT (serverlist_win), "delete_event",
|
||||
g_signal_connect (G_OBJECT (serverlist_win), "delete-event",
|
||||
G_CALLBACK (servlist_delete_cb), 0);
|
||||
g_signal_connect (G_OBJECT (serverlist_win), "configure_event",
|
||||
g_signal_connect (G_OBJECT (serverlist_win), "configure-event",
|
||||
G_CALLBACK (servlist_configure_cb), 0);
|
||||
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (networks_tree))),
|
||||
"changed", G_CALLBACK (servlist_network_row_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (networks_tree), "key_press_event",
|
||||
g_signal_connect (G_OBJECT (networks_tree), "key-press-event",
|
||||
G_CALLBACK (servlist_net_keypress_cb), networks_tree);
|
||||
|
||||
gtk_widget_show (serverlist_win);
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "maingui.h"
|
||||
#include "pixmaps.h"
|
||||
#include "menu.h"
|
||||
#include "preferences-persistence.h"
|
||||
#include "plugin-tray.h"
|
||||
#include "notifications/notification-backend.h"
|
||||
|
||||
@@ -535,6 +536,7 @@ static const setting general_settings[] =
|
||||
{ST_TOGGLE, N_("WHOIS on notify"), P_OFFINTNL(hex_notify_whois_online), N_("Sends a /WHOIS when a user comes online in your notify list."), 0, 0},
|
||||
{ST_TOGGLE, N_("Hide join and part messages"), P_OFFINTNL(hex_irc_conf_mode), N_("Hide channel join/part messages by default."), 0, 0},
|
||||
{ST_TOGGLE, N_("Hide nick change messages"), P_OFFINTNL(hex_irc_hide_nickchange), 0, 0, 0},
|
||||
{ST_TOGGLE, N_("Enable Ctrl+Q to quit"), P_OFFINTNL(hex_gui_ctrlq_quit), 0, 0, 0},
|
||||
|
||||
{ST_END, 0, 0, 0, 0, 0}
|
||||
};
|
||||
@@ -893,7 +895,7 @@ setup_create_spin (GtkWidget *table, int row, const setting *set)
|
||||
gtk_widget_set_tooltip_text (wid, _(set->tooltip));
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
setup_get_int (&setup_prefs, set));
|
||||
g_signal_connect (G_OBJECT (wid), "value_changed",
|
||||
g_signal_connect (G_OBJECT (wid), "value-changed",
|
||||
G_CALLBACK (setup_spin_cb), (gpointer)set);
|
||||
gtk_box_pack_start (GTK_BOX (rbox), wid, 0, 0, 0);
|
||||
|
||||
@@ -949,7 +951,7 @@ setup_create_hscale (GtkWidget *table, int row, const setting *set)
|
||||
wid = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0., 255., 1.);
|
||||
gtk_scale_set_value_pos (GTK_SCALE (wid), GTK_POS_RIGHT);
|
||||
gtk_range_set_value (GTK_RANGE (wid), setup_get_int (&setup_prefs, set));
|
||||
g_signal_connect (G_OBJECT(wid), "value_changed",
|
||||
g_signal_connect (G_OBJECT(wid), "value-changed",
|
||||
G_CALLBACK (setup_hscale_cb), (gpointer)set);
|
||||
setup_table_attach (table, wid, 3, 6, row, row + 1, TRUE, FALSE,
|
||||
SETUP_ALIGN_FILL, SETUP_ALIGN_FILL, 0, 0);
|
||||
@@ -1713,11 +1715,11 @@ setup_create_sound_page (void)
|
||||
|
||||
vbox2 = gtkutil_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
|
||||
gtk_widget_show (vbox2);
|
||||
gtk_container_add (GTK_CONTAINER (vbox1), vbox2);
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), vbox2, TRUE, TRUE, 0);
|
||||
|
||||
scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_show (scrolledwindow1);
|
||||
gtk_container_add (GTK_CONTAINER (vbox2), scrolledwindow1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), scrolledwindow1, TRUE, TRUE, 0);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1),
|
||||
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow1),
|
||||
@@ -1792,7 +1794,7 @@ setup_add_page (const char *title, GtkWidget *book, GtkWidget *tab)
|
||||
gtk_widget_set_margin_bottom (label, 1);
|
||||
gtk_box_pack_start (GTK_BOX (vvbox), label, FALSE, FALSE, 2);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (vvbox), tab);
|
||||
gtk_box_pack_start (GTK_BOX (vvbox), tab, TRUE, TRUE, 0);
|
||||
|
||||
sw = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new (NULL, NULL));
|
||||
gtk_scrolled_window_set_shadow_type (sw, GTK_SHADOW_IN);
|
||||
@@ -1965,7 +1967,7 @@ setup_create_tree (GtkWidget *box, GtkWidget *book)
|
||||
gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
|
||||
gtk_tree_selection_set_select_function (sel, setup_tree_select_filter,
|
||||
NULL, NULL);
|
||||
g_signal_connect (G_OBJECT (tree), "cursor_changed",
|
||||
g_signal_connect (G_OBJECT (tree), "cursor-changed",
|
||||
G_CALLBACK (setup_tree_cb), book);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
@@ -2069,6 +2071,7 @@ setup_apply_real (const ThemeChangedEvent *event)
|
||||
}
|
||||
|
||||
mg_apply_setup ();
|
||||
menu_update_quit_accel ();
|
||||
tray_apply_setup ();
|
||||
zoitechat_reinit_timers ();
|
||||
|
||||
@@ -2191,10 +2194,32 @@ setup_apply (struct zoitechatprefs *pr)
|
||||
static void
|
||||
setup_ok_cb (GtkWidget *but, GtkWidget *win)
|
||||
{
|
||||
gtk_widget_destroy (win);
|
||||
PreferencesPersistenceResult save_result;
|
||||
struct zoitechatprefs old_prefs;
|
||||
char buffer[192];
|
||||
|
||||
memcpy (&old_prefs, &prefs, sizeof (prefs));
|
||||
setup_apply (&setup_prefs);
|
||||
save_config ();
|
||||
theme_manager_save_preferences ();
|
||||
theme_preferences_stage_apply ();
|
||||
save_result = preferences_persistence_save_all ();
|
||||
if (save_result.success)
|
||||
{
|
||||
theme_preferences_stage_commit ();
|
||||
gtk_widget_destroy (win);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy (&prefs, &old_prefs, sizeof (prefs));
|
||||
theme_preferences_stage_discard ();
|
||||
|
||||
if (save_result.partial_failure)
|
||||
{
|
||||
fe_message (_("Preferences were partially saved. zoitechat.conf succeeded, colors.conf failed. Retry is possible."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
g_snprintf (buffer, sizeof (buffer), _("Could not save preferences (%s). Retry is possible."), save_result.failed_file ? save_result.failed_file : _("unknown file"));
|
||||
fe_message (buffer, FE_MSG_ERROR);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
@@ -2242,6 +2267,7 @@ setup_close_cb (GtkWidget *win, GtkWidget **swin)
|
||||
{
|
||||
*swin = NULL;
|
||||
|
||||
theme_preferences_stage_discard ();
|
||||
|
||||
if (font_dialog)
|
||||
{
|
||||
@@ -2262,6 +2288,7 @@ setup_open (void)
|
||||
memcpy (&setup_prefs, &prefs, sizeof (prefs));
|
||||
|
||||
color_change = FALSE;
|
||||
theme_preferences_stage_begin ();
|
||||
setup_window = setup_window_open ();
|
||||
|
||||
g_signal_connect (G_OBJECT (setup_window), "destroy",
|
||||
|
||||
@@ -136,6 +136,7 @@ static void sexy_spell_entry_finalize(GObject *obj);
|
||||
static void sexy_spell_entry_destroy(GObject *obj);
|
||||
static gboolean sexy_spell_entry_draw(GtkWidget *widget, cairo_t *cr);
|
||||
static gint sexy_spell_entry_button_press(GtkWidget *widget, GdkEventButton *event);
|
||||
static void sexy_spell_entry_style_updated (GtkWidget *widget);
|
||||
|
||||
/* GtkEditable handlers */
|
||||
static void sexy_spell_entry_changed(GtkEditable *editable, gpointer data);
|
||||
@@ -280,6 +281,7 @@ sexy_spell_entry_class_init(SexySpellEntryClass *klass)
|
||||
|
||||
widget_class->draw = sexy_spell_entry_draw;
|
||||
widget_class->button_press_event = sexy_spell_entry_button_press;
|
||||
widget_class->style_updated = sexy_spell_entry_style_updated;
|
||||
|
||||
/**
|
||||
* SexySpellEntry::word-check:
|
||||
@@ -348,24 +350,54 @@ gtk_entry_find_position (GtkEntry *entry, gint x)
|
||||
static void
|
||||
insert_hiddenchar (SexySpellEntry *entry, guint start, guint end)
|
||||
{
|
||||
/* FIXME: Pango does not properly reflect the new widths after a char
|
||||
* is 'hidden' */
|
||||
#if 0
|
||||
PangoAttribute *hattr;
|
||||
PangoRectangle *rect = g_new (PangoRectangle, 1);
|
||||
PangoRectangle rect = { 0 };
|
||||
|
||||
rect->x = 0;
|
||||
rect->y = 0;
|
||||
rect->width = 0;
|
||||
rect->height = 0;
|
||||
|
||||
hattr = pango_attr_shape_new (rect, rect);
|
||||
hattr = pango_attr_shape_new (&rect, &rect);
|
||||
hattr->start_index = start;
|
||||
hattr->end_index = end;
|
||||
pango_attr_list_insert (entry->priv->attr_list, hattr);
|
||||
}
|
||||
|
||||
g_free (rect);
|
||||
#endif
|
||||
static guint8
|
||||
sexy_spell_entry_contrasting_caret_component (guint16 red, guint16 green, guint16 blue)
|
||||
{
|
||||
const guint16 luma = (guint16) (((red >> 8) * 299 + (green >> 8) * 587 + (blue >> 8) * 114) / 1000);
|
||||
return luma >= 128 ? 0x00 : 0xff;
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_spell_entry_apply_caret_style (SexySpellEntry *entry)
|
||||
{
|
||||
ThemeWidgetStyleValues style_values;
|
||||
guint16 bg_red = 0, bg_green = 0, bg_blue = 0;
|
||||
guint8 caret;
|
||||
GtkCssProvider *provider;
|
||||
GtkStyleContext *context;
|
||||
char css[120];
|
||||
|
||||
theme_get_widget_style_values_for_widget (GTK_WIDGET (entry), &style_values);
|
||||
theme_palette_color_get_rgb16 (&style_values.background, &bg_red, &bg_green, &bg_blue);
|
||||
caret = sexy_spell_entry_contrasting_caret_component (bg_red, bg_green, bg_blue);
|
||||
provider = g_object_get_data (G_OBJECT (entry), "sexy-spell-entry-caret-provider");
|
||||
if (!provider)
|
||||
{
|
||||
provider = gtk_css_provider_new ();
|
||||
g_object_set_data_full (G_OBJECT (entry), "sexy-spell-entry-caret-provider", provider, g_object_unref);
|
||||
}
|
||||
g_snprintf (css, sizeof (css), "#zoitechat-inputbox, #zoitechat-inputbox text { caret-color: #%02x%02x%02x; }",
|
||||
caret, caret, caret);
|
||||
gtk_css_provider_load_from_data (provider, css, -1, NULL);
|
||||
context = gtk_widget_get_style_context (GTK_WIDGET (entry));
|
||||
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
}
|
||||
|
||||
static void
|
||||
sexy_spell_entry_style_updated (GtkWidget *widget)
|
||||
{
|
||||
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
|
||||
if (SEXY_IS_SPELL_ENTRY (widget))
|
||||
sexy_spell_entry_apply_caret_style (SEXY_SPELL_ENTRY (widget));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -878,6 +910,7 @@ sexy_spell_entry_init(SexySpellEntry *entry)
|
||||
g_signal_connect(G_OBJECT(entry), "popup-menu", G_CALLBACK(sexy_spell_entry_popup_menu), entry);
|
||||
g_signal_connect(G_OBJECT(entry), "populate-popup", G_CALLBACK(sexy_spell_entry_populate_popup), NULL);
|
||||
g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sexy_spell_entry_changed), NULL);
|
||||
sexy_spell_entry_apply_caret_style (entry);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -964,6 +997,9 @@ default_word_check(SexySpellEntry *entry, const gchar *word)
|
||||
/* We only want to check words */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (g_utf8_strlen (word, -1) > 20)
|
||||
return FALSE;
|
||||
for (li = entry->priv->dict_list; li; li = g_slist_next (li)) {
|
||||
struct EnchantDict *dict = (struct EnchantDict *) li->data;
|
||||
if (enchant_dict_check(dict, word, strlen(word)) == 0) {
|
||||
@@ -1086,6 +1122,7 @@ check_attributes (SexySpellEntry *entry, const char *text, int len)
|
||||
break;
|
||||
|
||||
case ATTR_COLOR:
|
||||
insert_hiddenchar (entry, i, i + 1);
|
||||
parsing_color = 1;
|
||||
offset = 1;
|
||||
break;
|
||||
@@ -1099,6 +1136,7 @@ check_color:
|
||||
{
|
||||
if (text[i] == ',' && parsing_color <= 3)
|
||||
{
|
||||
insert_hiddenchar (entry, i, i + 1);
|
||||
parsing_color = 3;
|
||||
offset++;
|
||||
continue;
|
||||
@@ -1115,21 +1153,25 @@ check_color:
|
||||
{
|
||||
case 1:
|
||||
fg_color[0] = text[i];
|
||||
insert_hiddenchar (entry, i, i + 1);
|
||||
parsing_color++;
|
||||
offset++;
|
||||
continue;
|
||||
case 2:
|
||||
fg_color[1] = text[i];
|
||||
insert_hiddenchar (entry, i, i + 1);
|
||||
parsing_color++;
|
||||
offset++;
|
||||
continue;
|
||||
case 3:
|
||||
bg_color[0] = text[i];
|
||||
insert_hiddenchar (entry, i, i + 1);
|
||||
parsing_color++;
|
||||
offset++;
|
||||
continue;
|
||||
case 4:
|
||||
bg_color[1] = text[i];
|
||||
insert_hiddenchar (entry, i, i + 1);
|
||||
parsing_color++;
|
||||
offset++;
|
||||
continue;
|
||||
@@ -1159,6 +1201,52 @@ check_color:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsing_color)
|
||||
{
|
||||
if (bg_color[0] != 0)
|
||||
{
|
||||
insert_hiddenchar (entry, len - offset, len);
|
||||
insert_color (entry, len, atoi (fg_color), atoi (bg_color));
|
||||
}
|
||||
else if (fg_color[0] != 0)
|
||||
{
|
||||
insert_hiddenchar (entry, len - offset, len);
|
||||
insert_color (entry, len, atoi (fg_color), -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
insert_hiddenchar (entry, len - offset, len - offset + 1);
|
||||
insert_color (entry, len, -1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
attr_list_has_attrs (PangoAttrList *attrs)
|
||||
{
|
||||
PangoAttrIterator *it;
|
||||
gboolean has = FALSE;
|
||||
|
||||
if (!attrs)
|
||||
return FALSE;
|
||||
|
||||
it = pango_attr_list_get_iterator (attrs);
|
||||
if (!it)
|
||||
return FALSE;
|
||||
|
||||
do
|
||||
{
|
||||
GSList *list = pango_attr_iterator_get_attrs (it);
|
||||
has = (list != NULL);
|
||||
g_slist_free_full (list, (GDestroyNotify) pango_attribute_destroy);
|
||||
if (has)
|
||||
break;
|
||||
}
|
||||
while (pango_attr_iterator_next (it));
|
||||
pango_attr_iterator_destroy (it);
|
||||
|
||||
return has;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1167,7 +1255,6 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry)
|
||||
GdkRectangle rect;
|
||||
GtkAllocation allocation;
|
||||
GtkWidget *widget = GTK_WIDGET(entry);
|
||||
PangoLayout *layout;
|
||||
int length, i, text_len;
|
||||
const char *text;
|
||||
|
||||
@@ -1196,8 +1283,7 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry)
|
||||
}
|
||||
}
|
||||
|
||||
layout = gtk_entry_get_layout(GTK_ENTRY(entry));
|
||||
pango_layout_set_attributes(layout, entry->priv->attr_list);
|
||||
gtk_entry_set_attributes (GTK_ENTRY (entry), attr_list_has_attrs (entry->priv->attr_list) ? entry->priv->attr_list : NULL);
|
||||
|
||||
if (gtk_widget_get_realized (GTK_WIDGET(entry)))
|
||||
{
|
||||
@@ -1213,13 +1299,6 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry)
|
||||
static gboolean
|
||||
sexy_spell_entry_draw(GtkWidget *widget, cairo_t *cr)
|
||||
{
|
||||
SexySpellEntry *entry = SEXY_SPELL_ENTRY(widget);
|
||||
GtkEntry *gtk_entry = GTK_ENTRY(widget);
|
||||
PangoLayout *layout;
|
||||
|
||||
layout = gtk_entry_get_layout(gtk_entry);
|
||||
pango_layout_set_attributes(layout, entry->priv->attr_list);
|
||||
|
||||
return GTK_WIDGET_CLASS(parent_class)->draw (widget, cr);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../../fe-gtk.h"
|
||||
|
||||
#include <math.h>
|
||||
@@ -57,9 +77,10 @@ theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
gboolean
|
||||
theme_runtime_save (void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../../fe-gtk.h"
|
||||
|
||||
#include "../theme-application.h"
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../../../common/zoitechat.h"
|
||||
#include "../../../common/zoitechatc.h"
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 <glib.h>
|
||||
|
||||
#include "../theme-gtk3.h"
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../../fe-gtk.h"
|
||||
|
||||
#include "../theme-manager.h"
|
||||
@@ -119,8 +139,9 @@ void theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void theme_runtime_save (void)
|
||||
gboolean theme_runtime_save (void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_is_dark_active (void)
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../../fe-gtk.h"
|
||||
|
||||
#include <string.h>
|
||||
@@ -112,8 +132,9 @@ void theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void theme_runtime_save (void)
|
||||
gboolean theme_runtime_save (void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_is_dark_active (void)
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../../fe-gtk.h"
|
||||
|
||||
#include "../theme-palette.h"
|
||||
@@ -122,8 +142,9 @@ void theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void theme_runtime_save (void)
|
||||
gboolean theme_runtime_save (void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_is_dark_active (void)
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../../fe-gtk.h"
|
||||
|
||||
#include "../../../common/zoitechat.h"
|
||||
@@ -48,6 +68,16 @@ theme_get_color (ThemeSemanticToken token, GdkRGBA *color)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
(void)widget;
|
||||
if (!out_values)
|
||||
return;
|
||||
gdk_rgba_parse (&out_values->foreground, "#111111");
|
||||
gdk_rgba_parse (&out_values->background, "#f0f0f0");
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_set_token_color (unsigned int dark_mode, ThemeSemanticToken token, const GdkRGBA *color, gboolean *changed)
|
||||
{
|
||||
@@ -66,9 +96,16 @@ theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed)
|
||||
*palette_changed = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gboolean
|
||||
theme_manager_save_preferences (void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_dispatch_changed (ThemeChangedReason reasons)
|
||||
{
|
||||
(void)reasons;
|
||||
}
|
||||
|
||||
ThemePaletteBehavior
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../../../common/zoitechat.h"
|
||||
#include "../../../common/zoitechatc.h"
|
||||
|
||||
@@ -19,6 +39,12 @@ struct zoitechatprefs prefs;
|
||||
|
||||
static char *test_home_dir;
|
||||
|
||||
static char *
|
||||
test_home_path (const char *file)
|
||||
{
|
||||
return g_build_filename (test_home_dir, file, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
read_line_value (const char *cfg, const char *key, char *out, gsize out_len)
|
||||
{
|
||||
@@ -97,6 +123,12 @@ cfg_put_int (int fh, int value, char *var)
|
||||
return write (fh, line, (size_t) len) == len;
|
||||
}
|
||||
|
||||
char *
|
||||
get_xdir (void)
|
||||
{
|
||||
return test_home_dir;
|
||||
}
|
||||
|
||||
int
|
||||
zoitechat_open_file (const char *file, int flags, int mode, int xof_flags)
|
||||
{
|
||||
@@ -145,7 +177,7 @@ read_colors_conf (void)
|
||||
gsize length = 0;
|
||||
gboolean ok;
|
||||
|
||||
path = g_build_filename (test_home_dir, "colors.conf", NULL);
|
||||
path = test_home_path ("colors.conf");
|
||||
ok = g_file_get_contents (path, &content, &length, NULL);
|
||||
g_free (path);
|
||||
g_assert_true (ok);
|
||||
@@ -222,7 +254,7 @@ test_loads_legacy_color_keys_via_migration_loader (void)
|
||||
gboolean ok;
|
||||
|
||||
setup_temp_home ();
|
||||
path = g_build_filename (test_home_dir, "colors.conf", NULL);
|
||||
path = test_home_path ("colors.conf");
|
||||
ok = g_file_set_contents (path, legacy_cfg, -1, NULL);
|
||||
g_free (path);
|
||||
g_assert_true (ok);
|
||||
@@ -336,6 +368,50 @@ test_gtk_map_uses_theme_defaults_until_custom_token_is_set (void)
|
||||
g_assert_true (colors_equal (&values.foreground, &custom));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_save_finalize_replaces_colors_conf_atomically (void)
|
||||
{
|
||||
char *path;
|
||||
char *temp_path = NULL;
|
||||
char *cfg = NULL;
|
||||
gboolean ok;
|
||||
|
||||
setup_temp_home ();
|
||||
path = test_home_path ("colors.conf");
|
||||
ok = g_file_set_contents (path, "theme.mode.light.token.mirc_0 = 0000 0000 0000\n", -1, NULL);
|
||||
g_assert_true (ok);
|
||||
|
||||
theme_runtime_load ();
|
||||
g_assert_true (theme_runtime_save_prepare (&temp_path));
|
||||
g_assert_nonnull (temp_path);
|
||||
g_assert_nonnull (g_strrstr (temp_path, "colors.conf.new."));
|
||||
g_assert_true (g_file_test (temp_path, G_FILE_TEST_EXISTS));
|
||||
g_assert_true (theme_runtime_save_finalize (temp_path));
|
||||
g_assert_false (g_file_test (temp_path, G_FILE_TEST_EXISTS));
|
||||
ok = g_file_get_contents (path, &cfg, NULL, NULL);
|
||||
g_assert_true (ok);
|
||||
g_assert_nonnull (g_strstr_len (cfg, -1, "theme.palette.semantic_migrated = 1"));
|
||||
g_free (cfg);
|
||||
g_free (temp_path);
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
static void
|
||||
test_save_discard_unlinks_temp_file (void)
|
||||
{
|
||||
char *temp_path = NULL;
|
||||
|
||||
setup_temp_home ();
|
||||
theme_runtime_load ();
|
||||
g_assert_true (theme_runtime_save_prepare (&temp_path));
|
||||
g_assert_nonnull (temp_path);
|
||||
g_assert_true (g_file_test (temp_path, G_FILE_TEST_EXISTS));
|
||||
theme_runtime_save_discard (temp_path);
|
||||
g_assert_false (g_file_test (temp_path, G_FILE_TEST_EXISTS));
|
||||
g_free (temp_path);
|
||||
}
|
||||
|
||||
static void
|
||||
test_save_writes_only_custom_token_keys (void)
|
||||
{
|
||||
@@ -368,6 +444,10 @@ main (int argc, char **argv)
|
||||
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_finalize_replaces_colors_conf_atomically",
|
||||
test_save_finalize_replaces_colors_conf_atomically);
|
||||
g_test_add_func ("/theme/runtime/save_discard_unlinks_temp_file",
|
||||
test_save_discard_unlinks_temp_file);
|
||||
g_test_add_func ("/theme/runtime/save_writes_only_custom_token_keys",
|
||||
test_save_writes_only_custom_token_keys);
|
||||
return g_test_run ();
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "theme-access.h"
|
||||
|
||||
#include "theme-runtime.h"
|
||||
@@ -7,10 +27,47 @@
|
||||
|
||||
enum
|
||||
{
|
||||
THEME_XTEXT_FG_INDEX = 34,
|
||||
THEME_XTEXT_BG_INDEX = 35
|
||||
THEME_XTEXT_MIRC_COLS = 99,
|
||||
THEME_XTEXT_MARK_FG_INDEX = 99,
|
||||
THEME_XTEXT_MARK_BG_INDEX = 100,
|
||||
THEME_XTEXT_FG_INDEX = 101,
|
||||
THEME_XTEXT_BG_INDEX = 102,
|
||||
THEME_XTEXT_MARKER_INDEX = 103
|
||||
};
|
||||
|
||||
static const guint8 theme_default_99_mirc_colors[THEME_XTEXT_MIRC_COLS][3] = {
|
||||
{ 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x7f }, { 0x00, 0x93, 0x00 }, { 0xff, 0x00, 0x00 }, { 0x7f, 0x00, 0x00 }, { 0x9c, 0x00, 0x9c }, { 0xfc, 0x7f, 0x00 },
|
||||
{ 0xff, 0xff, 0x00 }, { 0x00, 0xfc, 0x00 }, { 0x00, 0x93, 0x93 }, { 0x00, 0xff, 0xff }, { 0x00, 0x00, 0xfc }, { 0xff, 0x00, 0xff }, { 0x7f, 0x7f, 0x7f }, { 0xd2, 0xd2, 0xd2 },
|
||||
{ 0x47, 0x00, 0x00 }, { 0x47, 0x21, 0x00 }, { 0x47, 0x47, 0x00 }, { 0x32, 0x47, 0x00 }, { 0x00, 0x47, 0x00 }, { 0x00, 0x47, 0x2c }, { 0x00, 0x47, 0x47 }, { 0x00, 0x2f, 0x47 },
|
||||
{ 0x00, 0x00, 0x47 }, { 0x2e, 0x00, 0x47 }, { 0x47, 0x00, 0x47 }, { 0x47, 0x00, 0x2a }, { 0x74, 0x00, 0x00 }, { 0x74, 0x3a, 0x00 }, { 0x74, 0x74, 0x00 }, { 0x51, 0x74, 0x00 },
|
||||
{ 0x00, 0x74, 0x00 }, { 0x00, 0x74, 0x49 }, { 0x00, 0x74, 0x74 }, { 0x00, 0x4d, 0x74 }, { 0x00, 0x00, 0x74 }, { 0x4b, 0x00, 0x74 }, { 0x74, 0x00, 0x74 }, { 0x74, 0x00, 0x45 },
|
||||
{ 0xb5, 0x00, 0x00 }, { 0xb5, 0x63, 0x00 }, { 0xb5, 0xb5, 0x00 }, { 0x7d, 0xb5, 0x00 }, { 0x00, 0xb5, 0x00 }, { 0x00, 0xb5, 0x71 }, { 0x00, 0xb5, 0xb5 }, { 0x00, 0x75, 0xb5 },
|
||||
{ 0x00, 0x00, 0xb5 }, { 0x75, 0x00, 0xb5 }, { 0xb5, 0x00, 0xb5 }, { 0xb5, 0x00, 0x6b }, { 0xff, 0x00, 0x00 }, { 0xff, 0x8c, 0x00 }, { 0xff, 0xff, 0x00 }, { 0xb2, 0xff, 0x00 },
|
||||
{ 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0xa0 }, { 0x00, 0xff, 0xff }, { 0x00, 0xa9, 0xff }, { 0x00, 0x00, 0xff }, { 0xa5, 0x00, 0xff }, { 0xff, 0x00, 0xff }, { 0xff, 0x00, 0x98 },
|
||||
{ 0xff, 0x59, 0x59 }, { 0xff, 0xb4, 0x59 }, { 0xff, 0xff, 0x71 }, { 0xcf, 0xff, 0x60 }, { 0x6f, 0xff, 0x6f }, { 0x65, 0xff, 0xc9 }, { 0x6d, 0xff, 0xff }, { 0x59, 0xcd, 0xff },
|
||||
{ 0x59, 0x59, 0xff }, { 0xc4, 0x59, 0xff }, { 0xff, 0x66, 0xff }, { 0xff, 0x59, 0xbc }, { 0xff, 0x9c, 0x9c }, { 0xff, 0xd3, 0x9c }, { 0xff, 0xff, 0x9c }, { 0xe2, 0xff, 0x9c },
|
||||
{ 0x9c, 0xff, 0x9c }, { 0x9c, 0xff, 0xdb }, { 0x9c, 0xff, 0xff }, { 0x9c, 0xe2, 0xff }, { 0x9c, 0x9c, 0xff }, { 0xdc, 0x9c, 0xff }, { 0xff, 0x9c, 0xff }, { 0xff, 0x94, 0xd3 },
|
||||
{ 0x00, 0x00, 0x00 }, { 0x13, 0x13, 0x13 }, { 0x28, 0x28, 0x28 }, { 0x36, 0x36, 0x36 }, { 0x4d, 0x4d, 0x4d }, { 0x65, 0x65, 0x65 }, { 0x81, 0x81, 0x81 }, { 0x9f, 0x9f, 0x9f },
|
||||
{ 0xbc, 0xbc, 0xbc }, { 0xe2, 0xe2, 0xe2 }, { 0xff, 0xff, 0xff }
|
||||
};
|
||||
|
||||
static void
|
||||
theme_access_apply_default_99_palette (XTextColor *palette, size_t palette_len, gboolean apply_base)
|
||||
{
|
||||
size_t i;
|
||||
size_t start = apply_base ? 0 : 32;
|
||||
|
||||
if (palette_len == 0)
|
||||
return;
|
||||
for (i = start; i < THEME_XTEXT_MIRC_COLS && i < palette_len; i++)
|
||||
{
|
||||
palette[i].red = theme_default_99_mirc_colors[i][0] / 255.0;
|
||||
palette[i].green = theme_default_99_mirc_colors[i][1] / 255.0;
|
||||
palette[i].blue = theme_default_99_mirc_colors[i][2] / 255.0;
|
||||
palette[i].alpha = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_token_to_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue)
|
||||
{
|
||||
@@ -63,9 +120,18 @@ gboolean
|
||||
theme_get_mirc_color (unsigned int mirc_index, GdkRGBA *out_rgba)
|
||||
{
|
||||
ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index);
|
||||
gboolean has_user_colors = theme_runtime_mode_has_user_colors (theme_runtime_is_dark_active ());
|
||||
|
||||
if (mirc_index >= 32)
|
||||
if (mirc_index >= THEME_XTEXT_MIRC_COLS)
|
||||
return FALSE;
|
||||
if (!has_user_colors || mirc_index >= 32)
|
||||
{
|
||||
out_rgba->red = theme_default_99_mirc_colors[mirc_index][0] / 255.0;
|
||||
out_rgba->green = theme_default_99_mirc_colors[mirc_index][1] / 255.0;
|
||||
out_rgba->blue = theme_default_99_mirc_colors[mirc_index][2] / 255.0;
|
||||
out_rgba->alpha = 1.0;
|
||||
return TRUE;
|
||||
}
|
||||
return theme_runtime_get_color (token, out_rgba);
|
||||
}
|
||||
|
||||
@@ -79,9 +145,17 @@ 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);
|
||||
gboolean has_user_colors = theme_runtime_mode_has_user_colors (theme_runtime_is_dark_active ());
|
||||
|
||||
if (mirc_index >= 32)
|
||||
if (mirc_index >= THEME_XTEXT_MIRC_COLS)
|
||||
return FALSE;
|
||||
if (!has_user_colors || mirc_index >= 32)
|
||||
{
|
||||
*red = (guint16) (theme_default_99_mirc_colors[mirc_index][0] * 257);
|
||||
*green = (guint16) (theme_default_99_mirc_colors[mirc_index][1] * 257);
|
||||
*blue = (guint16) (theme_default_99_mirc_colors[mirc_index][2] * 257);
|
||||
return TRUE;
|
||||
}
|
||||
return theme_token_to_rgb16 (token, red, green, blue);
|
||||
}
|
||||
|
||||
@@ -125,12 +199,39 @@ void
|
||||
theme_get_xtext_colors_for_widget (GtkWidget *widget, XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
ThemeWidgetStyleValues style_values;
|
||||
gboolean has_user_colors;
|
||||
GdkRGBA marker_color;
|
||||
|
||||
if (!palette)
|
||||
return;
|
||||
|
||||
theme_get_widget_style_values_for_widget (widget, &style_values);
|
||||
theme_runtime_get_xtext_colors (palette, palette_len);
|
||||
has_user_colors = theme_runtime_mode_has_user_colors (theme_runtime_is_dark_active ());
|
||||
theme_access_apply_default_99_palette (palette, palette_len, !has_user_colors);
|
||||
if (palette_len > THEME_XTEXT_MARK_FG_INDEX)
|
||||
{
|
||||
palette[THEME_XTEXT_MARK_FG_INDEX].red = style_values.selection_foreground.red;
|
||||
palette[THEME_XTEXT_MARK_FG_INDEX].green = style_values.selection_foreground.green;
|
||||
palette[THEME_XTEXT_MARK_FG_INDEX].blue = style_values.selection_foreground.blue;
|
||||
palette[THEME_XTEXT_MARK_FG_INDEX].alpha = style_values.selection_foreground.alpha;
|
||||
}
|
||||
if (palette_len > THEME_XTEXT_MARK_BG_INDEX)
|
||||
{
|
||||
palette[THEME_XTEXT_MARK_BG_INDEX].red = style_values.selection_background.red;
|
||||
palette[THEME_XTEXT_MARK_BG_INDEX].green = style_values.selection_background.green;
|
||||
palette[THEME_XTEXT_MARK_BG_INDEX].blue = style_values.selection_background.blue;
|
||||
palette[THEME_XTEXT_MARK_BG_INDEX].alpha = style_values.selection_background.alpha;
|
||||
}
|
||||
if (palette_len > THEME_XTEXT_MARKER_INDEX)
|
||||
{
|
||||
if (!theme_runtime_get_color (THEME_TOKEN_MARKER, &marker_color))
|
||||
marker_color = style_values.selection_background;
|
||||
palette[THEME_XTEXT_MARKER_INDEX].red = marker_color.red;
|
||||
palette[THEME_XTEXT_MARKER_INDEX].green = marker_color.green;
|
||||
palette[THEME_XTEXT_MARKER_INDEX].blue = marker_color.blue;
|
||||
palette[THEME_XTEXT_MARKER_INDEX].alpha = marker_color.alpha;
|
||||
}
|
||||
if (palette_len > THEME_XTEXT_FG_INDEX)
|
||||
{
|
||||
palette[THEME_XTEXT_FG_INDEX].red = style_values.foreground.red;
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_ACCESS_H
|
||||
#define ZOITECHAT_THEME_ACCESS_H
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "theme-application.h"
|
||||
|
||||
#include "../../common/fe.h"
|
||||
@@ -6,16 +26,29 @@
|
||||
#include "theme-runtime.h"
|
||||
#include "theme-gtk3.h"
|
||||
#include "../maingui.h"
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
extern char *theme_css_build_toplevel_classes (void) __attribute__ ((weak));
|
||||
#else
|
||||
extern char *theme_css_build_toplevel_classes (void);
|
||||
#endif
|
||||
|
||||
static char *
|
||||
theme_application_build_toplevel_css (void)
|
||||
{
|
||||
if (theme_css_build_toplevel_classes)
|
||||
return theme_css_build_toplevel_classes ();
|
||||
|
||||
return g_strdup ("");
|
||||
}
|
||||
|
||||
static void
|
||||
theme_application_apply_windows_theme (gboolean dark)
|
||||
theme_application_apply_toplevel_theme (gboolean dark)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
static GtkCssProvider *win_theme_provider = NULL;
|
||||
static gboolean win_theme_provider_installed = FALSE;
|
||||
static GtkCssProvider *theme_provider = NULL;
|
||||
static gboolean theme_provider_installed = FALSE;
|
||||
GdkScreen *screen;
|
||||
gboolean prefer_dark = dark;
|
||||
char *css;
|
||||
@@ -36,33 +69,21 @@ theme_application_apply_windows_theme (gboolean dark)
|
||||
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 (!theme_provider)
|
||||
theme_provider = gtk_css_provider_new ();
|
||||
|
||||
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);
|
||||
css = theme_application_build_toplevel_css ();
|
||||
gtk_css_provider_load_from_data (theme_provider, css, -1, NULL);
|
||||
g_free (css);
|
||||
|
||||
if (!win_theme_provider_installed)
|
||||
if (!theme_provider_installed)
|
||||
{
|
||||
gtk_style_context_add_provider_for_screen (screen,
|
||||
GTK_STYLE_PROVIDER (win_theme_provider),
|
||||
GTK_STYLE_PROVIDER (theme_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
|
||||
win_theme_provider_installed = TRUE;
|
||||
theme_provider_installed = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
gboolean
|
||||
theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
@@ -71,10 +92,7 @@ theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
|
||||
theme_runtime_load ();
|
||||
dark = theme_runtime_apply_mode (mode, palette_changed);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
theme_application_apply_windows_theme (dark);
|
||||
#endif
|
||||
theme_application_apply_toplevel_theme (dark);
|
||||
|
||||
theme_application_reload_input_style ();
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_APPLICATION_H
|
||||
#define ZOITECHAT_THEME_APPLICATION_H
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "theme-css.h"
|
||||
|
||||
#include "theme-runtime.h"
|
||||
@@ -27,6 +47,12 @@ typedef struct
|
||||
guint16 bg_red;
|
||||
guint16 bg_green;
|
||||
guint16 bg_blue;
|
||||
guint16 sel_fg_red;
|
||||
guint16 sel_fg_green;
|
||||
guint16 sel_fg_blue;
|
||||
guint16 sel_bg_red;
|
||||
guint16 sel_bg_green;
|
||||
guint16 sel_bg_blue;
|
||||
} ThemeCssInputFingerprint;
|
||||
|
||||
static GtkCssProvider *theme_css_input_provider;
|
||||
@@ -92,6 +118,12 @@ theme_css_input_fingerprint_matches (const ThemeCssInputFingerprint *next)
|
||||
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 (theme_css_input_fp.sel_fg_red != next->sel_fg_red || theme_css_input_fp.sel_fg_green != next->sel_fg_green
|
||||
|| theme_css_input_fp.sel_fg_blue != next->sel_fg_blue)
|
||||
return FALSE;
|
||||
if (theme_css_input_fp.sel_bg_red != next->sel_bg_red || theme_css_input_fp.sel_bg_green != next->sel_bg_green
|
||||
|| theme_css_input_fp.sel_bg_blue != next->sel_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)
|
||||
@@ -119,7 +151,9 @@ theme_css_input_fingerprint_clear (void)
|
||||
|
||||
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)
|
||||
guint16 bg_red, guint16 bg_green, guint16 bg_blue,
|
||||
guint16 sel_fg_red, guint16 sel_fg_green, guint16 sel_fg_blue,
|
||||
guint16 sel_bg_red, guint16 sel_bg_green, guint16 sel_bg_blue)
|
||||
{
|
||||
GString *css = g_string_new ("");
|
||||
|
||||
@@ -137,6 +171,11 @@ theme_css_build_input (const char *theme_name, guint16 fg_red, guint16 fg_green,
|
||||
"%s {"
|
||||
"color: #%02x%02x%02x;"
|
||||
"caret-color: #%02x%02x%02x;"
|
||||
"}"
|
||||
"%s selection, %s text selection, %s:focus selection, %s:focus text selection, %s *:selected, %s *:selected:focus {"
|
||||
"background-color: #%02x%02x%02x;"
|
||||
"color: #%02x%02x%02x;"
|
||||
"text-shadow: none;"
|
||||
"}",
|
||||
theme_css_selector_input,
|
||||
(bg_red >> 8), (bg_green >> 8), (bg_blue >> 8),
|
||||
@@ -144,7 +183,15 @@ theme_css_build_input (const char *theme_name, guint16 fg_red, guint16 fg_green,
|
||||
(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));
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
|
||||
theme_css_selector_input,
|
||||
theme_css_selector_input,
|
||||
theme_css_selector_input,
|
||||
theme_css_selector_input,
|
||||
theme_css_selector_input,
|
||||
theme_css_selector_input,
|
||||
(sel_bg_red >> 8), (sel_bg_green >> 8), (sel_bg_blue >> 8),
|
||||
(sel_fg_red >> 8), (sel_fg_green >> 8), (sel_fg_blue >> 8));
|
||||
|
||||
return g_string_free (css, FALSE);
|
||||
}
|
||||
@@ -177,6 +224,10 @@ theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font
|
||||
&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);
|
||||
theme_palette_color_get_rgb16 (&style_values.selection_foreground,
|
||||
&next.sel_fg_red, &next.sel_fg_green, &next.sel_fg_blue);
|
||||
theme_palette_color_get_rgb16 (&style_values.selection_background,
|
||||
&next.sel_bg_red, &next.sel_bg_green, &next.sel_bg_blue);
|
||||
next.colors_set = TRUE;
|
||||
}
|
||||
|
||||
@@ -193,7 +244,9 @@ theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font
|
||||
|
||||
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);
|
||||
next.bg_red, next.bg_green, next.bg_blue,
|
||||
next.sel_fg_red, next.sel_fg_green, next.sel_fg_blue,
|
||||
next.sel_bg_red, next.sel_bg_green, next.sel_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));
|
||||
@@ -276,7 +329,7 @@ theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkR
|
||||
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);
|
||||
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, .%s selection, .%s text selection, .%s entry selection, .%s entry text selection, .%s:focus selection, .%s:focus text selection {", 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, 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)
|
||||
@@ -308,20 +361,22 @@ theme_css_build_toplevel_classes (void)
|
||||
"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 menubar, window.%s menubar:backdrop, window.%s menubar box, window.%s menubar box:backdrop, window.%s menuitem, window.%s menuitem:backdrop {"
|
||||
"background-color: @theme_bg_color;"
|
||||
"background-image: none;"
|
||||
"color: @theme_fg_color;"
|
||||
"border-color: @theme_bg_color;"
|
||||
"}"
|
||||
"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;"
|
||||
"window.%s menubar, window.%s menubar:backdrop, window.%s menubar box, window.%s menubar box:backdrop, window.%s menuitem, window.%s menuitem:backdrop {"
|
||||
"background-color: @theme_bg_color;"
|
||||
"background-image: none;"
|
||||
"color: @theme_fg_color;"
|
||||
"border-color: @theme_bg_color;"
|
||||
"}",
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_dark_class,
|
||||
@@ -330,6 +385,10 @@ theme_css_build_toplevel_classes (void)
|
||||
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,
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_CSS_H
|
||||
#define ZOITECHAT_THEME_CSS_H
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "theme-gtk3.h"
|
||||
|
||||
#include "../../common/zoitechat.h"
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_GTK3_H
|
||||
#define ZOITECHAT_THEME_GTK3_H
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "../fe-gtk.h"
|
||||
#include "theme-manager.h"
|
||||
|
||||
@@ -196,8 +216,6 @@ theme_manager_queue_auto_refresh (GtkSettings *settings, GParamSpec *pspec, gpoi
|
||||
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);
|
||||
@@ -205,21 +223,10 @@ theme_manager_init (void)
|
||||
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 ());
|
||||
|
||||
fe_set_auto_dark_mode_state (FALSE);
|
||||
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
|
||||
@@ -284,10 +291,10 @@ theme_manager_commit_preferences (unsigned int old_mode, gboolean *color_change)
|
||||
fe_set_auto_dark_mode_state (theme_policy_is_dark_mode_active (ZOITECHAT_DARK_MODE_AUTO));
|
||||
}
|
||||
|
||||
void
|
||||
gboolean
|
||||
theme_manager_save_preferences (void)
|
||||
{
|
||||
theme_runtime_save ();
|
||||
return theme_runtime_save ();
|
||||
}
|
||||
|
||||
gboolean
|
||||
@@ -474,7 +481,6 @@ theme_manager_apply_wayland_kde_csd (GtkWidget *window)
|
||||
static void
|
||||
theme_manager_apply_platform_window_theme (GtkWidget *window)
|
||||
{
|
||||
#ifdef G_OS_WIN32
|
||||
GtkStyleContext *context;
|
||||
gboolean dark;
|
||||
|
||||
@@ -496,6 +502,7 @@ theme_manager_apply_platform_window_theme (GtkWidget *window)
|
||||
gtk_style_context_remove_class (context, "zoitechat-light");
|
||||
gtk_style_context_add_class (context, dark ? "zoitechat-dark" : "zoitechat-light");
|
||||
}
|
||||
#ifdef G_OS_WIN32
|
||||
fe_win32_apply_native_titlebar (window, dark);
|
||||
#else
|
||||
theme_manager_apply_wayland_kde_csd (window);
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_MANAGER_H
|
||||
#define ZOITECHAT_THEME_MANAGER_H
|
||||
|
||||
@@ -43,7 +63,7 @@ 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_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);
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "theme-palette.h"
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_PALETTE_H
|
||||
#define ZOITECHAT_THEME_PALETTE_H
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 "theme-policy.h"
|
||||
|
||||
#include "../fe-gtk.h"
|
||||
@@ -7,38 +27,7 @@
|
||||
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;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_POLICY_H
|
||||
#define ZOITECHAT_THEME_POLICY_H
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
@@ -70,6 +90,155 @@ typedef struct
|
||||
|
||||
#define COLOR_MANAGER_RESPONSE_RESET 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean active;
|
||||
gboolean changed;
|
||||
gboolean snapshot_valid[THEME_TOKEN_COUNT];
|
||||
gboolean staged_valid[THEME_TOKEN_COUNT];
|
||||
GdkRGBA snapshot[THEME_TOKEN_COUNT];
|
||||
GdkRGBA staged[THEME_TOKEN_COUNT];
|
||||
} theme_preferences_stage_state;
|
||||
|
||||
static theme_preferences_stage_state theme_preferences_stage;
|
||||
|
||||
static gboolean
|
||||
theme_preferences_staged_get_color (ThemeSemanticToken token, GdkRGBA *rgba)
|
||||
{
|
||||
if (token < 0 || token >= THEME_TOKEN_COUNT || !rgba)
|
||||
return FALSE;
|
||||
|
||||
if (theme_preferences_stage.active && theme_preferences_stage.staged_valid[token])
|
||||
{
|
||||
*rgba = theme_preferences_stage.staged[token];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return theme_get_color (token, rgba);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_stage_recompute_changed (void)
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
|
||||
theme_preferences_stage.changed = FALSE;
|
||||
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
||||
{
|
||||
if (!theme_preferences_stage.snapshot_valid[token] || !theme_preferences_stage.staged_valid[token])
|
||||
continue;
|
||||
if (!gdk_rgba_equal (&theme_preferences_stage.snapshot[token], &theme_preferences_stage.staged[token]))
|
||||
{
|
||||
theme_preferences_stage.changed = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_stage_sync_runtime_to_snapshot (void)
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
|
||||
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
||||
{
|
||||
if (theme_preferences_stage.snapshot_valid[token])
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, token,
|
||||
&theme_preferences_stage.snapshot[token], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_stage_sync_runtime_to_staged (void)
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
|
||||
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
||||
{
|
||||
if (theme_preferences_stage.staged_valid[token])
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, token,
|
||||
&theme_preferences_stage.staged[token], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_staged_set_color (ThemeSemanticToken token, const GdkRGBA *rgba,
|
||||
gboolean *color_change_flag, gboolean live_preview)
|
||||
{
|
||||
const GdkRGBA *preview_color = rgba;
|
||||
|
||||
if (token < 0 || token >= THEME_TOKEN_COUNT || !rgba)
|
||||
return;
|
||||
|
||||
if (theme_preferences_stage.active)
|
||||
{
|
||||
theme_preferences_stage.staged[token] = *rgba;
|
||||
theme_preferences_stage.staged_valid[token] = TRUE;
|
||||
theme_preferences_stage_recompute_changed ();
|
||||
if (color_change_flag)
|
||||
*color_change_flag = theme_preferences_stage.changed;
|
||||
|
||||
preview_color = &theme_preferences_stage.staged[token];
|
||||
}
|
||||
|
||||
if (live_preview)
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, token, preview_color, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
theme_preferences_stage_begin (void)
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
|
||||
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
|
||||
theme_preferences_stage.active = TRUE;
|
||||
|
||||
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
|
||||
if (!theme_preferences_staged_get_color (token, &rgba))
|
||||
continue;
|
||||
|
||||
theme_preferences_stage.snapshot[token] = rgba;
|
||||
theme_preferences_stage.staged[token] = rgba;
|
||||
theme_preferences_stage.snapshot_valid[token] = TRUE;
|
||||
theme_preferences_stage.staged_valid[token] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
theme_preferences_stage_apply (void)
|
||||
{
|
||||
if (!theme_preferences_stage.active)
|
||||
return;
|
||||
|
||||
theme_preferences_stage_sync_runtime_to_staged ();
|
||||
}
|
||||
|
||||
void
|
||||
theme_preferences_stage_commit (void)
|
||||
{
|
||||
if (!theme_preferences_stage.active)
|
||||
return;
|
||||
|
||||
theme_preferences_stage_apply ();
|
||||
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
|
||||
}
|
||||
|
||||
void
|
||||
theme_preferences_stage_discard (void)
|
||||
{
|
||||
if (!theme_preferences_stage.active)
|
||||
return;
|
||||
|
||||
theme_preferences_stage_sync_runtime_to_snapshot ();
|
||||
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_show_import_error (GtkWidget *button, const char *message);
|
||||
|
||||
static void
|
||||
theme_preferences_manager_row_free (gpointer data)
|
||||
{
|
||||
@@ -113,16 +282,16 @@ theme_preferences_manager_update_preview (theme_color_manager_ui *ui)
|
||||
if (!ui)
|
||||
return;
|
||||
|
||||
if (!theme_get_color (THEME_TOKEN_TEXT_FOREGROUND, &text_fg)
|
||||
|| !theme_get_color (THEME_TOKEN_TEXT_BACKGROUND, &text_bg)
|
||||
|| !theme_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &sel_fg)
|
||||
|| !theme_get_color (THEME_TOKEN_SELECTION_BACKGROUND, &sel_bg)
|
||||
|| !theme_get_color (THEME_TOKEN_MARKER, &marker)
|
||||
|| !theme_get_color (THEME_TOKEN_TAB_NEW_DATA, &tab_new_data)
|
||||
|| !theme_get_color (THEME_TOKEN_TAB_NEW_MESSAGE, &tab_new_message)
|
||||
|| !theme_get_color (THEME_TOKEN_TAB_HIGHLIGHT, &tab_highlight)
|
||||
|| !theme_get_color (THEME_TOKEN_TAB_AWAY, &tab_away)
|
||||
|| !theme_get_color (THEME_TOKEN_SPELL, &spell))
|
||||
if (!theme_preferences_staged_get_color (THEME_TOKEN_TEXT_FOREGROUND, &text_fg)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_TEXT_BACKGROUND, &text_bg)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &sel_fg)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_SELECTION_BACKGROUND, &sel_bg)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_MARKER, &marker)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_TAB_NEW_DATA, &tab_new_data)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_TAB_NEW_MESSAGE, &tab_new_message)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_TAB_HIGHLIGHT, &tab_highlight)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_TAB_AWAY, &tab_away)
|
||||
|| !theme_preferences_staged_get_color (THEME_TOKEN_SPELL, &spell))
|
||||
return;
|
||||
|
||||
gtkutil_apply_palette (ui->preview_window, &text_bg, &text_fg, NULL);
|
||||
@@ -302,15 +471,12 @@ theme_preferences_color_response_cb (GtkDialog *dialog, gint response_id, gpoint
|
||||
if (response_id == GTK_RESPONSE_OK)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog), &rgba);
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT,
|
||||
data->token,
|
||||
&rgba,
|
||||
&changed);
|
||||
if (data->color_change_flag)
|
||||
*data->color_change_flag = *data->color_change_flag || changed;
|
||||
theme_preferences_staged_set_color (data->token,
|
||||
&rgba,
|
||||
data->color_change_flag,
|
||||
TRUE);
|
||||
theme_preferences_color_button_apply (data->button, &rgba);
|
||||
theme_preferences_manager_update_preview ((theme_color_manager_ui *) data->manager_ui);
|
||||
}
|
||||
@@ -329,7 +495,7 @@ theme_preferences_color_cb (GtkWidget *button, gpointer userdata)
|
||||
|
||||
token = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "zoitechat-theme-token"));
|
||||
|
||||
if (!theme_get_color (token, &rgba))
|
||||
if (!theme_preferences_staged_get_color (token, &rgba))
|
||||
return;
|
||||
dialog = gtk_color_chooser_dialog_new (_("Select color"), GTK_WINDOW (userdata));
|
||||
theme_manager_attach_window (dialog);
|
||||
@@ -405,14 +571,7 @@ theme_preferences_manager_row_apply (theme_color_manager_row *row, const GdkRGBA
|
||||
static void
|
||||
theme_preferences_manager_row_commit (theme_color_manager_row *row, const GdkRGBA *rgba)
|
||||
{
|
||||
gboolean changed = FALSE;
|
||||
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT,
|
||||
row->token,
|
||||
rgba,
|
||||
&changed);
|
||||
if (row->color_change_flag)
|
||||
*row->color_change_flag = *row->color_change_flag || changed;
|
||||
theme_preferences_staged_set_color (row->token, rgba, row->color_change_flag, TRUE);
|
||||
theme_preferences_manager_row_apply (row, rgba);
|
||||
theme_preferences_manager_update_preview ((theme_color_manager_ui *) row->manager_ui);
|
||||
}
|
||||
@@ -425,7 +584,7 @@ theme_preferences_manager_entry_commit (theme_color_manager_row *row)
|
||||
|
||||
if (!gdk_rgba_parse (&rgba, text))
|
||||
{
|
||||
if (theme_get_color (row->token, &rgba))
|
||||
if (theme_preferences_staged_get_color (row->token, &rgba))
|
||||
theme_preferences_manager_row_apply (row, &rgba);
|
||||
return;
|
||||
}
|
||||
@@ -483,7 +642,7 @@ theme_preferences_manager_pick_cb (GtkWidget *button, gpointer user_data)
|
||||
GdkRGBA rgba;
|
||||
theme_manager_live_picker_data *data;
|
||||
|
||||
if (!theme_get_color (row->token, &rgba))
|
||||
if (!theme_preferences_staged_get_color (row->token, &rgba))
|
||||
return;
|
||||
|
||||
dialog = gtk_color_chooser_dialog_new (_("Select color"), row->parent);
|
||||
@@ -542,7 +701,7 @@ theme_preferences_manager_refresh_rows (theme_color_manager_ui *ui)
|
||||
theme_color_manager_row *row = g_ptr_array_index (ui->rows, i);
|
||||
GdkRGBA rgba;
|
||||
|
||||
if (theme_get_color (row->token, &rgba))
|
||||
if (theme_preferences_staged_get_color (row->token, &rgba))
|
||||
theme_preferences_manager_row_apply (row, &rgba);
|
||||
}
|
||||
|
||||
@@ -561,7 +720,31 @@ theme_preferences_manager_dialog_response_cb (GtkDialog *dialog, gint response_i
|
||||
gboolean changed = FALSE;
|
||||
|
||||
theme_manager_reset_mode_colors (ZOITECHAT_DARK_MODE_LIGHT, &changed);
|
||||
if (ui->color_change_flag)
|
||||
if (theme_preferences_stage.active)
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
ThemeWidgetStyleValues style_values;
|
||||
|
||||
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
|
||||
if (!theme_get_color (token, &rgba))
|
||||
continue;
|
||||
theme_preferences_stage.staged[token] = rgba;
|
||||
theme_preferences_stage.staged_valid[token] = TRUE;
|
||||
}
|
||||
theme_get_widget_style_values_for_widget (GTK_WIDGET (dialog), &style_values);
|
||||
theme_preferences_stage.staged[THEME_TOKEN_TEXT_FOREGROUND] = style_values.foreground;
|
||||
theme_preferences_stage.staged_valid[THEME_TOKEN_TEXT_FOREGROUND] = TRUE;
|
||||
theme_preferences_stage.staged[THEME_TOKEN_TEXT_BACKGROUND] = style_values.background;
|
||||
theme_preferences_stage.staged_valid[THEME_TOKEN_TEXT_BACKGROUND] = TRUE;
|
||||
theme_preferences_stage_sync_runtime_to_staged ();
|
||||
theme_preferences_stage_recompute_changed ();
|
||||
if (ui->color_change_flag)
|
||||
*ui->color_change_flag = theme_preferences_stage.changed;
|
||||
}
|
||||
else if (ui->color_change_flag)
|
||||
*ui->color_change_flag = *ui->color_change_flag || changed;
|
||||
}
|
||||
|
||||
@@ -695,7 +878,7 @@ theme_preferences_create_color_manager_dialog (GtkWindow *parent, gboolean *colo
|
||||
g_free (token_code);
|
||||
g_free (search_text);
|
||||
|
||||
if (theme_get_color (token, &rgba))
|
||||
if (theme_preferences_staged_get_color (token, &rgba))
|
||||
theme_preferences_manager_row_apply (row, &rgba);
|
||||
|
||||
g_signal_connect (G_OBJECT (button), "clicked",
|
||||
@@ -724,19 +907,15 @@ static void
|
||||
theme_preferences_manage_colors_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
gboolean *color_change_flag = user_data;
|
||||
gboolean old_changed = FALSE;
|
||||
GtkWidget *dialog;
|
||||
|
||||
if (color_change_flag)
|
||||
old_changed = *color_change_flag;
|
||||
|
||||
dialog = theme_preferences_create_color_manager_dialog (GTK_WINDOW (gtk_widget_get_toplevel (button)),
|
||||
color_change_flag);
|
||||
gtk_dialog_run (GTK_DIALOG (dialog));
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
if (color_change_flag && *color_change_flag != old_changed)
|
||||
theme_manager_save_preferences ();
|
||||
if (color_change_flag)
|
||||
*color_change_flag = theme_preferences_stage.active ? theme_preferences_stage.changed : *color_change_flag;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -865,12 +1044,8 @@ theme_preferences_import_colors_conf_cb (GtkWidget *button, gpointer user_data)
|
||||
char *cfg;
|
||||
GError *error = NULL;
|
||||
gboolean any_imported = FALSE;
|
||||
gboolean old_changed = FALSE;
|
||||
ThemeSemanticToken token;
|
||||
|
||||
if (color_change_flag)
|
||||
old_changed = *color_change_flag;
|
||||
|
||||
dialog = gtk_file_chooser_dialog_new (_("Import colors.conf colors"),
|
||||
GTK_WINDOW (gtk_widget_get_toplevel (button)),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
@@ -906,14 +1081,14 @@ theme_preferences_import_colors_conf_cb (GtkWidget *button, gpointer user_data)
|
||||
if (!theme_preferences_read_import_color (cfg, token, &rgba))
|
||||
continue;
|
||||
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, token, &rgba, color_change_flag);
|
||||
theme_preferences_staged_set_color (token, &rgba, color_change_flag, TRUE);
|
||||
any_imported = TRUE;
|
||||
}
|
||||
|
||||
if (!any_imported)
|
||||
theme_preferences_show_import_error (button, _("No importable colors were found in that colors.conf file."));
|
||||
else if (color_change_flag && *color_change_flag != old_changed)
|
||||
theme_manager_save_preferences ();
|
||||
else if (color_change_flag)
|
||||
*color_change_flag = theme_preferences_stage.active ? theme_preferences_stage.changed : *color_change_flag;
|
||||
|
||||
g_free (cfg);
|
||||
g_free (path);
|
||||
@@ -957,7 +1132,7 @@ theme_preferences_create_color_button (GtkWidget *table,
|
||||
g_object_set_data (G_OBJECT (but), "zoitechat-theme-color-change", color_change_flag);
|
||||
gtk_grid_attach (GTK_GRID (table), but, col, row, 1, 1);
|
||||
g_signal_connect (G_OBJECT (but), "clicked", G_CALLBACK (theme_preferences_color_cb), parent);
|
||||
if (theme_get_color (token, &color))
|
||||
if (theme_preferences_staged_get_color (token, &color))
|
||||
theme_preferences_color_button_apply (but, &color);
|
||||
}
|
||||
|
||||
@@ -1132,25 +1307,12 @@ theme_preferences_create_color_page (GtkWindow *parent,
|
||||
static void
|
||||
theme_preferences_open_gtk3_folder_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
theme_preferences_ui *ui = user_data;
|
||||
GAppInfo *handler;
|
||||
char *themes_dir;
|
||||
|
||||
(void)user_data;
|
||||
(void)button;
|
||||
themes_dir = zoitechat_gtk3_theme_service_get_user_themes_dir ();
|
||||
g_mkdir_with_parents (themes_dir, 0700);
|
||||
|
||||
handler = g_app_info_get_default_for_uri_scheme ("file");
|
||||
if (!handler)
|
||||
{
|
||||
theme_preferences_show_message (ui,
|
||||
GTK_MESSAGE_ERROR,
|
||||
_("No application is configured to open folders."));
|
||||
g_free (themes_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_unref (handler);
|
||||
fe_open_url (themes_dir);
|
||||
g_free (themes_dir);
|
||||
}
|
||||
@@ -1188,6 +1350,19 @@ theme_preferences_gtk3_sync_remove_state (theme_preferences_ui *ui)
|
||||
gtk_widget_set_sensitive (ui->gtk3_remove, source == ZOITECHAT_GTK3_THEME_SOURCE_USER);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_preferences_gtk3_apply_and_refresh (GError **error)
|
||||
{
|
||||
if (!theme_gtk3_apply_current (error))
|
||||
return FALSE;
|
||||
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);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_gtk3_changed_cb (GtkComboBox *combo, gpointer user_data)
|
||||
{
|
||||
@@ -1218,7 +1393,7 @@ theme_preferences_gtk3_changed_cb (GtkComboBox *combo, gpointer user_data)
|
||||
ui->setup_prefs->hex_gui_gtk3_variant = prefs.hex_gui_gtk3_variant;
|
||||
}
|
||||
|
||||
if (selection_changed && !theme_gtk3_apply_current (&error))
|
||||
if (selection_changed && !theme_preferences_gtk3_apply_and_refresh (&error))
|
||||
{
|
||||
theme_preferences_show_message (ui, GTK_MESSAGE_ERROR,
|
||||
error ? error->message : _("Failed to apply GTK3 theme."));
|
||||
@@ -1231,55 +1406,20 @@ theme_preferences_gtk3_changed_cb (GtkComboBox *combo, gpointer user_data)
|
||||
static GdkPixbuf *
|
||||
theme_preferences_load_thumbnail (const char *path)
|
||||
{
|
||||
char *data = NULL;
|
||||
gsize length = 0;
|
||||
GdkPixbufLoader *loader;
|
||||
GError *error = NULL;
|
||||
GdkPixbuf *pixbuf;
|
||||
GdkPixbuf *scaled;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (!path || !g_file_get_contents (path, &data, &length, &error))
|
||||
{
|
||||
g_clear_error (&error);
|
||||
if (!path)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
loader = gdk_pixbuf_loader_new ();
|
||||
if (!gdk_pixbuf_loader_write (loader, (const guchar *) data, length, &error))
|
||||
{
|
||||
g_clear_error (&error);
|
||||
g_object_unref (loader);
|
||||
g_free (data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_free (data);
|
||||
|
||||
if (!gdk_pixbuf_loader_close (loader, &error))
|
||||
{
|
||||
g_clear_error (&error);
|
||||
g_object_unref (loader);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
|
||||
pixbuf = gdk_pixbuf_new_from_file_at_scale (path, 48, 48, TRUE, &error);
|
||||
if (!pixbuf)
|
||||
{
|
||||
g_object_unref (loader);
|
||||
g_clear_error (&error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
width = gdk_pixbuf_get_width (pixbuf);
|
||||
height = gdk_pixbuf_get_height (pixbuf);
|
||||
if (width > 48 || height > 48)
|
||||
scaled = gdk_pixbuf_scale_simple (pixbuf, 48, 48, GDK_INTERP_BILINEAR);
|
||||
else
|
||||
scaled = gdk_pixbuf_copy (pixbuf);
|
||||
|
||||
g_object_unref (loader);
|
||||
return scaled;
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1395,7 +1535,7 @@ theme_preferences_populate_gtk3 (theme_preferences_ui *ui)
|
||||
g_free (final_id);
|
||||
}
|
||||
|
||||
if (should_apply && !theme_gtk3_apply_current (&error))
|
||||
if (should_apply && !theme_preferences_gtk3_apply_and_refresh (&error))
|
||||
{
|
||||
theme_preferences_show_message (ui, GTK_MESSAGE_ERROR,
|
||||
error ? error->message : _("Failed to apply GTK3 theme."));
|
||||
@@ -1405,26 +1545,34 @@ theme_preferences_populate_gtk3 (theme_preferences_ui *ui)
|
||||
g_ptr_array_unref (themes);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_gtk3_import_path (theme_preferences_ui *ui, char *path)
|
||||
{
|
||||
char *id = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!zoitechat_gtk3_theme_service_import (path, &id, &error))
|
||||
theme_preferences_show_message (ui, GTK_MESSAGE_ERROR,
|
||||
error ? error->message : _("Failed to import GTK3 theme."));
|
||||
g_clear_error (&error);
|
||||
g_free (id);
|
||||
g_free (path);
|
||||
theme_preferences_populate_gtk3 (ui);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_gtk3_import_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
theme_preferences_ui *ui = user_data;
|
||||
GtkWidget *dialog;
|
||||
GtkFileChooserNative *dialog;
|
||||
GtkFileFilter *filter;
|
||||
GtkWidget *folder_dialog;
|
||||
char *path;
|
||||
char *id = NULL;
|
||||
GError *error = NULL;
|
||||
gint response;
|
||||
|
||||
(void)button;
|
||||
dialog = gtk_file_chooser_dialog_new (_("Import GTK3 Theme"), ui->parent,
|
||||
dialog = gtk_file_chooser_native_new (_("Import GTK3 Theme"), ui->parent,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
_("Import _Folder"), 1,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_Import"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
theme_manager_attach_window (dialog);
|
||||
_("_Import"),
|
||||
_("_Cancel"));
|
||||
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
|
||||
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE);
|
||||
filter = gtk_file_filter_new ();
|
||||
@@ -1439,43 +1587,15 @@ theme_preferences_gtk3_import_cb (GtkWidget *button, gpointer user_data)
|
||||
gtk_file_filter_add_pattern (filter, "*.tbz");
|
||||
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
|
||||
|
||||
response = gtk_dialog_run (GTK_DIALOG (dialog));
|
||||
if (response == 1)
|
||||
if (gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
gtk_widget_destroy (dialog);
|
||||
folder_dialog = gtk_file_chooser_dialog_new (_("Import GTK3 Theme Folder"), ui->parent,
|
||||
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_Import"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
theme_manager_attach_window (folder_dialog);
|
||||
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (folder_dialog), TRUE);
|
||||
if (gtk_dialog_run (GTK_DIALOG (folder_dialog)) != GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
gtk_widget_destroy (folder_dialog);
|
||||
return;
|
||||
}
|
||||
|
||||
path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (folder_dialog));
|
||||
gtk_widget_destroy (folder_dialog);
|
||||
}
|
||||
else if (response == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
|
||||
gtk_widget_destroy (dialog);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_destroy (dialog);
|
||||
g_object_unref (dialog);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!zoitechat_gtk3_theme_service_import (path, &id, &error))
|
||||
theme_preferences_show_message (ui, GTK_MESSAGE_ERROR,
|
||||
error ? error->message : _("Failed to import GTK3 theme."));
|
||||
g_clear_error (&error);
|
||||
g_free (path);
|
||||
theme_preferences_populate_gtk3 (ui);
|
||||
path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
|
||||
g_object_unref (dialog);
|
||||
theme_preferences_gtk3_import_path (ui, path);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_PREFERENCES_H
|
||||
#define ZOITECHAT_THEME_PREFERENCES_H
|
||||
|
||||
@@ -13,5 +33,9 @@ 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);
|
||||
void theme_preferences_stage_begin (void);
|
||||
void theme_preferences_stage_apply (void);
|
||||
void theme_preferences_stage_commit (void);
|
||||
void theme_preferences_stage_discard (void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
@@ -276,7 +297,7 @@ theme_runtime_load_migrated_legacy_color (char *cfg,
|
||||
return palette_read_legacy_color (cfg, mode->legacy_prefix, def->legacy_index, out_color);
|
||||
}
|
||||
|
||||
static void
|
||||
static gboolean
|
||||
palette_write_token_color (int fh, const char *mode_name, const ThemePaletteTokenDef *def, const GdkRGBA *color)
|
||||
{
|
||||
char prefname[256];
|
||||
@@ -284,13 +305,13 @@ palette_write_token_color (int fh, const char *mode_name, const ThemePaletteToke
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
|
||||
g_return_if_fail (mode_name != NULL);
|
||||
g_return_if_fail (def != NULL);
|
||||
g_return_if_fail (color != NULL);
|
||||
g_return_val_if_fail (mode_name != NULL, FALSE);
|
||||
g_return_val_if_fail (def != NULL, FALSE);
|
||||
g_return_val_if_fail (color != NULL, FALSE);
|
||||
|
||||
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);
|
||||
return cfg_put_color (fh, red, green, blue, prefname);
|
||||
}
|
||||
|
||||
|
||||
@@ -305,7 +326,8 @@ theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
|
||||
gboolean
|
||||
theme_runtime_mode_has_user_colors (gboolean dark_mode)
|
||||
{
|
||||
(void) dark_mode;
|
||||
if (dark_mode)
|
||||
return dark_user_colors_valid;
|
||||
return user_colors_valid;
|
||||
}
|
||||
|
||||
@@ -446,15 +468,13 @@ theme_runtime_load (void)
|
||||
|
||||
active_palette = light_palette;
|
||||
dark_mode_active = FALSE;
|
||||
user_colors_valid = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_save (void)
|
||||
static gboolean
|
||||
theme_runtime_save_to_fd (int fh)
|
||||
{
|
||||
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 },
|
||||
@@ -485,11 +505,8 @@ theme_runtime_save (void)
|
||||
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);
|
||||
if (!cfg_put_int (fh, THEME_PALETTE_MIGRATION_MARKER_VALUE, (char *) THEME_PALETTE_MIGRATION_MARKER_KEY))
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < mode_count; i++)
|
||||
{
|
||||
@@ -507,11 +524,101 @@ theme_runtime_save (void)
|
||||
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);
|
||||
if (!palette_write_token_color (fh, modes[i].mode_name, def, &color))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
close (fh);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_save_prepare (char **temp_path)
|
||||
{
|
||||
int fh;
|
||||
char *temp_name;
|
||||
const char *config_dir;
|
||||
|
||||
if (!temp_path)
|
||||
return FALSE;
|
||||
|
||||
config_dir = get_xdir ();
|
||||
if (!config_dir)
|
||||
return FALSE;
|
||||
|
||||
temp_name = g_build_filename (config_dir, "colors.conf.new.XXXXXX", NULL);
|
||||
fh = g_mkstemp (temp_name);
|
||||
if (fh == -1)
|
||||
{
|
||||
g_free (temp_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!theme_runtime_save_to_fd (fh))
|
||||
{
|
||||
close (fh);
|
||||
g_unlink (temp_name);
|
||||
g_free (temp_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (close (fh) == -1)
|
||||
{
|
||||
g_unlink (temp_name);
|
||||
g_free (temp_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*temp_path = temp_name;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_save_finalize (const char *temp_path)
|
||||
{
|
||||
char *config_path;
|
||||
|
||||
if (!temp_path)
|
||||
return FALSE;
|
||||
|
||||
config_path = g_build_filename (get_xdir (), "colors.conf", NULL);
|
||||
#ifdef WIN32
|
||||
g_unlink (config_path);
|
||||
#endif
|
||||
if (g_rename (temp_path, config_path) == -1)
|
||||
{
|
||||
g_free (config_path);
|
||||
return FALSE;
|
||||
}
|
||||
g_free (config_path);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_save_discard (const char *temp_path)
|
||||
{
|
||||
if (!temp_path)
|
||||
return;
|
||||
|
||||
g_unlink (temp_path);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_save (void)
|
||||
{
|
||||
char *temp_path = NULL;
|
||||
gboolean result;
|
||||
|
||||
if (!theme_runtime_save_prepare (&temp_path))
|
||||
return FALSE;
|
||||
|
||||
result = theme_runtime_save_finalize (temp_path);
|
||||
if (!result)
|
||||
theme_runtime_save_discard (temp_path);
|
||||
g_free (temp_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/* ZoiteChat
|
||||
* Copyright (C) 1998-2010 Peter Zelezny.
|
||||
* Copyright (C) 2009-2013 Berke Viktor.
|
||||
* Copyright (C) 2026 deepend-tildeclub.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef ZOITECHAT_THEME_RUNTIME_H
|
||||
#define ZOITECHAT_THEME_RUNTIME_H
|
||||
|
||||
@@ -18,7 +38,10 @@ typedef struct
|
||||
} ThemeGtkPaletteMap;
|
||||
|
||||
void theme_runtime_load (void);
|
||||
void theme_runtime_save (void);
|
||||
gboolean theme_runtime_save (void);
|
||||
gboolean theme_runtime_save_prepare (char **temp_path);
|
||||
gboolean theme_runtime_save_finalize (const char *temp_path);
|
||||
void theme_runtime_save_discard (const char *temp_path);
|
||||
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);
|
||||
|
||||
@@ -99,7 +99,7 @@ url_treeview_new (GtkWidget *box)
|
||||
scroll = gtk_widget_get_parent (view);
|
||||
gtk_widget_set_hexpand (scroll, TRUE);
|
||||
gtk_widget_set_vexpand (scroll, TRUE);
|
||||
g_signal_connect (G_OBJECT (view), "button_press_event",
|
||||
g_signal_connect (G_OBJECT (view), "button-press-event",
|
||||
G_CALLBACK (url_treeview_url_clicked_cb), NULL);
|
||||
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
|
||||
gtk_widget_show (view);
|
||||
|
||||
@@ -44,14 +44,74 @@
|
||||
enum
|
||||
{
|
||||
COL_PIX=0, /* GdkPixbuf * */
|
||||
COL_NICK=1, /* char * */
|
||||
COL_HOST=2, /* char * */
|
||||
COL_USER=3, /* struct User * */
|
||||
COL_GDKCOLOR=4 /* GdkRGBA */
|
||||
COL_PREFIX=1, /* char * */
|
||||
COL_NICK=2, /* char * */
|
||||
COL_HOST=3, /* char * */
|
||||
COL_USER=4, /* struct User * */
|
||||
COL_GDKCOLOR=5 /* GdkRGBA */
|
||||
};
|
||||
|
||||
static void userlist_store_color (GtkListStore *store, GtkTreeIter *iter, ThemeSemanticToken token, gboolean has_token);
|
||||
|
||||
static const char *
|
||||
userlist_prefix_color (char prefix)
|
||||
{
|
||||
switch (prefix)
|
||||
{
|
||||
case '~': return "#d46a6a";
|
||||
case '@': return "#5ea36a";
|
||||
case '%': return "#d39a5f";
|
||||
case '&': return "#79aecd";
|
||||
case '+': return "#d2bf6a";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
userlist_column_width_notify_cb (GtkTreeViewColumn *column, GParamSpec *pspec, gpointer userdata)
|
||||
{
|
||||
(void)pspec;
|
||||
|
||||
int width = gtk_tree_view_column_get_width (column);
|
||||
int *target = (int *)userdata;
|
||||
|
||||
if (!target || width < 1 || *target == width)
|
||||
return;
|
||||
|
||||
*target = width;
|
||||
}
|
||||
|
||||
static void
|
||||
userlist_apply_saved_column_width (GtkTreeViewColumn *column, int width)
|
||||
{
|
||||
if (!column || width < 1)
|
||||
return;
|
||||
|
||||
gtk_tree_view_column_set_fixed_width (column, width);
|
||||
}
|
||||
|
||||
static void
|
||||
userlist_update_min_width (session *sess)
|
||||
{
|
||||
GtkRequisition minimum;
|
||||
GtkRequisition natural;
|
||||
GtkWidget *scrolled_window;
|
||||
int width;
|
||||
|
||||
if (!sess || !sess->gui || !sess->gui->user_box || !sess->gui->namelistinfo || !sess->gui->user_tree)
|
||||
return;
|
||||
|
||||
gtk_widget_get_preferred_size (sess->gui->namelistinfo, &minimum, &natural);
|
||||
width = MAX (minimum.width, natural.width);
|
||||
if (width < 1)
|
||||
width = 1;
|
||||
|
||||
scrolled_window = gtk_widget_get_parent (sess->gui->user_tree);
|
||||
if (GTK_IS_SCROLLED_WINDOW (scrolled_window))
|
||||
gtk_scrolled_window_set_min_content_width (GTK_SCROLLED_WINDOW (scrolled_window), width);
|
||||
gtk_widget_set_size_request (sess->gui->user_box, width, -1);
|
||||
}
|
||||
|
||||
GdkPixbuf *
|
||||
get_user_icon (server *serv, struct User *user)
|
||||
{
|
||||
@@ -110,9 +170,11 @@ fe_userlist_numbers (session *sess)
|
||||
g_snprintf (tbuf, sizeof (tbuf), _("%d ops, %d total"), sess->ops, sess->total);
|
||||
tbuf[sizeof (tbuf) - 1] = 0;
|
||||
gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), tbuf);
|
||||
userlist_update_min_width (sess);
|
||||
} else
|
||||
{
|
||||
gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), NULL);
|
||||
userlist_update_min_width (sess);
|
||||
}
|
||||
|
||||
if (sess->type == SESS_CHANNEL && prefs.hex_gui_win_ucount)
|
||||
@@ -453,6 +515,11 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
|
||||
GdkPixbuf *pix = get_user_icon (sess->server, newuser);
|
||||
GtkTreeIter iter;
|
||||
char *nick;
|
||||
char *nick_escaped;
|
||||
char *prefix = NULL;
|
||||
char *prefix_escaped;
|
||||
char prefix_text[2];
|
||||
const char *prefix_color;
|
||||
ThemeSemanticToken nick_token = THEME_TOKEN_TEXT_FOREGROUND;
|
||||
gboolean have_nick_token = FALSE;
|
||||
|
||||
@@ -472,30 +539,36 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
|
||||
}
|
||||
}
|
||||
|
||||
nick = newuser->nick;
|
||||
nick_escaped = g_markup_escape_text (newuser->nick, -1);
|
||||
nick = nick_escaped;
|
||||
if (!prefs.hex_gui_ulist_icons)
|
||||
{
|
||||
nick = g_malloc (strlen (newuser->nick) + 2);
|
||||
nick[0] = newuser->prefix[0];
|
||||
if (nick[0] == '\0' || nick[0] == ' ')
|
||||
strcpy (nick, newuser->nick);
|
||||
else
|
||||
strcpy (nick + 1, newuser->nick);
|
||||
if (newuser->prefix[0] != '\0' && newuser->prefix[0] != ' ')
|
||||
{
|
||||
prefix_text[0] = newuser->prefix[0];
|
||||
prefix_text[1] = '\0';
|
||||
prefix_escaped = g_markup_escape_text (prefix_text, -1);
|
||||
prefix_color = userlist_prefix_color (newuser->prefix[0]);
|
||||
if (prefix_color)
|
||||
prefix = g_strdup_printf ("<b><span foreground=\"%s\">%s</span></b>", prefix_color, prefix_escaped);
|
||||
else
|
||||
prefix = g_strdup_printf ("<b>%s</b>", prefix_escaped);
|
||||
g_free (prefix_escaped);
|
||||
}
|
||||
pix = NULL;
|
||||
}
|
||||
|
||||
gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, 0,
|
||||
COL_PIX, pix,
|
||||
COL_PREFIX, prefix,
|
||||
COL_NICK, nick,
|
||||
COL_HOST, newuser->hostname,
|
||||
COL_USER, newuser,
|
||||
-1);
|
||||
userlist_store_color (GTK_LIST_STORE (model), &iter, nick_token, have_nick_token);
|
||||
|
||||
if (!prefs.hex_gui_ulist_icons)
|
||||
{
|
||||
g_free (nick);
|
||||
}
|
||||
g_free (prefix);
|
||||
g_free (nick_escaped);
|
||||
|
||||
userlist_row_map_set (sess, model, newuser, &iter);
|
||||
|
||||
@@ -624,8 +697,8 @@ userlist_create_model (session *sess)
|
||||
GtkTreeIterCompareFunc cmp_func;
|
||||
GtkSortType sort_type;
|
||||
|
||||
store = gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
|
||||
G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
|
||||
store = gtk_list_store_new (6, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
|
||||
G_TYPE_STRING, G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
|
||||
|
||||
switch (prefs.hex_gui_ulist_sort)
|
||||
{
|
||||
@@ -662,6 +735,7 @@ static void
|
||||
userlist_add_columns (GtkTreeView * treeview)
|
||||
{
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *column;
|
||||
|
||||
/* icon column */
|
||||
renderer = gtk_cell_renderer_pixbuf_new ();
|
||||
@@ -669,16 +743,38 @@ userlist_add_columns (GtkTreeView * treeview)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, NULL, renderer,
|
||||
"pixbuf", 0, NULL);
|
||||
"pixbuf", COL_PIX, NULL);
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 0);
|
||||
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
|
||||
/* nick column */
|
||||
column = gtk_tree_view_column_new ();
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (prefs.hex_gui_compact)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, NULL, renderer,
|
||||
"text", 1, THEME_GTK_FOREGROUND_PROPERTY, 4, NULL);
|
||||
gtk_tree_view_column_pack_start (column, renderer, FALSE);
|
||||
gtk_tree_view_column_add_attribute (column, renderer, "markup", COL_PREFIX);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (prefs.hex_gui_compact)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
gtk_tree_view_column_pack_start (column, renderer, TRUE);
|
||||
gtk_tree_view_column_add_attribute (column, renderer, "markup", COL_NICK);
|
||||
gtk_tree_view_column_add_attribute (column, renderer, THEME_GTK_FOREGROUND_PROPERTY, COL_GDKCOLOR);
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 1);
|
||||
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_expand (column, TRUE);
|
||||
gtk_tree_view_column_set_min_width (column, 1);
|
||||
gtk_tree_view_column_set_resizable (column, TRUE);
|
||||
userlist_apply_saved_column_width (column, prefs.hex_gui_ulist_nick_width);
|
||||
g_signal_connect (G_OBJECT (column), "notify::width",
|
||||
G_CALLBACK (userlist_column_width_notify_cb),
|
||||
&prefs.hex_gui_ulist_nick_width);
|
||||
|
||||
if (prefs.hex_gui_ulist_show_hosts)
|
||||
{
|
||||
@@ -686,10 +782,20 @@ userlist_add_columns (GtkTreeView * treeview)
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (prefs.hex_gui_compact)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
|
||||
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", 2, NULL);
|
||||
"text", COL_HOST, NULL);
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 2);
|
||||
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_expand (column, TRUE);
|
||||
gtk_tree_view_column_set_min_width (column, 1);
|
||||
gtk_tree_view_column_set_resizable (column, TRUE);
|
||||
userlist_apply_saved_column_width (column, prefs.hex_gui_ulist_host_width);
|
||||
g_signal_connect (G_OBJECT (column), "notify::width",
|
||||
G_CALLBACK (userlist_column_width_notify_cb),
|
||||
&prefs.hex_gui_ulist_host_width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -827,26 +933,26 @@ userlist_create (GtkWidget *box)
|
||||
gtk_drag_source_set (treeview, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_MOVE);
|
||||
|
||||
/* file DND (for DCC) */
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_motion",
|
||||
g_signal_connect (G_OBJECT (treeview), "drag-motion",
|
||||
G_CALLBACK (userlist_dnd_motion), treeview);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_leave",
|
||||
g_signal_connect (G_OBJECT (treeview), "drag-leave",
|
||||
G_CALLBACK (userlist_dnd_leave), 0);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_data_received",
|
||||
g_signal_connect (G_OBJECT (treeview), "drag-data-received",
|
||||
G_CALLBACK (userlist_dnd_drop), treeview);
|
||||
|
||||
g_signal_connect (G_OBJECT (treeview), "button_press_event",
|
||||
g_signal_connect (G_OBJECT (treeview), "button-press-event",
|
||||
G_CALLBACK (userlist_click_cb), 0);
|
||||
g_signal_connect (G_OBJECT (treeview), "key_press_event",
|
||||
g_signal_connect (G_OBJECT (treeview), "key-press-event",
|
||||
G_CALLBACK (userlist_key_cb), 0);
|
||||
|
||||
/* tree/chanview DND */
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_begin",
|
||||
g_signal_connect (G_OBJECT (treeview), "drag-begin",
|
||||
G_CALLBACK (mg_drag_begin_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_drop",
|
||||
g_signal_connect (G_OBJECT (treeview), "drag-drop",
|
||||
G_CALLBACK (mg_drag_drop_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_motion",
|
||||
g_signal_connect (G_OBJECT (treeview), "drag-motion",
|
||||
G_CALLBACK (mg_drag_motion_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_end",
|
||||
g_signal_connect (G_OBJECT (treeview), "drag-end",
|
||||
G_CALLBACK (mg_drag_end_cb), NULL);
|
||||
|
||||
userlist_add_columns (GTK_TREE_VIEW (treeview));
|
||||
|
||||
@@ -2640,6 +2640,11 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
|
||||
xtext->select_start_x = x;
|
||||
xtext->select_start_y = y;
|
||||
xtext->select_start_adj = xtext_adj_get_value (xtext->adj);
|
||||
if (xtext->buffer->last_ent_start)
|
||||
{
|
||||
gtk_xtext_unselect (xtext);
|
||||
xtext->mark_stamp = FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -49,14 +49,14 @@ GType gtk_xtext_get_type (void);
|
||||
#define ATTR_UNDERLINE '\037'
|
||||
|
||||
/* these match palette.h */
|
||||
#define XTEXT_MIRC_COLS 32
|
||||
#define XTEXT_COLS 37 /* 32 plus 5 for extra stuff below */
|
||||
#define XTEXT_MARK_FG 32 /* for marking text */
|
||||
#define XTEXT_MARK_BG 33
|
||||
#define XTEXT_FG 34
|
||||
#define XTEXT_BG 35
|
||||
#define XTEXT_MARKER 36 /* for marker line */
|
||||
#define XTEXT_MAX_COLOR 41
|
||||
#define XTEXT_MIRC_COLS 99
|
||||
#define XTEXT_COLS 104 /* 99 plus 5 for extra stuff below */
|
||||
#define XTEXT_MARK_FG 99 /* for marking text */
|
||||
#define XTEXT_MARK_BG 100
|
||||
#define XTEXT_FG 101
|
||||
#define XTEXT_BG 102
|
||||
#define XTEXT_MARKER 103 /* for marker line */
|
||||
#define XTEXT_MAX_COLOR 98
|
||||
typedef struct textentry textentry;
|
||||
|
||||
/*
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#include <glib/gwin32.h>
|
||||
#define STDIN_FILENO 0
|
||||
#define STDOUT_FILENO 1
|
||||
#else
|
||||
@@ -478,9 +479,28 @@ fe_args (int argc, char *argv[])
|
||||
{
|
||||
GError *error = NULL;
|
||||
GOptionContext *context;
|
||||
#ifdef WIN32
|
||||
char *base_path = NULL;
|
||||
char *locale_path = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NLS
|
||||
#ifdef WIN32
|
||||
base_path = g_win32_get_package_installation_directory_of_module (NULL);
|
||||
if (base_path)
|
||||
{
|
||||
locale_path = g_build_filename (base_path, "share", "locale", NULL);
|
||||
bindtextdomain (GETTEXT_PACKAGE, locale_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
||||
}
|
||||
g_free (locale_path);
|
||||
g_free (base_path);
|
||||
#else
|
||||
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
||||
#endif
|
||||
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
||||
textdomain (GETTEXT_PACKAGE);
|
||||
#endif
|
||||
@@ -581,6 +601,19 @@ fe_exit (void)
|
||||
g_main_loop_quit(main_loop);
|
||||
}
|
||||
|
||||
fe_preferences_save_result
|
||||
fe_preferences_persistence_save_all (void)
|
||||
{
|
||||
fe_preferences_save_result result;
|
||||
|
||||
result.success = save_config () != 0;
|
||||
result.partial_failure = FALSE;
|
||||
result.config_failed = !result.success;
|
||||
result.theme_failed = FALSE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
fe_new_server (struct server *serv)
|
||||
{
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
;#define APPARCH "x64"
|
||||
;#define PROJECTDIR "C:\...\zoitechat\win32\installer\"
|
||||
|
||||
;http://mitrich.net23.net/?/inno-download-plugin.html
|
||||
#ifexist "idp.iss"
|
||||
#define USE_INNO_DOWNLOAD_PLUGIN
|
||||
#include <idp.iss>
|
||||
#endif
|
||||
|
||||
[Setup]
|
||||
AppName=ZoiteChat
|
||||
AppVersion={#APPVER}
|
||||
@@ -65,8 +59,10 @@ Name: "plugins\upd"; Description: "Update Checker"; Types: normal custom; Flags:
|
||||
Name: "plugins\winamp"; Description: "Winamp"; Types: custom; Flags: disablenouninstallwarning
|
||||
Name: "langs"; Description: "Language Interfaces"; Types: custom; Flags: disablenouninstallwarning
|
||||
Name: "langs\lua"; Description: "Lua"; Types: normal custom; Flags: disablenouninstallwarning
|
||||
Name: "langs\perl"; Description: "Perl (requires Perl 5.20)"; Types: custom; Flags: disablenouninstallwarning
|
||||
Name: "langs\python"; Description: "Python (requires Python 3.14.2)"; Types: custom; Flags: disablenouninstallwarning
|
||||
Name: "langs\perl"; Description: "Perl (requires Perl 5.42)"; Types: custom; Flags: disablenouninstallwarning
|
||||
Name: "langs\python"; Description: "Python (requires Python 3.14.3)"; Types: custom; Flags: disablenouninstallwarning
|
||||
Name: "deps"; Description: "Dependencies"; Types: custom; Flags: disablenouninstallwarning
|
||||
Name: "deps\vcredist2015"; Description: "Visual C++ Redistributable 2015"; Types: normal minimal custom; Flags: disablenouninstallwarning
|
||||
|
||||
[Tasks]
|
||||
Name: portable; Description: "Yes"; GroupDescription: "Portable Mode: Stores configuration files within install directory for portable drives."; Flags: unchecked
|
||||
@@ -87,7 +83,7 @@ Root: HKCR; Subkey: "ZoiteChat.Theme\shell\open\command"; ValueType: string; Val
|
||||
[Run]
|
||||
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}\vcredist.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Components: deps\vcredist2015; 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
|
||||
@@ -202,6 +198,13 @@ BeveledLabel= {#APPNAM}
|
||||
|
||||
[Code]
|
||||
#ifndef USE_INNO_DOWNLOAD_PLUGIN
|
||||
var
|
||||
FallbackDownloadUrls: array of String;
|
||||
FallbackDownloadFiles: array of String;
|
||||
|
||||
function URLDownloadToFile(Caller: Integer; URL: String; FileName: String; Reserved: Integer; StatusCB: Integer): Integer;
|
||||
external 'URLDownloadToFileW@urlmon.dll stdcall delayload';
|
||||
|
||||
// The Inno Download Plugin isn't always installed in CI environments.
|
||||
// Provide no-op fallback procedures so installer compilation still succeeds.
|
||||
procedure idpDownloadAfter(PageID: Integer);
|
||||
@@ -210,10 +213,40 @@ end;
|
||||
|
||||
procedure idpClearFiles;
|
||||
begin
|
||||
SetArrayLength(FallbackDownloadUrls, 0);
|
||||
SetArrayLength(FallbackDownloadFiles, 0);
|
||||
end;
|
||||
|
||||
procedure idpAddFile(URL: String; Filename: String);
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
I := GetArrayLength(FallbackDownloadUrls);
|
||||
SetArrayLength(FallbackDownloadUrls, I + 1);
|
||||
SetArrayLength(FallbackDownloadFiles, I + 1);
|
||||
FallbackDownloadUrls[I] := URL;
|
||||
FallbackDownloadFiles[I] := Filename;
|
||||
end;
|
||||
|
||||
function idpDownloadQueuedFiles(): Boolean;
|
||||
var
|
||||
I: Integer;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
for I := 0 to GetArrayLength(FallbackDownloadUrls) - 1 do
|
||||
begin
|
||||
if not FileExists(FallbackDownloadFiles[I]) then
|
||||
begin
|
||||
ResultCode := URLDownloadToFile(0, FallbackDownloadUrls[I], FallbackDownloadFiles[I], 0, 0);
|
||||
if ResultCode <> 0 then
|
||||
begin
|
||||
MsgBox('Unable to download required installer dependency:' + #13#10 + FallbackDownloadUrls[I], mbError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
#endif
|
||||
|
||||
@@ -245,8 +278,30 @@ end;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function CheckVCInstall(): Boolean;
|
||||
var
|
||||
Installed: Cardinal;
|
||||
begin
|
||||
Result := FileExists(GetSysDir() + 'vcruntime140.dll');;
|
||||
Result := False;
|
||||
if RegQueryDWordValue(HKLM64, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Installed', Installed) then
|
||||
Result := Installed = 1
|
||||
else if RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Installed', Installed) then
|
||||
Result := Installed = 1;
|
||||
end;
|
||||
|
||||
procedure UpdateVCRedistComponentState;
|
||||
var
|
||||
I: Integer;
|
||||
Installed: Boolean;
|
||||
begin
|
||||
Installed := CheckVCInstall();
|
||||
for I := 0 to WizardForm.ComponentsList.Items.Count - 1 do
|
||||
begin
|
||||
if WizardForm.ComponentsList.ItemCaption[I] = 'Visual C++ Redistributable 2015' then
|
||||
begin
|
||||
WizardForm.ComponentsList.Checked[I] := not Installed;
|
||||
WizardForm.ComponentsList.ItemEnabled[I] := not Installed;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
@@ -274,6 +329,9 @@ var
|
||||
PY3: String;
|
||||
SPELL: String;
|
||||
begin
|
||||
if (CurPageID = wpSelectComponents) then
|
||||
UpdateVCRedistComponentState();
|
||||
|
||||
if(CurPageID = wpReady) then
|
||||
begin
|
||||
idpClearFiles;
|
||||
@@ -281,12 +339,12 @@ begin
|
||||
if not IsTaskSelected('portable') then
|
||||
begin
|
||||
|
||||
REDIST := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/vcredist_2015_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';
|
||||
REDIST := 'https://aka.ms/vs/17/release/vc_redist.x64.exe';
|
||||
PERL := 'https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_54201_64bit/strawberry-perl-5.42.0.1-64bit.msi';
|
||||
PY3 := 'https://www.python.org/ftp/python/3.14.3/python-3.14.3-amd64.exe';
|
||||
SPELL := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/ZoiteChat.Spelling.Dictionaries.r2.exe';
|
||||
|
||||
if not CheckVCInstall() then
|
||||
if IsComponentSelected('deps\vcredist2015') and not CheckVCInstall() then
|
||||
idpAddFile(REDIST, ExpandConstant('{tmp}\vcredist.exe'));
|
||||
|
||||
if IsComponentSelected('spell') and not CheckSpellInstall() then
|
||||
@@ -294,7 +352,7 @@ begin
|
||||
|
||||
if not WizardSilent() then
|
||||
begin
|
||||
if IsComponentSelected('langs\perl') and not CheckDLL('perl520.dll') then
|
||||
if IsComponentSelected('langs\perl') and not CheckDLL('perl542.dll') then
|
||||
begin
|
||||
idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi'))
|
||||
end;
|
||||
@@ -311,6 +369,17 @@ end;
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function NextButtonClick(CurPageID: Integer): Boolean;
|
||||
begin
|
||||
Result := True;
|
||||
|
||||
#ifndef USE_INNO_DOWNLOAD_PLUGIN
|
||||
if CurPageID = wpReady then
|
||||
if not idpDownloadQueuedFiles() then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
#endif
|
||||
|
||||
if (CurPageID = wpSelectTasks) then
|
||||
if (WizardForm.TasksList.Checked[1] = True) then
|
||||
if (WizardDirValue() = ExpandConstant('{pf64}\ZoiteChat')) then
|
||||
@@ -319,7 +388,13 @@ begin
|
||||
MsgBox('Portable mode is only intended for use on portable drives and has been disabled.', mbInformation, MB_OK)
|
||||
end;
|
||||
|
||||
Result := True; // Always continue
|
||||
if CurPageID = wpReady then
|
||||
if IsComponentSelected('deps\vcredist2015') and not CheckVCInstall() and not FileExists(ExpandConstant('{tmp}\vcredist.exe')) then
|
||||
begin
|
||||
MsgBox('Visual C++ Redistributable could not be downloaded. Please retry setup or install it manually and rerun setup.', mbError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.18.0~pre3
|
||||
2.18.0~pre5
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<YourDepsPath>c:\gtk-build\gtk</YourDepsPath>
|
||||
<YourGendefPath>c:\gtk-build\gendef</YourGendefPath>
|
||||
<YourPerlPath>c:\gtk-build\perl-5.20</YourPerlPath>
|
||||
<YourPerlPath>c:\gtk-build\perl-5.42.0.1</YourPerlPath>
|
||||
<YourPython3Path>c:\gtk-build\python-3.14</YourPython3Path>
|
||||
<YourWinSparklePath>c:\gtk-build\WinSparkle</YourWinSparklePath>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<GendefPath>$(YourGendefPath)</GendefPath>
|
||||
<WinSparklePath>$(YourWinSparklePath)\$(ZoiteChatPlatform)</WinSparklePath>
|
||||
<PerlPath>$(YourPerlPath)\$(ZoiteChatPlatform)</PerlPath>
|
||||
<PerlLib>perl520</PerlLib>
|
||||
<PerlLib>perl542</PerlLib>
|
||||
|
||||
<Python3Path>$(YourPython3Path)\$(ZoiteChatPlatform)</Python3Path>
|
||||
<Python3Lib>python314</Python3Lib>
|
||||
|
||||