6 Commits

Author SHA1 Message Date
deepend-tildeclub
e2bfc9b7c9 Merge pull request #150 from ZoiteChat/localization-fixes
Fix Windows locale path resolution in both frontends
2026-03-23 22:56:31 -06:00
deepend-tildeclub
5f3f91bf1c Merge pull request #149 from ZoiteChat/vcredist-download-broken
Fail loud on missing VC++ redist, use aka.ms
2026-03-23 22:45:20 -06:00
6527c08e4d Fix Windows locale path resolution in both frontends 2026-03-23 22:33:34 -06:00
a57104469d Fail loud on missing VC++ redist, use aka.ms 2026-03-23 22:32:06 -06:00
94f450ba67 Add clickable emails + irc/ircs URLs 2026-03-23 22:18:08 -06:00
b9e4113c81 ui: clicking anywhere off the selected text will now clear the selection reliably. 2026-03-23 16:31:06 -06:00
5 changed files with 98 additions and 2 deletions

View File

@@ -35,11 +35,13 @@ GTree *url_btree = NULL;
static gboolean regex_match (const GRegex *re, const char *word, static gboolean regex_match (const GRegex *re, const char *word,
int *start, int *end); int *start, int *end);
static const GRegex *re_url (void); static const GRegex *re_url (void);
static const GRegex *re_email (void);
static const GRegex *re_nick (void); static const GRegex *re_nick (void);
static const GRegex *re_channel (void); static const GRegex *re_channel (void);
static gboolean match_nick (const char *word, int *start, int *end); static gboolean match_nick (const char *word, int *start, int *end);
static gboolean match_channel (const char *word, int *start, int *end); static gboolean match_channel (const char *word, int *start, int *end);
static gboolean match_url (const char *word, int *start, int *end); static gboolean match_url (const char *word, int *start, int *end);
static gboolean match_email (const char *word, int *start, int *end);
static int static int
url_free (char *url, void *data) url_free (char *url, void *data)
@@ -107,12 +109,18 @@ url_add (char *urltext, int len)
{ {
char *data; char *data;
int size; int size;
GUri *parsed;
if (!prefs.hex_url_grabber && !prefs.hex_url_logging) if (!prefs.hex_url_grabber && !prefs.hex_url_logging)
{ {
return; return;
} }
if (len <= 0)
{
return;
}
data = g_strndup (urltext, len); data = g_strndup (urltext, len);
if (data[len - 1] == '.') if (data[len - 1] == '.')
@@ -125,6 +133,16 @@ url_add (char *urltext, int len)
data[len - 1] = 0; data[len - 1] = 0;
} }
parsed = g_uri_parse (data, G_URI_FLAGS_NONE, NULL);
if (parsed == NULL || g_uri_get_host (parsed) == NULL || *g_uri_get_host (parsed) == '\0')
{
if (parsed)
g_uri_unref (parsed);
g_free (data);
return;
}
g_uri_unref (parsed);
if (prefs.hex_url_logging) if (prefs.hex_url_logging)
{ {
url_save_node (data); url_save_node (data);
@@ -182,6 +200,7 @@ url_check_word (const char *word)
int type; int type;
} m[] = { } m[] = {
{ match_url, WORD_URL }, { match_url, WORD_URL },
{ match_email, WORD_EMAIL },
{ match_channel, WORD_CHANNEL }, { match_channel, WORD_CHANNEL },
{ match_nick, WORD_NICK }, { match_nick, WORD_NICK },
{ NULL, 0} { NULL, 0}
@@ -261,6 +280,12 @@ match_url (const char *word, int *start, int *end)
return regex_match (re_url (), word, start, end); return regex_match (re_url (), word, start, end);
} }
static gboolean
match_email (const char *word, int *start, int *end)
{
return regex_match (re_email (), word, start, end);
}
/* List of IRC commands for which contents (and thus possible URLs) /* List of IRC commands for which contents (and thus possible URLs)
* are visible to the user. NOTE: Trailing blank required in each. */ * are visible to the user. NOTE: Trailing blank required in each. */
static char *commands[] = { static char *commands[] = {
@@ -425,6 +450,8 @@ struct
{ "ftp" }, { "ftp" },
{ "gopher" }, { "gopher" },
{ "gemini" }, { "gemini" },
{ "irc" },
{ "ircs" },
{ NULL } { NULL }
}; };
@@ -461,6 +488,22 @@ re_url (void)
return url_ret; return url_ret;
} }
#define EMAIL_LOCAL_ATOM "[\\pL\\pN!#$%&'*+/=?^_`{|}~-]+"
#define EMAIL_LOCAL EMAIL_LOCAL_ATOM "(\\." EMAIL_LOCAL_ATOM ")*"
#define EMAIL EMAIL_LOCAL "@" DOMAIN TLD
static const GRegex *
re_email (void)
{
static GRegex *email_ret;
if (email_ret) return email_ret;
email_ret = make_re ("(" EMAIL ")");
return email_ret;
}
/* NICK description --- */ /* NICK description --- */
/* For NICKPRE see before url_check_word() */ /* For NICKPRE see before url_check_word() */
#define NICKHYP "-" #define NICKHYP "-"

View File

@@ -29,6 +29,7 @@
#ifdef WIN32 #ifdef WIN32
#include <windows.h> #include <windows.h>
#include <dwmapi.h> #include <dwmapi.h>
#include <glib/gwin32.h>
#else #else
#include <unistd.h> #include <unistd.h>
#endif #endif
@@ -301,9 +302,28 @@ fe_args (int argc, char *argv[])
GOptionContext *context; GOptionContext *context;
char *buffer; char *buffer;
const char *desktop_id = "net.zoite.Zoitechat"; const char *desktop_id = "net.zoite.Zoitechat";
#ifdef WIN32
char *base_path = NULL;
char *locale_path = NULL;
#endif
#ifdef ENABLE_NLS #ifdef ENABLE_NLS
#ifdef WIN32
base_path = g_win32_get_package_installation_directory_of_module (NULL);
if (base_path)
{
locale_path = g_build_filename (base_path, "share", "locale", NULL);
bindtextdomain (GETTEXT_PACKAGE, locale_path);
}
else
{
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
}
g_free (locale_path);
g_free (base_path);
#else
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
#endif
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE); textdomain (GETTEXT_PACKAGE);
#endif #endif

View File

@@ -2640,6 +2640,11 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
xtext->select_start_x = x; xtext->select_start_x = x;
xtext->select_start_y = y; xtext->select_start_y = y;
xtext->select_start_adj = xtext_adj_get_value (xtext->adj); xtext->select_start_adj = xtext_adj_get_value (xtext->adj);
if (xtext->buffer->last_ent_start)
{
gtk_xtext_unselect (xtext);
xtext->mark_stamp = FALSE;
}
return FALSE; return FALSE;
} }

View File

@@ -26,6 +26,7 @@
#endif #endif
#ifdef WIN32 #ifdef WIN32
#include <io.h> #include <io.h>
#include <glib/gwin32.h>
#define STDIN_FILENO 0 #define STDIN_FILENO 0
#define STDOUT_FILENO 1 #define STDOUT_FILENO 1
#else #else
@@ -478,9 +479,28 @@ fe_args (int argc, char *argv[])
{ {
GError *error = NULL; GError *error = NULL;
GOptionContext *context; GOptionContext *context;
#ifdef WIN32
char *base_path = NULL;
char *locale_path = NULL;
#endif
#ifdef ENABLE_NLS #ifdef ENABLE_NLS
#ifdef WIN32
base_path = g_win32_get_package_installation_directory_of_module (NULL);
if (base_path)
{
locale_path = g_build_filename (base_path, "share", "locale", NULL);
bindtextdomain (GETTEXT_PACKAGE, locale_path);
}
else
{
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
}
g_free (locale_path);
g_free (base_path);
#else
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
#endif
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE); textdomain (GETTEXT_PACKAGE);
#endif #endif

View File

@@ -83,7 +83,7 @@ Root: HKCR; Subkey: "ZoiteChat.Theme\shell\open\command"; ValueType: string; Val
[Run] [Run]
Filename: "{app}\zoitechat.exe"; Description: "Run ZoiteChat after closing the Wizard"; Flags: nowait postinstall skipifsilent Filename: "{app}\zoitechat.exe"; Description: "Run ZoiteChat after closing the Wizard"; Flags: nowait postinstall skipifsilent
Filename: "http://docs.zoitechat.org/en/latest/changelog.html"; Description: "See what's changed"; Flags: shellexec runasoriginaluser postinstall skipifsilent unchecked Filename: "http://docs.zoitechat.org/en/latest/changelog.html"; Description: "See what's changed"; Flags: shellexec runasoriginaluser postinstall skipifsilent unchecked
Filename: "{tmp}\vcredist.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Components: deps\vcredist2015; Flags: skipifdoesntexist; Tasks: not portable Filename: "{tmp}\vcredist.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Components: deps\vcredist2015; Tasks: not portable
Filename: "{tmp}\perl.msi"; StatusMsg: "Installing Perl"; Components: langs\perl; Flags: shellexec skipifdoesntexist; Tasks: not portable Filename: "{tmp}\perl.msi"; StatusMsg: "Installing Perl"; Components: langs\perl; Flags: shellexec skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\python.msi"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable Filename: "{tmp}\python.msi"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\python.exe"; Parameters: "InstallAllUsers=1 PrependPath=1"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable Filename: "{tmp}\python.exe"; Parameters: "InstallAllUsers=1 PrependPath=1"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable
@@ -339,7 +339,7 @@ begin
if not IsTaskSelected('portable') then if not IsTaskSelected('portable') then
begin begin
REDIST := 'https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/vc_redist.x64.exe'; REDIST := 'https://aka.ms/vs/17/release/vc_redist.x64.exe';
PERL := 'https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_54201_64bit/strawberry-perl-5.42.0.1-64bit.msi'; 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'; 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';
@@ -387,6 +387,14 @@ begin
WizardForm.TasksList.Checked[1] := False WizardForm.TasksList.Checked[1] := False
MsgBox('Portable mode is only intended for use on portable drives and has been disabled.', mbInformation, MB_OK) MsgBox('Portable mode is only intended for use on portable drives and has been disabled.', mbInformation, MB_OK)
end; end;
if CurPageID = wpReady then
if IsComponentSelected('deps\vcredist2015') and not CheckVCInstall() and not FileExists(ExpandConstant('{tmp}\vcredist.exe')) then
begin
MsgBox('Visual C++ Redistributable could not be downloaded. Please retry setup or install it manually and rerun setup.', mbError, MB_OK);
Result := False;
Exit;
end;
end; end;
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////