mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-09-22 23:57:10 +00:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa52236c01 | ||
|
|
cd5b61d9d0 | ||
|
|
4eefdd6011 | ||
|
|
21748b3133 | ||
|
|
e7db202f88 | ||
|
|
a93e76eaf4 | ||
|
|
3c5bdb2ab2 | ||
|
|
8f7c40caf1 | ||
|
|
150ad62771 | ||
|
|
5e3715dc31 | ||
|
|
05648ba8e3 | ||
|
|
0956471a10 | ||
|
|
1f79e62f53 | ||
|
|
e49333340f |
@@ -25,6 +25,9 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Generate PSL list
|
||||
run: curl -fsSL https://publicsuffix.org/list/public_suffix_list.dat -o src/common/public_suffix_list.dat
|
||||
|
||||
- name: Build Flatpak
|
||||
id: flatpak_builder
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
ZoiteChat ChangeLog
|
||||
=================
|
||||
|
||||
2.18.0 (2026-04-20)
|
||||
|
||||
- Added optional close buttons on tabs.
|
||||
- Added Ctrl+W to close tabs and Ctrl+Shift+T to reopen recently closed tabs.
|
||||
- Expanded theme import support with .hct support in colors.conf import.
|
||||
- Added pevent import support from .hct theme files and improved import result messaging.
|
||||
- Expanded palette and selection CSS styling for better theme consistency.
|
||||
- Auto-sizes the topic bar to wrapped text for better multi-line topic handling.
|
||||
- Refined command-character parsing when pasting path-like text.
|
||||
- Tightened topic link detection and opening rules.
|
||||
- Added Public Suffix List validation for host links to reduce bad link matches.
|
||||
- Improved channel tree behavior by ignoring collapsed parent selections.
|
||||
- Sanitized the Linux open environment for AppImage builds and added safer fallback handling.
|
||||
- Labeled Windows installer runtimes with exact versions.
|
||||
- Fixed the notification icon and corrected the Flatpak screenshot asset.
|
||||
- Improved AppStream metainfo validation.
|
||||
|
||||
2.18.0~pre6 (2026-03-30)
|
||||
|
||||
- Applied app theme CSS to the menubar consistently across the app.
|
||||
|
||||
@@ -29,6 +29,36 @@
|
||||
<id>zoitechat.desktop</id>
|
||||
</provides>
|
||||
<releases>
|
||||
<release date="2026-04-20" version="2.18.0">
|
||||
<description>
|
||||
<p>Tabs and navigation:</p>
|
||||
<ul>
|
||||
<li>Added optional close buttons on tabs.</li>
|
||||
<li>Added Ctrl+W to close tabs and Ctrl+Shift+T to reopen recently closed tabs.</li>
|
||||
<li>Improved channel tree behavior by ignoring collapsed parent selections.</li>
|
||||
</ul>
|
||||
<p>Themes and appearance:</p>
|
||||
<ul>
|
||||
<li>Expanded theme import support with .hct support in colors.conf import.</li>
|
||||
<li>Added pevent import support from .hct theme files and improved import result messaging.</li>
|
||||
<li>Expanded palette and selection CSS styling for better theme consistency.</li>
|
||||
<li>Auto-sizes the topic bar to wrapped text for better multi-line topic handling.</li>
|
||||
</ul>
|
||||
<p>Links and text handling:</p>
|
||||
<ul>
|
||||
<li>Refined command-character parsing when pasting path-like text.</li>
|
||||
<li>Tightened topic link detection and opening rules.</li>
|
||||
<li>Added Public Suffix List validation for host links to reduce bad link matches.</li>
|
||||
</ul>
|
||||
<p>Packaging and platform integration:</p>
|
||||
<ul>
|
||||
<li>Sanitized the Linux open environment for AppImage builds and added safer fallback handling.</li>
|
||||
<li>Labeled Windows installer runtimes with exact versions.</li>
|
||||
<li>Fixed the notification icon and corrected the Flatpak screenshot asset.</li>
|
||||
<li>Improved AppStream metainfo validation.</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release date="2026-03-30" version="2.18.0~pre6">
|
||||
<description>
|
||||
<p>GTK theme and UI:</p>
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
"command": "zoitechat",
|
||||
"finish-args": [
|
||||
"--share=ipc",
|
||||
"--socket=x11",
|
||||
"--socket=wayland",
|
||||
"--socket=fallback-x11",
|
||||
"--share=network",
|
||||
"--socket=pulseaudio",
|
||||
"--filesystem=xdg-download",
|
||||
"--filesystem=xdg-data/themes:ro",
|
||||
"--filesystem=xdg-data/icons:ro",
|
||||
"--filesystem=~/.themes:ro",
|
||||
"--filesystem=~/.icons:ro",
|
||||
"--filesystem=xdg-run/tray-icon:create",
|
||||
"--env=GTK_CSD=1",
|
||||
"--talk-name=org.freedesktop.Notifications",
|
||||
@@ -45,9 +44,9 @@
|
||||
"buildsystem": "meson",
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/pavouk/lgi.git",
|
||||
"commit": "c9b8e4473c6421f2a215d8c06c0d94b86eb0b26a"
|
||||
"type": "archive",
|
||||
"url": "https://github.com/pavouk/lgi/archive/c9b8e4473c6421f2a215d8c06c0d94b86eb0b26a.tar.gz",
|
||||
"sha256": "db67b2b7ee89fa566f783486d56be7203552a997bc55f35020b57dd2776b9943"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
project('zoitechat', 'c',
|
||||
version: '2.18.0~pre6',
|
||||
version: '2.18.0',
|
||||
meson_version: '>= 0.55.0',
|
||||
default_options: [
|
||||
'c_std=c17',
|
||||
|
||||
@@ -19,7 +19,7 @@ else:
|
||||
if not hasattr(sys, 'argv'):
|
||||
sys.argv = ['<zoitechat>']
|
||||
|
||||
VERSION = b'2.18.0~pre6'
|
||||
VERSION = b'2.18.0'
|
||||
PLUGIN_NAME = ffi.new('char[]', b'Python')
|
||||
PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1]))
|
||||
PLUGIN_VERSION = ffi.new('char[]', VERSION)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<ClInclude Include="plugin-timer.h" />
|
||||
<ClInclude Include="plugin.h" />
|
||||
<ClInclude Include="proto-irc.h" />
|
||||
<ClInclude Include="public_suffix_data.h" />
|
||||
<ClInclude Include="server.h" />
|
||||
<ClInclude Include="servlist.h" />
|
||||
<ClInclude Include="ssl.h" />
|
||||
@@ -109,6 +110,7 @@
|
||||
<Command><![CDATA[
|
||||
SET SOLUTIONDIR=$(SolutionDir)..\
|
||||
"$(Python3Path)\python.exe" $(ProjectDir)make-te.py "$(ProjectDir)textevents.in" "$(ZoiteChatLib)textevents.h" "$(ZoiteChatLib)textenums.h"
|
||||
"$(Python3Path)\python.exe" $(ProjectDir)gen-public-suffix.py "$(ZoiteChatLib)public_suffix_data.h"
|
||||
powershell -File "$(SolutionDir)..\win32\version-template.ps1" "$(SolutionDir)..\win32\config.h.tt" "$(ZoiteChatLib)config.h"
|
||||
$(GlibGenMarshal) --prefix=_zoitechat_marshal --header "$(ProjectDir)marshalers.list" --output "$(ZoiteChatLib)marshal.h"
|
||||
$(GlibGenMarshal) --prefix=_zoitechat_marshal --body "$(ProjectDir)marshalers.list" --output "$(ZoiteChatLib)marshal.c"
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
<ClInclude Include="proto-irc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="public_suffix_data.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="server.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
URLS = (
|
||||
"https://raw.githubusercontent.com/publicsuffix/list/main/public_suffix_list.dat",
|
||||
"https://publicsuffix.org/list/public_suffix_list.dat",
|
||||
)
|
||||
|
||||
|
||||
def parse_rules(text: str):
|
||||
rules = []
|
||||
for raw in text.splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith("//"):
|
||||
continue
|
||||
if " " in line or "\t" in line:
|
||||
line = line.split()[0]
|
||||
rules.append(line.lower())
|
||||
return sorted(set(rules))
|
||||
|
||||
|
||||
def emit_header(path: str, rules):
|
||||
with open(path, "w", encoding="utf-8", newline="\n") as out:
|
||||
out.write("#pragma once\n")
|
||||
out.write("static const char * const public_suffix_rules[] = {\n")
|
||||
for rule in rules:
|
||||
escaped = rule.replace("\\", "\\\\").replace('"', '\\"')
|
||||
out.write(f'\t"{escaped}",\n')
|
||||
out.write("};\n")
|
||||
out.write(
|
||||
"static const unsigned int public_suffix_rules_len = sizeof(public_suffix_rules) / sizeof(public_suffix_rules[0]);\n"
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) not in (2, 3):
|
||||
raise SystemExit("usage: gen-public-suffix.py <output> [source]")
|
||||
output = Path(sys.argv[1])
|
||||
sources = []
|
||||
if len(sys.argv) == 3:
|
||||
sources.append(Path(sys.argv[2]))
|
||||
sources.extend(
|
||||
[
|
||||
Path(__file__).with_name("public_suffix_list.dat"),
|
||||
Path("/usr/share/publicsuffix/public_suffix_list.dat"),
|
||||
Path("/app/share/publicsuffix/public_suffix_list.dat"),
|
||||
]
|
||||
)
|
||||
data = None
|
||||
for url in URLS:
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=30) as resp:
|
||||
data = resp.read().decode("utf-8")
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
if data is None:
|
||||
for source in sources:
|
||||
if source.exists():
|
||||
data = source.read_text(encoding="utf-8")
|
||||
break
|
||||
if data is None:
|
||||
raise SystemExit("unable to load public suffix list")
|
||||
rules = parse_rules(data)
|
||||
emit_header(str(output), rules)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+10
-2
@@ -96,6 +96,14 @@ marshal = [
|
||||
|
||||
make_te = find_program('make-te.py')
|
||||
|
||||
|
||||
python3 = find_program('python3', required: true)
|
||||
|
||||
public_suffix_data = custom_target('public_suffix_data_h',
|
||||
output: 'public_suffix_data.h',
|
||||
command: [python3, files('gen-public-suffix.py'), '@OUTPUT@']
|
||||
)
|
||||
|
||||
textevents = custom_target('textevents',
|
||||
input: 'textevents.in',
|
||||
output: ['textevents.h', 'textenums.h'],
|
||||
@@ -119,7 +127,7 @@ if get_option('plugin')
|
||||
endif
|
||||
|
||||
zoitechat_common = static_library('zoitechatcommon',
|
||||
sources: [textevents] + marshal + common_sources,
|
||||
sources: [textevents, public_suffix_data] + marshal + common_sources,
|
||||
include_directories: config_h_include,
|
||||
dependencies: common_deps + common_sysinfo_deps,
|
||||
c_args: common_cflags,
|
||||
@@ -127,7 +135,7 @@ zoitechat_common = static_library('zoitechatcommon',
|
||||
)
|
||||
|
||||
zoitechat_common_dep = declare_dependency(
|
||||
sources: [textevents] + marshal,
|
||||
sources: [textevents, public_suffix_data] + marshal,
|
||||
link_with: zoitechat_common,
|
||||
include_directories: common_includes,
|
||||
compile_args: common_cflags,
|
||||
|
||||
+145
-1
@@ -20,12 +20,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <glib.h>
|
||||
#include "zoitechat.h"
|
||||
#include "zoitechatc.h"
|
||||
#include "cfgfiles.h"
|
||||
#include "fe.h"
|
||||
#include "tree.h"
|
||||
#include "url.h"
|
||||
#include "public_suffix_data.h"
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
@@ -35,6 +37,7 @@ GTree *url_btree = NULL;
|
||||
static gboolean regex_match (const GRegex *re, const char *word,
|
||||
int *start, int *end);
|
||||
static const GRegex *re_url (void);
|
||||
static const GRegex *re_url_no_scheme (void);
|
||||
static const GRegex *re_email (void);
|
||||
static const GRegex *re_nick (void);
|
||||
static const GRegex *re_channel (void);
|
||||
@@ -42,6 +45,8 @@ 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_url (const char *word, int *start, int *end);
|
||||
static gboolean match_email (const char *word, int *start, int *end);
|
||||
static gboolean host_has_public_suffix (const char *host);
|
||||
static gboolean host_has_public_suffix_range (const char *word, int start, int end);
|
||||
|
||||
static int
|
||||
url_free (char *url, void *data)
|
||||
@@ -266,7 +271,16 @@ match_channel (const char *word, int *start, int *end)
|
||||
static gboolean
|
||||
match_url (const char *word, int *start, int *end)
|
||||
{
|
||||
return regex_match (re_url (), word, start, end);
|
||||
if (regex_match (re_url (), word, start, end))
|
||||
return TRUE;
|
||||
|
||||
if (!regex_match (re_url_no_scheme (), word, start, end))
|
||||
return FALSE;
|
||||
|
||||
if (*start > 0 && word[*start - 1] == '@')
|
||||
return FALSE;
|
||||
|
||||
return host_has_public_suffix_range (word, *start, *end);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -393,6 +407,114 @@ regex_match (const GRegex *re, const char *word, int *start, int *end)
|
||||
return found;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
host_has_public_suffix_range (const char *word, int start, int end)
|
||||
{
|
||||
char *candidate;
|
||||
const char *host_start;
|
||||
const char *host_end;
|
||||
const char *host_colon;
|
||||
gboolean ok;
|
||||
int host_len;
|
||||
char *host;
|
||||
|
||||
candidate = g_strndup (word + start, end - start);
|
||||
host_start = candidate;
|
||||
host_end = candidate + strlen (candidate);
|
||||
if (*host_start == '[')
|
||||
{
|
||||
g_free (candidate);
|
||||
return FALSE;
|
||||
}
|
||||
host_colon = strchr (host_start, ':');
|
||||
if (host_colon)
|
||||
host_end = host_colon;
|
||||
host_colon = strchr (host_start, '/');
|
||||
if (host_colon && host_colon < host_end)
|
||||
host_end = host_colon;
|
||||
host_len = (int)(host_end - host_start);
|
||||
if (host_len <= 0)
|
||||
{
|
||||
g_free (candidate);
|
||||
return FALSE;
|
||||
}
|
||||
host = g_strndup (host_start, host_len);
|
||||
ok = host_has_public_suffix (host);
|
||||
g_free (host);
|
||||
g_free (candidate);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
public_suffix_table (void)
|
||||
{
|
||||
static GHashTable *table = NULL;
|
||||
unsigned int i;
|
||||
|
||||
if (table)
|
||||
return table;
|
||||
|
||||
table = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
for (i = 0; i < public_suffix_rules_len; i++)
|
||||
{
|
||||
g_hash_table_add (table, (gpointer)public_suffix_rules[i]);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
host_has_public_suffix (const char *host)
|
||||
{
|
||||
GHashTable *table;
|
||||
gchar **labels;
|
||||
int i;
|
||||
int n;
|
||||
gboolean matched = FALSE;
|
||||
|
||||
if (!strchr (host, '.'))
|
||||
return FALSE;
|
||||
|
||||
labels = g_strsplit (host, ".", -1);
|
||||
for (n = 0; labels[n]; n++)
|
||||
{
|
||||
if (labels[n][0] == '\0')
|
||||
{
|
||||
g_strfreev (labels);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
table = public_suffix_table ();
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
char *tail = g_strjoinv (".", &labels[i]);
|
||||
if (g_hash_table_contains (table, tail))
|
||||
matched = TRUE;
|
||||
if (i + 1 < n)
|
||||
{
|
||||
char *tail_wild = g_strjoinv (".", &labels[i + 1]);
|
||||
char *wild = g_strconcat ("*.", tail_wild, NULL);
|
||||
if (g_hash_table_contains (table, wild))
|
||||
matched = TRUE;
|
||||
g_free (tail_wild);
|
||||
g_free (wild);
|
||||
}
|
||||
if (i > 0)
|
||||
{
|
||||
char *exc = g_strconcat ("!", tail, NULL);
|
||||
if (g_hash_table_contains (table, exc))
|
||||
matched = TRUE;
|
||||
g_free (exc);
|
||||
}
|
||||
g_free (tail);
|
||||
if (matched)
|
||||
break;
|
||||
}
|
||||
|
||||
g_strfreev (labels);
|
||||
return matched;
|
||||
}
|
||||
|
||||
/* Miscellaneous description --- */
|
||||
#define DOMAIN_LABEL "[\\pL\\pN](?:[-\\pL\\pN]{0,61}[\\pL\\pN])?"
|
||||
#define DOMAIN DOMAIN_LABEL "(\\." DOMAIN_LABEL ")*"
|
||||
@@ -477,6 +599,28 @@ re_url (void)
|
||||
return url_ret;
|
||||
}
|
||||
|
||||
static const GRegex *
|
||||
re_url_no_scheme (void)
|
||||
{
|
||||
static GRegex *url_ret = NULL;
|
||||
GString *grist_gstr;
|
||||
char *grist;
|
||||
|
||||
if (url_ret) return url_ret;
|
||||
|
||||
grist_gstr = g_string_new (NULL);
|
||||
g_string_append (grist_gstr, "(");
|
||||
g_string_append (grist_gstr, HOST_URL_OPT_TLD OPT_PORT);
|
||||
g_string_append_printf (grist_gstr, "(/" PATH ")?");
|
||||
g_string_append (grist_gstr, ")");
|
||||
|
||||
grist = g_string_free (grist_gstr, FALSE);
|
||||
url_ret = make_re (grist);
|
||||
g_free (grist);
|
||||
|
||||
return url_ret;
|
||||
}
|
||||
|
||||
#define EMAIL_LOCAL_ATOM "[\\pL\\pN!#$%&'*+/=?^_`{|}~-]+"
|
||||
#define EMAIL_LOCAL EMAIL_LOCAL_ATOM "(\\." EMAIL_LOCAL_ATOM ")*"
|
||||
#define EMAIL EMAIL_LOCAL "@" DOMAIN TLD
|
||||
|
||||
@@ -443,6 +443,27 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
|
||||
if (!list)
|
||||
return FALSE;
|
||||
current_sess = sess;
|
||||
if ((evt->state & GDK_CONTROL_MASK) &&
|
||||
!(evt->state & (GDK_MOD1_MASK | GDK_META_MASK)))
|
||||
{
|
||||
if (!(evt->state & GDK_SHIFT_MASK) &&
|
||||
(evt->keyval == GDK_KEY_w || evt->keyval == GDK_KEY_W))
|
||||
{
|
||||
if (sess->type == SESS_CHANNEL)
|
||||
{
|
||||
fe_close_window (sess);
|
||||
g_signal_stop_emission_by_name (G_OBJECT (wid), "key-press-event");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if ((evt->state & GDK_SHIFT_MASK) &&
|
||||
(evt->keyval == GDK_KEY_t || evt->keyval == GDK_KEY_T))
|
||||
{
|
||||
mg_reopen_closed_channel_tab ();
|
||||
g_signal_stop_emission_by_name (G_OBJECT (wid), "key-press-event");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin_emit_keypress (sess, evt->state, evt->keyval, gdk_keyval_to_unicode (evt->keyval)))
|
||||
return 1;
|
||||
|
||||
+84
-7
@@ -468,6 +468,14 @@ static session_gui static_mg_gui;
|
||||
static session_gui *mg_gui = NULL; /* the shared irc tab */
|
||||
static int ignore_chanmode = FALSE;
|
||||
static const char chan_flags[] = { 'c', 'n', 't', 'i', 'm', 'l', 'k' };
|
||||
typedef struct
|
||||
{
|
||||
int server_id;
|
||||
char channel[CHANLEN];
|
||||
char key[64];
|
||||
}
|
||||
mg_closed_channel_tab;
|
||||
static GSList *mg_closed_channel_tabs;
|
||||
|
||||
static chan *active_tab = NULL; /* active tab */
|
||||
GtkWidget *parent_window = NULL; /* the master window */
|
||||
@@ -1643,6 +1651,71 @@ mg_tab_close_cb (GtkWidget *dialog, gint arg1, session *sess)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mg_closed_channel_tabs_add (session *sess)
|
||||
{
|
||||
mg_closed_channel_tab *item;
|
||||
GSList *last;
|
||||
|
||||
if (!sess || sess->type != SESS_CHANNEL || !sess->channel[0])
|
||||
return;
|
||||
|
||||
item = g_new0 (mg_closed_channel_tab, 1);
|
||||
item->server_id = sess->server->id;
|
||||
g_strlcpy (item->channel, sess->channel, sizeof (item->channel));
|
||||
g_strlcpy (item->key, sess->channelkey, sizeof (item->key));
|
||||
mg_closed_channel_tabs = g_slist_prepend (mg_closed_channel_tabs, item);
|
||||
if (g_slist_length (mg_closed_channel_tabs) > 20)
|
||||
{
|
||||
last = g_slist_last (mg_closed_channel_tabs);
|
||||
g_free (last->data);
|
||||
mg_closed_channel_tabs = g_slist_delete_link (mg_closed_channel_tabs, last);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mg_reopen_closed_channel_tab (void)
|
||||
{
|
||||
mg_closed_channel_tab *item;
|
||||
GSList *head;
|
||||
GSList *list;
|
||||
server *serv;
|
||||
session *sess;
|
||||
|
||||
if (!mg_closed_channel_tabs)
|
||||
return;
|
||||
|
||||
head = mg_closed_channel_tabs;
|
||||
item = head->data;
|
||||
mg_closed_channel_tabs = g_slist_delete_link (mg_closed_channel_tabs, head);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
serv = NULL;
|
||||
for (list = serv_list; list; list = list->next)
|
||||
{
|
||||
server *candidate = list->data;
|
||||
if (candidate->id == item->server_id)
|
||||
{
|
||||
serv = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (serv && serv->connected && item->channel[0])
|
||||
{
|
||||
sess = find_channel (serv, item->channel);
|
||||
if (sess)
|
||||
fe_ctrl_gui (sess, 2, 0);
|
||||
else
|
||||
{
|
||||
new_ircwindow (serv, item->channel, SESS_CHANNEL, 1);
|
||||
serv->p_join (serv, item->channel, item->key);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (item);
|
||||
}
|
||||
|
||||
void
|
||||
mg_tab_close (session *sess)
|
||||
{
|
||||
@@ -1650,11 +1723,12 @@ mg_tab_close (session *sess)
|
||||
GSList *list;
|
||||
int i;
|
||||
|
||||
if (chan_remove (sess->res->tab, FALSE))
|
||||
{
|
||||
sess->res->tab = NULL;
|
||||
mg_ircdestroy (sess);
|
||||
}
|
||||
if (chan_remove (sess->res->tab, FALSE))
|
||||
{
|
||||
mg_closed_channel_tabs_add (sess);
|
||||
sess->res->tab = NULL;
|
||||
mg_ircdestroy (sess);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0, list = sess_list; list; list = list->next)
|
||||
@@ -2497,6 +2571,7 @@ mg_topic_word_is_clickable (const char *word, int word_pos)
|
||||
{
|
||||
int start;
|
||||
int end;
|
||||
int word_type;
|
||||
|
||||
if (!word || word[0] == 0)
|
||||
return FALSE;
|
||||
@@ -2504,7 +2579,8 @@ mg_topic_word_is_clickable (const char *word, int word_pos)
|
||||
if (strcmp (word, "/") == 0)
|
||||
return FALSE;
|
||||
|
||||
if (url_check_word (word) == 0)
|
||||
word_type = url_check_word (word);
|
||||
if (word_type != WORD_URL && word_type != WORD_HOST && word_type != WORD_HOST6)
|
||||
return FALSE;
|
||||
|
||||
url_last (&start, &end);
|
||||
@@ -2553,7 +2629,8 @@ mg_topic_button_release_cb (GtkWidget *entry, GdkEventButton *event, gpointer us
|
||||
if (!word)
|
||||
return FALSE;
|
||||
|
||||
if (mg_topic_word_is_clickable (word, word_pos))
|
||||
if ((event->state & 13) == prefs.hex_gui_url_mod &&
|
||||
mg_topic_word_is_clickable (word, word_pos))
|
||||
{
|
||||
url_last (&start, &end);
|
||||
word[end] = 0;
|
||||
|
||||
@@ -40,6 +40,7 @@ void mg_apply_setup (void);
|
||||
void mg_apply_session_font_prefs (session_gui *gui);
|
||||
void mg_close_sess (session *);
|
||||
void mg_tab_close (session *sess);
|
||||
void mg_reopen_closed_channel_tab (void);
|
||||
void mg_detach (session *sess, int mode);
|
||||
void mg_progressbar_create (session_gui *gui);
|
||||
void mg_progressbar_destroy (session_gui *gui);
|
||||
|
||||
+97
-4
@@ -367,7 +367,7 @@ static const setting filexfer_settings[] =
|
||||
{ST_TOGGLE, N_("Receive window"), P_OFFINTNL(hex_gui_autoopen_recv), 0, 0, 0},
|
||||
{ST_TOGGLE, N_("Chat window"), P_OFFINTNL(hex_gui_autoopen_chat), 0, 0, 0},
|
||||
|
||||
{ST_HEADER, N_("Maximum File Transfer Speeds (Byte per Second)"), 0, 0, 0},
|
||||
{ST_HEADER, N_("Maximum File Transfer Speeds (KiB/s or MiB/s)"), 0, 0, 0},
|
||||
{ST_NUMBER, N_("One upload:"), P_OFFINTNL(hex_dcc_max_send_cps),
|
||||
N_("Maximum speed for one transfer"), 0, 10000000},
|
||||
{ST_NUMBER, N_("One download:"), P_OFFINTNL(hex_dcc_max_get_cps),
|
||||
@@ -873,6 +873,67 @@ setup_create_italic_label (char *text)
|
||||
return label;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const setting *set;
|
||||
GtkSpinButton *spin;
|
||||
GtkComboBox *unit;
|
||||
} setup_dcc_speed_data;
|
||||
|
||||
static gboolean
|
||||
setup_is_dcc_speed_setting (const setting *set)
|
||||
{
|
||||
return set->offset == P_OFFINTNL (hex_dcc_max_send_cps)
|
||||
|| set->offset == P_OFFINTNL (hex_dcc_max_get_cps)
|
||||
|| set->offset == P_OFFINTNL (hex_dcc_global_max_send_cps)
|
||||
|| set->offset == P_OFFINTNL (hex_dcc_global_max_get_cps);
|
||||
}
|
||||
|
||||
static int
|
||||
setup_dcc_speed_multiplier (GtkComboBox *combo)
|
||||
{
|
||||
return gtk_combo_box_get_active (combo) == 1 ? 1024 * 1024 : 1024;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_dcc_speed_spin_range (setup_dcc_speed_data *data)
|
||||
{
|
||||
int max = data->set->extra / setup_dcc_speed_multiplier (data->unit);
|
||||
gtk_spin_button_set_range (data->spin, 0, max > 0 ? max : 0);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_dcc_speed_store (setup_dcc_speed_data *data)
|
||||
{
|
||||
int value = gtk_spin_button_get_value_as_int (data->spin);
|
||||
int speed = value * setup_dcc_speed_multiplier (data->unit);
|
||||
if (speed > data->set->extra)
|
||||
speed = data->set->extra;
|
||||
setup_set_int (&setup_prefs, data->set, speed);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_dcc_speed_spin_cb (GtkSpinButton *spin, setup_dcc_speed_data *data)
|
||||
{
|
||||
(void)spin;
|
||||
setup_dcc_speed_store (data);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_dcc_speed_unit_cb (GtkComboBox *combo, setup_dcc_speed_data *data)
|
||||
{
|
||||
int speed = setup_get_int (&setup_prefs, data->set);
|
||||
int multiplier;
|
||||
int value;
|
||||
|
||||
(void)combo;
|
||||
setup_dcc_speed_spin_range (data);
|
||||
multiplier = setup_dcc_speed_multiplier (data->unit);
|
||||
value = (speed + (multiplier / 2)) / multiplier;
|
||||
gtk_spin_button_set_value (data->spin, value);
|
||||
setup_dcc_speed_store (data);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_spin_cb (GtkSpinButton *spin, const setting *set)
|
||||
{
|
||||
@@ -902,11 +963,43 @@ setup_create_spin (GtkWidget *table, int row, const setting *set)
|
||||
g_object_set_data (G_OBJECT (wid), "lbl", label);
|
||||
if (set->tooltip)
|
||||
gtk_widget_set_tooltip_text (wid, _(set->tooltip));
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
if (setup_is_dcc_speed_setting (set))
|
||||
{
|
||||
GtkWidget *unit;
|
||||
setup_dcc_speed_data *data = g_new0 (setup_dcc_speed_data, 1);
|
||||
int speed = setup_get_int (&setup_prefs, set);
|
||||
int use_mib = speed >= (1024 * 1024) && speed % (1024 * 1024) == 0;
|
||||
int multiplier;
|
||||
|
||||
data->set = set;
|
||||
data->spin = GTK_SPIN_BUTTON (wid);
|
||||
|
||||
unit = gtk_combo_box_text_new ();
|
||||
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (unit), _("KiB/s"));
|
||||
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (unit), _("MiB/s"));
|
||||
gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (unit), 1);
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX (unit), use_mib ? 1 : 0);
|
||||
data->unit = GTK_COMBO_BOX (unit);
|
||||
|
||||
setup_dcc_speed_spin_range (data);
|
||||
multiplier = setup_dcc_speed_multiplier (data->unit);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid), (speed + (multiplier / 2)) / multiplier);
|
||||
|
||||
g_signal_connect (G_OBJECT (wid), "value-changed",
|
||||
G_CALLBACK (setup_dcc_speed_spin_cb), data);
|
||||
g_signal_connect (G_OBJECT (unit), "changed",
|
||||
G_CALLBACK (setup_dcc_speed_unit_cb), data);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (rbox), wid, 0, 0, 0);
|
||||
gtk_box_pack_start (GTK_BOX (rbox), unit, 0, 0, 6);
|
||||
} else
|
||||
{
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
setup_get_int (&setup_prefs, set));
|
||||
g_signal_connect (G_OBJECT (wid), "value-changed",
|
||||
g_signal_connect (G_OBJECT (wid), "value-changed",
|
||||
G_CALLBACK (setup_spin_cb), (gpointer)set);
|
||||
gtk_box_pack_start (GTK_BOX (rbox), wid, 0, 0, 0);
|
||||
gtk_box_pack_start (GTK_BOX (rbox), wid, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (set->list)
|
||||
{
|
||||
|
||||
@@ -59,6 +59,17 @@ fe_open_url (const char *url)
|
||||
(void)url;
|
||||
}
|
||||
|
||||
char *
|
||||
get_xdir (void)
|
||||
{
|
||||
return (char *)"/tmp";
|
||||
}
|
||||
|
||||
void
|
||||
load_text_events (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_get_color (ThemeSemanticToken token, GdkRGBA *color)
|
||||
{
|
||||
|
||||
@@ -34,9 +34,9 @@ static const char *theme_css_selector_light_class = "zoitechat-light";
|
||||
static const char *theme_css_palette_provider_key = "zoitechat-palette-provider";
|
||||
static const guint theme_css_provider_priority = GTK_STYLE_PROVIDER_PRIORITY_USER;
|
||||
static const char *theme_css_palette_scope_selectors =
|
||||
".zoitechat-palette, .zoitechat-palette *, .zoitechat-palette box, .zoitechat-palette grid, .zoitechat-palette stack, .zoitechat-palette frame, .zoitechat-palette paned, .zoitechat-palette paned > separator, .zoitechat-palette separator, .zoitechat-palette viewport, .zoitechat-palette overlay, .zoitechat-palette revealer, .zoitechat-palette notebook, .zoitechat-palette notebook > header, .zoitechat-palette notebook > header > tabs, .zoitechat-palette notebook > header > tabs > tab, .zoitechat-palette scrolledwindow, .zoitechat-palette scrollbar, .zoitechat-palette scrollbar slider, .zoitechat-palette scrollbar trough, .zoitechat-palette treeview, .zoitechat-palette treeview.view, .zoitechat-palette treeview.view text, .zoitechat-palette treeview.view cell, .zoitechat-palette treeview.view row, .zoitechat-palette treeview header, .zoitechat-palette treeview header button, .zoitechat-palette list, .zoitechat-palette list row, .zoitechat-palette row, .zoitechat-palette textview, .zoitechat-palette textview text, .zoitechat-palette text, .zoitechat-palette entry, .zoitechat-palette entry text, .zoitechat-palette button, .zoitechat-palette button label, .zoitechat-palette check, .zoitechat-palette radio, .zoitechat-palette switch, .zoitechat-palette slider, .zoitechat-palette scale, .zoitechat-palette scale trough, .zoitechat-palette scale highlight, .zoitechat-palette spinbutton, .zoitechat-palette combobox, .zoitechat-palette combobox box, .zoitechat-palette progressbar, .zoitechat-palette progressbar trough, .zoitechat-palette levelbar, .zoitechat-palette levelbar trough, .zoitechat-palette menubar, .zoitechat-palette menu, .zoitechat-palette menuitem, .zoitechat-palette menuitem label, .zoitechat-palette toolbar, .zoitechat-palette headerbar, .zoitechat-palette label, .zoitechat-palette link, .zoitechat-palette infobar, .zoitechat-palette statusbar, .zoitechat-palette statusbar frame, .zoitechat-palette tooltip, .zoitechat-palette tooltip label";
|
||||
".zoitechat-palette, .zoitechat-palette *, .zoitechat-palette box, .zoitechat-palette grid, .zoitechat-palette stack, .zoitechat-palette frame, .zoitechat-palette paned, .zoitechat-palette paned > separator, .zoitechat-palette separator, .zoitechat-palette viewport, .zoitechat-palette overlay, .zoitechat-palette revealer, .zoitechat-palette notebook, .zoitechat-palette notebook > header, .zoitechat-palette notebook > header > tabs, .zoitechat-palette notebook > header > tabs > tab, .zoitechat-palette scrolledwindow, .zoitechat-palette scrollbar, .zoitechat-palette scrollbar slider, .zoitechat-palette scrollbar trough, .zoitechat-palette treeview, .zoitechat-palette treeview.view, .zoitechat-palette treeview.view text, .zoitechat-palette treeview.view cell, .zoitechat-palette treeview.view row, .zoitechat-palette treeview header, .zoitechat-palette treeview header button, .zoitechat-palette list, .zoitechat-palette list row, .zoitechat-palette row, .zoitechat-palette textview, .zoitechat-palette textview text, .zoitechat-palette text, .zoitechat-palette entry, .zoitechat-palette entry text, .zoitechat-palette button, .zoitechat-palette button label, .zoitechat-palette check, .zoitechat-palette switch, .zoitechat-palette slider, .zoitechat-palette scale, .zoitechat-palette scale trough, .zoitechat-palette scale highlight, .zoitechat-palette spinbutton, .zoitechat-palette combobox, .zoitechat-palette combobox box, .zoitechat-palette progressbar, .zoitechat-palette progressbar trough, .zoitechat-palette levelbar, .zoitechat-palette levelbar trough, .zoitechat-palette menubar, .zoitechat-palette menu, .zoitechat-palette menuitem, .zoitechat-palette menuitem label, .zoitechat-palette toolbar, .zoitechat-palette headerbar, .zoitechat-palette label, .zoitechat-palette link, .zoitechat-palette infobar, .zoitechat-palette statusbar, .zoitechat-palette statusbar frame, .zoitechat-palette tooltip, .zoitechat-palette tooltip label";
|
||||
static const char *theme_css_palette_selection_selectors =
|
||||
".zoitechat-palette *:selected, .zoitechat-palette *:selected:focus, .zoitechat-palette *:selected:hover, .zoitechat-palette treeview.view:selected, .zoitechat-palette treeview.view:selected:focus, .zoitechat-palette treeview.view:selected:hover, .zoitechat-palette row:selected, .zoitechat-palette row:selected:focus, .zoitechat-palette row:selected:hover, .zoitechat-palette selection, .zoitechat-palette text selection, .zoitechat-palette entry selection, .zoitechat-palette entry text selection, .zoitechat-palette button:selected, .zoitechat-palette button:checked, .zoitechat-palette check:checked, .zoitechat-palette radio:checked, .zoitechat-palette switch:checked, .zoitechat-palette slider:active, .zoitechat-palette menuitem:selected, .zoitechat-palette menuitem:hover, .zoitechat-palette notebook > header > tabs > tab:checked, .zoitechat-palette notebook > header > tabs > tab:hover, .zoitechat-palette treeview header button:hover, .zoitechat-palette treeview header button:active, .zoitechat-palette progressbar progress, .zoitechat-palette levelbar block.filled, .zoitechat-palette:focus selection, .zoitechat-palette:focus text selection";
|
||||
".zoitechat-palette *:selected, .zoitechat-palette *:selected:focus, .zoitechat-palette *:selected:hover, .zoitechat-palette treeview.view:selected, .zoitechat-palette treeview.view:selected:focus, .zoitechat-palette treeview.view:selected:hover, .zoitechat-palette row:selected, .zoitechat-palette row:selected:focus, .zoitechat-palette row:selected:hover, .zoitechat-palette selection, .zoitechat-palette text selection, .zoitechat-palette entry selection, .zoitechat-palette entry text selection, .zoitechat-palette button:selected, .zoitechat-palette button:checked, .zoitechat-palette check:checked, .zoitechat-palette switch:checked, .zoitechat-palette slider:active, .zoitechat-palette menuitem:selected, .zoitechat-palette menuitem:hover, .zoitechat-palette notebook > header > tabs > tab:checked, .zoitechat-palette notebook > header > tabs > tab:hover, .zoitechat-palette treeview header button:hover, .zoitechat-palette treeview header button:active, .zoitechat-palette progressbar progress, .zoitechat-palette levelbar block.filled, .zoitechat-palette:focus selection, .zoitechat-palette:focus text selection";
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
2.18.0~pre6
|
||||
2.18.0
|
||||
|
||||
Reference in New Issue
Block a user