mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-03-17 11:10:18 +00:00
Compare commits
13 Commits
zoitechat-
...
save-fixes
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e808c57b4 | |||
| 896a761e24 | |||
| 96af9bdde6 | |||
| e90f0188c1 | |||
| 0ab7eb7207 | |||
| 81dcdbe648 | |||
| f4090cb0e5 | |||
| 0f5dfb147e | |||
| 0bcd369426 | |||
|
|
b316d4a281 | ||
| 86ab0135ce | |||
| 2d42d4e181 | |||
| 23e6313e2f |
21
.github/workflows/windows-build.yml
vendored
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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1009,15 +1009,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 +1064,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);
|
||||
|
||||
@@ -292,7 +292,13 @@ match_host (const char *word, int *start, int *end)
|
||||
static gboolean
|
||||
match_host6 (const char *word, int *start, int *end)
|
||||
{
|
||||
return regex_match (re_host6 (), word, start, end);
|
||||
if (!regex_match (re_host6 (), word, start, end))
|
||||
return FALSE;
|
||||
|
||||
if (word[*start] != '[')
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -373,40 +379,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 ")?"
|
||||
|
||||
|
||||
@@ -1104,7 +1104,8 @@ zoitechat_exit (void)
|
||||
plugin_kill_all ();
|
||||
fe_cleanup ();
|
||||
|
||||
save_config ();
|
||||
if (!save_config ())
|
||||
g_printerr ("Could not save zoitechat.conf.\n");
|
||||
if (prefs.save_pevents)
|
||||
{
|
||||
pevent_save (NULL);
|
||||
|
||||
@@ -508,36 +508,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 +650,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 +673,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 +694,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 +751,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 ();
|
||||
@@ -831,7 +895,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 +907,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;
|
||||
@@ -879,6 +945,7 @@ chanlist_opengui (server *serv, int do_refresh)
|
||||
chanlist_add_column (view, COL_USERS, 50, _("Users"), TRUE);
|
||||
chanlist_add_column (view, COL_TOPIC, 50, _("Topic"), FALSE);
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1339,22 +1339,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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -670,7 +746,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 +837,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 +849,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,17 +887,46 @@ mg_windowstate_cb (GtkWindow *wid, GdkEventWindowState *event, gpointer userdata
|
||||
static gboolean
|
||||
mg_configure_cb (GtkWidget *wid, GdkEventConfigure *event, session *sess)
|
||||
{
|
||||
gboolean changed = FALSE;
|
||||
|
||||
if (sess == NULL) /* for the main_window */
|
||||
{
|
||||
if (mg_gui)
|
||||
{
|
||||
if (prefs.hex_gui_win_save && !prefs.hex_gui_win_state && !prefs.hex_gui_win_fullscreen)
|
||||
{
|
||||
int win_left;
|
||||
int win_top;
|
||||
int win_width;
|
||||
int win_height;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -808,13 +935,46 @@ mg_configure_cb (GtkWidget *wid, GdkEventConfigure *event, session *sess)
|
||||
{
|
||||
if (sess->type == SESS_DIALOG && prefs.hex_gui_win_save)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1063,9 +1223,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)
|
||||
{
|
||||
@@ -2179,12 +2344,13 @@ mg_topic_key_press_cb (GtkWidget *entry, GdkEventKey *event, gpointer userdata)
|
||||
}
|
||||
|
||||
static char *
|
||||
mg_topic_get_word_at_pos (GtkWidget *entry, gdouble event_x, gdouble event_y)
|
||||
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;
|
||||
|
||||
@@ -2194,6 +2360,7 @@ mg_topic_get_word_at_pos (GtkWidget *entry, gdouble event_x, gdouble event_y)
|
||||
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))
|
||||
{
|
||||
@@ -2221,6 +2388,16 @@ mg_topic_get_word_at_pos (GtkWidget *entry, gdouble event_x, gdouble event_y)
|
||||
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);
|
||||
}
|
||||
@@ -2243,25 +2420,34 @@ mg_topic_set_cursor (GtkWidget *entry, GdkCursorType cursor_type)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mg_topic_word_is_clickable (const char *word)
|
||||
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;
|
||||
|
||||
return url_check_word (word) != 0;
|
||||
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 = mg_topic_get_word_at_pos (entry, event->x, event->y);
|
||||
word_is_clickable = mg_topic_word_is_clickable (word);
|
||||
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
|
||||
@@ -2282,17 +2468,19 @@ 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 = mg_topic_get_word_at_pos (entry, event->x, event->y);
|
||||
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))
|
||||
if (mg_topic_word_is_clickable (word, word_pos))
|
||||
{
|
||||
url_last (&start, &end);
|
||||
word[end] = 0;
|
||||
@@ -2311,6 +2499,8 @@ 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 */
|
||||
@@ -3115,6 +3305,8 @@ mg_create_userlist (session_gui *gui, GtkWidget *box)
|
||||
gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, GUI_SPACING);
|
||||
|
||||
gui->namelistinfo = gtk_label_new (NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (gui->namelistinfo), 0.0f);
|
||||
gtk_widget_set_halign (gui->namelistinfo, GTK_ALIGN_START);
|
||||
gtk_container_add (GTK_CONTAINER (frame), gui->namelistinfo);
|
||||
|
||||
gui->user_tree = ulist = userlist_create (vbox);
|
||||
@@ -3197,7 +3389,7 @@ mg_create_center (session *sess, session_gui *gui, GtkWidget *box)
|
||||
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, FALSE);
|
||||
gtk_paned_pack2 (GTK_PANED (gui->hpane_right), gui->vpane_right, FALSE, TRUE);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (box), gui->hpane_left, TRUE, TRUE, 0);
|
||||
|
||||
@@ -3653,7 +3845,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
|
||||
|
||||
@@ -28,6 +28,7 @@ zoitechat_gtk_sources = [
|
||||
'maingui.c',
|
||||
'notifygui.c',
|
||||
'pixmaps.c',
|
||||
'preferences-persistence.c',
|
||||
'plugin-tray.c',
|
||||
'plugin-notification.c',
|
||||
'rawlog.c',
|
||||
|
||||
66
src/fe-gtk/preferences-persistence.c
Normal file
66
src/fe-gtk/preferences-persistence.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#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;
|
||||
}
|
||||
18
src/fe-gtk/preferences-persistence.h
Normal file
18
src/fe-gtk/preferences-persistence.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#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
|
||||
@@ -1002,7 +1002,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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -2191,10 +2192,23 @@ setup_apply (struct zoitechatprefs *pr)
|
||||
static void
|
||||
setup_ok_cb (GtkWidget *but, GtkWidget *win)
|
||||
{
|
||||
PreferencesPersistenceResult save_result;
|
||||
char buffer[192];
|
||||
|
||||
gtk_widget_destroy (win);
|
||||
setup_apply (&setup_prefs);
|
||||
save_config ();
|
||||
theme_manager_save_preferences ();
|
||||
save_result = preferences_persistence_save_all ();
|
||||
if (save_result.success)
|
||||
return;
|
||||
|
||||
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 *
|
||||
|
||||
@@ -964,6 +964,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) {
|
||||
@@ -1161,13 +1164,34 @@ check_color:
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
attr_list_has_attrs (PangoAttrList *attrs)
|
||||
{
|
||||
PangoAttrIterator *it;
|
||||
GSList *list;
|
||||
gboolean has = FALSE;
|
||||
|
||||
if (!attrs)
|
||||
return FALSE;
|
||||
|
||||
it = pango_attr_list_get_iterator (attrs);
|
||||
if (!it)
|
||||
return FALSE;
|
||||
|
||||
list = pango_attr_iterator_get_attrs (it);
|
||||
has = (list != NULL);
|
||||
g_slist_free_full (list, (GDestroyNotify) pango_attribute_destroy);
|
||||
pango_attr_iterator_destroy (it);
|
||||
|
||||
return has;
|
||||
}
|
||||
|
||||
static void
|
||||
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 +1220,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 +1236,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,9 +57,10 @@ theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
gboolean
|
||||
theme_runtime_save (void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -119,8 +119,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)
|
||||
|
||||
@@ -112,8 +112,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)
|
||||
|
||||
@@ -122,8 +122,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)
|
||||
|
||||
@@ -66,9 +66,10 @@ theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed)
|
||||
*palette_changed = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gboolean
|
||||
theme_manager_save_preferences (void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ThemePaletteBehavior
|
||||
|
||||
@@ -284,10 +284,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
|
||||
|
||||
@@ -43,7 +43,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);
|
||||
|
||||
@@ -70,6 +70,9 @@ typedef struct
|
||||
|
||||
#define COLOR_MANAGER_RESPONSE_RESET 1
|
||||
|
||||
static void
|
||||
theme_preferences_show_import_error (GtkWidget *button, const char *message);
|
||||
|
||||
static void
|
||||
theme_preferences_manager_row_free (gpointer data)
|
||||
{
|
||||
@@ -735,8 +738,9 @@ theme_preferences_manage_colors_cb (GtkWidget *button, gpointer user_data)
|
||||
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 != old_changed &&
|
||||
!theme_manager_save_preferences ())
|
||||
theme_preferences_show_import_error (button, _("Could not save colors.conf."));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -912,8 +916,9 @@ theme_preferences_import_colors_conf_cb (GtkWidget *button, gpointer user_data)
|
||||
|
||||
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 != old_changed &&
|
||||
!theme_manager_save_preferences ())
|
||||
theme_preferences_show_import_error (button, _("Could not save colors.conf."));
|
||||
|
||||
g_free (cfg);
|
||||
g_free (path);
|
||||
@@ -1392,26 +1397,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 ();
|
||||
@@ -1426,43 +1439,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
|
||||
|
||||
@@ -276,7 +276,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 +284,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -449,12 +449,11 @@ theme_runtime_load (void)
|
||||
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 +484,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 +503,130 @@ 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;
|
||||
const char *temp_name;
|
||||
|
||||
if (!temp_path)
|
||||
return FALSE;
|
||||
|
||||
temp_name = "colors.conf.new";
|
||||
fh = zoitechat_open_file (temp_name, O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh == -1)
|
||||
return FALSE;
|
||||
|
||||
if (!theme_runtime_save_to_fd (fh))
|
||||
{
|
||||
close (fh);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (close (fh) == -1)
|
||||
return FALSE;
|
||||
|
||||
*temp_path = g_strdup (temp_name);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_save_finalize (const char *temp_path)
|
||||
{
|
||||
int src_fh;
|
||||
int dst_fh;
|
||||
char buffer[4096];
|
||||
ssize_t read_len;
|
||||
|
||||
if (!temp_path)
|
||||
return FALSE;
|
||||
|
||||
src_fh = zoitechat_open_file (temp_path, O_RDONLY, 0, XOF_DOMODE);
|
||||
if (src_fh == -1)
|
||||
return FALSE;
|
||||
|
||||
dst_fh = zoitechat_open_file ("colors.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (dst_fh == -1)
|
||||
{
|
||||
close (src_fh);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while ((read_len = read (src_fh, buffer, sizeof (buffer))) > 0)
|
||||
{
|
||||
ssize_t offset;
|
||||
|
||||
offset = 0;
|
||||
while (offset < read_len)
|
||||
{
|
||||
ssize_t written;
|
||||
|
||||
written = write (dst_fh, buffer + offset, (size_t) (read_len - offset));
|
||||
if (written <= 0)
|
||||
{
|
||||
close (src_fh);
|
||||
close (dst_fh);
|
||||
return FALSE;
|
||||
}
|
||||
offset += written;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_len < 0)
|
||||
{
|
||||
close (src_fh);
|
||||
close (dst_fh);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (close (src_fh) == -1)
|
||||
{
|
||||
close (dst_fh);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (close (dst_fh) == -1)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_save_discard (const char *temp_path)
|
||||
{
|
||||
int fh;
|
||||
|
||||
if (!temp_path)
|
||||
return;
|
||||
|
||||
fh = zoitechat_open_file (temp_path, O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh != -1)
|
||||
close (fh);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -18,7 +18,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);
|
||||
|
||||
@@ -57,16 +57,21 @@ 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)
|
||||
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) + 16;
|
||||
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_tree, width, -1);
|
||||
gtk_widget_set_size_request (sess->gui->user_box, width, -1);
|
||||
}
|
||||
|
||||
@@ -682,6 +687,7 @@ static void
|
||||
userlist_add_columns (GtkTreeView * treeview)
|
||||
{
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *column;
|
||||
|
||||
/* icon column */
|
||||
renderer = gtk_cell_renderer_pixbuf_new ();
|
||||
@@ -690,15 +696,22 @@ userlist_add_columns (GtkTreeView * treeview)
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, NULL, renderer,
|
||||
"pixbuf", 0, 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 */
|
||||
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", 1, THEME_GTK_FOREGROUND_PROPERTY, 4, NULL);
|
||||
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);
|
||||
|
||||
if (prefs.hex_gui_ulist_show_hosts)
|
||||
{
|
||||
@@ -706,10 +719,15 @@ 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,8 @@ 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
|
||||
|
||||
[Tasks]
|
||||
Name: portable; Description: "Yes"; GroupDescription: "Portable Mode: Stores configuration files within install directory for portable drives."; Flags: unchecked
|
||||
@@ -282,8 +276,8 @@ begin
|
||||
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';
|
||||
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
|
||||
@@ -294,7 +288,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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user