7 Commits

11 changed files with 288 additions and 141 deletions

View File

@@ -29,7 +29,7 @@ jobs:
- uses: actions/setup-python@v6 - uses: actions/setup-python@v6
with: with:
python-version: '3.14.2' python-version: '3.14.3'
architecture: ${{ matrix.arch }} architecture: ${{ matrix.arch }}
- name: Install Dependencies - 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 & 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 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 & 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 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()" 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()" 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) { 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 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 & 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 & 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 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.20\${{ matrix.platform }} & 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.42.0.1\${{ matrix.platform }}
$pyRoot = $env:pythonLocation $pyRoot = $env:pythonLocation
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" } 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 New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
$target = Join-Path $pyDir "${{ matrix.platform }}" $target = Join-Path $pyDir "${{ matrix.platform }}"
if (Test-Path $target) { Remove-Item $target -Recurse -Force } if (Test-Path $target) { Remove-Item $target -Recurse -Force }
@@ -120,7 +117,7 @@ jobs:
run: | run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" 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" ( if not exist "%PYTHON_DIR%\libs\python314.lib" (
echo Missing %PYTHON_DIR%\libs\python314.lib echo Missing %PYTHON_DIR%\libs\python314.lib
dir "%PYTHON_DIR%\libs" dir "%PYTHON_DIR%\libs"

View File

@@ -292,7 +292,13 @@ match_host (const char *word, int *start, int *end)
static gboolean static gboolean
match_host6 (const char *word, int *start, int *end) 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 static gboolean
@@ -373,40 +379,64 @@ url_last (int *lstart, int *lend)
} }
static gboolean 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)
g_regex_match (re, word, 0, &gmi);
if (!g_match_info_matches (gmi))
{ {
g_match_info_free (gmi); char prev = word[start - 1];
if (g_ascii_isalnum ((guchar)prev) || prev == '_' || prev == '-')
return FALSE; return FALSE;
} }
if (word[end] != '\0')
{
char next = word[end];
if (g_ascii_isalnum ((guchar)next) || next == '_')
return FALSE;
}
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)) while (g_match_info_matches (gmi))
{ {
g_match_info_fetch_pos (gmi, 0, start, end); 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_next (gmi, NULL);
} }
g_match_info_free (gmi); g_match_info_free (gmi);
return TRUE; return found;
} }
/* Miscellaneous description --- */ /* Miscellaneous description --- */
#define DOMAIN "[_\\pL\\pN\\pS][-_\\pL\\pN\\pS]*(\\.[-_\\pL\\pN\\pS]+)*" #define DOMAIN_LABEL "[\\pL\\pN](?:[-\\pL\\pN]{0,61}[\\pL\\pN])?"
#define TLD "\\.[\\pL][-\\pL\\pN]*[\\pL]" #define DOMAIN DOMAIN_LABEL "(\\." DOMAIN_LABEL ")*"
#define IPADDR "[0-9]{1,3}(\\.[0-9]{1,3}){3}" #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 IPV6GROUP "([0-9a-f]{0,4})"
#define IPV6ADDR "((" IPV6GROUP "(:" IPV6GROUP "){7})" \ #define IPV6ADDR "((" IPV6GROUP "(:" IPV6GROUP "){7})" \
"|(" IPV6GROUP "(:" IPV6GROUP ")*:(:" IPV6GROUP ")+))" /* with :: compression */ "|(" IPV6GROUP "(:" IPV6GROUP ")*:(:" IPV6GROUP ")+))" /* with :: compression */
#define HOST "(" DOMAIN TLD "|" IPADDR "|" IPV6ADDR ")" #define HOST "(" DOMAIN TLD "|" IPADDR "|" IPV6ADDR ")"
/* In urls the IPv6 must be enclosed in square brackets */ /* In urls the IPv6 must be enclosed in square brackets */
#define HOST_URL "(" DOMAIN TLD "|" IPADDR "|" "\\[" IPV6ADDR "\\]" ")" #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 PORT "(:[1-9][0-9]{0,4})"
#define OPT_PORT "(" PORT ")?" #define OPT_PORT "(" PORT ")?"

View File

@@ -508,38 +508,74 @@ chanlist_match_topic_button_toggled (GtkWidget * wid, server *serv)
serv->gui->chanlist_match_wants_topic = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)); 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 * static char *
chanlist_get_selected (server *serv, gboolean get_topic) chanlist_get_selected (server *serv, gboolean get_topic)
{ {
char *chan; GSList *selection = chanlist_get_selection (serv, get_topic ? COL_TOPIC : COL_CHANNEL);
GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (serv->gui->chanlist_list)); char *value;
GtkTreeModel *model;
GtkTreeIter iter;
if (!gtk_tree_selection_get_selected (sel, &model, &iter)) if (!selection)
return NULL; return NULL;
gtk_tree_model_get (model, &iter, get_topic ? COL_TOPIC : COL_CHANNEL, &chan, -1); value = selection->data;
return chan; selection->data = NULL;
g_slist_free_full (selection, g_free);
return value;
} }
static void static void
chanlist_join (GtkWidget * wid, server *serv) chanlist_join (GtkWidget * wid, server *serv)
{ {
char tbuf[CHANLEN + 6]; char tbuf[CHANLEN + 6];
char *chan = chanlist_get_selected (serv, FALSE); GSList *selection;
if (chan) 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); g_snprintf (tbuf, sizeof (tbuf), "join %s", chan);
handle_command (serv->server_session, tbuf, FALSE); handle_command (serv->server_session, tbuf, FALSE);
} else joined = TRUE;
gdk_display_beep (gdk_display_get_default ());
g_free (chan);
} }
} }
if (!joined && selection)
gdk_display_beep (gdk_display_get_default ());
g_slist_free_full (selection, g_free);
}
static void static void
chanlist_filereq_done (server *serv, char *file) chanlist_filereq_done (server *serv, char *file)
{ {
@@ -656,23 +692,47 @@ chanlist_menu_destroy (GtkWidget *menu, gpointer userdata)
static void static void
chanlist_copychannel (GtkWidget *item, server *serv) chanlist_copychannel (GtkWidget *item, server *serv)
{ {
char *chan = chanlist_get_selected (serv, FALSE); GSList *selection = chanlist_get_selection (serv, COL_CHANNEL);
if (chan) 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); if (text->len)
g_free (chan); 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 static void
chanlist_copytopic (GtkWidget *item, server *serv) chanlist_copytopic (GtkWidget *item, server *serv)
{ {
char *topic = chanlist_get_selected (serv, TRUE); GSList *selection = chanlist_get_selection (serv, COL_TOPIC);
if (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); if (text->len)
g_free (topic); 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 static gboolean
@@ -689,10 +749,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)) if (!gtk_tree_view_get_path_at_pos (tree, event->x, event->y, &path, 0, 0, 0))
return FALSE; return FALSE;
/* select what they right-clicked on */
sel = gtk_tree_view_get_selection (tree); sel = gtk_tree_view_get_selection (tree);
if (!gtk_tree_selection_path_is_selected (sel, path))
{
gtk_tree_selection_unselect_all (sel); gtk_tree_selection_unselect_all (sel);
gtk_tree_selection_select_path (sel, path); gtk_tree_selection_select_path (sel, path);
}
gtk_tree_path_free (path); gtk_tree_path_free (path);
menu = gtk_menu_new (); menu = gtk_menu_new ();
@@ -879,6 +941,7 @@ chanlist_opengui (server *serv, int do_refresh)
chanlist_add_column (view, COL_USERS, 50, _("Users"), TRUE); chanlist_add_column (view, COL_USERS, 50, _("Users"), TRUE);
chanlist_add_column (view, COL_TOPIC, 50, _("Topic"), FALSE); 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_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 :( */ /* this is a speed up, but no horizontal scrollbar :( */
/*gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);*/ /*gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);*/
gtk_widget_show (view); gtk_widget_show (view);

View File

@@ -131,6 +131,7 @@ cv_tree_init (chanview *cv)
GTK_SHADOW_IN); GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win), gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); 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_container_add (GTK_CONTAINER (cv->box), win);
gtk_widget_show (win); gtk_widget_show (win);
@@ -165,7 +166,6 @@ cv_tree_init (chanview *cv)
gtk_container_add (GTK_CONTAINER (win), view); gtk_container_add (GTK_CONTAINER (win), view);
col = gtk_tree_view_column_new(); col = gtk_tree_view_column_new();
/* icon column */
if (cv->use_icons) if (cv->use_icons)
{ {
renderer = gtk_cell_renderer_pixbuf_new (); renderer = gtk_cell_renderer_pixbuf_new ();
@@ -176,10 +176,10 @@ cv_tree_init (chanview *cv)
gtk_tree_view_column_set_attributes (col, renderer, "pixbuf", COL_PIXBUF, NULL); gtk_tree_view_column_set_attributes (col, renderer, "pixbuf", COL_PIXBUF, NULL);
} }
/* main column */
renderer = gtk_cell_renderer_text_new (); renderer = gtk_cell_renderer_text_new ();
if (prefs.hex_gui_compact) if (prefs.hex_gui_compact)
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL); 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_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, gtk_tree_view_column_set_attributes (col, renderer,
@@ -188,7 +188,10 @@ cv_tree_init (chanview *cv)
"underline", COL_UNDERLINE, "underline", COL_UNDERLINE,
NULL); NULL);
gtk_tree_view_column_set_expand (col, TRUE); gtk_tree_view_column_set_expand (col, TRUE);
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_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))), g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (view))),
"changed", G_CALLBACK (cv_tree_sel_cb), cv); "changed", G_CALLBACK (cv_tree_sel_cb), cv);

View File

@@ -1339,22 +1339,61 @@ fe_open_url_inner (const char *url)
g_free (escaped_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;
}
void void
fe_open_url (const char *url) fe_open_url (const char *url)
{ {
int url_type = url_check_word (url); int url_type = url_check_word (url);
char *uri; char *uri;
char *path;
char *path_uri;
if (fe_open_url_is_local_path (url))
{
path = g_canonicalize_filename (url, NULL);
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:// */ /* gvfs likes file:// */
if (url_type == WORD_PATH) if (url_type == WORD_PATH)
{ {
#ifndef WIN32 path = g_canonicalize_filename (url, NULL);
uri = g_strconcat ("file://", url, NULL); path_uri = g_filename_to_uri (path, NULL, NULL);
fe_open_url_inner (uri); g_free (path);
g_free (uri); if (path_uri)
#else {
fe_open_url_inner (path_uri);
g_free (path_uri);
}
else
{
fe_open_url_inner (url); fe_open_url_inner (url);
#endif }
} }
/* IPv6 addr. Add http:// */ /* IPv6 addr. Add http:// */
else if (url_type == WORD_HOST6) else if (url_type == WORD_HOST6)

View File

@@ -670,7 +670,7 @@ mg_spellcheck_cb (SexySpellEntry *entry, gchar *word, gpointer data)
{ {
/* This can cause freezes on long words, nicks arn't very long anyway. */ /* This can cause freezes on long words, nicks arn't very long anyway. */
if (strlen (word) > 20) if (strlen (word) > 20)
return TRUE; return FALSE;
/* Ignore anything we think is a valid url */ /* Ignore anything we think is a valid url */
if (url_check_word (word) != 0) if (url_check_word (word) != 0)
@@ -1063,9 +1063,14 @@ mg_userlist_showhide (session *sess, int show)
session_gui *gui = sess->gui; session_gui *gui = sess->gui;
int handle_size; int handle_size;
int right_size; int right_size;
int min_right_size;
GtkAllocation allocation; 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) if (show)
{ {
@@ -3115,6 +3120,8 @@ mg_create_userlist (session_gui *gui, GtkWidget *box)
gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, GUI_SPACING); gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, GUI_SPACING);
gui->namelistinfo = gtk_label_new (NULL); 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); gtk_container_add (GTK_CONTAINER (frame), gui->namelistinfo);
gui->user_tree = ulist = userlist_create (vbox); gui->user_tree = ulist = userlist_create (vbox);
@@ -3197,7 +3204,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_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_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); gtk_box_pack_start (GTK_BOX (box), gui->hpane_left, TRUE, TRUE, 0);

View File

@@ -964,6 +964,9 @@ default_word_check(SexySpellEntry *entry, const gchar *word)
/* We only want to check words */ /* We only want to check words */
return FALSE; return FALSE;
} }
if (g_utf8_strlen (word, -1) > 20)
return FALSE;
for (li = entry->priv->dict_list; li; li = g_slist_next (li)) { for (li = entry->priv->dict_list; li; li = g_slist_next (li)) {
struct EnchantDict *dict = (struct EnchantDict *) li->data; struct EnchantDict *dict = (struct EnchantDict *) li->data;
if (enchant_dict_check(dict, word, strlen(word)) == 0) { 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 static void
sexy_spell_entry_recheck_all(SexySpellEntry *entry) sexy_spell_entry_recheck_all(SexySpellEntry *entry)
{ {
GdkRectangle rect; GdkRectangle rect;
GtkAllocation allocation; GtkAllocation allocation;
GtkWidget *widget = GTK_WIDGET(entry); GtkWidget *widget = GTK_WIDGET(entry);
PangoLayout *layout;
int length, i, text_len; int length, i, text_len;
const char *text; const char *text;
@@ -1196,8 +1220,7 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry)
} }
} }
layout = gtk_entry_get_layout(GTK_ENTRY(entry)); gtk_entry_set_attributes (GTK_ENTRY (entry), attr_list_has_attrs (entry->priv->attr_list) ? entry->priv->attr_list : NULL);
pango_layout_set_attributes(layout, entry->priv->attr_list);
if (gtk_widget_get_realized (GTK_WIDGET(entry))) if (gtk_widget_get_realized (GTK_WIDGET(entry)))
{ {
@@ -1213,13 +1236,6 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry)
static gboolean static gboolean
sexy_spell_entry_draw(GtkWidget *widget, cairo_t *cr) 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); return GTK_WIDGET_CLASS(parent_class)->draw (widget, cr);
} }

View File

@@ -1392,26 +1392,34 @@ theme_preferences_populate_gtk3 (theme_preferences_ui *ui)
g_ptr_array_unref (themes); 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 static void
theme_preferences_gtk3_import_cb (GtkWidget *button, gpointer user_data) theme_preferences_gtk3_import_cb (GtkWidget *button, gpointer user_data)
{ {
theme_preferences_ui *ui = user_data; theme_preferences_ui *ui = user_data;
GtkWidget *dialog; GtkFileChooserNative *dialog;
GtkFileFilter *filter; GtkFileFilter *filter;
GtkWidget *folder_dialog;
char *path; char *path;
char *id = NULL;
GError *error = NULL;
gint response;
(void)button; (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, GTK_FILE_CHOOSER_ACTION_OPEN,
_("Import _Folder"), 1, _("_Import"),
_("_Cancel"), GTK_RESPONSE_CANCEL, _("_Cancel"));
_("_Import"), GTK_RESPONSE_ACCEPT,
NULL);
theme_manager_attach_window (dialog);
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE); gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE);
filter = gtk_file_filter_new (); filter = gtk_file_filter_new ();
@@ -1426,43 +1434,15 @@ theme_preferences_gtk3_import_cb (GtkWidget *button, gpointer user_data)
gtk_file_filter_add_pattern (filter, "*.tbz"); gtk_file_filter_add_pattern (filter, "*.tbz");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
response = gtk_dialog_run (GTK_DIALOG (dialog)); if (gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
if (response == 1)
{ {
gtk_widget_destroy (dialog); g_object_unref (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; 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)); path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
gtk_widget_destroy (dialog); g_object_unref (dialog);
} theme_preferences_gtk3_import_path (ui, path);
else
{
gtk_widget_destroy (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);
} }
static void static void

View File

@@ -57,16 +57,21 @@ userlist_update_min_width (session *sess)
{ {
GtkRequisition minimum; GtkRequisition minimum;
GtkRequisition natural; GtkRequisition natural;
GtkWidget *scrolled_window;
int width; 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; return;
gtk_widget_get_preferred_size (sess->gui->namelistinfo, &minimum, &natural); 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) if (width < 1)
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); gtk_widget_set_size_request (sess->gui->user_box, width, -1);
} }
@@ -682,6 +687,7 @@ static void
userlist_add_columns (GtkTreeView * treeview) userlist_add_columns (GtkTreeView * treeview)
{ {
GtkCellRenderer *renderer; GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
/* icon column */ /* icon column */
renderer = gtk_cell_renderer_pixbuf_new (); 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), gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
-1, NULL, renderer, -1, NULL, renderer,
"pixbuf", 0, NULL); "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 */ /* nick column */
renderer = gtk_cell_renderer_text_new (); renderer = gtk_cell_renderer_text_new ();
if (prefs.hex_gui_compact) if (prefs.hex_gui_compact)
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL); 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_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), gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
-1, NULL, renderer, -1, NULL, renderer,
"text", 1, THEME_GTK_FOREGROUND_PROPERTY, 4, NULL); "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) if (prefs.hex_gui_ulist_show_hosts)
{ {
@@ -706,10 +719,15 @@ userlist_add_columns (GtkTreeView * treeview)
renderer = gtk_cell_renderer_text_new (); renderer = gtk_cell_renderer_text_new ();
if (prefs.hex_gui_compact) if (prefs.hex_gui_compact)
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL); 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_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), gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
-1, NULL, renderer, -1, NULL, renderer,
"text", 2, NULL); "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);
} }
} }

View File

@@ -4,12 +4,6 @@
;#define APPARCH "x64" ;#define APPARCH "x64"
;#define PROJECTDIR "C:\...\zoitechat\win32\installer\" ;#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] [Setup]
AppName=ZoiteChat AppName=ZoiteChat
AppVersion={#APPVER} 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: "plugins\winamp"; Description: "Winamp"; Types: custom; Flags: disablenouninstallwarning
Name: "langs"; Description: "Language Interfaces"; 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\lua"; Description: "Lua"; Types: normal custom; Flags: disablenouninstallwarning
Name: "langs\perl"; Description: "Perl (requires Perl 5.20)"; 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.2)"; Types: custom; Flags: disablenouninstallwarning Name: "langs\python"; Description: "Python (requires Python 3.14.3)"; Types: custom; Flags: disablenouninstallwarning
[Tasks] [Tasks]
Name: portable; Description: "Yes"; GroupDescription: "Portable Mode: Stores configuration files within install directory for portable drives."; Flags: unchecked Name: portable; Description: "Yes"; GroupDescription: "Portable Mode: Stores configuration files within install directory for portable drives."; Flags: unchecked
@@ -282,8 +276,8 @@ begin
begin begin
REDIST := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/vcredist_2015_x64.exe'; 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'; 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.2/python-3.14.2-amd64.exe'; 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'; SPELL := 'https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.16.2/ZoiteChat.Spelling.Dictionaries.r2.exe';
if not CheckVCInstall() then if not CheckVCInstall() then
@@ -294,7 +288,7 @@ begin
if not WizardSilent() then if not WizardSilent() then
begin begin
if IsComponentSelected('langs\perl') and not CheckDLL('perl520.dll') then if IsComponentSelected('langs\perl') and not CheckDLL('perl542.dll') then
begin begin
idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi')) idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi'))
end; end;

View File

@@ -6,7 +6,7 @@
<YourDepsPath>c:\gtk-build\gtk</YourDepsPath> <YourDepsPath>c:\gtk-build\gtk</YourDepsPath>
<YourGendefPath>c:\gtk-build\gendef</YourGendefPath> <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> <YourPython3Path>c:\gtk-build\python-3.14</YourPython3Path>
<YourWinSparklePath>c:\gtk-build\WinSparkle</YourWinSparklePath> <YourWinSparklePath>c:\gtk-build\WinSparkle</YourWinSparklePath>
@@ -24,7 +24,7 @@
<GendefPath>$(YourGendefPath)</GendefPath> <GendefPath>$(YourGendefPath)</GendefPath>
<WinSparklePath>$(YourWinSparklePath)\$(ZoiteChatPlatform)</WinSparklePath> <WinSparklePath>$(YourWinSparklePath)\$(ZoiteChatPlatform)</WinSparklePath>
<PerlPath>$(YourPerlPath)\$(ZoiteChatPlatform)</PerlPath> <PerlPath>$(YourPerlPath)\$(ZoiteChatPlatform)</PerlPath>
<PerlLib>perl520</PerlLib> <PerlLib>perl542</PerlLib>
<Python3Path>$(YourPython3Path)\$(ZoiteChatPlatform)</Python3Path> <Python3Path>$(YourPython3Path)\$(ZoiteChatPlatform)</Python3Path>
<Python3Lib>python314</Python3Lib> <Python3Lib>python314</Python3Lib>