52 Commits

Author SHA1 Message Date
23558f1963 Reapply left-justify tag on topic refresh 2026-03-26 18:05:11 -06:00
40b99450ea Use topicbox selector to isolate topic CSS 2026-03-26 17:34:44 -06:00
6a7f44a1fb Fix topic wrap spacing in topic bar 2026-03-26 15:49:56 -06:00
deepend-tildeclub
808976eefe Merge pull request #162 from ZoiteChat/chanlist_colors_fix
Fix short-palette fallback clobbering tab colors
2026-03-26 13:45:57 -06:00
ee735b80d5 Fix short-palette fallback clobbering tab colors 2026-03-26 13:28:11 -06:00
7a1317a803 Fix Ctrl+K color entry caret visibility 2026-03-26 08:35:20 -06:00
dac1cbfe30 Make GTK3 theme selector text-only 2026-03-26 08:28:07 -06:00
deepend-tildeclub
0ca099c1a0 Merge pull request #161 from ZoiteChat/99-colors-support
feat: add support for 99 colors.
2026-03-26 01:12:13 -06:00
deepend-tildeclub
f01211ae2e Merge pull request #160 from ZoiteChat/xtext-link-hit-fix
Fix xtext link hit-testing coords
2026-03-26 01:11:51 -06:00
7c23a48ef8 feat: add support for 99 colors. 2026-03-26 00:45:03 -06:00
89f20a93e0 Fix xtext link hit-testing coords 2026-03-26 00:14:16 -06:00
f3fd7fd2b3 fixed new logo image sizes. 2026-03-25 23:45:16 -06:00
10940a37dd new logo 2026-03-25 22:53:43 -06:00
8249be816a Fix input caret contrast in spell entry 2026-03-25 17:42:14 -06:00
deepend-tildeclub
05f8b4d4b3 Merge pull request #158 from sney/sysinfo-updates
sysinfo plugin: modernize output
2026-03-25 16:10:29 -06:00
Jesse Rhodes
935c35fd43 Fix typo. 2026-03-25 17:05:38 -04:00
Jesse Rhodes
33b18cb48d Return NULL for chipset on windows. 2026-03-25 16:48:06 -04:00
Jesse Rhodes
d9442d6e94 Include separate chipset function on windows. 2026-03-25 15:57:28 -04:00
Jesse Rhodes
b9ac55fe7f Refresh usage text. 2026-03-25 14:36:58 -04:00
Jesse Rhodes
ca558e7fa2 Rename VGA -> GPU. 2026-03-25 14:33:27 -04:00
Jesse Rhodes
08f367fef0 Split chipset/agp bridge to its own function and disable by default. 2026-03-25 14:32:37 -04:00
Jesse Rhodes
ca5c94f75e Remove ui from get_client. 2026-03-25 14:03:37 -04:00
deepend-tildeclub
6f4e5f95a6 Merge pull request #156 from ZoiteChat/one-click-client-certs
Add one-click client cert tools, switch to P-256
2026-03-25 00:38:49 -06:00
2defd0ed42 Fix Win cert gen with temp openssl.cnf 2026-03-25 00:26:23 -06:00
86aca30744 Add one-click client cert tools, switch to P-256 2026-03-24 23:00:09 -06:00
deepend-tildeclub
d5f7299a8e Merge pull request #155 from ZoiteChat/fix-input-color-rendering
Hide formatting control bytes again
2026-03-24 19:51:59 -06:00
e73a5d4c8b Hide formatting control bytes again 2026-03-24 19:21:37 -06:00
deepend-tildeclub
ec18c95d32 Merge pull request #154 from ZoiteChat/auto-replace-end-of-line
Fix auto-replace cursor snapback in GTK
2026-03-24 15:18:54 -06:00
d9557c7da1 Fix auto-replace cursor snapback in GTK 2026-03-24 15:05:10 -06:00
fc90fd41be Tighten IRCv3 STS spec handling 2026-03-24 08:49:27 -06:00
ec5e38d1f9 Drop GUri validation from url_add 2026-03-24 08:31:55 -06:00
0eb4d08daa Simplify GTK3 thumbnail loading path 2026-03-24 01:22:20 -06:00
deepend-tildeclub
4b365132ce Merge pull request #151 from ZoiteChat/about-dialog-fixes
UI: duplicate dialog buttons persisted. This should fix them now.
2026-03-24 01:17:06 -06:00
a5ff902ae2 UI: duplicate dialog buttons persisted. This should fix them now. 2026-03-24 01:07:58 -06:00
bd874540bb UI: in fixing website/license buttons I had duplicates shown on other builds. should be fixed now. 2026-03-24 00:56:35 -06:00
f6e1af5701 UI: Fix license/website buttons on debian/manually compiled builds. 2026-03-24 00:42:31 -06:00
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
65707f53b7 issue suggesting this idea at: https://github.com/hexchat/hexchat/issues/2797 2026-03-23 14:46:23 -06:00
21c73e699b translation spelling fix. issue from https://github.com/hexchat/hexchat/issues/2815 2026-03-23 13:50:02 -06:00
63226a7267 Remove duplicate GTK AboutDialog link buttons 2026-03-23 12:53:47 -06:00
fb37810367 UI: Replaced website/license links with buttons. added Patrick Griffis to Copyright notice. 2026-03-23 11:12:26 -06:00
deepend-tildeclub
2b66734fa6 Update copyright information for HexChat 2026-03-23 11:05:30 -06:00
380aae139f UI: Improve text user channel mode status (op,voice,founder etc). 2026-03-23 10:50:48 -06:00
deepend-tildeclub
e4e823c4d6 Merge pull request #148 from ZoiteChat/menu-blank-spaces
Put horizontal lines back on menus
2026-03-23 09:55:02 -06:00
d4b16d5866 Put horizontal line back where blank spaces were showing in recent versions. 2026-03-23 09:22:31 -06:00
b9cfd9a9f9 replace icons again. reverted icons. Will address these at a later date. 2026-03-23 09:11:02 -06:00
664aa150fb enhanced low quality icons. 2026-03-22 18:44:24 -06:00
37 changed files with 946 additions and 540 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -26,6 +26,7 @@ char *sysinfo_backend_get_disk(void);
char *sysinfo_backend_get_memory(void); char *sysinfo_backend_get_memory(void);
char *sysinfo_backend_get_cpu(void); char *sysinfo_backend_get_cpu(void);
char *sysinfo_backend_get_gpu(void); char *sysinfo_backend_get_gpu(void);
char *sysinfo_backend_get_chipset(void);
char *sysinfo_backend_get_sound(void); char *sysinfo_backend_get_sound(void);
char *sysinfo_backend_get_uptime(void); char *sysinfo_backend_get_uptime(void);
char *sysinfo_backend_get_network(void); char *sysinfo_backend_get_network(void);

View File

@@ -41,7 +41,7 @@ static zoitechat_plugin *ph;
static char name[] = "Sysinfo"; static char name[] = "Sysinfo";
static char desc[] = "Display info about your hardware and OS"; static char desc[] = "Display info about your hardware and OS";
static char version[] = "1.0"; static char version[] = "1.0";
static char sysinfo_help[] = "SysInfo Usage:\n /SYSINFO [-e|-o] [CLIENT|UI|OS|CPU|RAM|DISK|VGA|SOUND|ETHERNET|UPTIME], print various details about your system or print a summary without arguments\n /SYSINFO SET <variable>\n"; static char sysinfo_help[] = "SysInfo Usage:\n /SYSINFO [-e|-o] [CLIENT|UI|OS|CPU|RAM|DISK|GPU|CHIPSET|SOUND|ETHERNET|UPTIME], print various details about your system or print a summary without arguments\n /SYSINFO SET <variable>\n";
typedef struct typedef struct
{ {
@@ -54,16 +54,11 @@ typedef struct
static char * static char *
get_client (void) get_client (void)
{ {
char *ui = sysinfo_backend_get_ui(); const char *ver = zoitechat_get_info(ph, "version");
const char *ver = zoitechat_get_info(ph, "version");
char *out; char *out;
if (ui != NULL && *ui != '\0')
out = g_strdup_printf ("ZoiteChat %s (%s)", ver, ui);
else
out = g_strdup_printf ("ZoiteChat %s", ver); out = g_strdup_printf ("ZoiteChat %s", ver);
g_free (ui);
return out; return out;
} }
@@ -74,7 +69,8 @@ static hwinfo hwinfos[] = {
{"cpu", "CPU", sysinfo_backend_get_cpu}, {"cpu", "CPU", sysinfo_backend_get_cpu},
{"memory", "Memory", sysinfo_backend_get_memory}, {"memory", "Memory", sysinfo_backend_get_memory},
{"storage", "Storage", sysinfo_backend_get_disk}, {"storage", "Storage", sysinfo_backend_get_disk},
{"vga", "VGA", sysinfo_backend_get_gpu}, {"gpu", "GPU", sysinfo_backend_get_gpu},
{"chipset", "CHIPSET", sysinfo_backend_get_chipset, TRUE},
{"sound", "Sound", sysinfo_backend_get_sound, TRUE}, {"sound", "Sound", sysinfo_backend_get_sound, TRUE},
{"ethernet", "Ethernet", sysinfo_backend_get_network, TRUE}, {"ethernet", "Ethernet", sysinfo_backend_get_network, TRUE},
{"uptime", "Uptime", sysinfo_backend_get_uptime}, {"uptime", "Uptime", sysinfo_backend_get_uptime},

View File

@@ -121,7 +121,6 @@ char *sysinfo_backend_get_cpu(void)
char *sysinfo_backend_get_gpu(void) char *sysinfo_backend_get_gpu(void)
{ {
char vid_card[bsize]; char vid_card[bsize];
char agp_bridge[bsize];
char buffer[bsize]; char buffer[bsize];
int ret; int ret;
@@ -130,18 +129,29 @@ char *sysinfo_backend_get_gpu(void)
return NULL; return NULL;
} }
if (xs_parse_agpbridge (agp_bridge) != 0) g_snprintf (buffer, bsize, "%s", vid_card);
{
g_snprintf (buffer, bsize, "%s", vid_card);
}
else
{
g_snprintf (buffer, bsize, "%s @ %s", vid_card, agp_bridge);
}
return g_strdup (buffer); return g_strdup (buffer);
} }
char *sysinfo_backend_get_chipset(void)
{
char agp_bridge[bsize];
char buffer[bsize];
int ret;
if ((ret = xs_parse_agpbridge (agp_bridge)) != 0)
{
return NULL;
}
g_snprintf (buffer, bsize, "%s", agp_bridge);
return g_strdup (buffer);
}
char *sysinfo_backend_get_sound(void) char *sysinfo_backend_get_sound(void)
{ {
char sound[bsize]; char sound[bsize];

View File

@@ -89,6 +89,12 @@ sysinfo_backend_get_gpu (void)
return sysinfo_get_gpu (); return sysinfo_get_gpu ();
} }
char *
sysinfo_backend_get_chipset (void)
{
return NULL;
}
char * char *
sysinfo_backend_get_os (void) sysinfo_backend_get_os (void)
{ {

View File

@@ -1325,7 +1325,7 @@ msgstr "%C29*%O$t%C29MOTD übersprungen%O"
#: src/common/textevents.h:277 #: src/common/textevents.h:277
msgid "%C23*%O$t%C28$1%C is already in use. Retrying with %C18$2%O..." msgid "%C23*%O$t%C28$1%C is already in use. Retrying with %C18$2%O..."
msgstr "%C23*%O$t%C28$1%C wird bereits verwendet. Erneurter Versuch mit %C18$2%O …" msgstr "%C23*%O$t%C28$1%C wird bereits verwendet. Erneuter Versuch mit %C18$2%O …"
#: src/common/textevents.h:280 #: src/common/textevents.h:280
msgid "%C23*%O$t%C28$1%C is erroneous. Retrying with %C18$2%O..." msgid "%C23*%O$t%C28$1%C is erroneous. Retrying with %C18$2%O..."

View File

@@ -44,6 +44,7 @@ For more information on ZoiteChat please read our [documentation](https://docs.z
<sub> <sub>
X-Chat ("xchat") Copyright (c) 1998-2010 By Peter Zelezny. X-Chat ("xchat") Copyright (c) 1998-2010 By Peter Zelezny.
HexChat ("hexchat") Copyright (c) 2009-2014 By Berke Viktor. HexChat ("hexchat") Copyright (c) 2009-2014 By Berke Viktor.
Hexchat ("hexchat") Copyright (c) 2015-2025 By Patrick Griffis.
ZoiteChat ("zoitechat") Copyright (c) 2026 By deepend. ZoiteChat ("zoitechat") Copyright (c) 2026 By deepend.
</sub> </sub>

View File

@@ -411,6 +411,7 @@ const struct prefs vars[] =
{"gui_chanlist_width_topic", P_OFFINT (hex_gui_chanlist_width_topic), TYPE_INT}, {"gui_chanlist_width_topic", P_OFFINT (hex_gui_chanlist_width_topic), TYPE_INT},
{"gui_chanlist_width_users", P_OFFINT (hex_gui_chanlist_width_users), TYPE_INT}, {"gui_chanlist_width_users", P_OFFINT (hex_gui_chanlist_width_users), TYPE_INT},
{"gui_compact", P_OFFINT (hex_gui_compact), TYPE_BOOL}, {"gui_compact", P_OFFINT (hex_gui_compact), TYPE_BOOL},
{"gui_ctrlq_quit", P_OFFINT (hex_gui_ctrlq_quit), TYPE_BOOL},
{"gui_dialog_height", P_OFFINT (hex_gui_dialog_height), TYPE_INT}, {"gui_dialog_height", P_OFFINT (hex_gui_dialog_height), TYPE_INT},
{"gui_dialog_left", P_OFFINT (hex_gui_dialog_left), TYPE_INT}, {"gui_dialog_left", P_OFFINT (hex_gui_dialog_left), TYPE_INT},
{"gui_dialog_top", P_OFFINT (hex_gui_dialog_top), TYPE_INT}, {"gui_dialog_top", P_OFFINT (hex_gui_dialog_top), TYPE_INT},
@@ -765,6 +766,7 @@ load_default_config(void)
#ifdef HAVE_GTK_MAC #ifdef HAVE_GTK_MAC
prefs.hex_gui_hide_menu = 1; prefs.hex_gui_hide_menu = 1;
#endif #endif
prefs.hex_gui_ctrlq_quit = 1;
prefs.hex_gui_input_attr = 1; prefs.hex_gui_input_attr = 1;
prefs.hex_gui_input_icon = 1; prefs.hex_gui_input_icon = 1;
prefs.hex_gui_input_nick = 1; prefs.hex_gui_input_nick = 1;

View File

@@ -1796,6 +1796,36 @@ inbound_cap_ack (server *serv, char *nick, char *extensions,
inbound_toggle_caps (serv, extensions, TRUE); inbound_toggle_caps (serv, extensions, TRUE);
} }
void
inbound_cap_new (server *serv, char *nick, char *extensions,
const message_tags_data *tags_data)
{
if (extensions)
{
char **tokens = g_strsplit (extensions, " ", 0);
int i;
for (i = 0; tokens[i]; i++)
{
char **parts = g_strsplit (tokens[i], "=", 2);
if (!g_strcmp0 (parts[0], "sts") && parts[1] && parts[1][0])
{
sts_handle_capability (serv, parts[1]);
}
g_strfreev (parts);
}
g_strfreev (tokens);
}
EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions,
NULL, NULL, 0, tags_data->timestamp);
inbound_toggle_caps (serv, extensions, TRUE);
}
void void
inbound_cap_del (server *serv, char *nick, char *extensions, inbound_cap_del (server *serv, char *nick, char *extensions,
const message_tags_data *tags_data) const message_tags_data *tags_data)

View File

@@ -95,6 +95,8 @@ void inbound_cap_ls (server *serv, char *nick, char *extensions,
void inbound_cap_nak (server *serv, char *extensions, const message_tags_data *tags_data); void inbound_cap_nak (server *serv, char *extensions, const message_tags_data *tags_data);
void inbound_cap_list (server *serv, char *nick, char *extensions, void inbound_cap_list (server *serv, char *nick, char *extensions,
const message_tags_data *tags_data); const message_tags_data *tags_data);
void inbound_cap_new (server *serv, char *nick, char *extensions,
const message_tags_data *tags_data);
void inbound_cap_del (server *serv, char *nick, char *extensions, void inbound_cap_del (server *serv, char *nick, char *extensions,
const message_tags_data *tags_data); const message_tags_data *tags_data);
void inbound_sasl_authenticate (server *serv, char *data); void inbound_sasl_authenticate (server *serv, char *data);

View File

@@ -1359,12 +1359,18 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
tags_data); tags_data);
} }
else if (g_ascii_strncasecmp(word[4], "LS", 2) == 0 || g_ascii_strncasecmp(word[4], "NEW", 3) == 0) else if (g_ascii_strncasecmp(word[4], "LS", 2) == 0)
{ {
inbound_cap_ls (serv, word[1], inbound_cap_ls (serv, word[1],
word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
tags_data); tags_data);
} }
else if (g_ascii_strncasecmp(word[4], "NEW", 3) == 0)
{
inbound_cap_new (serv, word[1],
word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
tags_data);
}
else if (g_ascii_strncasecmp(word[4], "NAK", 3) == 0) else if (g_ascii_strncasecmp(word[4], "NAK", 3) == 0)
{ {
inbound_cap_nak (serv, word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data); inbound_cap_nak (serv, word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data);

View File

@@ -305,6 +305,7 @@ sts_parse_value (const char *value, guint16 *port, guint64 *duration, gboolean *
{ {
char **tokens; char **tokens;
gsize i; gsize i;
char *end;
if (!value || !*value) if (!value || !*value)
{ {
@@ -349,8 +350,9 @@ sts_parse_value (const char *value, guint16 *port, guint64 *duration, gboolean *
continue; continue;
} }
port_value = g_ascii_strtoll (val, NULL, 10); end = NULL;
if (port_value > 0 && port_value <= G_MAXUINT16) port_value = g_ascii_strtoll (val, &end, 10);
if (end && *end == '\0' && port_value > 0 && port_value <= G_MAXUINT16)
{ {
*port = (guint16) port_value; *port = (guint16) port_value;
*has_port = TRUE; *has_port = TRUE;
@@ -371,7 +373,12 @@ sts_parse_value (const char *value, guint16 *port, guint64 *duration, gboolean *
continue; continue;
} }
duration_value = g_ascii_strtoull (val, NULL, 10); end = NULL;
duration_value = g_ascii_strtoull (val, &end, 10);
if (!end || *end != '\0')
{
continue;
}
*duration = duration_value; *duration = duration_value;
*has_duration = TRUE; *has_duration = TRUE;
} }
@@ -612,7 +619,7 @@ sts_handle_capability (struct server *serv, const char *value)
{ {
time_t now = time (NULL); time_t now = time (NULL);
time_t expires_at = now + (time_t) duration; time_t expires_at = now + (time_t) duration;
guint16 effective_port = 0; guint16 effective_port = (guint16) serv->port;
sts_profile *existing_profile; sts_profile *existing_profile;
sts_profile *profile; sts_profile *profile;

View File

@@ -35,20 +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_url_no_scheme (void);
static const GRegex *re_host (void);
static const GRegex *re_host6 (void);
static const GRegex *re_email (void); static const GRegex *re_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 const GRegex *re_path (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_email (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_host (const char *word, int *start, int *end); static gboolean match_email (const char *word, int *start, int *end);
static gboolean match_host6 (const char *word, int *start, int *end);
static gboolean match_path (const char *word, int *start, int *end);
static int static int
url_free (char *url, void *data) url_free (char *url, void *data)
@@ -122,6 +115,11 @@ url_add (char *urltext, int len)
return; return;
} }
if (len <= 0)
{
return;
}
data = g_strndup (urltext, len); data = g_strndup (urltext, len);
if (data[len - 1] == '.') if (data[len - 1] == '.')
@@ -193,9 +191,6 @@ url_check_word (const char *word)
{ match_url, WORD_URL }, { match_url, WORD_URL },
{ match_email, WORD_EMAIL }, { match_email, WORD_EMAIL },
{ match_channel, WORD_CHANNEL }, { match_channel, WORD_CHANNEL },
{ match_host6, WORD_HOST6 },
{ match_host, WORD_HOST },
{ match_path, WORD_PATH },
{ match_nick, WORD_NICK }, { match_nick, WORD_NICK },
{ NULL, 0} { NULL, 0}
}; };
@@ -268,45 +263,18 @@ match_channel (const char *word, int *start, int *end)
return FALSE; return FALSE;
} }
static gboolean
match_url (const char *word, int *start, int *end)
{
return regex_match (re_url (), word, start, end);
}
static gboolean static gboolean
match_email (const char *word, int *start, int *end) match_email (const char *word, int *start, int *end)
{ {
return regex_match (re_email (), word, start, end); return regex_match (re_email (), word, start, end);
} }
static gboolean
match_url (const char *word, int *start, int *end)
{
if (regex_match (re_url (), word, start, end))
return TRUE;
return regex_match (re_url_no_scheme (), word, start, end);
}
static gboolean
match_host (const char *word, int *start, int *end)
{
return regex_match (re_host (), word, start, end);
}
static gboolean
match_host6 (const char *word, int *start, int *end)
{
if (!regex_match (re_host6 (), word, start, end))
return FALSE;
if (word[*start] != '[')
return FALSE;
return TRUE;
}
static gboolean
match_path (const char *word, int *start, int *end)
{
return regex_match (re_path (), word, start, end);
}
/* List of IRC commands for which contents (and thus possible URLs) /* 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[] = {
@@ -451,34 +419,7 @@ make_re (const char *grist)
return ret; return ret;
} }
/* HOST description --- */
/* (see miscellaneous above) */
static const GRegex *
re_host (void)
{
static GRegex *host_ret;
if (host_ret) return host_ret;
host_ret = make_re ("(" "(" HOST_URL PORT ")|(" HOST ")" ")");
return host_ret;
}
static const GRegex *
re_host6 (void)
{
static GRegex *host6_ret;
if (host6_ret) return host6_ret;
host6_ret = make_re ("(" "(" IPV6ADDR ")|(" "\\[" IPV6ADDR "\\]" PORT ")" ")");
return host6_ret;
}
/* URL description --- */ /* URL description --- */
#define SCHEME "(%s)"
#define LPAR "\\(" #define LPAR "\\("
#define RPAR "\\)" #define RPAR "\\)"
#define NOPARENS "[^() \t]*" #define NOPARENS "[^() \t]*"
@@ -489,86 +430,20 @@ re_host6 (void)
"(" NOPARENS ")" \ "(" NOPARENS ")" \
")*" /* Zero or more occurrences of either of these */ \ ")*" /* Zero or more occurrences of either of these */ \
"(?<![.,?!\\]])" /* Not allowed to end with these */ "(?<![.,?!\\]])" /* Not allowed to end with these */
#define USERINFO "([-a-z0-9._~%]+(:[-a-z0-9._~%]*)?@)"
/* Flags used to describe URIs (RFC 3986)
*
* Bellow is an example of what the flags match.
*
* URI_AUTHORITY - http://example.org:80/foo/bar
* ^^^^^^^^^^^^^^^^
* URI_USERINFO/URI_OPT_USERINFO - http://user@example.org:80/foo/bar
* ^^^^^
* URI_PATH - http://example.org:80/foo/bar
* ^^^^^^^^
*/
#define URI_AUTHORITY (1 << 0)
#define URI_OPT_USERINFO (1 << 1)
#define URI_USERINFO (1 << 2)
#define URI_PATH (1 << 3)
struct struct
{ {
const char *scheme; /* scheme name. e.g. http */ const char *scheme;
const char *path_sep; /* string that begins the path */
int flags; /* see above (flag macros) */
} uri[] = { } uri[] = {
{ "irc", "/", URI_PATH }, { "http" },
{ "ircs", "/", URI_PATH }, { "https" },
{ "rtsp", "/", URI_AUTHORITY | URI_PATH }, { "ftp" },
{ "feed", "/", URI_AUTHORITY | URI_PATH }, { "gopher" },
{ "teamspeak", "?", URI_AUTHORITY | URI_PATH }, { "gemini" },
{ "ftp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "irc" },
{ "sftp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "ircs" },
{ "ftps", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { NULL }
{ "http", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "https", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "cvs", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "svn", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "git", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "bzr", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "rsync", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "mumble", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "ventrilo", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "xmpp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "h323", ";", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "imap", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "pop", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "nfs", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "smb", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
{ "gopher", "/", URI_AUTHORITY | URI_PATH },
{ "gemini", "/", URI_AUTHORITY | URI_PATH },
{ "ssh", "", URI_AUTHORITY | URI_OPT_USERINFO },
{ "sip", "", URI_AUTHORITY | URI_USERINFO },
{ "sips", "", URI_AUTHORITY | URI_USERINFO },
{ "magnet", "?", URI_PATH },
{ "mailto", "", URI_PATH },
{ "bitcoin", "", URI_PATH },
{ "gtalk", "", URI_PATH },
{ "steam", "", URI_PATH },
{ "file", "/", URI_PATH },
{ "callto", "", URI_PATH },
{ "skype", "", URI_PATH },
{ "geo", "", URI_PATH },
{ "spotify", "", URI_PATH },
{ "lastfm", "/", URI_PATH },
{ "xfire", "", URI_PATH },
{ "ts3server", "", URI_PATH },
{ NULL, "", 0}
}; };
static const GRegex *
re_url_no_scheme (void)
{
static GRegex *url_ret = NULL;
if (url_ret) return url_ret;
url_ret = make_re ("(" HOST_URL OPT_PORT "/" "(" PATH ")?" ")");
return url_ret;
}
static const GRegex * static const GRegex *
re_url (void) re_url (void)
{ {
@@ -587,27 +462,9 @@ re_url (void)
g_string_append (grist_gstr, "|"); g_string_append (grist_gstr, "|");
g_string_append (grist_gstr, "("); g_string_append (grist_gstr, "(");
g_string_append_printf (grist_gstr, "%s:", uri[i].scheme); g_string_append_printf (grist_gstr, "%s://", uri[i].scheme);
g_string_append (grist_gstr, HOST_URL_OPT_TLD OPT_PORT);
if (uri[i].flags & URI_AUTHORITY) g_string_append_printf (grist_gstr, "(/" PATH ")?");
g_string_append (grist_gstr, "//");
if (uri[i].flags & URI_USERINFO)
g_string_append (grist_gstr, USERINFO);
else if (uri[i].flags & URI_OPT_USERINFO)
g_string_append (grist_gstr, USERINFO "?");
if (uri[i].flags & URI_AUTHORITY)
g_string_append (grist_gstr, HOST_URL_OPT_TLD OPT_PORT);
if (uri[i].flags & URI_PATH)
{
char *sep_escaped = g_regex_escape_string (uri[i].path_sep, strlen(uri[i].path_sep));
g_string_append_printf (grist_gstr, "(" "%s" PATH ")?", sep_escaped);
g_free (sep_escaped);
}
g_string_append (grist_gstr, ")"); g_string_append (grist_gstr, ")");
} }
@@ -620,8 +477,9 @@ re_url (void)
return url_ret; return url_ret;
} }
/* EMAIL description --- */ #define EMAIL_LOCAL_ATOM "[\\pL\\pN!#$%&'*+/=?^_`{|}~-]+"
#define EMAIL "[a-z0-9][._%+-a-z0-9]+@" "(" HOST_URL ")" #define EMAIL_LOCAL EMAIL_LOCAL_ATOM "(\\." EMAIL_LOCAL_ATOM ")*"
#define EMAIL EMAIL_LOCAL "@" DOMAIN TLD
static const GRegex * static const GRegex *
re_email (void) re_email (void)
@@ -680,24 +538,3 @@ re_channel (void)
return channel_ret; return channel_ret;
} }
/* PATH description --- */
#ifdef WIN32
/* Windows path can be .\ ..\ or C: D: etc */
#define FS_PATH "^(\\.{1,2}\\\\|[a-z]:).*"
#else
/* Linux path can be / or ./ or ../ etc */
#define FS_PATH "^(/|\\./|\\.\\./).*"
#endif
static const GRegex *
re_path (void)
{
static GRegex *path_ret;
if (path_ret) return path_ret;
path_ret = make_re ("(" FS_PATH ")");
return path_ret;
}

View File

@@ -122,6 +122,7 @@ struct zoitechatprefs
unsigned int hex_gui_autoopen_recv; unsigned int hex_gui_autoopen_recv;
unsigned int hex_gui_autoopen_send; unsigned int hex_gui_autoopen_send;
unsigned int hex_gui_compact; unsigned int hex_gui_compact;
unsigned int hex_gui_ctrlq_quit;
unsigned int hex_gui_filesize_iec; unsigned int hex_gui_filesize_iec;
unsigned int hex_gui_focus_omitalerts; unsigned int hex_gui_focus_omitalerts;
unsigned int hex_gui_hide_menu; unsigned int hex_gui_hide_menu;

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);
}
g_free (locale_path);
g_free (base_path);
#else
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); 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
@@ -772,18 +792,21 @@ fe_set_topic (session *sess, char *topic, char *stripped_topic)
{ {
if (!sess->gui->is_tab || sess == current_tab) if (!sess->gui->is_tab || sess == current_tab)
{ {
GtkTextBuffer *topic_buffer;
GtkTextIter start;
GtkTextIter end;
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry));
if (prefs.hex_text_stripcolor_topic) if (prefs.hex_text_stripcolor_topic)
{ {
gtk_text_buffer_set_text ( gtk_text_buffer_set_text (topic_buffer, stripped_topic, -1);
gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry)),
stripped_topic, -1);
} }
else else
{ {
gtk_text_buffer_set_text ( gtk_text_buffer_set_text (topic_buffer, topic, -1);
gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry)),
topic, -1);
} }
gtk_text_buffer_get_bounds (topic_buffer, &start, &end);
gtk_text_buffer_apply_tag_by_name (topic_buffer, "zoitechat-topic-left", &start, &end);
mg_set_topic_tip (sess); mg_set_topic_tip (sess);
} }
else else

View File

@@ -170,7 +170,7 @@ typedef struct session_gui
GtkWidget *shbox, *shentry; /* search bar hbox */ GtkWidget *shbox, *shentry; /* search bar hbox */
gulong search_changed_signal; /* hook for search change event so blanking the box doesn't suck */ gulong search_changed_signal; /* hook for search change event so blanking the box doesn't suck */
#define MENU_ID_NUM 14 #define MENU_ID_NUM 15
GtkWidget *menu_item[MENU_ID_NUM+1]; /* some items we may change state of */ GtkWidget *menu_item[MENU_ID_NUM+1]; /* some items we may change state of */
void *chanview; /* chanview.h */ void *chanview; /* chanview.h */

View File

@@ -1888,6 +1888,20 @@ key_action_put_history (GtkWidget * wid, GdkEventKey * ent, char *d1,
return 2; return 2;
} }
static gboolean
replace_set_pos_idle (gpointer data)
{
GtkWidget *t = GTK_WIDGET (data);
gpointer pos_data = g_object_get_data (G_OBJECT (t), "zoitechat-replace-pos");
if (pos_data)
SPELL_ENTRY_SET_POS (t, GPOINTER_TO_INT (pos_data));
g_object_set_data (G_OBJECT (t), "zoitechat-replace-pos", NULL);
g_object_unref (t);
return G_SOURCE_REMOVE;
}
static void static void
replace_handle (GtkWidget *t) replace_handle (GtkWidget *t)
{ {
@@ -1903,11 +1917,12 @@ replace_handle (GtkWidget *t)
size_t match_len; size_t match_len;
ptrdiff_t cursor_byte_offset; ptrdiff_t cursor_byte_offset;
ptrdiff_t match_start_offset; ptrdiff_t match_start_offset;
ptrdiff_t match_end_offset;
ptrdiff_t new_cursor_offset; ptrdiff_t new_cursor_offset;
const char *best_match; const char *best_match;
size_t best_len; size_t best_len;
struct popup *best_pop; struct popup *best_pop;
int best_rank;
ptrdiff_t best_distance;
text = SPELL_ENTRY_GET_TEXT (t); text = SPELL_ENTRY_GET_TEXT (t);
@@ -1922,19 +1937,50 @@ replace_handle (GtkWidget *t)
best_match = NULL; best_match = NULL;
best_len = 0; best_len = 0;
best_pop = NULL; best_pop = NULL;
best_rank = 3;
best_distance = 0;
cursor_byte_offset = cursor_ptr - text;
while (list) while (list)
{ {
pop = (struct popup *) list->data; pop = (struct popup *) list->data;
if (pop->name[0] != '\0') if (pop->name[0] != '\0')
{ {
size_t pop_len = strlen (pop->name); size_t pop_len = strlen (pop->name);
const char *found = strstr (text, pop->name); const char *found = text;
if (found && (!best_match || found < best_match)) while ((found = strstr (found, pop->name)) != NULL)
{ {
best_match = found; ptrdiff_t found_offset = found - text;
best_len = pop_len; ptrdiff_t found_end_offset = found_offset + (ptrdiff_t) pop_len;
best_pop = pop; int rank;
ptrdiff_t distance;
if (cursor_byte_offset >= found_offset && cursor_byte_offset <= found_end_offset)
{
rank = 0;
distance = found_end_offset - cursor_byte_offset;
}
else if (found_end_offset <= cursor_byte_offset)
{
rank = 1;
distance = cursor_byte_offset - found_end_offset;
}
else
{
rank = 2;
distance = found_offset - cursor_byte_offset;
}
if (rank < best_rank || (rank == best_rank && distance < best_distance))
{
best_rank = rank;
best_distance = distance;
best_match = found;
best_len = pop_len;
best_pop = pop;
}
found++;
} }
} }
list = list->next; list = list->next;
@@ -1952,21 +1998,17 @@ replace_handle (GtkWidget *t)
return; return;
replacement_len = strlen (pop->cmd); replacement_len = strlen (pop->cmd);
cursor_byte_offset = cursor_ptr - text;
match_start_offset = match_start - text; match_start_offset = match_start - text;
match_end_offset = match_start_offset + (ptrdiff_t) match_len; new_cursor_offset = match_start_offset + (ptrdiff_t) replacement_len;
if (cursor_byte_offset <= match_start_offset)
new_cursor_offset = cursor_byte_offset;
else if (cursor_byte_offset >= match_end_offset)
new_cursor_offset = cursor_byte_offset + ((ptrdiff_t) replacement_len - (ptrdiff_t) match_len);
else
new_cursor_offset = match_start_offset + (ptrdiff_t) replacement_len;
buf = g_string_sized_new (strlen (text) + 32); buf = g_string_sized_new (strlen (text) + 32);
g_string_append_len (buf, text, match_start - text); g_string_append_len (buf, text, match_start - text);
g_string_append (buf, pop->cmd); g_string_append (buf, pop->cmd);
g_string_append (buf, match_start + match_len); g_string_append (buf, match_start + match_len);
SPELL_ENTRY_SET_TEXT (t, buf->str); SPELL_ENTRY_SET_TEXT (t, buf->str);
SPELL_ENTRY_SET_POS (t, len_to_offset (buf->str, new_cursor_offset)); new_cursor_offset = len_to_offset (buf->str, new_cursor_offset);
SPELL_ENTRY_SET_POS (t, new_cursor_offset);
g_object_set_data (G_OBJECT (t), "zoitechat-replace-pos", GINT_TO_POINTER ((gint) new_cursor_offset));
g_idle_add (replace_set_pos_idle, g_object_ref (t));
g_string_free (buf, TRUE); g_string_free (buf, TRUE);
} }

View File

@@ -1410,16 +1410,26 @@ mg_populate (session *sess)
{ {
GtkTextBuffer *topic_buffer; GtkTextBuffer *topic_buffer;
GtkTextIter start;
GtkTextIter end;
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gui->topic_entry)); topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gui->topic_entry));
gtk_text_buffer_set_text (topic_buffer, res->topic_text, -1); gtk_text_buffer_set_text (topic_buffer, res->topic_text, -1);
gtk_text_buffer_get_bounds (topic_buffer, &start, &end);
gtk_text_buffer_apply_tag_by_name (topic_buffer, "zoitechat-topic-left", &start, &end);
g_free (res->topic_text); g_free (res->topic_text);
res->topic_text = NULL; res->topic_text = NULL;
} else } else
{ {
GtkTextBuffer *topic_buffer; GtkTextBuffer *topic_buffer;
GtkTextIter start;
GtkTextIter end;
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gui->topic_entry)); topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gui->topic_entry));
gtk_text_buffer_set_text (topic_buffer, "", -1); gtk_text_buffer_set_text (topic_buffer, "", -1);
gtk_text_buffer_get_bounds (topic_buffer, &start, &end);
gtk_text_buffer_apply_tag_by_name (topic_buffer, "zoitechat-topic-left", &start, &end);
} }
mg_restore_speller (gui->input_box, &res->input_text); mg_restore_speller (gui->input_box, &res->input_text);
mg_restore_entry (gui->key_entry, &res->key_text); mg_restore_entry (gui->key_entry, &res->key_text);
@@ -2988,10 +2998,13 @@ mg_create_topicbar (session *sess, GtkWidget *box)
sess->res->tab = NULL; sess->res->tab = NULL;
gui->topic_entry = topic = gtk_text_view_new (); gui->topic_entry = topic = gtk_text_view_new ();
gtk_widget_set_name (topic, "zoitechat-inputbox"); gtk_widget_set_name (topic, "zoitechat-topicbox");
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (topic), GTK_WRAP_WORD_CHAR); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (topic), GTK_WRAP_WORD_CHAR);
gtk_text_view_set_justification (GTK_TEXT_VIEW (topic), GTK_JUSTIFY_LEFT);
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (topic), 4); gtk_text_view_set_left_margin (GTK_TEXT_VIEW (topic), 4);
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (topic), 4); gtk_text_view_set_right_margin (GTK_TEXT_VIEW (topic), 4);
gtk_text_buffer_create_tag (gtk_text_view_get_buffer (GTK_TEXT_VIEW (topic)), "zoitechat-topic-left",
"justification", GTK_JUSTIFY_LEFT, NULL);
gtk_box_pack_start (GTK_BOX (hbox), topic, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), topic, TRUE, TRUE, 0);
mg_apply_emoji_fallback_widget (topic); mg_apply_emoji_fallback_widget (topic);
gtk_widget_add_events (topic, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | gtk_widget_add_events (topic, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |

View File

@@ -1802,6 +1802,44 @@ menu_change_layout (void)
} }
} }
void
menu_update_quit_accel (void)
{
GSList *list;
list = sess_list;
while (list)
{
session *sess = list->data;
session_gui *gui = sess->gui;
GtkWidget *item;
GtkAccelGroup *accel_group;
int enabled;
list = list->next;
if (!gui)
continue;
item = gui->menu_item[MENU_ID_QUIT];
if (!item)
continue;
enabled = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "zc-ctrlq-enabled"));
if (enabled == (int)prefs.hex_gui_ctrlq_quit)
continue;
accel_group = g_object_get_data (G_OBJECT (item), "zc-quit-accel-group");
if (!accel_group)
continue;
if (prefs.hex_gui_ctrlq_quit)
gtk_widget_add_accelerator (item, "activate", accel_group, GDK_KEY_q, STATE_CTRL, GTK_ACCEL_VISIBLE);
else
gtk_widget_remove_accelerator (item, accel_group, GDK_KEY_q, STATE_CTRL);
g_object_set_data (G_OBJECT (item), "zc-ctrlq-enabled", GINT_TO_POINTER (prefs.hex_gui_ctrlq_quit));
}
}
static void static void
menu_layout_cb (GtkWidget *item, gpointer none) menu_layout_cb (GtkWidget *item, gpointer none)
{ {
@@ -1867,8 +1905,18 @@ menu_metres_both (GtkWidget *item, gpointer none)
} }
static void static void
about_dialog_close (GtkDialog *dialog, int response, gpointer data) about_dialog_response (GtkDialog *dialog, int response, gpointer data)
{ {
if (response == GTK_RESPONSE_HELP)
{
fe_open_url ("http://zoitechat.zoite.net");
return;
}
if (response == GTK_RESPONSE_APPLY)
{
fe_open_url ("https://www.gnu.org/licenses/old-licenses/gpl-2.0.html");
return;
}
gtk_widget_destroy (GTK_WIDGET(dialog)); gtk_widget_destroy (GTK_WIDGET(dialog));
} }
@@ -1879,25 +1927,17 @@ about_dialog_openurl (GtkAboutDialog *dialog, char *uri, gpointer data)
return TRUE; return TRUE;
} }
static void
about_dialog_add_links (GtkAboutDialog *dialog)
{
GtkWidget *content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
GtkWidget *row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
GtkWidget *website = gtk_link_button_new_with_label ("http://zoitechat.zoite.net", "Website");
GtkWidget *license = gtk_link_button_new_with_label ("https://www.gnu.org/licenses/old-licenses/gpl-2.0.html", "License");
gtk_button_set_relief (GTK_BUTTON (website), GTK_RELIEF_NONE);
gtk_button_set_relief (GTK_BUTTON (license), GTK_RELIEF_NONE);
gtk_box_pack_start (GTK_BOX (row), website, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (row), license, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (content), row, FALSE, FALSE, 0);
gtk_widget_show_all (row);
}
static void static void
menu_about (GtkWidget *wid, gpointer sess) menu_about (GtkWidget *wid, gpointer sess)
{ {
GtkAboutDialog *dialog = GTK_ABOUT_DIALOG(gtk_about_dialog_new()); GtkAboutDialog *dialog = GTK_ABOUT_DIALOG (g_object_new (GTK_TYPE_ABOUT_DIALOG, "use-header-bar", FALSE, NULL));
GtkWidget *website;
GtkWidget *license;
GtkWidget *close;
GtkWidget *actions;
GList *children;
GList *child;
static const gchar *empty_people[] = { NULL };
theme_manager_attach_window (GTK_WIDGET (dialog)); theme_manager_attach_window (GTK_WIDGET (dialog));
char comment[512]; char comment[512];
g_snprintf (comment, sizeof(comment), "" g_snprintf (comment, sizeof(comment), ""
@@ -1914,17 +1954,31 @@ menu_about (GtkWidget *wid, gpointer sess)
gtk_about_dialog_set_program_name (dialog, _(DISPLAY_NAME)); gtk_about_dialog_set_program_name (dialog, _(DISPLAY_NAME));
gtk_about_dialog_set_version (dialog, PACKAGE_VERSION); gtk_about_dialog_set_version (dialog, PACKAGE_VERSION);
gtk_about_dialog_set_authors (dialog, NULL); gtk_about_dialog_set_authors (dialog, empty_people);
gtk_about_dialog_set_documenters (dialog, NULL); gtk_about_dialog_set_documenters (dialog, empty_people);
gtk_about_dialog_set_artists (dialog, NULL); gtk_about_dialog_set_artists (dialog, empty_people);
gtk_about_dialog_set_translator_credits (dialog, NULL); gtk_about_dialog_set_translator_credits (dialog, "");
gtk_about_dialog_set_website (dialog, NULL);
gtk_about_dialog_set_website_label (dialog, NULL);
gtk_about_dialog_set_license (dialog, NULL);
gtk_about_dialog_set_wrap_license (dialog, FALSE);
gtk_about_dialog_set_logo (dialog, pix_zoitechat); gtk_about_dialog_set_logo (dialog, pix_zoitechat);
gtk_about_dialog_set_copyright (dialog, "\302\251 1998-2010 Peter \305\275elezn\303\275\n\302\251 2009-2014 Berke Viktor\n\302\251 2026 deepend"); gtk_about_dialog_set_copyright (dialog, "\302\251 1998-2010 Peter \305\275elezn\303\275\n\302\251 2009-2014 Berke Viktor\n\302\251 2015-2025 Patrick Griffis\n\302\251 2026 deepend");
gtk_about_dialog_set_comments (dialog, comment); gtk_about_dialog_set_comments (dialog, comment);
about_dialog_add_links (dialog); actions = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
children = gtk_container_get_children (GTK_CONTAINER (actions));
for (child = children; child; child = child->next)
gtk_widget_destroy (GTK_WIDGET (child->data));
g_list_free (children);
website = gtk_dialog_add_button (GTK_DIALOG (dialog), "Website", GTK_RESPONSE_HELP);
license = gtk_dialog_add_button (GTK_DIALOG (dialog), "License", GTK_RESPONSE_APPLY);
close = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Close"), GTK_RESPONSE_CLOSE);
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (actions), website, TRUE);
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (actions), license, TRUE);
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (actions), close, FALSE);
gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent_window)); gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent_window));
g_signal_connect (G_OBJECT(dialog), "response", G_CALLBACK(about_dialog_close), NULL); g_signal_connect (G_OBJECT(dialog), "response", G_CALLBACK(about_dialog_response), NULL);
g_signal_connect (G_OBJECT(dialog), "activate-link", G_CALLBACK(about_dialog_openurl), NULL); g_signal_connect (G_OBJECT(dialog), "activate-link", G_CALLBACK(about_dialog_openurl), NULL);
gtk_widget_show_all (GTK_WIDGET(dialog)); gtk_widget_show_all (GTK_WIDGET(dialog));
@@ -1950,7 +2004,7 @@ static struct mymenu mymenu[] = {
#define CLOSE_OFFSET (13) #define CLOSE_OFFSET (13)
{0, menu_close, 0, M_MENUITEM, 0, 0, 1}, {0, menu_close, 0, M_MENUITEM, 0, 0, 1},
{0, 0, 0, M_SEP, 0, 0, 0}, {0, 0, 0, M_SEP, 0, 0, 0},
{N_("_Quit"), menu_quit, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_q}, /* 15 */ {N_("_Quit"), menu_quit, 0, M_MENUITEM, MENU_ID_QUIT, 0, 1, GDK_KEY_q}, /* 15 */
{N_("_View"), 0, 0, M_NEWMENU, 0, 0, 1}, {N_("_View"), 0, 0, M_NEWMENU, 0, 0, 1},
#define MENUBAR_OFFSET (17) #define MENUBAR_OFFSET (17)
@@ -2634,7 +2688,7 @@ menu_create_main (void *accel_group, int bar, int away, int toplevel,
case M_MENUITEM: case M_MENUITEM:
item = gtk_menu_item_new_with_mnemonic (_(mymenu[i].text)); item = gtk_menu_item_new_with_mnemonic (_(mymenu[i].text));
normalitem: normalitem:
if (mymenu[i].key != 0) if (mymenu[i].key != 0 && !(mymenu[i].id == MENU_ID_QUIT && !prefs.hex_gui_ctrlq_quit))
gtk_widget_add_accelerator (item, "activate", accel_group, gtk_widget_add_accelerator (item, "activate", accel_group,
mymenu[i].key, mymenu[i].key,
mymenu[i].key == GDK_KEY_F1 ? 0 : mymenu[i].key == GDK_KEY_F1 ? 0 :
@@ -2644,6 +2698,11 @@ normalitem:
STATE_SHIFT | STATE_CTRL : STATE_SHIFT | STATE_CTRL :
STATE_CTRL, STATE_CTRL,
GTK_ACCEL_VISIBLE); GTK_ACCEL_VISIBLE);
if (mymenu[i].id == MENU_ID_QUIT)
{
g_object_set_data (G_OBJECT (item), "zc-quit-accel-group", accel_group);
g_object_set_data (G_OBJECT (item), "zc-ctrlq-enabled", GINT_TO_POINTER (prefs.hex_gui_ctrlq_quit));
}
if (mymenu[i].callback) if (mymenu[i].callback)
g_signal_connect (G_OBJECT (item), "activate", g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (mymenu[i].callback), 0); G_CALLBACK (mymenu[i].callback), 0);
@@ -2685,9 +2744,11 @@ togitem:
goto togitem; goto togitem;
case M_SEP: case M_SEP:
item = gtk_menu_item_new (); item = gtk_separator_menu_item_new ();
gtk_widget_set_sensitive (item, FALSE); if (submenu)
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item);
else
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item); gtk_widget_show (item);
break; break;

View File

@@ -38,6 +38,7 @@ void menu_create (GtkWidget *menu, GSList *list, char *target, int check_path);
void menu_bar_toggle (void); void menu_bar_toggle (void);
void menu_add_plugin_items (GtkWidget *menu, char *root, char *target); void menu_add_plugin_items (GtkWidget *menu, char *root, char *target);
void menu_change_layout (void); void menu_change_layout (void);
void menu_update_quit_accel (void);
void menu_set_away (session_gui *gui, int away); void menu_set_away (session_gui *gui, int away);
void menu_set_fullscreen (session_gui *gui, int fullscreen); void menu_set_fullscreen (session_gui *gui, int fullscreen);
@@ -63,8 +64,9 @@ void menu_set_fullscreen (session_gui *gui, int fullscreen);
#define MENU_ID_USERMENU 12 #define MENU_ID_USERMENU 12
#define MENU_ID_FULLSCREEN 13 #define MENU_ID_FULLSCREEN 13
#define MENU_ID_ZOITECHAT 14 #define MENU_ID_ZOITECHAT 14
#define MENU_ID_QUIT 15
#if (MENU_ID_NUM < MENU_ID_ZOITECHAT) #if (MENU_ID_NUM < MENU_ID_QUIT)
#error MENU_ID_NUM is set wrong #error MENU_ID_NUM is set wrong
#endif #endif

View File

@@ -20,6 +20,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <sys/stat.h>
#include <gdk/gdkkeysyms.h> #include <gdk/gdkkeysyms.h>
@@ -89,6 +90,9 @@ static GtkWidget *edit_label_nick2;
static GtkWidget *edit_label_real; static GtkWidget *edit_label_real;
static GtkWidget *edit_label_user; static GtkWidget *edit_label_user;
static GtkWidget *edit_trees[N_TREES]; static GtkWidget *edit_trees[N_TREES];
static GtkWidget *edit_button_cert_generate;
static GtkWidget *edit_button_cert_info;
static GtkWidget *edit_button_cert_delete;
static ircnet *selected_net = NULL; static ircnet *selected_net = NULL;
static ircserver *selected_serv = NULL; static ircserver *selected_serv = NULL;
@@ -99,6 +103,281 @@ static session *servlist_sess;
static void servlist_network_row_cb (GtkTreeSelection *sel, gpointer user_data); static void servlist_network_row_cb (GtkTreeSelection *sel, gpointer user_data);
static GtkWidget *servlist_open_edit (GtkWidget *parent, ircnet *net); static GtkWidget *servlist_open_edit (GtkWidget *parent, ircnet *net);
static char *
servlist_get_cert_file (ircnet *net)
{
if (!net || !net->name || !net->name[0])
return NULL;
return g_strdup_printf ("%s" G_DIR_SEPARATOR_S "certs" G_DIR_SEPARATOR_S "%s.pem",
get_xdir (), net->name);
}
static gboolean
servlist_network_cert_exists (ircnet *net)
{
char *cert_file;
gboolean exists;
cert_file = servlist_get_cert_file (net);
if (!cert_file)
return FALSE;
exists = g_file_test (cert_file, G_FILE_TEST_IS_REGULAR);
g_free (cert_file);
return exists;
}
static void
servlist_update_cert_buttons (ircnet *net)
{
gboolean has_cert = servlist_network_cert_exists (net);
if (edit_button_cert_generate)
gtk_widget_set_visible (edit_button_cert_generate, !has_cert);
if (edit_button_cert_info)
gtk_widget_set_visible (edit_button_cert_info, has_cert);
if (edit_button_cert_delete)
gtk_widget_set_visible (edit_button_cert_delete, has_cert);
}
static void
servlist_generate_client_cert_cb (GtkWidget *button, gpointer userdata)
{
#ifdef USE_OPENSSL
ircnet *net = (ircnet *)userdata;
GtkWidget *dialog;
char *cert_dir;
char *cert_file;
char *key_file;
char *crt_file;
char *subject;
char *openssl_conf;
const char *conf_data;
char *key_data;
char *crt_data;
char *pem_data;
char *stderr_data;
char *stdout_data;
gsize key_len;
gsize crt_len;
gboolean spawned;
gboolean success;
gint status;
char *argv[20];
if (!net || !net->name || !net->name[0])
return;
cert_dir = g_build_filename (get_xdir (), "certs", NULL);
cert_file = servlist_get_cert_file (net);
key_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.key", cert_dir, net->name);
crt_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.crt", cert_dir, net->name);
subject = g_strdup_printf ("/CN=%s", net->name);
openssl_conf = g_build_filename (cert_dir, "openssl.cnf", NULL);
conf_data = "[req]\n"
"distinguished_name=req_distinguished_name\n"
"[req_distinguished_name]\n";
key_data = NULL;
crt_data = NULL;
pem_data = NULL;
stderr_data = NULL;
stdout_data = NULL;
key_len = 0;
crt_len = 0;
success = FALSE;
status = 0;
if (g_mkdir_with_parents (cert_dir, 0700) == 0 &&
g_file_set_contents (openssl_conf, conf_data, -1, NULL))
{
argv[0] = "openssl";
argv[1] = "req";
argv[2] = "-x509";
argv[3] = "-newkey";
argv[4] = "ec";
argv[5] = "-pkeyopt";
argv[6] = "ec_paramgen_curve:P-256";
argv[7] = "-sha256";
argv[8] = "-days";
argv[9] = "3650";
argv[10] = "-nodes";
argv[11] = "-keyout";
argv[12] = key_file;
argv[13] = "-out";
argv[14] = crt_file;
argv[15] = "-config";
argv[16] = openssl_conf;
argv[17] = "-subj";
argv[18] = subject;
argv[19] = NULL;
spawned = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
&stdout_data, &stderr_data, &status, NULL);
if (spawned && g_spawn_check_exit_status (status, NULL) &&
g_file_get_contents (key_file, &key_data, &key_len, NULL) &&
g_file_get_contents (crt_file, &crt_data, &crt_len, NULL))
{
pem_data = g_strconcat (key_data, crt_data, NULL);
if (pem_data && g_file_set_contents (cert_file, pem_data, -1, NULL))
{
chmod (cert_file, 0600);
success = TRUE;
}
}
}
g_remove (key_file);
g_remove (crt_file);
g_remove (openssl_conf);
if (success)
{
servlist_update_cert_buttons (net);
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE,
_("Client certificate generated for \"%s\"."),
net->name);
}
else
{
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
_("Failed to generate the client certificate for \"%s\"."),
net->name);
if (stderr_data && stderr_data[0])
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", stderr_data);
}
theme_manager_attach_window (dialog);
g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
gtk_widget_show (dialog);
g_free (stdout_data);
g_free (stderr_data);
g_free (pem_data);
g_free (key_data);
g_free (crt_data);
g_free (subject);
g_free (crt_file);
g_free (key_file);
g_free (openssl_conf);
g_free (cert_file);
g_free (cert_dir);
#else
return;
#endif
}
static void
servlist_cert_info_cb (GtkWidget *button, gpointer userdata)
{
#ifdef USE_OPENSSL
ircnet *net = (ircnet *)userdata;
GtkWidget *dialog;
char *cert_file;
char *stdout_data;
char *stderr_data;
gboolean spawned;
gint status;
char *argv[12];
cert_file = servlist_get_cert_file (net);
if (!cert_file)
return;
stdout_data = NULL;
stderr_data = NULL;
status = 0;
argv[0] = "openssl";
argv[1] = "x509";
argv[2] = "-in";
argv[3] = cert_file;
argv[4] = "-noout";
argv[5] = "-subject";
argv[6] = "-issuer";
argv[7] = "-startdate";
argv[8] = "-enddate";
argv[9] = "-fingerprint";
argv[10] = "-sha256";
argv[11] = NULL;
spawned = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
&stdout_data, &stderr_data, &status, NULL);
if (spawned && g_spawn_check_exit_status (status, NULL) && stdout_data && stdout_data[0])
{
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE,
_("Client certificate information for \"%s\"."),
net->name);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", stdout_data);
}
else
{
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
_("Failed to read client certificate information for \"%s\"."),
net->name);
if (stderr_data && stderr_data[0])
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", stderr_data);
}
theme_manager_attach_window (dialog);
g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
gtk_widget_show (dialog);
g_free (stdout_data);
g_free (stderr_data);
g_free (cert_file);
#else
return;
#endif
}
static void
servlist_delete_client_cert_cb (GtkWidget *button, gpointer userdata)
{
ircnet *net = (ircnet *)userdata;
GtkWidget *dialog;
char *cert_file;
cert_file = servlist_get_cert_file (net);
if (!cert_file)
return;
if (g_remove (cert_file) == 0)
{
servlist_update_cert_buttons (net);
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE,
_("Client certificate removed for \"%s\"."),
net->name);
}
else
{
dialog = gtk_message_dialog_new (GTK_WINDOW (edit_win),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
_("Failed to remove client certificate for \"%s\"."),
net->name);
}
theme_manager_attach_window (dialog);
g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
gtk_widget_show (dialog);
g_free (cert_file);
}
static GtkWidget * static GtkWidget *
servlist_icon_button_new (const char *label, const char *icon_name) servlist_icon_button_new (const char *label, const char *icon_name)
{ {
@@ -1783,6 +2062,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
GtkWidget *buttonadd; GtkWidget *buttonadd;
GtkWidget *buttonremove; GtkWidget *buttonremove;
GtkWidget *buttonedit; GtkWidget *buttonedit;
GtkWidget *hbox_cert_buttons;
GtkWidget *hseparator2; GtkWidget *hseparator2;
GtkWidget *hbuttonbox4; GtkWidget *hbuttonbox4;
GtkWidget *button10; GtkWidget *button10;
@@ -1947,7 +2227,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
/* Checkboxes and entries */ /* Checkboxes and entries */
table3 = gtkutil_grid_new (13, 2, FALSE); table3 = gtkutil_grid_new (14, 2, FALSE);
gtk_box_pack_start (GTK_BOX (vbox5), table3, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox5), table3, FALSE, FALSE, 0);
gtk_grid_set_row_spacing (GTK_GRID (table3), 2); gtk_grid_set_row_spacing (GTK_GRID (table3), 2);
gtk_grid_set_column_spacing (GTK_GRID (table3), 8); gtk_grid_set_column_spacing (GTK_GRID (table3), 8);
@@ -2002,6 +2282,27 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
SERVLIST_ALIGN_FILL, SERVLIST_ALIGN_FILL, SERVLIST_ALIGN_FILL, SERVLIST_ALIGN_FILL,
4, 2); 4, 2);
hbox_cert_buttons = gtkutil_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 6);
edit_button_cert_generate = gtk_button_new_with_mnemonic (_("Generate client SSL cert"));
g_signal_connect (G_OBJECT (edit_button_cert_generate), "clicked",
G_CALLBACK (servlist_generate_client_cert_cb), net);
gtk_box_pack_start (GTK_BOX (hbox_cert_buttons), edit_button_cert_generate, FALSE, FALSE, 0);
edit_button_cert_info = gtk_button_new_with_mnemonic (_("Client SSL cert info"));
g_signal_connect (G_OBJECT (edit_button_cert_info), "clicked",
G_CALLBACK (servlist_cert_info_cb), net);
gtk_box_pack_start (GTK_BOX (hbox_cert_buttons), edit_button_cert_info, FALSE, FALSE, 0);
edit_button_cert_delete = gtk_button_new_with_mnemonic (_("Delete cert"));
g_signal_connect (G_OBJECT (edit_button_cert_delete), "clicked",
G_CALLBACK (servlist_delete_client_cert_cb), net);
gtk_box_pack_start (GTK_BOX (hbox_cert_buttons), edit_button_cert_delete, FALSE, FALSE, 0);
servlist_table_attach (table3, hbox_cert_buttons, 0, 2, 13, 14,
FALSE, FALSE,
SERVLIST_ALIGN_START, SERVLIST_ALIGN_CENTER,
SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
/* Rule and Close button */ /* Rule and Close button */
hseparator2 = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); hseparator2 = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
@@ -2026,6 +2327,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
gtk_widget_grab_default (button10); gtk_widget_grab_default (button10);
gtk_widget_show_all (editwindow); gtk_widget_show_all (editwindow);
servlist_update_cert_buttons (net);
/* We can't set the active tab without child elements being shown, so this must be *after* gtk_widget_show()s! */ /* We can't set the active tab without child elements being shown, so this must be *after* gtk_widget_show()s! */
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), netedit_active_tab); gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), netedit_active_tab);

View File

@@ -536,6 +536,7 @@ static const setting general_settings[] =
{ST_TOGGLE, N_("WHOIS on notify"), P_OFFINTNL(hex_notify_whois_online), N_("Sends a /WHOIS when a user comes online in your notify list."), 0, 0}, {ST_TOGGLE, N_("WHOIS on notify"), P_OFFINTNL(hex_notify_whois_online), N_("Sends a /WHOIS when a user comes online in your notify list."), 0, 0},
{ST_TOGGLE, N_("Hide join and part messages"), P_OFFINTNL(hex_irc_conf_mode), N_("Hide channel join/part messages by default."), 0, 0}, {ST_TOGGLE, N_("Hide join and part messages"), P_OFFINTNL(hex_irc_conf_mode), N_("Hide channel join/part messages by default."), 0, 0},
{ST_TOGGLE, N_("Hide nick change messages"), P_OFFINTNL(hex_irc_hide_nickchange), 0, 0, 0}, {ST_TOGGLE, N_("Hide nick change messages"), P_OFFINTNL(hex_irc_hide_nickchange), 0, 0, 0},
{ST_TOGGLE, N_("Enable Ctrl+Q to quit"), P_OFFINTNL(hex_gui_ctrlq_quit), 0, 0, 0},
{ST_END, 0, 0, 0, 0, 0} {ST_END, 0, 0, 0, 0, 0}
}; };
@@ -2070,6 +2071,7 @@ setup_apply_real (const ThemeChangedEvent *event)
} }
mg_apply_setup (); mg_apply_setup ();
menu_update_quit_accel ();
tray_apply_setup (); tray_apply_setup ();
zoitechat_reinit_timers (); zoitechat_reinit_timers ();

View File

@@ -136,6 +136,7 @@ static void sexy_spell_entry_finalize(GObject *obj);
static void sexy_spell_entry_destroy(GObject *obj); static void sexy_spell_entry_destroy(GObject *obj);
static gboolean sexy_spell_entry_draw(GtkWidget *widget, cairo_t *cr); static gboolean sexy_spell_entry_draw(GtkWidget *widget, cairo_t *cr);
static gint sexy_spell_entry_button_press(GtkWidget *widget, GdkEventButton *event); static gint sexy_spell_entry_button_press(GtkWidget *widget, GdkEventButton *event);
static void sexy_spell_entry_style_updated (GtkWidget *widget);
/* GtkEditable handlers */ /* GtkEditable handlers */
static void sexy_spell_entry_changed(GtkEditable *editable, gpointer data); static void sexy_spell_entry_changed(GtkEditable *editable, gpointer data);
@@ -280,6 +281,7 @@ sexy_spell_entry_class_init(SexySpellEntryClass *klass)
widget_class->draw = sexy_spell_entry_draw; widget_class->draw = sexy_spell_entry_draw;
widget_class->button_press_event = sexy_spell_entry_button_press; widget_class->button_press_event = sexy_spell_entry_button_press;
widget_class->style_updated = sexy_spell_entry_style_updated;
/** /**
* SexySpellEntry::word-check: * SexySpellEntry::word-check:
@@ -348,24 +350,54 @@ gtk_entry_find_position (GtkEntry *entry, gint x)
static void static void
insert_hiddenchar (SexySpellEntry *entry, guint start, guint end) insert_hiddenchar (SexySpellEntry *entry, guint start, guint end)
{ {
/* FIXME: Pango does not properly reflect the new widths after a char
* is 'hidden' */
#if 0
PangoAttribute *hattr; PangoAttribute *hattr;
PangoRectangle *rect = g_new (PangoRectangle, 1); PangoRectangle rect = { 0 };
rect->x = 0; hattr = pango_attr_shape_new (&rect, &rect);
rect->y = 0;
rect->width = 0;
rect->height = 0;
hattr = pango_attr_shape_new (rect, rect);
hattr->start_index = start; hattr->start_index = start;
hattr->end_index = end; hattr->end_index = end;
pango_attr_list_insert (entry->priv->attr_list, hattr); pango_attr_list_insert (entry->priv->attr_list, hattr);
}
g_free (rect); static guint8
#endif sexy_spell_entry_contrasting_caret_component (guint16 red, guint16 green, guint16 blue)
{
const guint16 luma = (guint16) (((red >> 8) * 299 + (green >> 8) * 587 + (blue >> 8) * 114) / 1000);
return luma >= 128 ? 0x00 : 0xff;
}
static void
sexy_spell_entry_apply_caret_style (SexySpellEntry *entry)
{
ThemeWidgetStyleValues style_values;
guint16 bg_red = 0, bg_green = 0, bg_blue = 0;
guint8 caret;
GtkCssProvider *provider;
GtkStyleContext *context;
char css[120];
theme_get_widget_style_values_for_widget (GTK_WIDGET (entry), &style_values);
theme_palette_color_get_rgb16 (&style_values.background, &bg_red, &bg_green, &bg_blue);
caret = sexy_spell_entry_contrasting_caret_component (bg_red, bg_green, bg_blue);
provider = g_object_get_data (G_OBJECT (entry), "sexy-spell-entry-caret-provider");
if (!provider)
{
provider = gtk_css_provider_new ();
g_object_set_data_full (G_OBJECT (entry), "sexy-spell-entry-caret-provider", provider, g_object_unref);
}
g_snprintf (css, sizeof (css), "#zoitechat-inputbox, #zoitechat-inputbox text { caret-color: #%02x%02x%02x; }",
caret, caret, caret);
gtk_css_provider_load_from_data (provider, css, -1, NULL);
context = gtk_widget_get_style_context (GTK_WIDGET (entry));
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
}
static void
sexy_spell_entry_style_updated (GtkWidget *widget)
{
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
if (SEXY_IS_SPELL_ENTRY (widget))
sexy_spell_entry_apply_caret_style (SEXY_SPELL_ENTRY (widget));
} }
static void static void
@@ -878,6 +910,7 @@ sexy_spell_entry_init(SexySpellEntry *entry)
g_signal_connect(G_OBJECT(entry), "popup-menu", G_CALLBACK(sexy_spell_entry_popup_menu), entry); g_signal_connect(G_OBJECT(entry), "popup-menu", G_CALLBACK(sexy_spell_entry_popup_menu), entry);
g_signal_connect(G_OBJECT(entry), "populate-popup", G_CALLBACK(sexy_spell_entry_populate_popup), NULL); g_signal_connect(G_OBJECT(entry), "populate-popup", G_CALLBACK(sexy_spell_entry_populate_popup), NULL);
g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sexy_spell_entry_changed), NULL); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sexy_spell_entry_changed), NULL);
sexy_spell_entry_apply_caret_style (entry);
} }
static void static void
@@ -1089,6 +1122,7 @@ check_attributes (SexySpellEntry *entry, const char *text, int len)
break; break;
case ATTR_COLOR: case ATTR_COLOR:
insert_hiddenchar (entry, i, i + 1);
parsing_color = 1; parsing_color = 1;
offset = 1; offset = 1;
break; break;
@@ -1162,13 +1196,28 @@ check_color:
} }
} }
} }
if (parsing_color)
{
if (bg_color[0] != 0)
{
insert_color (entry, len, atoi (fg_color), atoi (bg_color));
}
else if (fg_color[0] != 0)
{
insert_color (entry, len, atoi (fg_color), -1);
}
else
{
insert_color (entry, len, -1, -1);
}
}
} }
static gboolean static gboolean
attr_list_has_attrs (PangoAttrList *attrs) attr_list_has_attrs (PangoAttrList *attrs)
{ {
PangoAttrIterator *it; PangoAttrIterator *it;
GSList *list;
gboolean has = FALSE; gboolean has = FALSE;
if (!attrs) if (!attrs)
@@ -1178,9 +1227,15 @@ attr_list_has_attrs (PangoAttrList *attrs)
if (!it) if (!it)
return FALSE; return FALSE;
list = pango_attr_iterator_get_attrs (it); do
has = (list != NULL); {
g_slist_free_full (list, (GDestroyNotify) pango_attribute_destroy); GSList *list = pango_attr_iterator_get_attrs (it);
has = (list != NULL);
g_slist_free_full (list, (GDestroyNotify) pango_attribute_destroy);
if (has)
break;
}
while (pango_attr_iterator_next (it));
pango_attr_iterator_destroy (it); pango_attr_iterator_destroy (it);
return has; return has;

View File

@@ -27,10 +27,47 @@
enum enum
{ {
THEME_XTEXT_FG_INDEX = 34, THEME_XTEXT_MIRC_COLS = 99,
THEME_XTEXT_BG_INDEX = 35 THEME_XTEXT_MARK_FG_INDEX = 99,
THEME_XTEXT_MARK_BG_INDEX = 100,
THEME_XTEXT_FG_INDEX = 101,
THEME_XTEXT_BG_INDEX = 102,
THEME_XTEXT_MARKER_INDEX = 103
}; };
static const guint8 theme_default_99_mirc_colors[THEME_XTEXT_MIRC_COLS][3] = {
{ 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x7f }, { 0x00, 0x93, 0x00 }, { 0xff, 0x00, 0x00 }, { 0x7f, 0x00, 0x00 }, { 0x9c, 0x00, 0x9c }, { 0xfc, 0x7f, 0x00 },
{ 0xff, 0xff, 0x00 }, { 0x00, 0xfc, 0x00 }, { 0x00, 0x93, 0x93 }, { 0x00, 0xff, 0xff }, { 0x00, 0x00, 0xfc }, { 0xff, 0x00, 0xff }, { 0x7f, 0x7f, 0x7f }, { 0xd2, 0xd2, 0xd2 },
{ 0x47, 0x00, 0x00 }, { 0x47, 0x21, 0x00 }, { 0x47, 0x47, 0x00 }, { 0x32, 0x47, 0x00 }, { 0x00, 0x47, 0x00 }, { 0x00, 0x47, 0x2c }, { 0x00, 0x47, 0x47 }, { 0x00, 0x2f, 0x47 },
{ 0x00, 0x00, 0x47 }, { 0x2e, 0x00, 0x47 }, { 0x47, 0x00, 0x47 }, { 0x47, 0x00, 0x2a }, { 0x74, 0x00, 0x00 }, { 0x74, 0x3a, 0x00 }, { 0x74, 0x74, 0x00 }, { 0x51, 0x74, 0x00 },
{ 0x00, 0x74, 0x00 }, { 0x00, 0x74, 0x49 }, { 0x00, 0x74, 0x74 }, { 0x00, 0x4d, 0x74 }, { 0x00, 0x00, 0x74 }, { 0x4b, 0x00, 0x74 }, { 0x74, 0x00, 0x74 }, { 0x74, 0x00, 0x45 },
{ 0xb5, 0x00, 0x00 }, { 0xb5, 0x63, 0x00 }, { 0xb5, 0xb5, 0x00 }, { 0x7d, 0xb5, 0x00 }, { 0x00, 0xb5, 0x00 }, { 0x00, 0xb5, 0x71 }, { 0x00, 0xb5, 0xb5 }, { 0x00, 0x75, 0xb5 },
{ 0x00, 0x00, 0xb5 }, { 0x75, 0x00, 0xb5 }, { 0xb5, 0x00, 0xb5 }, { 0xb5, 0x00, 0x6b }, { 0xff, 0x00, 0x00 }, { 0xff, 0x8c, 0x00 }, { 0xff, 0xff, 0x00 }, { 0xb2, 0xff, 0x00 },
{ 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0xa0 }, { 0x00, 0xff, 0xff }, { 0x00, 0xa9, 0xff }, { 0x00, 0x00, 0xff }, { 0xa5, 0x00, 0xff }, { 0xff, 0x00, 0xff }, { 0xff, 0x00, 0x98 },
{ 0xff, 0x59, 0x59 }, { 0xff, 0xb4, 0x59 }, { 0xff, 0xff, 0x71 }, { 0xcf, 0xff, 0x60 }, { 0x6f, 0xff, 0x6f }, { 0x65, 0xff, 0xc9 }, { 0x6d, 0xff, 0xff }, { 0x59, 0xcd, 0xff },
{ 0x59, 0x59, 0xff }, { 0xc4, 0x59, 0xff }, { 0xff, 0x66, 0xff }, { 0xff, 0x59, 0xbc }, { 0xff, 0x9c, 0x9c }, { 0xff, 0xd3, 0x9c }, { 0xff, 0xff, 0x9c }, { 0xe2, 0xff, 0x9c },
{ 0x9c, 0xff, 0x9c }, { 0x9c, 0xff, 0xdb }, { 0x9c, 0xff, 0xff }, { 0x9c, 0xe2, 0xff }, { 0x9c, 0x9c, 0xff }, { 0xdc, 0x9c, 0xff }, { 0xff, 0x9c, 0xff }, { 0xff, 0x94, 0xd3 },
{ 0x00, 0x00, 0x00 }, { 0x13, 0x13, 0x13 }, { 0x28, 0x28, 0x28 }, { 0x36, 0x36, 0x36 }, { 0x4d, 0x4d, 0x4d }, { 0x65, 0x65, 0x65 }, { 0x81, 0x81, 0x81 }, { 0x9f, 0x9f, 0x9f },
{ 0xbc, 0xbc, 0xbc }, { 0xe2, 0xe2, 0xe2 }, { 0xff, 0xff, 0xff }
};
static void
theme_access_apply_default_99_palette (XTextColor *palette, size_t palette_len, gboolean apply_base)
{
size_t i;
size_t start = apply_base ? 0 : 32;
if (palette_len == 0)
return;
for (i = start; i < THEME_XTEXT_MIRC_COLS && i < palette_len; i++)
{
palette[i].red = theme_default_99_mirc_colors[i][0] / 255.0;
palette[i].green = theme_default_99_mirc_colors[i][1] / 255.0;
palette[i].blue = theme_default_99_mirc_colors[i][2] / 255.0;
palette[i].alpha = 1.0;
}
}
static gboolean static gboolean
theme_token_to_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue) theme_token_to_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue)
{ {
@@ -52,7 +89,7 @@ theme_access_get_gtk_palette_map (GtkWidget *widget, ThemeGtkPaletteMap *out_map
GdkRGBA accent; GdkRGBA accent;
g_return_val_if_fail (out_map != NULL, FALSE); g_return_val_if_fail (out_map != NULL, FALSE);
if (!theme_gtk3_is_active () || widget == NULL) if (!theme_gtk3_is_active () || widget == NULL || !GTK_IS_WIDGET (widget))
return FALSE; return FALSE;
context = gtk_widget_get_style_context (widget); context = gtk_widget_get_style_context (widget);
@@ -83,9 +120,18 @@ gboolean
theme_get_mirc_color (unsigned int mirc_index, GdkRGBA *out_rgba) theme_get_mirc_color (unsigned int mirc_index, GdkRGBA *out_rgba)
{ {
ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index); ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index);
gboolean has_user_colors = theme_runtime_mode_has_user_colors (theme_runtime_is_dark_active ());
if (mirc_index >= 32) if (mirc_index >= THEME_XTEXT_MIRC_COLS)
return FALSE; return FALSE;
if (!has_user_colors || mirc_index >= 32)
{
out_rgba->red = theme_default_99_mirc_colors[mirc_index][0] / 255.0;
out_rgba->green = theme_default_99_mirc_colors[mirc_index][1] / 255.0;
out_rgba->blue = theme_default_99_mirc_colors[mirc_index][2] / 255.0;
out_rgba->alpha = 1.0;
return TRUE;
}
return theme_runtime_get_color (token, out_rgba); return theme_runtime_get_color (token, out_rgba);
} }
@@ -99,9 +145,17 @@ gboolean
theme_get_mirc_color_rgb16 (unsigned int mirc_index, guint16 *red, guint16 *green, guint16 *blue) theme_get_mirc_color_rgb16 (unsigned int mirc_index, guint16 *red, guint16 *green, guint16 *blue)
{ {
ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index); ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index);
gboolean has_user_colors = theme_runtime_mode_has_user_colors (theme_runtime_is_dark_active ());
if (mirc_index >= 32) if (mirc_index >= THEME_XTEXT_MIRC_COLS)
return FALSE; return FALSE;
if (!has_user_colors || mirc_index >= 32)
{
*red = (guint16) (theme_default_99_mirc_colors[mirc_index][0] * 257);
*green = (guint16) (theme_default_99_mirc_colors[mirc_index][1] * 257);
*blue = (guint16) (theme_default_99_mirc_colors[mirc_index][2] * 257);
return TRUE;
}
return theme_token_to_rgb16 (token, red, green, blue); return theme_token_to_rgb16 (token, red, green, blue);
} }
@@ -145,12 +199,40 @@ void
theme_get_xtext_colors_for_widget (GtkWidget *widget, XTextColor *palette, size_t palette_len) theme_get_xtext_colors_for_widget (GtkWidget *widget, XTextColor *palette, size_t palette_len)
{ {
ThemeWidgetStyleValues style_values; ThemeWidgetStyleValues style_values;
gboolean has_user_colors;
GdkRGBA marker_color;
if (!palette) if (!palette)
return; return;
theme_get_widget_style_values_for_widget (widget, &style_values); theme_get_widget_style_values_for_widget (widget, &style_values);
theme_runtime_get_xtext_colors (palette, palette_len); theme_runtime_get_xtext_colors (palette, palette_len);
has_user_colors = theme_runtime_mode_has_user_colors (theme_runtime_is_dark_active ());
if (palette_len >= THEME_XTEXT_MIRC_COLS)
theme_access_apply_default_99_palette (palette, palette_len, !has_user_colors);
if (palette_len > THEME_XTEXT_MARK_FG_INDEX)
{
palette[THEME_XTEXT_MARK_FG_INDEX].red = style_values.selection_foreground.red;
palette[THEME_XTEXT_MARK_FG_INDEX].green = style_values.selection_foreground.green;
palette[THEME_XTEXT_MARK_FG_INDEX].blue = style_values.selection_foreground.blue;
palette[THEME_XTEXT_MARK_FG_INDEX].alpha = style_values.selection_foreground.alpha;
}
if (palette_len > THEME_XTEXT_MARK_BG_INDEX)
{
palette[THEME_XTEXT_MARK_BG_INDEX].red = style_values.selection_background.red;
palette[THEME_XTEXT_MARK_BG_INDEX].green = style_values.selection_background.green;
palette[THEME_XTEXT_MARK_BG_INDEX].blue = style_values.selection_background.blue;
palette[THEME_XTEXT_MARK_BG_INDEX].alpha = style_values.selection_background.alpha;
}
if (palette_len > THEME_XTEXT_MARKER_INDEX)
{
if (!theme_runtime_get_color (THEME_TOKEN_MARKER, &marker_color))
marker_color = style_values.selection_background;
palette[THEME_XTEXT_MARKER_INDEX].red = marker_color.red;
palette[THEME_XTEXT_MARKER_INDEX].green = marker_color.green;
palette[THEME_XTEXT_MARKER_INDEX].blue = marker_color.blue;
palette[THEME_XTEXT_MARKER_INDEX].alpha = marker_color.alpha;
}
if (palette_len > THEME_XTEXT_FG_INDEX) if (palette_len > THEME_XTEXT_FG_INDEX)
{ {
palette[THEME_XTEXT_FG_INDEX].red = style_values.foreground.red; palette[THEME_XTEXT_FG_INDEX].red = style_values.foreground.red;

View File

@@ -93,7 +93,7 @@ theme_css_apply_widget_provider (GtkWidget *widget, GtkStyleProvider *provider)
{ {
GtkStyleContext *context; GtkStyleContext *context;
if (!widget || !provider) if (!widget || !provider || !GTK_IS_WIDGET (widget))
return; return;
context = gtk_widget_get_style_context (widget); context = gtk_widget_get_style_context (widget);
@@ -167,10 +167,14 @@ theme_css_build_input (const char *theme_name, guint16 fg_red, guint16 fg_green,
"background-color: #%02x%02x%02x;" "background-color: #%02x%02x%02x;"
"color: #%02x%02x%02x;" "color: #%02x%02x%02x;"
"caret-color: #%02x%02x%02x;" "caret-color: #%02x%02x%02x;"
"word-spacing: normal;"
"letter-spacing: normal;"
"}" "}"
"%s {" "%s {"
"color: #%02x%02x%02x;" "color: #%02x%02x%02x;"
"caret-color: #%02x%02x%02x;" "caret-color: #%02x%02x%02x;"
"word-spacing: normal;"
"letter-spacing: normal;"
"}" "}"
"%s selection, %s text selection, %s:focus selection, %s:focus text selection, %s *:selected, %s *:selected:focus {" "%s selection, %s text selection, %s:focus selection, %s:focus text selection, %s *:selected, %s *:selected:focus {"
"background-color: #%02x%02x%02x;" "background-color: #%02x%02x%02x;"
@@ -276,7 +280,7 @@ theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkR
gchar *sel_bg_color = NULL; gchar *sel_bg_color = NULL;
gchar *sel_fg_color = NULL; gchar *sel_fg_color = NULL;
if (!widget) if (!widget || !GTK_IS_WIDGET (widget))
return; return;
provider = g_object_get_data (G_OBJECT (widget), theme_css_palette_provider_key); provider = g_object_get_data (G_OBJECT (widget), theme_css_palette_provider_key);

View File

@@ -394,15 +394,18 @@ theme_manager_handle_theme_applied (void)
static gboolean static gboolean
theme_manager_is_kde_wayland (void) theme_manager_is_kde_wayland (void)
{ {
const char *wayland_display;
const char *desktop; const char *desktop;
char *desktop_lower; char *desktop_lower;
gboolean is_kde; gboolean is_kde;
wayland_display = g_getenv ("WAYLAND_DISPLAY");
if (!wayland_display || !wayland_display[0])
return FALSE;
desktop = g_getenv ("XDG_CURRENT_DESKTOP"); desktop = g_getenv ("XDG_CURRENT_DESKTOP");
if (!desktop || !desktop[0]) if (!desktop || !desktop[0])
desktop = g_getenv ("XDG_SESSION_DESKTOP"); desktop = g_getenv ("XDG_SESSION_DESKTOP");
if ((!desktop || !desktop[0]) && g_getenv ("KDE_FULL_SESSION"))
return TRUE;
if (!desktop || !desktop[0]) if (!desktop || !desktop[0])
return FALSE; return FALSE;
@@ -438,7 +441,7 @@ theme_manager_apply_wayland_kde_csd (GtkWidget *window)
headerbar = gtk_header_bar_new (); headerbar = gtk_header_bar_new ();
gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE); gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (headerbar), ":minimize,maximize,close"); gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (headerbar), "menu:minimize,maximize,close");
icon_pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/icons/zoitechat.svg", 32, 32, TRUE, NULL); icon_pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/icons/zoitechat.svg", 32, 32, TRUE, NULL);
if (!icon_pixbuf) if (!icon_pixbuf)
icon_pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/icons/zoitechat.png", 32, 32, TRUE, NULL); icon_pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/icons/zoitechat.png", 32, 32, TRUE, NULL);

View File

@@ -427,7 +427,6 @@ enum
GTK3_THEME_COL_ID = 0, GTK3_THEME_COL_ID = 0,
GTK3_THEME_COL_LABEL, GTK3_THEME_COL_LABEL,
GTK3_THEME_COL_SOURCE, GTK3_THEME_COL_SOURCE,
GTK3_THEME_COL_THUMBNAIL,
GTK3_THEME_COL_COUNT GTK3_THEME_COL_COUNT
}; };
@@ -1403,60 +1402,6 @@ theme_preferences_gtk3_changed_cb (GtkComboBox *combo, gpointer user_data)
g_free (id); g_free (id);
} }
static GdkPixbuf *
theme_preferences_load_thumbnail (const char *path)
{
char *data = NULL;
gsize length = 0;
GdkPixbufLoader *loader;
GError *error = NULL;
GdkPixbuf *pixbuf;
GdkPixbuf *scaled;
int width;
int height;
if (!path || !g_file_get_contents (path, &data, &length, &error))
{
g_clear_error (&error);
return NULL;
}
loader = gdk_pixbuf_loader_new ();
if (!gdk_pixbuf_loader_write (loader, (const guchar *) data, length, &error))
{
g_clear_error (&error);
g_object_unref (loader);
g_free (data);
return NULL;
}
g_free (data);
if (!gdk_pixbuf_loader_close (loader, &error))
{
g_clear_error (&error);
g_object_unref (loader);
return NULL;
}
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
if (!pixbuf)
{
g_object_unref (loader);
return NULL;
}
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
if (width > 48 || height > 48)
scaled = gdk_pixbuf_scale_simple (pixbuf, 48, 48, GDK_INTERP_BILINEAR);
else
scaled = gdk_pixbuf_copy (pixbuf);
g_object_unref (loader);
return scaled;
}
static int static int
theme_preferences_gtk3_find_system_theme_index (GPtrArray *themes) theme_preferences_gtk3_find_system_theme_index (GPtrArray *themes)
{ {
@@ -1515,22 +1460,15 @@ theme_preferences_populate_gtk3 (theme_preferences_ui *ui)
ZoitechatGtk3Theme *theme = g_ptr_array_index (themes, i); ZoitechatGtk3Theme *theme = g_ptr_array_index (themes, i);
char *label = g_strdup_printf ("%s (%s)", theme->display_name, char *label = g_strdup_printf ("%s (%s)", theme->display_name,
theme->source == ZOITECHAT_GTK3_THEME_SOURCE_USER ? _("user") : _("system")); theme->source == ZOITECHAT_GTK3_THEME_SOURCE_USER ? _("user") : _("system"));
GdkPixbuf *thumbnail = NULL;
if (theme->thumbnail_path && g_file_test (theme->thumbnail_path, G_FILE_TEST_IS_REGULAR))
thumbnail = theme_preferences_load_thumbnail (theme->thumbnail_path);
gtk_tree_store_append (store, &iter, NULL); gtk_tree_store_append (store, &iter, NULL);
gtk_tree_store_set (store, &iter, gtk_tree_store_set (store, &iter,
GTK3_THEME_COL_ID, theme->id, GTK3_THEME_COL_ID, theme->id,
GTK3_THEME_COL_LABEL, label, GTK3_THEME_COL_LABEL, label,
GTK3_THEME_COL_SOURCE, theme->source, GTK3_THEME_COL_SOURCE, theme->source,
GTK3_THEME_COL_THUMBNAIL, thumbnail,
-1); -1);
if (g_strcmp0 (prefs.hex_gui_gtk3_theme, theme->id) == 0) if (g_strcmp0 (prefs.hex_gui_gtk3_theme, theme->id) == 0)
active = i; active = i;
if (thumbnail)
g_object_unref (thumbnail);
g_free (label); g_free (label);
} }
if (active < 0 && using_system_default) if (active < 0 && using_system_default)
@@ -1715,13 +1653,9 @@ theme_preferences_create_page (GtkWindow *parent,
gtk3_store = gtk_tree_store_new (GTK3_THEME_COL_COUNT, gtk3_store = gtk_tree_store_new (GTK3_THEME_COL_COUNT,
G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_INT, G_TYPE_INT);
GDK_TYPE_PIXBUF);
ui->gtk3_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (gtk3_store)); ui->gtk3_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (gtk3_store));
g_object_unref (gtk3_store); g_object_unref (gtk3_store);
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, FALSE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, "pixbuf", GTK3_THEME_COL_THUMBNAIL);
renderer = gtk_cell_renderer_text_new (); renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, TRUE); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, TRUE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, "text", GTK3_THEME_COL_LABEL); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, "text", GTK3_THEME_COL_LABEL);

View File

@@ -326,7 +326,8 @@ theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
gboolean gboolean
theme_runtime_mode_has_user_colors (gboolean dark_mode) theme_runtime_mode_has_user_colors (gboolean dark_mode)
{ {
(void) dark_mode; if (dark_mode)
return dark_user_colors_valid;
return user_colors_valid; return user_colors_valid;
} }
@@ -467,7 +468,6 @@ theme_runtime_load (void)
active_palette = light_palette; active_palette = light_palette;
dark_mode_active = FALSE; dark_mode_active = FALSE;
user_colors_valid = TRUE;
} }
static gboolean static gboolean

View File

@@ -44,14 +44,29 @@
enum enum
{ {
COL_PIX=0, /* GdkPixbuf * */ COL_PIX=0, /* GdkPixbuf * */
COL_NICK=1, /* char * */ COL_PREFIX=1, /* char * */
COL_HOST=2, /* char * */ COL_NICK=2, /* char * */
COL_USER=3, /* struct User * */ COL_HOST=3, /* char * */
COL_GDKCOLOR=4 /* GdkRGBA */ COL_USER=4, /* struct User * */
COL_GDKCOLOR=5 /* GdkRGBA */
}; };
static void userlist_store_color (GtkListStore *store, GtkTreeIter *iter, ThemeSemanticToken token, gboolean has_token); static void userlist_store_color (GtkListStore *store, GtkTreeIter *iter, ThemeSemanticToken token, gboolean has_token);
static const char *
userlist_prefix_color (char prefix)
{
switch (prefix)
{
case '~': return "#d46a6a";
case '@': return "#5ea36a";
case '%': return "#d39a5f";
case '&': return "#79aecd";
case '+': return "#d2bf6a";
default: return NULL;
}
}
static void static void
userlist_column_width_notify_cb (GtkTreeViewColumn *column, GParamSpec *pspec, gpointer userdata) userlist_column_width_notify_cb (GtkTreeViewColumn *column, GParamSpec *pspec, gpointer userdata)
{ {
@@ -500,6 +515,11 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
GdkPixbuf *pix = get_user_icon (sess->server, newuser); GdkPixbuf *pix = get_user_icon (sess->server, newuser);
GtkTreeIter iter; GtkTreeIter iter;
char *nick; char *nick;
char *nick_escaped;
char *prefix = NULL;
char *prefix_escaped;
char prefix_text[2];
const char *prefix_color;
ThemeSemanticToken nick_token = THEME_TOKEN_TEXT_FOREGROUND; ThemeSemanticToken nick_token = THEME_TOKEN_TEXT_FOREGROUND;
gboolean have_nick_token = FALSE; gboolean have_nick_token = FALSE;
@@ -519,30 +539,36 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
} }
} }
nick = newuser->nick; nick_escaped = g_markup_escape_text (newuser->nick, -1);
nick = nick_escaped;
if (!prefs.hex_gui_ulist_icons) if (!prefs.hex_gui_ulist_icons)
{ {
nick = g_malloc (strlen (newuser->nick) + 2); if (newuser->prefix[0] != '\0' && newuser->prefix[0] != ' ')
nick[0] = newuser->prefix[0]; {
if (nick[0] == '\0' || nick[0] == ' ') prefix_text[0] = newuser->prefix[0];
strcpy (nick, newuser->nick); prefix_text[1] = '\0';
else prefix_escaped = g_markup_escape_text (prefix_text, -1);
strcpy (nick + 1, newuser->nick); prefix_color = userlist_prefix_color (newuser->prefix[0]);
if (prefix_color)
prefix = g_strdup_printf ("<b><span foreground=\"%s\">%s</span></b>", prefix_color, prefix_escaped);
else
prefix = g_strdup_printf ("<b>%s</b>", prefix_escaped);
g_free (prefix_escaped);
}
pix = NULL; pix = NULL;
} }
gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, 0, gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, 0,
COL_PIX, pix, COL_PIX, pix,
COL_PREFIX, prefix,
COL_NICK, nick, COL_NICK, nick,
COL_HOST, newuser->hostname, COL_HOST, newuser->hostname,
COL_USER, newuser, COL_USER, newuser,
-1); -1);
userlist_store_color (GTK_LIST_STORE (model), &iter, nick_token, have_nick_token); userlist_store_color (GTK_LIST_STORE (model), &iter, nick_token, have_nick_token);
if (!prefs.hex_gui_ulist_icons) g_free (prefix);
{ g_free (nick_escaped);
g_free (nick);
}
userlist_row_map_set (sess, model, newuser, &iter); userlist_row_map_set (sess, model, newuser, &iter);
@@ -671,8 +697,8 @@ userlist_create_model (session *sess)
GtkTreeIterCompareFunc cmp_func; GtkTreeIterCompareFunc cmp_func;
GtkSortType sort_type; GtkSortType sort_type;
store = gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, store = gtk_list_store_new (6, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_POINTER, THEME_GTK_COLOR_TYPE); G_TYPE_STRING, G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
switch (prefs.hex_gui_ulist_sort) switch (prefs.hex_gui_ulist_sort)
{ {
@@ -717,19 +743,29 @@ userlist_add_columns (GtkTreeView * treeview)
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL); g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
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", COL_PIX, NULL);
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 0); column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 0);
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
/* nick column */ /* nick column */
column = gtk_tree_view_column_new ();
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
renderer = gtk_cell_renderer_text_new ();
if (prefs.hex_gui_compact)
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_add_attribute (column, renderer, "markup", COL_PREFIX);
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); 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_column_pack_start (column, renderer, TRUE);
-1, NULL, renderer, gtk_tree_view_column_add_attribute (column, renderer, "markup", COL_NICK);
"text", 1, THEME_GTK_FOREGROUND_PROPERTY, 4, NULL); gtk_tree_view_column_add_attribute (column, renderer, THEME_GTK_FOREGROUND_PROPERTY, COL_GDKCOLOR);
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 1); 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_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_view_column_set_expand (column, TRUE);
@@ -750,7 +786,7 @@ userlist_add_columns (GtkTreeView * treeview)
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", COL_HOST, NULL);
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 2); 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_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_view_column_set_expand (column, TRUE);

View File

@@ -2311,7 +2311,16 @@ gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event)
if (!window) if (!window)
return FALSE; return FALSE;
gtk_xtext_get_pointer (window, &x, &y, &mask); if (event->is_hint)
{
gtk_xtext_get_pointer (window, &x, &y, &mask);
}
else
{
x = (int)event->x;
y = (int)event->y;
mask = event->state;
}
if (xtext->moving_separator) if (xtext->moving_separator)
{ {
@@ -2487,9 +2496,12 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
GtkXText *xtext = GTK_XTEXT (widget); GtkXText *xtext = GTK_XTEXT (widget);
unsigned char *word; unsigned char *word;
int old; int old;
int event_x, event_y;
GtkAllocation allocation; GtkAllocation allocation;
gtk_widget_get_allocation (widget, &allocation); gtk_widget_get_allocation (widget, &allocation);
event_x = (int)event->x;
event_y = (int)event->y;
if (xtext->moving_separator) if (xtext->moving_separator)
{ {
@@ -2539,8 +2551,8 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
return FALSE; return FALSE;
} }
if (xtext->select_start_x == event->x && if (xtext->select_start_x == event_x &&
xtext->select_start_y == event->y && xtext->select_start_y == event_y &&
xtext->buffer->last_ent_start) xtext->buffer->last_ent_start)
{ {
gtk_xtext_unselect (xtext); gtk_xtext_unselect (xtext);
@@ -2550,7 +2562,7 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
if (!gtk_xtext_is_selecting (xtext)) if (!gtk_xtext_is_selecting (xtext))
{ {
word = gtk_xtext_get_word (xtext, event->x, event->y, 0, 0, 0, 0); word = gtk_xtext_get_word (xtext, event_x, event_y, 0, 0, 0, 0);
g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0, word ? word : NULL, event); g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0, word ? word : NULL, event);
} }
} }
@@ -2572,7 +2584,9 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
if (!window) if (!window)
return FALSE; return FALSE;
gtk_xtext_get_pointer (window, &x, &y, &mask); x = (int)event->x;
y = (int)event->y;
mask = event->state;
if (event->button == 3 || event->button == 2) /* right/middle click */ if (event->button == 3 || event->button == 2) /* right/middle click */
{ {
@@ -2640,6 +2654,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

@@ -49,14 +49,14 @@ GType gtk_xtext_get_type (void);
#define ATTR_UNDERLINE '\037' #define ATTR_UNDERLINE '\037'
/* these match palette.h */ /* these match palette.h */
#define XTEXT_MIRC_COLS 32 #define XTEXT_MIRC_COLS 99
#define XTEXT_COLS 37 /* 32 plus 5 for extra stuff below */ #define XTEXT_COLS 104 /* 99 plus 5 for extra stuff below */
#define XTEXT_MARK_FG 32 /* for marking text */ #define XTEXT_MARK_FG 99 /* for marking text */
#define XTEXT_MARK_BG 33 #define XTEXT_MARK_BG 100
#define XTEXT_FG 34 #define XTEXT_FG 101
#define XTEXT_BG 35 #define XTEXT_BG 102
#define XTEXT_MARKER 36 /* for marker line */ #define XTEXT_MARKER 103 /* for marker line */
#define XTEXT_MAX_COLOR 41 #define XTEXT_MAX_COLOR 98
typedef struct textentry textentry; typedef struct textentry textentry;
/* /*

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);
}
g_free (locale_path);
g_free (base_path);
#else
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); 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;
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////