2011-02-24 04:14:30 +01:00
/* X-Chat
* Copyright ( C ) 1998 - 2005 Peter Zelezny .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
2012-12-23 11:36:54 -08:00
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA
2011-02-24 04:14:30 +01:00
*/
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <ctype.h>
2026-03-09 14:21:20 -06:00
# include "fe-gtk.h"
2026-01-17 16:56:35 -07:00
# include <gdk/gdk.h>
2013-07-24 20:47:01 -04:00
# include <gdk/gdkkeysyms.h>
2026-01-17 16:19:56 -07:00
# include <gdk/gdkcairo.h>
2026-01-17 21:56:00 -07:00
# include <gdk-pixbuf/gdk-pixbuf.h>
2013-07-24 20:47:01 -04:00
2026-01-05 23:12:38 -07:00
# include "../common/zoitechat.h"
2011-02-24 04:14:30 +01:00
# include "../common/fe.h"
# include "../common/server.h"
2026-01-05 23:12:38 -07:00
# include "../common/zoitechatc.h"
2011-02-24 04:14:30 +01:00
# include "../common/outbound.h"
# include "../common/inbound.h"
# include "../common/plugin.h"
# include "../common/modes.h"
# include "../common/url.h"
2011-12-11 17:34:02 +01:00
# include "../common/util.h"
2012-07-21 14:26:19 +02:00
# include "../common/text.h"
2013-06-06 19:16:50 -04:00
# include "../common/chanopt.h"
2013-07-24 20:47:01 -04:00
# include "../common/cfgfiles.h"
2011-12-11 17:34:02 +01:00
2026-03-02 19:42:48 -07:00
# include "theme/theme-manager.h"
# include "theme/theme-css.h"
2011-02-24 04:14:30 +01:00
# include "banlist.h"
# include "gtkutil.h"
2026-03-13 12:39:55 -06:00
# include "icon-resolver.h"
2011-02-24 04:14:30 +01:00
# include "joind.h"
2026-03-02 19:42:48 -07:00
# include "theme/theme-access.h"
# include "theme/theme-palette.h"
2011-02-24 04:14:30 +01:00
# include "maingui.h"
# include "menu.h"
2026-03-16 22:45:30 -06:00
# include "preferences-persistence.h"
2011-02-24 04:14:30 +01:00
# include "fkeys.h"
# include "userlistgui.h"
# include "chanview.h"
# include "pixmaps.h"
# include "plugin-tray.h"
# include "xtext.h"
# include "sexy-spell-entry.h"
2017-08-31 09:52:45 -04:00
# include "gtkutil.h"
2011-02-24 04:14:30 +01:00
2019-12-16 03:42:31 -05:00
# ifdef G_OS_WIN32
2020-04-19 05:43:27 +02:00
# include <windows.h>
2026-02-25 14:48:18 -07:00
# include <shellapi.h>
2026-02-25 14:18:23 -07:00
# include <gdk/gdkwin32.h>
static void
mg_win32_allow_autohide_taskbar ( GtkWindow * window , GdkEventWindowState * event )
{
GdkWindow * gdk_window ;
HWND hwnd ;
if ( ! window | | ! event )
return ;
if ( ( event - > new_window_state & GDK_WINDOW_STATE_FULLSCREEN ) ! = 0 )
return ;
gdk_window = gtk_widget_get_window ( GTK_WIDGET ( window ) ) ;
if ( ! gdk_window )
return ;
hwnd = gdk_win32_window_get_handle ( gdk_window ) ;
if ( ! hwnd )
return ;
2026-02-25 14:48:18 -07:00
if ( event - > new_window_state & GDK_WINDOW_STATE_MAXIMIZED )
{
APPBARDATA appbar_data ;
RECT work_area ;
ZeroMemory ( & appbar_data , sizeof ( APPBARDATA ) ) ;
appbar_data . cbSize = sizeof ( APPBARDATA ) ;
if ( ( SHAppBarMessage ( ABM_GETSTATE , & appbar_data ) & ABS_AUTOHIDE ) ! = 0 & &
SHAppBarMessage ( ABM_GETTASKBARPOS , & appbar_data ) ! = 0 )
{
HMONITOR monitor ;
MONITORINFO monitor_info ;
monitor = MonitorFromWindow ( hwnd , MONITOR_DEFAULTTONEAREST ) ;
ZeroMemory ( & monitor_info , sizeof ( MONITORINFO ) ) ;
monitor_info . cbSize = sizeof ( MONITORINFO ) ;
if ( monitor & & GetMonitorInfo ( monitor , & monitor_info ) )
{
work_area = monitor_info . rcMonitor ;
switch ( appbar_data . uEdge )
{
case ABE_LEFT :
work_area . left + = 1 ;
break ;
case ABE_TOP :
work_area . top + = 1 ;
break ;
case ABE_RIGHT :
work_area . right - = 1 ;
break ;
case ABE_BOTTOM :
default :
work_area . bottom - = 1 ;
break ;
}
SetWindowPos ( hwnd ,
NULL ,
work_area . left ,
work_area . top ,
work_area . right - work_area . left ,
work_area . bottom - work_area . top ,
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER ) ;
}
}
}
2026-02-25 14:18:23 -07:00
SetWindowPos ( hwnd ,
HWND_NOTOPMOST ,
0 ,
0 ,
0 ,
0 ,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER ) ;
}
2019-12-16 03:42:31 -05:00
# endif
2026-02-17 20:58:15 -07:00
# define ICON_TAB_DETACH "zc-menu-detach"
# define ICON_TAB_CLOSE "zc-menu-close"
2026-02-18 00:32:42 -07:00
# define ICON_TAB_PREVIOUS "zc-menu-previous"
# define ICON_TAB_NEXT "zc-menu-next"
2026-01-30 15:27:01 -07:00
# define ICON_ENTRY_ERROR "dialog-error"
2011-02-24 04:14:30 +01:00
# define GUI_SPACING (3)
# define GUI_BORDER (0)
enum
{
2026-01-15 18:35:27 -07:00
POS_INVALID = 0 ,
POS_TOPLEFT = 1 ,
POS_BOTTOMLEFT = 2 ,
POS_TOPRIGHT = 3 ,
POS_BOTTOMRIGHT = 4 ,
POS_TOP = 5 , /* for tabs only */
POS_BOTTOM = 6 ,
POS_HIDDEN = 7
2011-02-24 04:14:30 +01:00
} ;
/* two different types of tabs */
2026-01-15 18:35:27 -07:00
# define TAG_IRC 0 /* server, channel, dialog */
# define TAG_UTIL 1 /* dcc, notify, chanlist */
static void mg_apply_emoji_fallback_widget ( GtkWidget * widget ) ;
2011-02-24 04:14:30 +01:00
2026-03-16 21:57:21 -06:00
# define MG_CONFIG_SAVE_DEBOUNCE_MS 250
static guint mg_config_save_source_id = 0 ;
static gboolean mg_config_prefs_dirty = FALSE ;
2026-03-16 22:45:30 -06:00
static void
mg_show_save_failure ( const PreferencesPersistenceResult * save_result )
{
char buffer [ 192 ] ;
if ( ! save_result | | save_result - > success )
return ;
if ( save_result - > partial_failure )
{
fe_message ( _ ( " Could not fully save preferences. zoitechat.conf was written, but colors.conf failed. Retry is possible. " ) , FE_MSG_ERROR ) ;
return ;
}
g_snprintf ( buffer , sizeof ( buffer ) , _ ( " Could not save preferences (%s). Retry is possible. " ) , save_result - > failed_file ? save_result - > failed_file : _ ( " unknown file " ) ) ;
fe_message ( buffer , FE_MSG_ERROR ) ;
}
2026-03-16 21:57:21 -06:00
static gboolean
mg_config_save_timeout_cb ( gpointer userdata )
{
2026-03-16 22:45:30 -06:00
PreferencesPersistenceResult save_result ;
2026-03-16 21:57:21 -06:00
mg_config_save_source_id = 0 ;
if ( ! mg_config_prefs_dirty )
return G_SOURCE_REMOVE ;
2026-03-16 22:45:30 -06:00
save_result = preferences_persistence_save_all ( ) ;
if ( ! save_result . success )
mg_show_save_failure ( & save_result ) ;
2026-03-16 21:57:21 -06:00
mg_config_prefs_dirty = FALSE ;
return G_SOURCE_REMOVE ;
}
static void
mg_schedule_config_save ( void )
{
if ( ! mg_config_prefs_dirty )
return ;
if ( mg_config_save_source_id ! = 0 )
g_source_remove ( mg_config_save_source_id ) ;
mg_config_save_source_id = g_timeout_add ( MG_CONFIG_SAVE_DEBOUNCE_MS ,
mg_config_save_timeout_cb ,
NULL ) ;
}
static void
mg_flush_config_save ( void )
{
2026-03-16 22:45:30 -06:00
PreferencesPersistenceResult save_result ;
2026-03-16 21:57:21 -06:00
if ( mg_config_save_source_id ! = 0 )
{
g_source_remove ( mg_config_save_source_id ) ;
mg_config_save_source_id = 0 ;
}
if ( mg_config_prefs_dirty )
{
2026-03-16 22:45:30 -06:00
save_result = preferences_persistence_save_all ( ) ;
if ( ! save_result . success )
mg_show_save_failure ( & save_result ) ;
2026-03-16 21:57:21 -06:00
mg_config_prefs_dirty = FALSE ;
}
}
2026-01-17 16:46:21 -07:00
static inline void
2026-01-17 21:56:00 -07:00
mg_set_source_color ( cairo_t * cr , const XTextColor * color )
2026-01-17 16:46:21 -07:00
{
2026-01-17 21:56:00 -07:00
cairo_set_source_rgba ( cr , color - > red , color - > green , color - > blue , color - > alpha ) ;
2026-01-17 16:46:21 -07:00
}
2026-01-17 21:56:00 -07:00
static inline guint16
mg_color_component_to_pango ( double value )
2026-01-17 17:34:38 -07:00
{
2026-01-17 21:56:00 -07:00
if ( value < 0.0 )
value = 0.0 ;
if ( value > 1.0 )
value = 1.0 ;
2026-01-17 17:34:38 -07:00
2026-01-17 21:56:00 -07:00
return ( guint16 ) ( value * 65535.0 + 0.5 ) ;
2026-01-17 17:34:38 -07:00
}
2026-01-31 13:49:06 -07:00
static void
mg_apply_font_css ( GtkWidget * widget , const PangoFontDescription * desc ,
const char * class_name , const char * provider_key )
{
GtkStyleContext * context ;
GtkCssProvider * provider ;
GString * css ;
if ( ! widget | | ! desc )
return ;
context = gtk_widget_get_style_context ( widget ) ;
if ( ! context )
return ;
provider = g_object_get_data ( G_OBJECT ( widget ) , provider_key ) ;
if ( ! provider )
{
provider = gtk_css_provider_new ( ) ;
g_object_set_data_full ( G_OBJECT ( widget ) , provider_key , provider , g_object_unref ) ;
}
css = g_string_new ( " . " ) ;
g_string_append ( css , class_name ) ;
2026-01-31 16:37:15 -07:00
g_string_append ( css , " { " ) ;
gtkutil_append_font_css ( css , desc ) ;
g_string_append ( css , " } " ) ;
2026-01-31 13:49:06 -07:00
gtk_css_provider_load_from_data ( provider , css - > str , - 1 , NULL ) ;
g_string_free ( css , TRUE ) ;
gtk_style_context_add_class ( context , class_name ) ;
2026-03-02 19:42:48 -07:00
theme_css_apply_widget_provider ( widget , GTK_STYLE_PROVIDER ( provider ) ) ;
2026-01-31 13:49:06 -07:00
}
2026-01-19 19:37:03 -07:00
static void
mg_set_label_alignment_start ( GtkWidget * widget )
{
gtk_widget_set_halign ( widget , GTK_ALIGN_START ) ;
gtk_widget_set_valign ( widget , GTK_ALIGN_CENTER ) ;
}
2026-03-18 09:10:40 -06:00
static void
mg_apply_compact_mode_css ( GtkWidget * widget )
{
GtkStyleContext * context ;
GtkCssProvider * provider ;
if ( ! widget )
return ;
context = gtk_widget_get_style_context ( widget ) ;
if ( ! context )
return ;
provider = g_object_get_data ( G_OBJECT ( widget ) , " mg-mode-css-provider " ) ;
if ( ! provider )
{
provider = gtk_css_provider_new ( ) ;
g_object_set_data_full ( G_OBJECT ( widget ) , " mg-mode-css-provider " , provider , g_object_unref ) ;
}
gtk_css_provider_load_from_data ( provider ,
" .zoitechat-mode-control { min-height: 11px; padding-top: 0; padding-bottom: 0; } "
" .zoitechat-mode-control label { padding-top: 0; padding-bottom: 0; } " ,
- 1 , NULL ) ;
gtk_style_context_add_class ( context , " zoitechat-mode-control " ) ;
theme_css_apply_widget_provider ( widget , GTK_STYLE_PROVIDER ( provider ) ) ;
}
2026-01-19 20:11:07 -07:00
static GtkWidget *
mg_box_new ( GtkOrientation orientation , gboolean homogeneous , gint spacing )
{
GtkWidget * box = gtk_box_new ( orientation , spacing ) ;
gtk_box_set_homogeneous ( GTK_BOX ( box ) , homogeneous ) ;
return box ;
}
2026-01-17 22:25:12 -07:00
static void
mg_pixbuf_destroy ( guchar * pixels , gpointer data )
{
g_free ( pixels ) ;
}
static GdkPixbuf *
mg_pixbuf_from_surface ( cairo_surface_t * surface , int width , int height )
{
const unsigned char * src ;
int src_stride ;
int rowstride ;
guchar * pixels ;
int x ;
int y ;
if ( ! surface | | width < = 0 | | height < = 0 )
return NULL ;
src = cairo_image_surface_get_data ( surface ) ;
src_stride = cairo_image_surface_get_stride ( surface ) ;
rowstride = width * 4 ;
pixels = g_malloc ( ( gsize ) rowstride * height ) ;
for ( y = 0 ; y < height ; y + + )
{
const unsigned char * src_row = src + ( y * src_stride ) ;
guchar * dest_row = pixels + ( y * rowstride ) ;
for ( x = 0 ; x < width ; x + + )
{
guint8 a ;
guint8 r ;
guint8 g ;
guint8 b ;
const unsigned char * src_px = src_row + ( x * 4 ) ;
guchar * dest_px = dest_row + ( x * 4 ) ;
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
b = src_px [ 0 ] ;
g = src_px [ 1 ] ;
r = src_px [ 2 ] ;
a = src_px [ 3 ] ;
# else
a = src_px [ 0 ] ;
r = src_px [ 1 ] ;
g = src_px [ 2 ] ;
b = src_px [ 3 ] ;
# endif
if ( a )
{
r = ( guint8 ) ( ( r * 255 + ( a / 2 ) ) / a ) ;
g = ( guint8 ) ( ( g * 255 + ( a / 2 ) ) / a ) ;
b = ( guint8 ) ( ( b * 255 + ( a / 2 ) ) / a ) ;
}
else
{
r = g = b = 0 ;
}
dest_px [ 0 ] = r ;
dest_px [ 1 ] = g ;
dest_px [ 2 ] = b ;
dest_px [ 3 ] = a ;
}
}
return gdk_pixbuf_new_from_data ( pixels , GDK_COLORSPACE_RGB , TRUE , 8 ,
width , height , rowstride , mg_pixbuf_destroy , NULL ) ;
}
2026-01-17 17:34:38 -07:00
static GdkPixbuf *
mg_pixbuf_from_window ( GdkWindow * window , int width , int height )
{
2026-01-17 21:56:00 -07:00
int src_width ;
int src_height ;
2026-01-17 22:25:12 -07:00
cairo_surface_t * surface ;
cairo_t * cr ;
GdkPixbuf * pixbuf ;
2026-01-17 17:34:38 -07:00
2026-01-17 22:25:12 -07:00
if ( ! window )
2026-01-17 21:56:00 -07:00
return NULL ;
2026-01-17 17:34:38 -07:00
2026-01-17 22:25:12 -07:00
src_width = gdk_window_get_width ( window ) ;
src_height = gdk_window_get_height ( window ) ;
2026-01-17 21:56:00 -07:00
if ( width < = 0 | | height < = 0 )
{
width = src_width ;
height = src_height ;
2026-01-17 17:34:38 -07:00
}
2026-01-17 22:25:12 -07:00
if ( width < = 0 | | height < = 0 )
return NULL ;
surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32 , width , height ) ;
if ( cairo_surface_status ( surface ) ! = CAIRO_STATUS_SUCCESS )
{
cairo_surface_destroy ( surface ) ;
return NULL ;
}
cr = cairo_create ( surface ) ;
gdk_cairo_set_source_window ( cr , window , 0.0 , 0.0 ) ;
cairo_paint ( cr ) ;
cairo_destroy ( cr ) ;
pixbuf = mg_pixbuf_from_surface ( surface , width , height ) ;
cairo_surface_destroy ( surface ) ;
return pixbuf ;
2026-01-17 17:34:38 -07:00
}
2011-02-24 04:14:30 +01:00
static void mg_create_entry ( session * sess , GtkWidget * box ) ;
2013-07-24 20:47:01 -04:00
static void mg_create_search ( session * sess , GtkWidget * box ) ;
2026-01-16 22:07:01 -07:00
# ifdef G_OS_WIN32
static GdkFilterReturn mg_win32_filter ( GdkXEvent * xevent , GdkEvent * event , gpointer data ) ;
# endif
2011-02-24 04:14:30 +01:00
static void mg_link_irctab ( session * sess , int focus ) ;
static session_gui static_mg_gui ;
2026-01-15 18:35:27 -07:00
static session_gui * mg_gui = NULL ; /* the shared irc tab */
2011-02-24 04:14:30 +01:00
static int ignore_chanmode = FALSE ;
2015-04-21 16:07:12 -04:00
static const char chan_flags [ ] = { ' c ' , ' n ' , ' t ' , ' i ' , ' m ' , ' l ' , ' k ' } ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
static chan * active_tab = NULL ; /* active tab */
GtkWidget * parent_window = NULL ; /* the master window */
2011-02-24 04:14:30 +01:00
2026-01-30 18:19:01 -07:00
InputStyle * input_style ;
2011-02-24 04:14:30 +01:00
static PangoAttrList * away_list ;
static PangoAttrList * newdata_list ;
static PangoAttrList * nickseen_list ;
static PangoAttrList * newmsg_list ;
static PangoAttrList * plain_list = NULL ;
static PangoAttrList *
2026-01-17 21:56:00 -07:00
mg_attr_list_create ( const XTextColor * col , int size )
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
PangoAttribute * attr ;
PangoAttrList * list ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
list = pango_attr_list_new ( ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( col )
{
2026-01-17 21:56:00 -07:00
attr = pango_attr_foreground_new (
mg_color_component_to_pango ( col - > red ) ,
mg_color_component_to_pango ( col - > green ) ,
mg_color_component_to_pango ( col - > blue ) ) ;
2026-01-15 18:35:27 -07:00
attr - > start_index = 0 ;
attr - > end_index = 0xffff ;
pango_attr_list_insert ( list , attr ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( size > 0 )
{
attr = pango_attr_scale_new ( size = = 1 ? PANGO_SCALE_SMALL : PANGO_SCALE_X_SMALL ) ;
attr - > start_index = 0 ;
attr - > end_index = 0xffff ;
pango_attr_list_insert ( list , attr ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return list ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_tab_colors ( void )
{
2026-03-10 23:05:56 -06:00
XTextColor gui_palette [ THEME_TOKEN_COUNT ] ;
2026-01-17 21:56:00 -07:00
2026-01-15 18:35:27 -07:00
if ( plain_list )
{
pango_attr_list_unref ( plain_list ) ;
pango_attr_list_unref ( newmsg_list ) ;
pango_attr_list_unref ( newdata_list ) ;
pango_attr_list_unref ( nickseen_list ) ;
pango_attr_list_unref ( away_list ) ;
}
2011-02-24 04:14:30 +01:00
2026-03-02 19:42:48 -07:00
theme_get_xtext_colors ( gui_palette , G_N_ELEMENTS ( gui_palette ) ) ;
2026-01-15 18:35:27 -07:00
plain_list = mg_attr_list_create ( NULL , prefs . hex_gui_tab_small ) ;
2026-03-02 19:42:48 -07:00
newdata_list = mg_attr_list_create ( & gui_palette [ THEME_TOKEN_TAB_NEW_DATA ] , prefs . hex_gui_tab_small ) ;
nickseen_list = mg_attr_list_create ( & gui_palette [ THEME_TOKEN_TAB_HIGHLIGHT ] , prefs . hex_gui_tab_small ) ;
newmsg_list = mg_attr_list_create ( & gui_palette [ THEME_TOKEN_TAB_NEW_MESSAGE ] , prefs . hex_gui_tab_small ) ;
away_list = mg_attr_list_create ( & gui_palette [ THEME_TOKEN_TAB_AWAY ] , FALSE ) ;
2011-02-24 04:14:30 +01:00
}
static void
set_window_urgency ( GtkWidget * win , gboolean set )
{
2026-01-15 18:35:27 -07:00
gtk_window_set_urgency_hint ( GTK_WINDOW ( win ) , set ) ;
2011-02-24 04:14:30 +01:00
}
2026-03-07 19:21:37 -07:00
static gboolean
is_wayland_display ( void )
{
GdkDisplay * display = gdk_display_get_default ( ) ;
const char * name ;
if ( ! display )
return FALSE ;
name = gdk_display_get_name ( display ) ;
if ( ! name )
return FALSE ;
return g_str_has_prefix ( name , " wayland " ) ;
}
static gboolean
is_kde_desktop ( void )
{
const char * desktop = g_getenv ( " XDG_CURRENT_DESKTOP " ) ;
if ( desktop & & strstr ( desktop , " KDE " ) )
return TRUE ;
return g_getenv ( " KDE_FULL_SESSION " ) ! = NULL ;
}
static gboolean
is_kde_wayland ( void )
{
return is_wayland_display ( ) & & is_kde_desktop ( ) ;
}
2011-02-24 04:14:30 +01:00
static void
flash_window ( GtkWidget * win )
{
2014-05-11 04:43:01 -04:00
# ifdef HAVE_GTK_MAC
2026-01-15 18:35:27 -07:00
gtkosx_application_attention_request ( osx_app , INFO_REQUEST ) ;
2014-05-11 04:43:01 -04:00
# endif
2026-03-07 19:21:37 -07:00
if ( is_kde_wayland ( ) )
gtk_window_present ( GTK_WINDOW ( win ) ) ;
2026-01-15 18:35:27 -07:00
set_window_urgency ( win , TRUE ) ;
2011-02-24 04:14:30 +01:00
}
static void
unflash_window ( GtkWidget * win )
{
2026-01-15 18:35:27 -07:00
set_window_urgency ( win , FALSE ) ;
2011-02-24 04:14:30 +01:00
}
/* flash the taskbar button */
void
fe_flash_window ( session * sess )
{
2026-01-15 18:35:27 -07:00
if ( fe_gui_info ( sess , 0 ) ! = 1 ) /* only do it if not focused */
flash_window ( sess - > gui - > window ) ;
2011-02-24 04:14:30 +01:00
}
/* set a tab plain, red, light-red, or blue */
void
2021-10-14 07:44:11 -07:00
fe_set_tab_color ( struct session * sess , tabcolor col )
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
struct session * server_sess = sess - > server - > server_session ;
int col_noflags = ( col & ~ FE_COLOR_ALLFLAGS ) ;
int col_shouldoverride = ! ( col & FE_COLOR_FLAG_NOOVERRIDE ) ;
if ( sess - > res - > tab & & sess - > gui - > is_tab & & ( col = = 0 | | sess ! = current_tab ) )
{
switch ( col_noflags )
{
case 0 : /* no particular color (theme default) */
sess - > tab_state = TAB_STATE_NONE ;
chan_set_color ( sess - > res - > tab , plain_list ) ;
break ;
case 1 : /* new data has been displayed (dark red) */
if ( col_shouldoverride | | ! ( ( sess - > tab_state & TAB_STATE_NEW_MSG )
| | ( sess - > tab_state & TAB_STATE_NEW_HILIGHT ) ) ) {
sess - > tab_state = TAB_STATE_NEW_DATA ;
chan_set_color ( sess - > res - > tab , newdata_list ) ;
}
if ( chan_is_collapsed ( sess - > res - > tab )
& & ! ( ( server_sess - > tab_state & TAB_STATE_NEW_MSG )
| | ( server_sess - > tab_state & TAB_STATE_NEW_HILIGHT ) )
& & ! ( server_sess = = current_tab ) )
{
server_sess - > tab_state = TAB_STATE_NEW_DATA ;
chan_set_color ( chan_get_parent ( sess - > res - > tab ) , newdata_list ) ;
}
break ;
case 2 : /* new message arrived in channel (light red) */
if ( col_shouldoverride | | ! ( sess - > tab_state & TAB_STATE_NEW_HILIGHT ) ) {
sess - > tab_state = TAB_STATE_NEW_MSG ;
chan_set_color ( sess - > res - > tab , newmsg_list ) ;
}
if ( chan_is_collapsed ( sess - > res - > tab )
& & ! ( server_sess - > tab_state & TAB_STATE_NEW_HILIGHT )
& & ! ( server_sess = = current_tab ) )
{
server_sess - > tab_state = TAB_STATE_NEW_MSG ;
chan_set_color ( chan_get_parent ( sess - > res - > tab ) , newmsg_list ) ;
}
break ;
case 3 : /* your nick has been seen (blue) */
sess - > tab_state = TAB_STATE_NEW_HILIGHT ;
chan_set_color ( sess - > res - > tab , nickseen_list ) ;
if ( chan_is_collapsed ( sess - > res - > tab ) & & ! ( server_sess = = current_tab ) )
{
server_sess - > tab_state = TAB_STATE_NEW_MSG ;
chan_set_color ( chan_get_parent ( sess - > res - > tab ) , nickseen_list ) ;
}
break ;
}
lastact_update ( sess ) ;
sess - > last_tab_state = sess - > tab_state ; /* For plugins handling future prints */
}
2011-02-24 04:14:30 +01:00
}
static void
mg_set_myself_away ( session_gui * gui , gboolean away )
{
2026-01-15 18:35:27 -07:00
gtk_label_set_attributes ( GTK_LABEL ( gtk_bin_get_child ( GTK_BIN ( gui - > nick_label ) ) ) ,
away ? away_list : NULL ) ;
2011-02-24 04:14:30 +01:00
}
/* change the little icon to the left of your nickname */
void
mg_set_access_icon ( session_gui * gui , GdkPixbuf * pix , gboolean away )
{
2026-01-15 18:35:27 -07:00
if ( gui - > op_xpm )
{
if ( pix = = gtk_image_get_pixbuf ( GTK_IMAGE ( gui - > op_xpm ) ) ) /* no change? */
{
mg_set_myself_away ( gui , away ) ;
return ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_destroy ( gui - > op_xpm ) ;
gui - > op_xpm = NULL ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( pix & & prefs . hex_gui_input_icon )
{
gui - > op_xpm = gtk_image_new_from_pixbuf ( pix ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > nick_box ) , gui - > op_xpm , 0 , 0 , 0 ) ;
gtk_widget_show ( gui - > op_xpm ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_set_myself_away ( gui , away ) ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_inputbox_focus ( GtkWidget * widget , GdkEventFocus * event , session_gui * gui )
{
2026-01-15 18:35:27 -07:00
GSList * list ;
session * sess ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( gui - > is_tab )
return FALSE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
list = sess_list ;
while ( list )
{
sess = list - > data ;
if ( sess - > gui = = gui )
{
current_sess = sess ;
if ( ! sess - > server - > server_session )
sess - > server - > server_session = sess ;
break ;
}
list = list - > next ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return FALSE ;
2011-02-24 04:14:30 +01:00
}
void
mg_inputbox_cb ( GtkWidget * igad , session_gui * gui )
{
2026-01-15 18:35:27 -07:00
char * cmd ;
static int ignore = FALSE ;
GSList * list ;
session * sess = NULL ;
if ( ignore )
return ;
cmd = SPELL_ENTRY_GET_TEXT ( igad ) ;
if ( cmd [ 0 ] = = 0 )
return ;
cmd = g_strdup ( cmd ) ;
/* avoid recursive loop */
ignore = TRUE ;
SPELL_ENTRY_SET_TEXT ( igad , " " ) ;
ignore = FALSE ;
/* where did this event come from? */
if ( gui - > is_tab )
{
sess = current_tab ;
} else
{
list = sess_list ;
while ( list )
{
sess = list - > data ;
if ( sess - > gui = = gui )
break ;
list = list - > next ;
}
if ( ! list )
sess = NULL ;
}
if ( sess )
handle_multiline ( sess , cmd , TRUE , FALSE ) ;
g_free ( cmd ) ;
2011-02-24 04:14:30 +01:00
}
2013-10-01 13:11:59 -04:00
static gboolean
mg_spellcheck_cb ( SexySpellEntry * entry , gchar * word , gpointer data )
{
2026-01-15 18:35:27 -07:00
/* This can cause freezes on long words, nicks arn't very long anyway. */
if ( strlen ( word ) > 20 )
2026-03-16 10:45:29 -06:00
return FALSE ;
2013-10-01 13:11:59 -04:00
2026-01-15 18:35:27 -07:00
/* Ignore anything we think is a valid url */
if ( url_check_word ( word ) ! = 0 )
return FALSE ;
2013-10-01 13:11:59 -04:00
2026-01-15 18:35:27 -07:00
return TRUE ;
2013-10-01 13:11:59 -04:00
}
2012-10-22 07:24:52 +02:00
#if 0
2011-02-24 04:14:30 +01:00
static gboolean
has_key ( char * modes )
{
2026-01-15 18:35:27 -07:00
if ( ! modes )
return FALSE ;
/* this is a crude check, but "-k" can't exist, so it works. */
while ( * modes )
{
if ( * modes = = ' k ' )
return TRUE ;
if ( * modes = = ' ' )
return FALSE ;
modes + + ;
}
return FALSE ;
2011-02-24 04:14:30 +01:00
}
2012-10-22 07:24:52 +02:00
# endif
2011-02-24 04:14:30 +01:00
void
fe_set_title ( session * sess )
{
2026-01-15 18:35:27 -07:00
char tbuf [ 512 ] ;
int type ;
if ( sess - > gui - > is_tab & & sess ! = current_tab )
return ;
type = sess - > type ;
if ( sess - > server - > connected = = FALSE & & sess - > type ! = SESS_DIALOG )
goto def ;
switch ( type )
{
case SESS_DIALOG :
g_snprintf ( tbuf , sizeof ( tbuf ) , " %s %s @ %s - %s " ,
_ ( " Dialog with " ) , sess - > channel , server_get_network ( sess - > server , TRUE ) ,
_ ( DISPLAY_NAME ) ) ;
break ;
case SESS_SERVER :
g_snprintf ( tbuf , sizeof ( tbuf ) , " %s%s%s - %s " ,
prefs . hex_gui_win_nick ? sess - > server - > nick : " " ,
prefs . hex_gui_win_nick ? " @ " : " " , server_get_network ( sess - > server , TRUE ) ,
_ ( DISPLAY_NAME ) ) ;
break ;
case SESS_CHANNEL :
/* don't display keys in the titlebar */
g_snprintf ( tbuf , sizeof ( tbuf ) ,
" %s%s%s / %s%s%s%s - %s " ,
prefs . hex_gui_win_nick ? sess - > server - > nick : " " ,
prefs . hex_gui_win_nick ? " @ " : " " ,
server_get_network ( sess - > server , TRUE ) , sess - > channel ,
prefs . hex_gui_win_modes & & sess - > current_modes ? " ( " : " " ,
prefs . hex_gui_win_modes & & sess - > current_modes ? sess - > current_modes : " " ,
prefs . hex_gui_win_modes & & sess - > current_modes ? " ) " : " " ,
_ ( DISPLAY_NAME ) ) ;
if ( prefs . hex_gui_win_ucount )
{
g_snprintf ( tbuf + strlen ( tbuf ) , 9 , " (%d) " , sess - > total ) ;
}
break ;
case SESS_NOTICES :
case SESS_SNOTICES :
g_snprintf ( tbuf , sizeof ( tbuf ) , " %s%s%s (notices) - %s " ,
prefs . hex_gui_win_nick ? sess - > server - > nick : " " ,
prefs . hex_gui_win_nick ? " @ " : " " , server_get_network ( sess - > server , TRUE ) ,
_ ( DISPLAY_NAME ) ) ;
break ;
default :
def :
g_snprintf ( tbuf , sizeof ( tbuf ) , _ ( DISPLAY_NAME ) ) ;
gtk_window_set_title ( GTK_WINDOW ( sess - > gui - > window ) , tbuf ) ;
return ;
}
gtk_window_set_title ( GTK_WINDOW ( sess - > gui - > window ) , tbuf ) ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_windowstate_cb ( GtkWindow * wid , GdkEventWindowState * event , gpointer userdata )
{
2026-03-16 21:57:21 -06:00
guint win_state ;
guint win_fullscreen ;
gboolean changed = FALSE ;
2026-02-18 13:11:04 -07:00
if ( ( event - > changed_mask & GDK_WINDOW_STATE_ICONIFIED ) & &
( event - > new_window_state & GDK_WINDOW_STATE_ICONIFIED ) & &
prefs . hex_gui_tray_minimize & & prefs . hex_gui_tray & &
gtkutil_tray_icon_supported ( wid ) )
{
tray_toggle_visibility ( TRUE ) ;
}
2011-02-24 04:14:30 +01:00
2026-03-16 21:57:21 -06:00
win_state = 0 ;
if ( event - > new_window_state & GDK_WINDOW_STATE_MAXIMIZED )
win_state = 1 ;
win_fullscreen = 0 ;
if ( event - > new_window_state & GDK_WINDOW_STATE_FULLSCREEN )
win_fullscreen = 1 ;
if ( prefs . hex_gui_win_state ! = win_state )
{
prefs . hex_gui_win_state = win_state ;
changed = TRUE ;
}
2012-12-16 12:54:15 -05:00
2026-03-16 21:57:21 -06:00
if ( prefs . hex_gui_win_fullscreen ! = win_fullscreen )
{
prefs . hex_gui_win_fullscreen = win_fullscreen ;
changed = TRUE ;
}
if ( changed )
{
mg_config_prefs_dirty = TRUE ;
mg_schedule_config_save ( ) ;
}
2013-09-19 17:52:17 -04:00
2026-01-15 18:35:27 -07:00
menu_set_fullscreen ( current_sess - > gui , prefs . hex_gui_win_fullscreen ) ;
2013-10-23 09:11:30 -04:00
2026-02-25 14:18:23 -07:00
# ifdef G_OS_WIN32
mg_win32_allow_autohide_taskbar ( wid , event ) ;
# endif
2026-01-15 18:35:27 -07:00
return FALSE ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_configure_cb ( GtkWidget * wid , GdkEventConfigure * event , session * sess )
{
2026-03-16 21:57:21 -06:00
gboolean changed = FALSE ;
2026-03-17 10:11:41 -06:00
if ( sess = = NULL )
2026-01-15 18:35:27 -07:00
{
if ( mg_gui )
{
if ( prefs . hex_gui_win_save & & ! prefs . hex_gui_win_state & & ! prefs . hex_gui_win_fullscreen )
{
2026-03-16 21:57:21 -06:00
int win_left ;
int win_top ;
int win_width ;
int win_height ;
gtk_window_get_position ( GTK_WINDOW ( wid ) , & win_left , & win_top ) ;
gtk_window_get_size ( GTK_WINDOW ( wid ) , & win_width , & win_height ) ;
if ( prefs . hex_gui_win_left ! = win_left )
{
prefs . hex_gui_win_left = win_left ;
changed = TRUE ;
}
if ( prefs . hex_gui_win_top ! = win_top )
{
prefs . hex_gui_win_top = win_top ;
changed = TRUE ;
}
if ( prefs . hex_gui_win_width ! = win_width )
{
prefs . hex_gui_win_width = win_width ;
changed = TRUE ;
}
if ( prefs . hex_gui_win_height ! = win_height )
{
prefs . hex_gui_win_height = win_height ;
changed = TRUE ;
}
2026-01-15 18:35:27 -07:00
}
}
}
2026-03-17 10:11:41 -06:00
else if ( sess - > type = = SESS_DIALOG & & prefs . hex_gui_win_save )
2026-01-15 18:35:27 -07:00
{
2026-03-17 10:11:41 -06:00
int dialog_left ;
int dialog_top ;
int dialog_width ;
int dialog_height ;
2026-03-16 21:57:21 -06:00
2026-03-17 10:11:41 -06:00
gtk_window_get_position ( GTK_WINDOW ( wid ) , & dialog_left , & dialog_top ) ;
gtk_window_get_size ( GTK_WINDOW ( wid ) , & dialog_width , & dialog_height ) ;
2026-03-16 21:57:21 -06:00
2026-03-17 10:11:41 -06:00
if ( prefs . hex_gui_dialog_left ! = dialog_left )
{
prefs . hex_gui_dialog_left = dialog_left ;
changed = TRUE ;
}
2026-03-16 21:57:21 -06:00
2026-03-17 10:11:41 -06:00
if ( prefs . hex_gui_dialog_top ! = dialog_top )
{
prefs . hex_gui_dialog_top = dialog_top ;
changed = TRUE ;
}
2026-03-16 21:57:21 -06:00
2026-03-17 10:11:41 -06:00
if ( prefs . hex_gui_dialog_width ! = dialog_width )
{
prefs . hex_gui_dialog_width = dialog_width ;
changed = TRUE ;
}
2026-03-16 21:57:21 -06:00
2026-03-17 10:11:41 -06:00
if ( prefs . hex_gui_dialog_height ! = dialog_height )
{
prefs . hex_gui_dialog_height = dialog_height ;
changed = TRUE ;
2026-01-15 18:35:27 -07:00
}
}
2026-03-16 21:57:21 -06:00
if ( changed )
{
mg_config_prefs_dirty = TRUE ;
mg_schedule_config_save ( ) ;
}
2026-01-15 18:35:27 -07:00
return FALSE ;
2011-02-24 04:14:30 +01:00
}
/* move to a non-irc tab */
static void
mg_show_generic_tab ( GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
int num ;
GtkWidget * f = NULL ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( current_sess & & gtk_widget_has_focus ( current_sess - > gui - > input_box ) )
f = current_sess - > gui - > input_box ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
num = gtk_notebook_page_num ( GTK_NOTEBOOK ( mg_gui - > note_book ) , box ) ;
gtk_notebook_set_current_page ( GTK_NOTEBOOK ( mg_gui - > note_book ) , num ) ;
gtk_tree_view_set_model ( GTK_TREE_VIEW ( mg_gui - > user_tree ) , NULL ) ;
gtk_window_set_title ( GTK_WINDOW ( mg_gui - > window ) ,
g_object_get_data ( G_OBJECT ( box ) , " title " ) ) ;
gtk_widget_set_sensitive ( mg_gui - > menu , FALSE ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( f )
gtk_widget_grab_focus ( f ) ;
2011-02-24 04:14:30 +01:00
}
/* a channel has been focused */
static void
mg_focus ( session * sess )
{
2026-01-15 18:35:27 -07:00
if ( sess - > gui - > is_tab )
current_tab = sess ;
current_sess = sess ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* dirty trick to avoid auto-selection */
SPELL_ENTRY_SET_EDITABLE ( sess - > gui - > input_box , FALSE ) ;
gtk_widget_grab_focus ( sess - > gui - > input_box ) ;
SPELL_ENTRY_SET_EDITABLE ( sess - > gui - > input_box , TRUE ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
sess - > server - > front_session = sess ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > server - > server_session ! = NULL )
{
if ( sess - > server - > server_session - > type ! = SESS_SERVER )
sess - > server - > server_session = sess ;
} else
{
sess - > server - > server_session = sess ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* when called via mg_changui_new, is_tab might be true, but
sess - > res - > tab is still NULL . */
if ( sess - > res - > tab )
fe_set_tab_color ( sess , FE_COLOR_NONE ) ;
2011-02-24 04:14:30 +01:00
}
static int
mg_progressbar_update ( GtkWidget * bar )
{
2026-01-15 18:35:27 -07:00
static int type = 0 ;
static gdouble pos = 0 ;
pos + = 0.05 ;
if ( pos > = 0.99 )
{
if ( type = = 0 )
{
type = 1 ;
2026-01-30 17:40:44 -07:00
gtk_progress_bar_set_inverted ( GTK_PROGRESS_BAR ( bar ) , TRUE ) ;
2026-01-15 18:35:27 -07:00
} else
{
type = 0 ;
2026-01-30 17:40:44 -07:00
gtk_progress_bar_set_inverted ( GTK_PROGRESS_BAR ( bar ) , FALSE ) ;
2026-01-15 18:35:27 -07:00
}
pos = 0.05 ;
}
gtk_progress_bar_set_fraction ( ( GtkProgressBar * ) bar , pos ) ;
return 1 ;
2011-02-24 04:14:30 +01:00
}
void
mg_progressbar_create ( session_gui * gui )
{
2026-01-15 18:35:27 -07:00
gui - > bar = gtk_progress_bar_new ( ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > nick_box ) , gui - > bar , 0 , 0 , 0 ) ;
gtk_widget_show ( gui - > bar ) ;
gui - > bartag = fe_timeout_add ( 50 , mg_progressbar_update , gui - > bar ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_progressbar_destroy ( session_gui * gui )
{
2026-01-15 18:35:27 -07:00
fe_timeout_remove ( gui - > bartag ) ;
gtk_widget_destroy ( gui - > bar ) ;
gui - > bar = 0 ;
gui - > bartag = 0 ;
2011-02-24 04:14:30 +01:00
}
/* switching tabs away from this one, so remember some info about it! */
static void
mg_unpopulate ( session * sess )
{
2026-01-15 18:35:27 -07:00
restore_gui * res ;
session_gui * gui ;
2026-03-14 19:57:42 -06:00
GtkTextBuffer * topic_buffer ;
GtkTextIter start ;
GtkTextIter end ;
2026-01-15 18:35:27 -07:00
int i ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gui = sess - > gui ;
res = sess - > res ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
res - > input_text = g_strdup ( SPELL_ENTRY_GET_TEXT ( gui - > input_box ) ) ;
2026-03-14 19:57:42 -06:00
topic_buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( gui - > topic_entry ) ) ;
gtk_text_buffer_get_bounds ( topic_buffer , & start , & end ) ;
res - > topic_text = gtk_text_buffer_get_text ( topic_buffer , & start , & end , FALSE ) ;
2026-01-15 18:35:27 -07:00
res - > limit_text = g_strdup ( gtk_entry_get_text ( GTK_ENTRY ( gui - > limit_entry ) ) ) ;
res - > key_text = g_strdup ( gtk_entry_get_text ( GTK_ENTRY ( gui - > key_entry ) ) ) ;
if ( gui - > laginfo )
res - > lag_text = g_strdup ( gtk_label_get_text ( GTK_LABEL ( gui - > laginfo ) ) ) ;
if ( gui - > throttleinfo )
res - > queue_text = g_strdup ( gtk_label_get_text ( GTK_LABEL ( gui - > throttleinfo ) ) ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
for ( i = 0 ; i < NUM_FLAG_WIDS - 1 ; i + + )
res - > flag_wid_state [ i ] = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( gui - > flag_wid [ i ] ) ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
res - > old_ul_value = userlist_get_value ( gui - > user_tree ) ;
if ( gui - > lagometer )
res - > lag_value = gtk_progress_bar_get_fraction (
GTK_PROGRESS_BAR ( gui - > lagometer ) ) ;
if ( gui - > throttlemeter )
res - > queue_value = gtk_progress_bar_get_fraction (
GTK_PROGRESS_BAR ( gui - > throttlemeter ) ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( gui - > bar )
{
res - > c_graph = TRUE ; /* still have a graph, just not visible now */
mg_progressbar_destroy ( gui ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_restore_label ( GtkWidget * label , char * * text )
{
2026-01-15 18:35:27 -07:00
if ( ! label )
return ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( * text )
{
gtk_label_set_text ( GTK_LABEL ( label ) , * text ) ;
g_free ( * text ) ;
* text = NULL ;
} else
{
gtk_label_set_text ( GTK_LABEL ( label ) , " " ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_restore_entry ( GtkWidget * entry , char * * text )
{
2026-01-15 18:35:27 -07:00
if ( * text )
{
gtk_entry_set_text ( GTK_ENTRY ( entry ) , * text ) ;
g_free ( * text ) ;
* text = NULL ;
} else
{
gtk_entry_set_text ( GTK_ENTRY ( entry ) , " " ) ;
}
gtk_editable_set_position ( GTK_EDITABLE ( entry ) , - 1 ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_restore_speller ( GtkWidget * entry , char * * text )
{
2026-01-15 18:35:27 -07:00
if ( * text )
{
SPELL_ENTRY_SET_TEXT ( entry , * text ) ;
g_free ( * text ) ;
* text = NULL ;
} else
{
SPELL_ENTRY_SET_TEXT ( entry , " " ) ;
}
SPELL_ENTRY_SET_POS ( entry , - 1 ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_set_topic_tip ( session * sess )
{
2026-01-15 18:35:27 -07:00
char * text ;
2026-03-14 19:57:42 -06:00
GtkTextBuffer * topic_buffer ;
GtkTextIter start ;
GtkTextIter end ;
2026-01-15 18:35:27 -07:00
switch ( sess - > type )
{
case SESS_CHANNEL :
if ( sess - > topic )
{
text = g_strdup_printf ( _ ( " Topic for %s is: %s " ) , sess - > channel ,
sess - > topic ) ;
gtk_widget_set_tooltip_text ( sess - > gui - > topic_entry , text ) ;
g_free ( text ) ;
} else
gtk_widget_set_tooltip_text ( sess - > gui - > topic_entry , _ ( " No topic is set " ) ) ;
break ;
default :
2026-03-14 19:57:42 -06:00
topic_buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( sess - > gui - > topic_entry ) ) ;
gtk_text_buffer_get_bounds ( topic_buffer , & start , & end ) ;
text = gtk_text_buffer_get_text ( topic_buffer , & start , & end , FALSE ) ;
if ( text [ 0 ] )
gtk_widget_set_tooltip_text ( sess - > gui - > topic_entry , text ) ;
2026-01-15 18:35:27 -07:00
else
gtk_widget_set_tooltip_text ( sess - > gui - > topic_entry , NULL ) ;
2026-03-14 19:57:42 -06:00
g_free ( text ) ;
2026-01-15 18:35:27 -07:00
}
2011-02-24 04:14:30 +01:00
}
static void
mg_hide_empty_pane ( GtkPaned * pane )
{
2026-01-15 18:35:27 -07:00
if ( ( gtk_paned_get_child1 ( pane ) = = NULL | | ! gtk_widget_get_visible ( gtk_paned_get_child1 ( pane ) ) ) & &
( gtk_paned_get_child2 ( pane ) = = NULL | | ! gtk_widget_get_visible ( gtk_paned_get_child2 ( pane ) ) ) )
{
gtk_widget_hide ( GTK_WIDGET ( pane ) ) ;
return ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_show ( GTK_WIDGET ( pane ) ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_hide_empty_boxes ( session_gui * gui )
{
2026-01-15 18:35:27 -07:00
/* hide empty vpanes - so the handle is not shown */
mg_hide_empty_pane ( ( GtkPaned * ) gui - > vpane_right ) ;
mg_hide_empty_pane ( ( GtkPaned * ) gui - > vpane_left ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_userlist_showhide ( session * sess , int show )
{
2026-01-15 18:35:27 -07:00
session_gui * gui = sess - > gui ;
int handle_size ;
int right_size ;
2026-03-15 18:21:01 -06:00
int min_right_size ;
2026-01-15 18:35:27 -07:00
GtkAllocation allocation ;
2012-05-28 23:17:09 +02:00
2026-03-15 18:21:01 -06:00
gtk_widget_get_size_request ( gui - > user_box , & min_right_size , NULL ) ;
if ( min_right_size < 1 )
min_right_size = 1 ;
right_size = MAX ( prefs . hex_gui_pane_right_size , min_right_size ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( show )
{
gtk_widget_show ( gui - > user_box ) ;
gui - > ul_hidden = 0 ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_get_allocation ( gui - > hpane_right , & allocation ) ;
gtk_widget_style_get ( GTK_WIDGET ( gui - > hpane_right ) , " handle-size " , & handle_size , NULL ) ;
gtk_paned_set_position ( GTK_PANED ( gui - > hpane_right ) , allocation . width - ( right_size + handle_size ) ) ;
}
else
{
gtk_widget_hide ( gui - > user_box ) ;
gui - > ul_hidden = 1 ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_hide_empty_boxes ( gui ) ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_is_userlist_and_tree_combined ( void )
{
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_tab_pos = = POS_TOPLEFT & & prefs . hex_gui_ulist_pos = = POS_BOTTOMLEFT )
return TRUE ;
if ( prefs . hex_gui_tab_pos = = POS_BOTTOMLEFT & & prefs . hex_gui_ulist_pos = = POS_TOPLEFT )
return TRUE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_tab_pos = = POS_TOPRIGHT & & prefs . hex_gui_ulist_pos = = POS_BOTTOMRIGHT )
return TRUE ;
if ( prefs . hex_gui_tab_pos = = POS_BOTTOMRIGHT & & prefs . hex_gui_ulist_pos = = POS_TOPRIGHT )
return TRUE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return FALSE ;
2011-02-24 04:14:30 +01:00
}
/* decide if the userlist should be shown or hidden for this tab */
void
mg_decide_userlist ( session * sess , gboolean switch_to_current )
{
2026-01-15 18:35:27 -07:00
/* when called from menu.c we need this */
if ( sess - > gui = = mg_gui & & switch_to_current )
sess = current_tab ;
if ( prefs . hex_gui_ulist_hide )
{
mg_userlist_showhide ( sess , FALSE ) ;
return ;
}
switch ( sess - > type )
{
case SESS_SERVER :
case SESS_DIALOG :
case SESS_NOTICES :
case SESS_SNOTICES :
if ( mg_is_userlist_and_tree_combined ( ) )
mg_userlist_showhide ( sess , TRUE ) ; /* show */
else
mg_userlist_showhide ( sess , FALSE ) ; /* hide */
break ;
default :
mg_userlist_showhide ( sess , TRUE ) ; /* show */
}
2011-02-24 04:14:30 +01:00
}
static int ul_tag = 0 ;
static gboolean
mg_populate_userlist ( session * sess )
{
2026-01-15 18:35:27 -07:00
if ( ! sess )
sess = current_tab ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( is_session ( sess ) )
{
if ( sess - > type = = SESS_DIALOG )
mg_set_access_icon ( sess - > gui , NULL , sess - > server - > is_away ) ;
else
mg_set_access_icon ( sess - > gui , get_user_icon ( sess - > server , sess - > me ) , sess - > server - > is_away ) ;
userlist_show ( sess ) ;
userlist_set_value ( sess - > gui - > user_tree , sess - > res - > old_ul_value ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
ul_tag = 0 ;
return 0 ;
2011-02-24 04:14:30 +01:00
}
/* fill the irc tab with a new channel */
static void
mg_populate ( session * sess )
{
2026-01-15 18:35:27 -07:00
session_gui * gui = sess - > gui ;
restore_gui * res = sess - > res ;
int i , render = TRUE ;
guint16 vis = gui - > ul_hidden ;
GtkAllocation allocation ;
switch ( sess - > type )
{
case SESS_DIALOG :
/* show the dialog buttons */
gtk_widget_show ( gui - > dialogbutton_box ) ;
/* hide the chan-mode buttons */
gtk_widget_hide ( gui - > topicbutton_box ) ;
/* hide the userlist */
mg_decide_userlist ( sess , FALSE ) ;
/* shouldn't edit the topic */
2026-03-14 19:57:42 -06:00
gtk_text_view_set_editable ( GTK_TEXT_VIEW ( gui - > topic_entry ) , FALSE ) ;
2026-01-15 18:35:27 -07:00
/* might be hidden from server tab */
if ( prefs . hex_gui_topicbar )
gtk_widget_show ( gui - > topic_bar ) ;
break ;
case SESS_SERVER :
if ( prefs . hex_gui_mode_buttons )
gtk_widget_show ( gui - > topicbutton_box ) ;
/* hide the dialog buttons */
gtk_widget_hide ( gui - > dialogbutton_box ) ;
/* hide the userlist */
mg_decide_userlist ( sess , FALSE ) ;
/* servers don't have topics */
gtk_widget_hide ( gui - > topic_bar ) ;
break ;
default :
/* hide the dialog buttons */
gtk_widget_hide ( gui - > dialogbutton_box ) ;
if ( prefs . hex_gui_mode_buttons )
gtk_widget_show ( gui - > topicbutton_box ) ;
/* show the userlist */
mg_decide_userlist ( sess , FALSE ) ;
2026-03-14 19:57:42 -06:00
/* let the topic be edited */
gtk_text_view_set_editable ( GTK_TEXT_VIEW ( gui - > topic_entry ) , TRUE ) ;
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_topicbar )
gtk_widget_show ( gui - > topic_bar ) ;
}
/* move to THE irc tab */
if ( gui - > is_tab )
gtk_notebook_set_current_page ( GTK_NOTEBOOK ( gui - > note_book ) , 0 ) ;
/* xtext size change? Then don't render, wait for the expose caused
2011-02-24 04:14:30 +01:00
by showing / hidding the userlist */
2026-01-15 18:35:27 -07:00
gtk_widget_get_allocation ( gui - > user_box , & allocation ) ;
if ( vis ! = gui - > ul_hidden & & allocation . width > 1 )
render = FALSE ;
gtk_xtext_buffer_show ( GTK_XTEXT ( gui - > xtext ) , res - > buffer , render ) ;
if ( gui - > is_tab )
gtk_widget_set_sensitive ( gui - > menu , TRUE ) ;
2026-03-14 19:57:42 -06:00
if ( res - > topic_text )
{
GtkTextBuffer * topic_buffer ;
topic_buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( gui - > topic_entry ) ) ;
gtk_text_buffer_set_text ( topic_buffer , res - > topic_text , - 1 ) ;
g_free ( res - > topic_text ) ;
res - > topic_text = NULL ;
} else
{
GtkTextBuffer * topic_buffer ;
topic_buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( gui - > topic_entry ) ) ;
gtk_text_buffer_set_text ( topic_buffer , " " , - 1 ) ;
}
2026-01-15 18:35:27 -07:00
mg_restore_speller ( gui - > input_box , & res - > input_text ) ;
mg_restore_entry ( gui - > key_entry , & res - > key_text ) ;
mg_restore_entry ( gui - > limit_entry , & res - > limit_text ) ;
mg_restore_label ( gui - > laginfo , & res - > lag_text ) ;
mg_restore_label ( gui - > throttleinfo , & res - > queue_text ) ;
mg_focus ( sess ) ;
fe_set_title ( sess ) ;
/* this one flickers, so only change if necessary */
if ( strcmp ( sess - > server - > nick , gtk_button_get_label ( GTK_BUTTON ( gui - > nick_label ) ) ) ! = 0 )
gtk_button_set_label ( GTK_BUTTON ( gui - > nick_label ) , sess - > server - > nick ) ;
/* this is slow, so make it a timeout event */
if ( ! gui - > is_tab )
{
mg_populate_userlist ( sess ) ;
} else
{
if ( ul_tag = = 0 )
ul_tag = g_idle_add ( ( GSourceFunc ) mg_populate_userlist , NULL ) ;
}
fe_userlist_numbers ( sess ) ;
/* restore all the channel mode buttons */
ignore_chanmode = TRUE ;
for ( i = 0 ; i < NUM_FLAG_WIDS - 1 ; i + + )
{
/* Hide if mode not supported */
if ( sess - > server & & strchr ( sess - > server - > chanmodes , chan_flags [ i ] ) = = NULL )
gtk_widget_hide ( sess - > gui - > flag_wid [ i ] ) ;
else
gtk_widget_show ( sess - > gui - > flag_wid [ i ] ) ;
/* Update state */
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( gui - > flag_wid [ i ] ) ,
res - > flag_wid_state [ i ] ) ;
}
ignore_chanmode = FALSE ;
if ( gui - > lagometer )
{
gtk_progress_bar_set_fraction ( GTK_PROGRESS_BAR ( gui - > lagometer ) ,
res - > lag_value ) ;
if ( res - > lag_tip )
gtk_widget_set_tooltip_text ( gtk_widget_get_parent ( sess - > gui - > lagometer ) , res - > lag_tip ) ;
}
if ( gui - > throttlemeter )
{
gtk_progress_bar_set_fraction ( GTK_PROGRESS_BAR ( gui - > throttlemeter ) ,
res - > queue_value ) ;
if ( res - > queue_tip )
gtk_widget_set_tooltip_text ( gtk_widget_get_parent ( sess - > gui - > throttlemeter ) , res - > queue_tip ) ;
}
/* did this tab have a connecting graph? restore it.. */
if ( res - > c_graph )
{
res - > c_graph = FALSE ;
mg_progressbar_create ( gui ) ;
}
/* menu items */
menu_set_away ( gui , sess - > server - > is_away ) ;
gtk_widget_set_sensitive ( gui - > menu_item [ MENU_ID_AWAY ] , sess - > server - > connected ) ;
gtk_widget_set_sensitive ( gui - > menu_item [ MENU_ID_JOIN ] , sess - > server - > end_of_motd ) ;
gtk_widget_set_sensitive ( gui - > menu_item [ MENU_ID_DISCONNECT ] ,
sess - > server - > connected | | sess - > server - > recondelay_tag ) ;
mg_set_topic_tip ( sess ) ;
plugin_emit_dummy_print ( sess , " Focus Tab " ) ;
2011-02-24 04:14:30 +01:00
}
void
2026-01-15 18:35:27 -07:00
mg_bring_tofront_sess ( session * sess ) /* IRC tab or window */
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
if ( sess - > gui - > is_tab )
chan_focus ( sess - > res - > tab ) ;
else
gtk_window_present ( GTK_WINDOW ( sess - > gui - > window ) ) ;
2011-02-24 04:14:30 +01:00
}
void
2026-01-15 18:35:27 -07:00
mg_bring_tofront ( GtkWidget * vbox ) /* non-IRC tab or window */
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
chan * ch ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
ch = g_object_get_data ( G_OBJECT ( vbox ) , " ch " ) ;
if ( ch )
chan_focus ( ch ) ;
else
gtk_window_present ( GTK_WINDOW ( gtk_widget_get_toplevel ( vbox ) ) ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_switch_page ( int relative , int num )
{
2026-01-15 18:35:27 -07:00
if ( mg_gui )
chanview_move_focus ( mg_gui - > chanview , relative , num ) ;
2011-02-24 04:14:30 +01:00
}
/* a toplevel IRC window was destroyed */
static void
mg_topdestroy_cb ( GtkWidget * win , session * sess )
{
2026-01-15 18:35:27 -07:00
session_free ( sess ) ; /* tell zoitechat.c about it */
2011-02-24 04:14:30 +01:00
}
/* cleanup an IRC tab */
static void
mg_ircdestroy ( session * sess )
{
2026-01-15 18:35:27 -07:00
GSList * list ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
session_free ( sess ) ; /* tell zoitechat.c about it */
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( mg_gui = = NULL )
{
return ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
list = sess_list ;
while ( list )
{
sess = list - > data ;
if ( sess - > gui - > is_tab )
{
return ;
}
list = list - > next ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_destroy ( mg_gui - > window ) ;
active_tab = NULL ;
mg_gui = NULL ;
parent_window = NULL ;
2011-02-24 04:14:30 +01:00
}
static void
mg_tab_close_cb ( GtkWidget * dialog , gint arg1 , session * sess )
{
2026-01-15 18:35:27 -07:00
GSList * list , * next ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_destroy ( dialog ) ;
if ( arg1 = = GTK_RESPONSE_OK & & is_session ( sess ) )
{
/* force it NOT to send individual PARTs */
sess - > server - > sent_quit = TRUE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
for ( list = sess_list ; list ; )
{
next = list - > next ;
if ( ( ( session * ) list - > data ) - > server = = sess - > server & &
( ( session * ) list - > data ) ! = sess )
fe_close_window ( ( session * ) list - > data ) ;
list = next ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* just send one QUIT - better for BNCs */
sess - > server - > sent_quit = FALSE ;
fe_close_window ( sess ) ;
}
2011-02-24 04:14:30 +01:00
}
void
mg_tab_close ( session * sess )
{
2026-01-15 18:35:27 -07:00
GtkWidget * dialog ;
GSList * list ;
int i ;
if ( chan_remove ( sess - > res - > tab , FALSE ) )
{
sess - > res - > tab = NULL ;
mg_ircdestroy ( sess ) ;
}
else
{
for ( i = 0 , list = sess_list ; list ; list = list - > next )
{
session * s = ( session * ) list - > data ;
if ( s - > server = = sess - > server & & ( s - > type = = SESS_CHANNEL | | s - > type = = SESS_DIALOG ) )
i + + ;
}
dialog = gtk_message_dialog_new ( GTK_WINDOW ( parent_window ) , 0 ,
GTK_MESSAGE_WARNING , GTK_BUTTONS_OK_CANCEL ,
_ ( " This server still has %d channels or dialogs associated with it. "
" Close them all? " ) , i ) ;
2026-03-04 23:28:01 -07:00
theme_manager_attach_window ( dialog ) ;
2026-01-15 18:35:27 -07:00
g_signal_connect ( G_OBJECT ( dialog ) , " response " ,
G_CALLBACK ( mg_tab_close_cb ) , sess ) ;
if ( prefs . hex_gui_tab_layout )
{
gtk_window_set_position ( GTK_WINDOW ( dialog ) , GTK_WIN_POS_MOUSE ) ;
}
else
{
gtk_window_set_position ( GTK_WINDOW ( dialog ) , GTK_WIN_POS_CENTER_ON_PARENT ) ;
}
gtk_widget_show ( dialog ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_menu_destroy ( GtkWidget * menu , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
gtk_widget_destroy ( menu ) ;
g_object_unref ( menu ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_create_icon_item ( char * label , char * stock , GtkWidget * menu ,
2026-01-15 18:35:27 -07:00
void * callback , void * userdata )
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
GtkWidget * item ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
item = create_icon_menu ( label , stock , TRUE ) ;
gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ) , item ) ;
g_signal_connect ( G_OBJECT ( item ) , " activate " , G_CALLBACK ( callback ) ,
userdata ) ;
gtk_widget_show ( item ) ;
2011-02-24 04:14:30 +01:00
}
static int
mg_count_networks ( void )
{
2026-01-15 18:35:27 -07:00
int cons = 0 ;
GSList * list ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
for ( list = serv_list ; list ; list = list - > next )
{
if ( ( ( server * ) list - > data ) - > connected )
cons + + ;
}
return cons ;
2011-02-24 04:14:30 +01:00
}
static int
mg_count_dccs ( void )
{
2026-01-15 18:35:27 -07:00
GSList * list ;
struct DCC * dcc ;
int dccs = 0 ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
list = dcc_list ;
while ( list )
{
dcc = list - > data ;
if ( ( dcc - > type = = TYPE_SEND | | dcc - > type = = TYPE_RECV ) & &
dcc - > dccstat = = STAT_ACTIVE )
dccs + + ;
list = list - > next ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return dccs ;
2011-02-24 04:14:30 +01:00
}
void
mg_open_quit_dialog ( gboolean minimize_button )
{
2026-01-15 18:35:27 -07:00
static GtkWidget * dialog = NULL ;
2026-02-05 00:04:07 -07:00
GtkWidget * dialog_vbox1 ;
GtkWidget * table1 ;
GtkWidget * image ;
GtkWidget * checkbutton1 ;
GtkWidget * label ;
GtkWidget * button ;
char * text , * connecttext ;
int cons ;
int dccs ;
2026-01-15 18:35:27 -07:00
if ( dialog )
{
gtk_window_present ( GTK_WINDOW ( dialog ) ) ;
return ;
}
dccs = mg_count_dccs ( ) ;
cons = mg_count_networks ( ) ;
if ( dccs + cons = = 0 | | ! prefs . hex_gui_quit_dialog )
{
zoitechat_exit ( ) ;
return ;
}
dialog = gtk_dialog_new ( ) ;
2026-03-04 23:28:01 -07:00
theme_manager_attach_window ( dialog ) ;
2026-01-15 18:35:27 -07:00
gtk_container_set_border_width ( GTK_CONTAINER ( dialog ) , 6 ) ;
gtk_window_set_title ( GTK_WINDOW ( dialog ) , _ ( " Quit ZoiteChat? " ) ) ;
gtk_window_set_transient_for ( GTK_WINDOW ( dialog ) , GTK_WINDOW ( parent_window ) ) ;
gtk_window_set_resizable ( GTK_WINDOW ( dialog ) , FALSE ) ;
dialog_vbox1 = gtk_dialog_get_content_area ( GTK_DIALOG ( dialog ) ) ;
gtk_widget_show ( dialog_vbox1 ) ;
2026-01-19 19:26:29 -07:00
table1 = gtk_grid_new ( ) ;
gtk_widget_show ( table1 ) ;
gtk_box_pack_start ( GTK_BOX ( dialog_vbox1 ) , table1 , TRUE , TRUE , 0 ) ;
gtk_container_set_border_width ( GTK_CONTAINER ( table1 ) , 6 ) ;
gtk_grid_set_row_spacing ( GTK_GRID ( table1 ) , 12 ) ;
gtk_grid_set_column_spacing ( GTK_GRID ( table1 ) , 12 ) ;
2026-01-15 18:35:27 -07:00
2026-01-30 08:47:31 -07:00
image = gtk_image_new_from_icon_name ( " dialog-warning " , GTK_ICON_SIZE_DIALOG ) ;
2026-01-15 18:35:27 -07:00
gtk_widget_show ( image ) ;
2026-01-30 16:04:50 -07:00
gtk_widget_set_hexpand ( image , FALSE ) ;
gtk_widget_set_vexpand ( image , FALSE ) ;
gtk_widget_set_halign ( image , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( image , GTK_ALIGN_FILL ) ;
2026-01-19 19:26:29 -07:00
gtk_grid_attach ( GTK_GRID ( table1 ) , image , 0 , 0 , 1 , 1 ) ;
2026-01-15 18:35:27 -07:00
checkbutton1 = gtk_check_button_new_with_mnemonic ( _ ( " Don't ask next time. " ) ) ;
gtk_widget_show ( checkbutton1 ) ;
2026-01-19 19:26:29 -07:00
gtk_widget_set_hexpand ( checkbutton1 , TRUE ) ;
2026-01-30 16:04:50 -07:00
gtk_widget_set_vexpand ( checkbutton1 , FALSE ) ;
2026-01-19 22:17:57 -07:00
gtk_widget_set_halign ( checkbutton1 , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( checkbutton1 , GTK_ALIGN_CENTER ) ;
2026-01-30 16:14:35 -07:00
gtk_widget_set_margin_top ( checkbutton1 , 4 ) ;
gtk_widget_set_margin_bottom ( checkbutton1 , 4 ) ;
2026-01-30 16:04:50 -07:00
gtk_grid_attach ( GTK_GRID ( table1 ) , checkbutton1 , 0 , 1 , 2 , 1 ) ;
2026-01-15 18:35:27 -07:00
connecttext = g_strdup_printf ( _ ( " You are connected to %i IRC networks. " ) , cons ) ;
text = g_strdup_printf ( " <span weight= \" bold \" size= \" larger \" >%s</span> \n \n %s \n %s " ,
_ ( " Are you sure you want to quit? " ) ,
cons ? connecttext : " " ,
dccs ? _ ( " Some file transfers are still active. " ) : " " ) ;
g_free ( connecttext ) ;
label = gtk_label_new ( text ) ;
g_free ( text ) ;
gtk_widget_show ( label ) ;
2026-01-19 19:37:03 -07:00
mg_set_label_alignment_start ( label ) ;
2026-01-19 19:26:29 -07:00
gtk_widget_set_hexpand ( label , TRUE ) ;
gtk_widget_set_vexpand ( label , TRUE ) ;
2026-01-30 16:14:35 -07:00
gtk_widget_set_halign ( label , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( label , GTK_ALIGN_CENTER ) ;
gtk_label_set_xalign ( GTK_LABEL ( label ) , 0.0f ) ;
2026-01-30 16:04:50 -07:00
gtk_grid_attach ( GTK_GRID ( table1 ) , label , 1 , 0 , 1 , 1 ) ;
2026-01-19 19:26:29 -07:00
gtk_label_set_use_markup ( GTK_LABEL ( label ) , TRUE ) ;
2026-01-15 18:35:27 -07:00
2026-02-05 00:04:07 -07:00
if ( minimize_button & & gtkutil_tray_icon_supported ( GTK_WINDOW ( dialog ) ) )
{
button = gtk_button_new_with_mnemonic ( _ ( " _Minimize to Tray " ) ) ;
gtk_widget_show ( button ) ;
2026-01-15 18:35:27 -07:00
gtk_dialog_add_action_widget ( GTK_DIALOG ( dialog ) , button , 1 ) ;
}
2026-01-30 08:47:31 -07:00
button = gtk_button_new_with_mnemonic ( _ ( " _Cancel " ) ) ;
gtk_button_set_image ( GTK_BUTTON ( button ) ,
2026-03-13 12:39:55 -06:00
gtkutil_image_new_from_stock ( " gtk-cancel " , GTK_ICON_SIZE_BUTTON ) ) ;
2026-01-15 18:35:27 -07:00
gtk_widget_show ( button ) ;
gtk_dialog_add_action_widget ( GTK_DIALOG ( dialog ) , button ,
GTK_RESPONSE_CANCEL ) ;
gtk_widget_grab_focus ( button ) ;
2026-01-30 08:47:31 -07:00
button = gtk_button_new_with_mnemonic ( _ ( " _Quit " ) ) ;
gtk_button_set_image ( GTK_BUTTON ( button ) ,
gtk_image_new_from_icon_name ( " application-exit " , GTK_ICON_SIZE_BUTTON ) ) ;
2026-01-15 18:35:27 -07:00
gtk_widget_show ( button ) ;
gtk_dialog_add_action_widget ( GTK_DIALOG ( dialog ) , button , 0 ) ;
gtk_widget_show ( dialog ) ;
switch ( gtk_dialog_run ( GTK_DIALOG ( dialog ) ) )
{
case 0 :
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( checkbutton1 ) ) )
prefs . hex_gui_quit_dialog = 0 ;
zoitechat_exit ( ) ;
break ;
case 1 : /* minimize to tray */
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( checkbutton1 ) ) )
{
prefs . hex_gui_tray_close = 1 ;
/*prefs.hex_gui_quit_dialog = 0;*/
}
/* force tray icon ON, if not already */
if ( ! prefs . hex_gui_tray )
{
prefs . hex_gui_tray = 1 ;
tray_apply_setup ( ) ;
}
tray_toggle_visibility ( TRUE ) ;
break ;
}
gtk_widget_destroy ( dialog ) ;
dialog = NULL ;
2011-02-24 04:14:30 +01:00
}
void
mg_close_sess ( session * sess )
{
2026-01-15 18:35:27 -07:00
if ( sess_list - > next = = NULL )
{
mg_open_quit_dialog ( FALSE ) ;
return ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
fe_close_window ( sess ) ;
2011-02-24 04:14:30 +01:00
}
static int
mg_chan_remove ( chan * ch )
{
2026-01-15 18:35:27 -07:00
/* remove the tab from chanview */
chan_remove ( ch , TRUE ) ;
/* any tabs left? */
if ( chanview_get_size ( mg_gui - > chanview ) < 1 )
{
/* if not, destroy the main tab window */
gtk_widget_destroy ( mg_gui - > window ) ;
current_tab = NULL ;
active_tab = NULL ;
mg_gui = NULL ;
parent_window = NULL ;
return TRUE ;
}
return FALSE ;
2011-02-24 04:14:30 +01:00
}
/* destroy non-irc tab/window */
static void
mg_close_gen ( chan * ch , GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
if ( ! ch )
ch = g_object_get_data ( G_OBJECT ( box ) , " ch " ) ;
if ( ch )
{
/* remove from notebook */
gtk_widget_destroy ( box ) ;
/* remove the tab from chanview */
mg_chan_remove ( ch ) ;
} else
{
gtk_widget_destroy ( gtk_widget_get_toplevel ( box ) ) ;
}
2011-02-24 04:14:30 +01:00
}
/* the "X" close button has been pressed (tab-view) */
static void
mg_xbutton_cb ( chanview * cv , chan * ch , int tag , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
if ( tag = = TAG_IRC ) /* irc tab */
mg_close_sess ( userdata ) ;
else /* non-irc utility tab */
mg_close_gen ( ch , userdata ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_link_gentab ( chan * ch , GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
int num ;
GtkWidget * win ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
g_object_ref ( box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
num = gtk_notebook_page_num ( GTK_NOTEBOOK ( mg_gui - > note_book ) , box ) ;
gtk_notebook_remove_page ( GTK_NOTEBOOK ( mg_gui - > note_book ) , num ) ;
mg_chan_remove ( ch ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
win = gtkutil_window_new ( g_object_get_data ( G_OBJECT ( box ) , " title " ) , " " ,
GPOINTER_TO_INT ( g_object_get_data ( G_OBJECT ( box ) , " w " ) ) ,
GPOINTER_TO_INT ( g_object_get_data ( G_OBJECT ( box ) , " h " ) ) ,
2 ) ;
/* so it doesn't try to chan_remove (there's no tab anymore) */
g_object_steal_data ( G_OBJECT ( box ) , " ch " ) ;
gtk_container_set_border_width ( GTK_CONTAINER ( box ) , 0 ) ;
gtk_container_add ( GTK_CONTAINER ( win ) , box ) ;
gtk_widget_show ( win ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
g_object_unref ( box ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_detach_tab_cb ( GtkWidget * item , chan * ch )
{
2026-01-15 18:35:27 -07:00
if ( chan_get_tag ( ch ) = = TAG_IRC ) /* IRC tab */
{
/* userdata is session * */
mg_link_irctab ( chan_get_userdata ( ch ) , 1 ) ;
return ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* userdata is GtkWidget * */
mg_link_gentab ( ch , chan_get_userdata ( ch ) ) ; /* non-IRC tab */
2011-02-24 04:14:30 +01:00
}
static void
mg_destroy_tab_cb ( GtkWidget * item , chan * ch )
{
2026-01-15 18:35:27 -07:00
/* treat it just like the X button press */
mg_xbutton_cb ( mg_gui - > chanview , ch , chan_get_tag ( ch ) , chan_get_userdata ( ch ) ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_color_insert ( GtkWidget * item , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
char buf [ 32 ] ;
char * text ;
int num = GPOINTER_TO_INT ( userdata ) ;
if ( num > 99 )
{
switch ( num )
{
case 100 :
text = " \002 " ; break ;
case 101 :
text = " \037 " ; break ;
case 102 :
text = " \035 " ; break ;
case 103 :
text = " \036 " ; break ;
default :
text = " \017 " ; break ;
}
key_action_insert ( current_sess - > gui - > input_box , 0 , text , 0 , 0 ) ;
} else
{
sprintf ( buf , " \003 %02d " , num ) ;
key_action_insert ( current_sess - > gui - > input_box , 0 , buf , 0 , 0 ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_markup_item ( GtkWidget * menu , char * text , int arg )
{
2026-01-15 18:35:27 -07:00
GtkWidget * item ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
item = gtk_menu_item_new_with_label ( " " ) ;
gtk_label_set_markup ( GTK_LABEL ( gtk_bin_get_child ( GTK_BIN ( item ) ) ) , text ) ;
g_signal_connect ( G_OBJECT ( item ) , " activate " ,
G_CALLBACK ( mg_color_insert ) , GINT_TO_POINTER ( arg ) ) ;
gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ) , item ) ;
gtk_widget_show ( item ) ;
2011-02-24 04:14:30 +01:00
}
GtkWidget *
mg_submenu ( GtkWidget * menu , char * text )
{
2026-01-15 18:35:27 -07:00
GtkWidget * submenu , * item ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
item = gtk_menu_item_new_with_mnemonic ( text ) ;
gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ) , item ) ;
gtk_widget_show ( item ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
submenu = gtk_menu_new ( ) ;
gtk_menu_item_set_submenu ( GTK_MENU_ITEM ( item ) , submenu ) ;
gtk_widget_show ( submenu ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return submenu ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_color_menu ( GtkWidget * menu , session * sess )
{
2026-01-15 18:35:27 -07:00
GtkWidget * submenu ;
GtkWidget * subsubmenu ;
char buf [ 256 ] ;
int i ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
submenu = mg_submenu ( menu , _ ( " Insert Attribute or Color Code " ) ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_markup_item ( submenu , _ ( " <b>Bold</b> " ) , 100 ) ;
mg_markup_item ( submenu , _ ( " <u>Underline</u> " ) , 101 ) ;
mg_markup_item ( submenu , _ ( " <i>Italic</i> " ) , 102 ) ;
mg_markup_item ( submenu , _ ( " <s>Strikethrough</s> " ) , 103 ) ;
mg_markup_item ( submenu , _ ( " Normal " ) , 999 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
subsubmenu = mg_submenu ( submenu , _ ( " Colors 0-7 " ) ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
for ( i = 0 ; i < 8 ; i + + )
{
2026-01-19 22:50:17 -07:00
guint16 red ;
guint16 green ;
guint16 blue ;
2026-03-02 19:42:48 -07:00
GdkRGBA color ;
if ( ! theme_get_mirc_color ( ( unsigned int ) i , & color ) )
continue ;
red = ( guint16 ) CLAMP ( color . red * 65535.0 + 0.5 , 0.0 , 65535.0 ) ;
green = ( guint16 ) CLAMP ( color . green * 65535.0 + 0.5 , 0.0 , 65535.0 ) ;
blue = ( guint16 ) CLAMP ( color . blue * 65535.0 + 0.5 , 0.0 , 65535.0 ) ;
2026-01-15 18:35:27 -07:00
sprintf ( buf , " <tt><sup>%02d</sup> <span background= \" #%02x%02x%02x \" > "
" </span></tt> " ,
2026-01-19 22:50:17 -07:00
i , red > > 8 , green > > 8 , blue > > 8 ) ;
2026-01-15 18:35:27 -07:00
mg_markup_item ( subsubmenu , buf , i ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
subsubmenu = mg_submenu ( submenu , _ ( " Colors 8-15 " ) ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
for ( i = 8 ; i < 16 ; i + + )
{
2026-01-19 22:50:17 -07:00
guint16 red ;
guint16 green ;
guint16 blue ;
2026-03-02 19:42:48 -07:00
GdkRGBA color ;
if ( ! theme_get_mirc_color ( ( unsigned int ) i , & color ) )
continue ;
red = ( guint16 ) CLAMP ( color . red * 65535.0 + 0.5 , 0.0 , 65535.0 ) ;
green = ( guint16 ) CLAMP ( color . green * 65535.0 + 0.5 , 0.0 , 65535.0 ) ;
blue = ( guint16 ) CLAMP ( color . blue * 65535.0 + 0.5 , 0.0 , 65535.0 ) ;
2026-01-15 18:35:27 -07:00
sprintf ( buf , " <tt><sup>%02d</sup> <span background= \" #%02x%02x%02x \" > "
" </span></tt> " ,
2026-01-19 22:50:17 -07:00
i , red > > 8 , green > > 8 , blue > > 8 ) ;
2026-01-15 18:35:27 -07:00
mg_markup_item ( subsubmenu , buf , i ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_set_guint8 ( GtkCheckMenuItem * item , guint8 * setting )
{
2026-01-15 18:35:27 -07:00
session * sess = current_sess ;
guint8 logging = sess - > text_logging ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
* setting = SET_OFF ;
if ( gtk_check_menu_item_get_active ( item ) )
* setting = SET_ON ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* has the logging setting changed? */
if ( logging ! = sess - > text_logging )
log_open_or_close ( sess ) ;
2013-06-06 19:16:50 -04:00
2026-01-15 18:35:27 -07:00
chanopt_save ( sess ) ;
chanopt_save_all ( FALSE ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_perchan_menu_item ( char * label , GtkWidget * menu , guint8 * setting , guint global )
{
2026-01-15 18:35:27 -07:00
guint8 initial_value = * setting ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* if it's using global value, use that as initial state */
if ( initial_value = = SET_DEFAULT )
initial_value = global ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
menu_toggle_item ( label , menu , mg_set_guint8 , setting , initial_value ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_perchannelmenu ( session * sess , GtkWidget * menu )
{
2026-01-15 18:35:27 -07:00
GtkWidget * submenu ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
submenu = menu_quick_sub ( _ ( " _Settings " ) , menu , NULL , XCMENU_MNEMONIC , - 1 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_perchan_menu_item ( _ ( " _Log to Disk " ) , submenu , & sess - > text_logging , prefs . hex_irc_logging ) ;
mg_perchan_menu_item ( _ ( " _Reload Scrollback " ) , submenu , & sess - > text_scrollback , prefs . hex_text_replay ) ;
if ( sess - > type = = SESS_CHANNEL )
{
mg_perchan_menu_item ( _ ( " Strip _Colors " ) , submenu , & sess - > text_strip , prefs . hex_text_stripcolor_msg ) ;
mg_perchan_menu_item ( _ ( " _Hide Join/Part Messages " ) , submenu , & sess - > text_hidejoinpart , prefs . hex_irc_conf_mode ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_create_alertmenu ( session * sess , GtkWidget * menu )
{
2026-01-15 18:35:27 -07:00
GtkWidget * submenu ;
int hex_balloon , hex_beep , hex_tray , hex_flash ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
switch ( sess - > type ) {
case SESS_DIALOG :
hex_balloon = prefs . hex_input_balloon_priv ;
hex_beep = prefs . hex_input_beep_priv ;
hex_tray = prefs . hex_input_tray_priv ;
hex_flash = prefs . hex_input_flash_priv ;
break ;
default :
hex_balloon = prefs . hex_input_balloon_chans ;
hex_beep = prefs . hex_input_beep_chans ;
hex_tray = prefs . hex_input_tray_chans ;
hex_flash = prefs . hex_input_flash_chans ;
}
2020-07-29 09:50:12 -05:00
2026-01-15 18:35:27 -07:00
submenu = menu_quick_sub ( _ ( " _Extra Alerts " ) , menu , NULL , XCMENU_MNEMONIC , - 1 ) ;
2020-07-29 09:50:12 -05:00
2026-01-15 18:35:27 -07:00
mg_perchan_menu_item ( _ ( " Show Notifications " ) , submenu , & sess - > alert_balloon , hex_balloon ) ;
2020-07-29 09:50:12 -05:00
2026-01-15 18:35:27 -07:00
mg_perchan_menu_item ( _ ( " Beep on _Message " ) , submenu , & sess - > alert_beep , hex_beep ) ;
2011-08-09 21:04:11 +02:00
2026-01-15 18:35:27 -07:00
mg_perchan_menu_item ( _ ( " Blink Tray _Icon " ) , submenu , & sess - > alert_tray , hex_tray ) ;
2011-08-09 21:04:11 +02:00
2026-01-15 18:35:27 -07:00
mg_perchan_menu_item ( _ ( " Blink Task _Bar " ) , submenu , & sess - > alert_taskbar , hex_flash ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_tabmenu ( session * sess , GdkEventButton * event , chan * ch )
{
2026-01-15 18:35:27 -07:00
GtkWidget * menu , * item ;
char buf [ 256 ] ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
menu = gtk_menu_new ( ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess )
{
char * name = g_markup_escape_text ( sess - > channel [ 0 ] ? sess - > channel : _ ( " <none> " ) , - 1 ) ;
g_snprintf ( buf , sizeof ( buf ) , " <span foreground= \" #3344cc \" ><b>%s</b></span> " , name ) ;
g_free ( name ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
item = gtk_menu_item_new_with_label ( " " ) ;
gtk_label_set_markup ( GTK_LABEL ( gtk_bin_get_child ( GTK_BIN ( item ) ) ) , buf ) ;
gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ) , item ) ;
gtk_widget_show ( item ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* separator */
menu_quick_item ( 0 , 0 , menu , XCMENU_SHADED , 0 , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* per-channel alerts */
mg_create_alertmenu ( sess , menu ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* per-channel settings */
mg_create_perchannelmenu ( sess , menu ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* separator */
menu_quick_item ( 0 , 0 , menu , XCMENU_SHADED , 0 , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > type = = SESS_CHANNEL )
menu_addfavoritemenu ( sess - > server , menu , sess - > channel , TRUE ) ;
else if ( sess - > type = = SESS_SERVER )
menu_addconnectmenu ( sess - > server , menu ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-30 15:27:01 -07:00
mg_create_icon_item ( _ ( " _Detach " ) , ICON_TAB_DETACH , menu ,
2026-01-15 18:35:27 -07:00
mg_detach_tab_cb , ch ) ;
2026-01-30 15:27:01 -07:00
mg_create_icon_item ( _ ( " _Close " ) , ICON_TAB_CLOSE , menu ,
2026-01-15 18:35:27 -07:00
mg_destroy_tab_cb , ch ) ;
if ( sess & & tabmenu_list )
menu_create ( menu , tabmenu_list , sess - > channel , FALSE ) ;
if ( sess )
menu_add_plugin_items ( menu , " \ x4$TAB " , sess - > channel ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( event - > window )
gtk_menu_set_screen ( GTK_MENU ( menu ) , gdk_window_get_screen ( event - > window ) ) ;
g_object_ref ( menu ) ;
g_object_ref_sink ( menu ) ;
g_object_unref ( menu ) ;
g_signal_connect ( G_OBJECT ( menu ) , " selection-done " ,
G_CALLBACK ( mg_menu_destroy ) , NULL ) ;
2026-01-31 12:57:05 -07:00
gtk_menu_popup_at_pointer ( GTK_MENU ( menu ) , ( GdkEvent * ) event ) ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_tab_contextmenu_cb ( chanview * cv , chan * ch , int tag , gpointer ud , GdkEventButton * event )
{
2026-01-15 18:35:27 -07:00
/* middle-click to close a tab */
if ( ( ( prefs . hex_gui_tab_middleclose & & event - > button = = 2 ) )
& & event - > type = = GDK_BUTTON_PRESS )
{
mg_xbutton_cb ( cv , ch , tag , ud ) ;
return TRUE ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( event - > button ! = 3 )
return FALSE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( tag = = TAG_IRC )
mg_create_tabmenu ( ud , event , ch ) ;
else
mg_create_tabmenu ( NULL , event , ch ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return TRUE ;
2011-02-24 04:14:30 +01:00
}
void
mg_dnd_drop_file ( session * sess , char * target , char * uri )
{
2026-01-15 18:35:27 -07:00
char * p , * data , * next , * fname ;
p = data = g_strdup ( uri ) ;
while ( * p )
{
next = strchr ( p , ' \r ' ) ;
if ( g_ascii_strncasecmp ( " file: " , p , 5 ) = = 0 )
{
if ( next )
* next = 0 ;
fname = g_filename_from_uri ( p , NULL , NULL ) ;
if ( fname )
{
/* dcc_send() expects utf-8 */
p = g_filename_from_utf8 ( fname , - 1 , 0 , 0 , 0 ) ;
if ( p )
{
dcc_send ( sess , target , p , prefs . hex_dcc_max_send_cps , 0 ) ;
g_free ( p ) ;
}
g_free ( fname ) ;
}
}
if ( ! next )
break ;
p = next + 1 ;
if ( * p = = ' \n ' )
p + + ;
}
g_free ( data ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_dialog_dnd_drop ( GtkWidget * widget , GdkDragContext * context , gint x ,
2026-01-15 18:35:27 -07:00
gint y , GtkSelectionData * selection_data , guint info ,
guint32 time , gpointer ud )
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
if ( current_sess - > type = = SESS_DIALOG )
/* sess->channel is really the nickname of dialogs */
mg_dnd_drop_file ( current_sess , current_sess - > channel , ( char * ) gtk_selection_data_get_data ( selection_data ) ) ;
2011-02-24 04:14:30 +01:00
}
/* add a tabbed channel */
static void
mg_add_chan ( session * sess )
{
2026-01-15 18:35:27 -07:00
GdkPixbuf * icon ;
char * name = _ ( " <none> " ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > channel [ 0 ] )
name = sess - > channel ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
switch ( sess - > type )
{
case SESS_CHANNEL :
icon = pix_tree_channel ;
break ;
case SESS_SERVER :
icon = pix_tree_server ;
break ;
default :
icon = pix_tree_dialog ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
sess - > res - > tab = chanview_add ( sess - > gui - > chanview , name , sess - > server , sess ,
sess - > type = = SESS_SERVER ? FALSE : TRUE ,
TAG_IRC , icon ) ;
if ( plain_list = = NULL )
mg_create_tab_colors ( ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
chan_set_color ( sess - > res - > tab , plain_list ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > res - > buffer = = NULL )
{
sess - > res - > buffer = gtk_xtext_buffer_new ( GTK_XTEXT ( sess - > gui - > xtext ) ) ;
gtk_xtext_set_time_stamp ( sess - > res - > buffer , prefs . hex_stamp_text ) ;
sess - > res - > user_model = userlist_create_model ( sess ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_userlist_button ( GtkWidget * box , char * label , char * cmd ,
2026-01-15 18:35:27 -07:00
int a , int b , int c , int d )
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
GtkWidget * wid = gtk_button_new_with_label ( label ) ;
g_signal_connect ( G_OBJECT ( wid ) , " clicked " ,
G_CALLBACK ( userlist_button_cb ) , cmd ) ;
2026-01-19 19:26:29 -07:00
gtk_widget_set_hexpand ( wid , TRUE ) ;
gtk_widget_set_vexpand ( wid , TRUE ) ;
gtk_widget_set_halign ( wid , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( wid , GTK_ALIGN_FILL ) ;
2026-01-30 16:04:50 -07:00
gtk_grid_attach ( GTK_GRID ( box ) , wid , a , c , b - a , d - c ) ;
2026-01-15 18:35:27 -07:00
show_and_unfocus ( wid ) ;
2011-02-24 04:14:30 +01:00
}
static GtkWidget *
mg_create_userlistbuttons ( GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
struct popup * pop ;
GSList * list = button_list ;
int a = 0 , b = 0 ;
GtkWidget * tab ;
2026-01-19 19:26:29 -07:00
tab = gtk_grid_new ( ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_end ( GTK_BOX ( box ) , tab , FALSE , FALSE , 0 ) ;
while ( list )
{
pop = list - > data ;
if ( pop - > cmd [ 0 ] )
{
mg_userlist_button ( tab , pop - > name , pop - > cmd , a , a + 1 , b , b + 1 ) ;
a + + ;
if ( a = = 2 )
{
a = 0 ;
b + + ;
}
}
list = list - > next ;
}
return tab ;
2011-02-24 04:14:30 +01:00
}
static void
2026-03-14 19:57:42 -06:00
mg_topic_cb ( GtkWidget * entry )
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
session * sess = current_sess ;
2026-03-14 19:57:42 -06:00
GtkTextBuffer * topic_buffer ;
GtkTextIter start ;
GtkTextIter end ;
2026-01-15 18:35:27 -07:00
char * text ;
if ( sess - > channel [ 0 ] & & sess - > server - > connected & & sess - > type = = SESS_CHANNEL )
{
2026-03-14 19:57:42 -06:00
topic_buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( entry ) ) ;
gtk_text_buffer_get_bounds ( topic_buffer , & start , & end ) ;
text = gtk_text_buffer_get_text ( topic_buffer , & start , & end , FALSE ) ;
2026-01-15 18:35:27 -07:00
if ( text [ 0 ] = = 0 )
2026-03-14 19:57:42 -06:00
sess - > server - > p_topic ( sess - > server , sess - > channel , NULL ) ;
else
sess - > server - > p_topic ( sess - > server , sess - > channel , text ) ;
g_free ( text ) ;
2026-01-15 18:35:27 -07:00
} else
2026-03-14 19:57:42 -06:00
{
topic_buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( entry ) ) ;
gtk_text_buffer_set_text ( topic_buffer , " " , - 1 ) ;
}
2026-01-15 18:35:27 -07:00
/* restore focus to the input widget, where the next input will most
2011-02-24 04:14:30 +01:00
likely be */
2026-01-15 18:35:27 -07:00
gtk_widget_grab_focus ( sess - > gui - > input_box ) ;
2011-02-24 04:14:30 +01:00
}
2026-03-14 19:57:42 -06:00
static gboolean
mg_topic_key_press_cb ( GtkWidget * entry , GdkEventKey * event , gpointer userdata )
{
if ( event - > keyval = = GDK_KEY_Return | | event - > keyval = = GDK_KEY_KP_Enter )
{
mg_topic_cb ( entry ) ;
return TRUE ;
}
return FALSE ;
}
static char *
2026-03-16 19:10:49 -06:00
mg_topic_get_word_at_pos ( GtkWidget * entry , gdouble event_x , gdouble event_y , int * word_pos )
2026-03-14 19:57:42 -06:00
{
GtkTextBuffer * buffer ;
GtkTextIter iter ;
GtkTextIter start ;
GtkTextIter end ;
2026-03-16 19:10:49 -06:00
GtkTextIter cursor ;
2026-03-14 19:57:42 -06:00
int x ;
int y ;
x = ( int ) event_x ;
y = ( int ) event_y ;
gtk_text_view_window_to_buffer_coords ( GTK_TEXT_VIEW ( entry ) , GTK_TEXT_WINDOW_TEXT ,
x , y , & x , & y ) ;
gtk_text_view_get_iter_at_location ( GTK_TEXT_VIEW ( entry ) , & iter , x , y ) ;
2026-03-16 19:10:49 -06:00
cursor = iter ;
2026-03-14 19:57:42 -06:00
start = iter ;
while ( ! gtk_text_iter_starts_line ( & start ) )
{
GtkTextIter prev = start ;
gunichar ch ;
gtk_text_iter_backward_char ( & prev ) ;
ch = gtk_text_iter_get_char ( & prev ) ;
if ( g_unichar_isspace ( ch ) )
break ;
start = prev ;
}
end = iter ;
while ( ! gtk_text_iter_ends_line ( & end ) )
{
gunichar ch ;
ch = gtk_text_iter_get_char ( & end ) ;
if ( ch = = 0 | | g_unichar_isspace ( ch ) )
break ;
gtk_text_iter_forward_char ( & end ) ;
}
if ( gtk_text_iter_equal ( & start , & end ) )
return NULL ;
2026-03-16 19:10:49 -06:00
if ( word_pos )
{
char * prefix ;
buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( entry ) ) ;
prefix = gtk_text_buffer_get_text ( buffer , & start , & cursor , FALSE ) ;
* word_pos = ( int ) strlen ( prefix ) ;
g_free ( prefix ) ;
}
2026-03-14 19:57:42 -06:00
buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( entry ) ) ;
return gtk_text_buffer_get_text ( buffer , & start , & end , FALSE ) ;
}
static void
mg_topic_set_cursor ( GtkWidget * entry , GdkCursorType cursor_type )
{
GdkWindow * text_window ;
GdkDisplay * display ;
GdkCursor * cursor ;
text_window = gtk_text_view_get_window ( GTK_TEXT_VIEW ( entry ) , GTK_TEXT_WINDOW_TEXT ) ;
if ( ! text_window )
return ;
display = gdk_window_get_display ( text_window ) ;
cursor = gdk_cursor_new_for_display ( display , cursor_type ) ;
gdk_window_set_cursor ( text_window , cursor ) ;
g_object_unref ( cursor ) ;
}
static gboolean
2026-03-16 19:10:49 -06:00
mg_topic_word_is_clickable ( const char * word , int word_pos )
2026-03-14 19:57:42 -06:00
{
2026-03-16 19:10:49 -06:00
int start ;
int end ;
2026-03-14 19:57:42 -06:00
if ( ! word | | word [ 0 ] = = 0 )
return FALSE ;
if ( strcmp ( word , " / " ) = = 0 )
return FALSE ;
2026-03-16 19:10:49 -06:00
if ( url_check_word ( word ) = = 0 )
return FALSE ;
url_last ( & start , & end ) ;
return word_pos > = start & & word_pos < end ;
2026-03-14 19:57:42 -06:00
}
static gboolean
mg_topic_motion_cb ( GtkWidget * entry , GdkEventMotion * event , gpointer userdata )
{
char * word ;
2026-03-16 19:10:49 -06:00
int word_pos ;
2026-03-14 19:57:42 -06:00
gboolean word_is_clickable ;
2026-03-16 19:10:49 -06:00
word_pos = 0 ;
word = mg_topic_get_word_at_pos ( entry , event - > x , event - > y , & word_pos ) ;
word_is_clickable = mg_topic_word_is_clickable ( word , word_pos ) ;
2026-03-14 19:57:42 -06:00
if ( word_is_clickable )
mg_topic_set_cursor ( entry , GDK_HAND2 ) ;
else
mg_topic_set_cursor ( entry , GDK_XTERM ) ;
g_free ( word ) ;
return FALSE ;
}
static gboolean
mg_topic_leave_cb ( GtkWidget * entry , GdkEventCrossing * event , gpointer userdata )
{
mg_topic_set_cursor ( entry , GDK_XTERM ) ;
return FALSE ;
}
static gboolean
mg_topic_button_release_cb ( GtkWidget * entry , GdkEventButton * event , gpointer userdata )
{
char * word ;
2026-03-16 19:10:49 -06:00
int word_pos ;
2026-03-14 19:57:42 -06:00
int start ;
int end ;
if ( event - > button ! = 1 )
return FALSE ;
2026-03-16 19:10:49 -06:00
word_pos = 0 ;
word = mg_topic_get_word_at_pos ( entry , event - > x , event - > y , & word_pos ) ;
2026-03-14 19:57:42 -06:00
if ( ! word )
return FALSE ;
2026-03-16 19:10:49 -06:00
if ( mg_topic_word_is_clickable ( word , word_pos ) )
2026-03-14 19:57:42 -06:00
{
url_last ( & start , & end ) ;
word [ end ] = 0 ;
fe_open_url ( word + start ) ;
g_free ( word ) ;
return TRUE ;
}
g_free ( word ) ;
return FALSE ;
}
2011-02-24 04:14:30 +01:00
static void
mg_tabwindow_kill_cb ( GtkWidget * win , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
GSList * list , * next ;
session * sess ;
2026-03-16 21:57:21 -06:00
mg_flush_config_save ( ) ;
2026-01-15 18:35:27 -07:00
zoitechat_is_quitting = TRUE ;
/* see if there's any non-tab windows left */
list = sess_list ;
while ( list )
{
sess = list - > data ;
next = list - > next ;
if ( ! sess - > gui - > is_tab )
{
zoitechat_is_quitting = FALSE ;
} else
{
mg_ircdestroy ( sess ) ;
}
list = next ;
}
current_tab = NULL ;
active_tab = NULL ;
mg_gui = NULL ;
parent_window = NULL ;
2011-02-24 04:14:30 +01:00
}
static GtkWidget *
mg_changui_destroy ( session * sess )
{
2026-01-15 18:35:27 -07:00
GtkWidget * ret = NULL ;
if ( sess - > gui - > is_tab )
{
/* avoid calling the "destroy" callback */
g_signal_handlers_disconnect_by_func ( G_OBJECT ( sess - > gui - > window ) ,
mg_tabwindow_kill_cb , 0 ) ;
/* remove the tab from the chanview */
if ( ! mg_chan_remove ( sess - > res - > tab ) )
/* if the window still exists, restore the signal handler */
g_signal_connect ( G_OBJECT ( sess - > gui - > window ) , " destroy " ,
G_CALLBACK ( mg_tabwindow_kill_cb ) , 0 ) ;
} else
{
/* avoid calling the "destroy" callback */
g_signal_handlers_disconnect_by_func ( G_OBJECT ( sess - > gui - > window ) ,
mg_topdestroy_cb , sess ) ;
/* don't destroy until the new one is created. Not sure why, but */
/* it fixes: Gdk-CRITICAL **: gdk_colormap_get_screen: */
/* assertion `GDK_IS_COLORMAP (cmap)' failed */
ret = sess - > gui - > window ;
g_free ( sess - > gui ) ;
sess - > gui = NULL ;
}
return ret ;
2011-02-24 04:14:30 +01:00
}
static void
mg_link_irctab ( session * sess , int focus )
{
2026-01-15 18:35:27 -07:00
GtkWidget * win ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > gui - > is_tab )
{
win = mg_changui_destroy ( sess ) ;
mg_changui_new ( sess , sess - > res , 0 , focus ) ;
mg_populate ( sess ) ;
zoitechat_is_quitting = FALSE ;
if ( win )
gtk_widget_destroy ( win ) ;
return ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_unpopulate ( sess ) ;
win = mg_changui_destroy ( sess ) ;
mg_changui_new ( sess , sess - > res , 1 , focus ) ;
/* the buffer is now attached to a different widget */
( ( xtext_buffer * ) sess - > res - > buffer ) - > xtext = ( GtkXText * ) sess - > gui - > xtext ;
if ( win )
gtk_widget_destroy ( win ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_detach ( session * sess , int mode )
{
2026-01-15 18:35:27 -07:00
switch ( mode )
{
/* detach only */
case 1 :
if ( sess - > gui - > is_tab )
mg_link_irctab ( sess , 1 ) ;
break ;
/* attach only */
case 2 :
if ( ! sess - > gui - > is_tab )
mg_link_irctab ( sess , 1 ) ;
break ;
/* toggle */
default :
mg_link_irctab ( sess , 1 ) ;
}
2011-02-24 04:14:30 +01:00
}
static int
check_is_number ( char * t )
{
2026-01-15 18:35:27 -07:00
while ( * t )
{
if ( * t < ' 0 ' | | * t > ' 9 ' )
return FALSE ;
t + + ;
}
return TRUE ;
2011-02-24 04:14:30 +01:00
}
static void
mg_change_flag ( GtkWidget * wid , session * sess , char flag )
{
2026-01-15 18:35:27 -07:00
server * serv = sess - > server ;
char mode [ 3 ] ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mode [ 1 ] = flag ;
mode [ 2 ] = ' \0 ' ;
if ( serv - > connected & & sess - > channel [ 0 ] )
{
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( wid ) ) )
mode [ 0 ] = ' + ' ;
else
mode [ 0 ] = ' - ' ;
serv - > p_mode ( serv , sess - > channel , mode ) ;
serv - > p_join_info ( serv , sess - > channel ) ;
sess - > ignore_mode = TRUE ;
sess - > ignore_date = TRUE ;
}
2011-02-24 04:14:30 +01:00
}
static void
flagl_hit ( GtkWidget * wid , struct session * sess )
{
2026-01-15 18:35:27 -07:00
char modes [ 512 ] ;
const char * limit_str ;
server * serv = sess - > server ;
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( wid ) ) )
{
if ( serv - > connected & & sess - > channel [ 0 ] )
{
limit_str = gtk_entry_get_text ( GTK_ENTRY ( sess - > gui - > limit_entry ) ) ;
if ( check_is_number ( ( char * ) limit_str ) = = FALSE )
{
fe_message ( _ ( " User limit must be a number! \n " ) , FE_MSG_ERROR ) ;
gtk_entry_set_text ( GTK_ENTRY ( sess - > gui - > limit_entry ) , " " ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( wid ) , FALSE ) ;
return ;
}
g_snprintf ( modes , sizeof ( modes ) , " +l %d " , atoi ( limit_str ) ) ;
serv - > p_mode ( serv , sess - > channel , modes ) ;
serv - > p_join_info ( serv , sess - > channel ) ;
}
} else
mg_change_flag ( wid , sess , ' l ' ) ;
2011-02-24 04:14:30 +01:00
}
static void
flagk_hit ( GtkWidget * wid , struct session * sess )
{
2026-01-15 18:35:27 -07:00
char modes [ 512 ] ;
server * serv = sess - > server ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( serv - > connected & & sess - > channel [ 0 ] )
{
g_snprintf ( modes , sizeof ( modes ) , " -k %s " ,
gtk_entry_get_text ( GTK_ENTRY ( sess - > gui - > key_entry ) ) ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( wid ) ) )
modes [ 0 ] = ' + ' ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
serv - > p_mode ( serv , sess - > channel , modes ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_flagbutton_cb ( GtkWidget * but , char * flag )
{
2026-01-15 18:35:27 -07:00
session * sess ;
char mode ;
if ( ignore_chanmode )
return ;
sess = current_sess ;
mode = tolower ( ( unsigned char ) flag [ 0 ] ) ;
switch ( mode )
{
case ' l ' :
flagl_hit ( but , sess ) ;
break ;
case ' k ' :
flagk_hit ( but , sess ) ;
break ;
case ' b ' :
ignore_chanmode = TRUE ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( sess - > gui - > flag_b ) , FALSE ) ;
ignore_chanmode = FALSE ;
banlist_opengui ( sess ) ;
break ;
default :
mg_change_flag ( but , sess , mode ) ;
}
2011-02-24 04:14:30 +01:00
}
static GtkWidget *
mg_create_flagbutton ( char * tip , GtkWidget * box , char * face )
{
2026-01-15 18:35:27 -07:00
GtkWidget * btn , * lbl ;
char label_markup [ 16 ] ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
g_snprintf ( label_markup , sizeof ( label_markup ) , " <tt>%s</tt> " , face ) ;
lbl = gtk_label_new ( NULL ) ;
gtk_label_set_markup ( GTK_LABEL ( lbl ) , label_markup ) ;
2015-04-21 15:35:15 -04:00
2026-01-15 18:35:27 -07:00
btn = gtk_toggle_button_new ( ) ;
2026-03-18 09:10:40 -06:00
gtk_widget_set_size_request ( btn , - 1 , 11 ) ;
2026-01-15 18:35:27 -07:00
gtk_widget_set_tooltip_text ( btn , tip ) ;
2026-03-18 09:10:40 -06:00
gtk_button_set_relief ( GTK_BUTTON ( btn ) , GTK_RELIEF_NONE ) ;
mg_apply_compact_mode_css ( btn ) ;
2026-01-15 18:35:27 -07:00
gtk_container_add ( GTK_CONTAINER ( btn ) , lbl ) ;
2015-04-21 15:35:15 -04:00
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , btn , 0 , 0 , 0 ) ;
g_signal_connect ( G_OBJECT ( btn ) , " toggled " ,
G_CALLBACK ( mg_flagbutton_cb ) , face ) ;
show_and_unfocus ( btn ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return btn ;
2011-02-24 04:14:30 +01:00
}
static void
mg_key_entry_cb ( GtkWidget * igad , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
char modes [ 512 ] ;
session * sess = current_sess ;
server * serv = sess - > server ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( serv - > connected & & sess - > channel [ 0 ] )
{
g_snprintf ( modes , sizeof ( modes ) , " +k %s " ,
gtk_entry_get_text ( GTK_ENTRY ( igad ) ) ) ;
serv - > p_mode ( serv , sess - > channel , modes ) ;
serv - > p_join_info ( serv , sess - > channel ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_limit_entry_cb ( GtkWidget * igad , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
char modes [ 512 ] ;
session * sess = current_sess ;
server * serv = sess - > server ;
if ( serv - > connected & & sess - > channel [ 0 ] )
{
if ( check_is_number ( ( char * ) gtk_entry_get_text ( GTK_ENTRY ( igad ) ) ) = = FALSE )
{
gtk_entry_set_text ( GTK_ENTRY ( igad ) , " " ) ;
fe_message ( _ ( " User limit must be a number! \n " ) , FE_MSG_ERROR ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( sess - > gui - > flag_l ) , FALSE ) ;
return ;
}
g_snprintf ( modes , sizeof ( modes ) , " +l %d " ,
atoi ( gtk_entry_get_text ( GTK_ENTRY ( igad ) ) ) ) ;
serv - > p_mode ( serv , sess - > channel , modes ) ;
serv - > p_join_info ( serv , sess - > channel ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_apply_entry_style ( GtkWidget * entry )
{
2026-03-02 19:42:48 -07:00
theme_manager_apply_entry_palette ( entry , input_style - > font_desc ) ;
2011-02-24 04:14:30 +01:00
}
2026-03-14 17:15:59 -06:00
static gboolean
mg_entry_select_all ( GtkWidget * entry , GdkEventKey * event , gpointer userdata )
{
if ( ( event - > state & GDK_CONTROL_MASK ) & &
! ( event - > state & ( GDK_SHIFT_MASK | GDK_MOD1_MASK | GDK_META_MASK ) ) & &
( event - > keyval = = GDK_KEY_a | | event - > keyval = = GDK_KEY_A ) )
{
gtk_editable_select_region ( GTK_EDITABLE ( entry ) , 0 , - 1 ) ;
return TRUE ;
}
return FALSE ;
}
2011-02-24 04:14:30 +01:00
static void
mg_create_chanmodebuttons ( session_gui * gui , GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
gui - > flag_c = mg_create_flagbutton ( _ ( " Filter Colors " ) , box , " c " ) ;
gui - > flag_n = mg_create_flagbutton ( _ ( " No outside messages " ) , box , " n " ) ;
gui - > flag_t = mg_create_flagbutton ( _ ( " Topic Protection " ) , box , " t " ) ;
gui - > flag_i = mg_create_flagbutton ( _ ( " Invite Only " ) , box , " i " ) ;
gui - > flag_m = mg_create_flagbutton ( _ ( " Moderated " ) , box , " m " ) ;
gui - > flag_b = mg_create_flagbutton ( _ ( " Ban List " ) , box , " b " ) ;
gui - > flag_k = mg_create_flagbutton ( _ ( " Keyword " ) , box , " k " ) ;
gui - > key_entry = gtk_entry_new ( ) ;
gtk_widget_set_name ( gui - > key_entry , " zoitechat-inputbox " ) ;
gtk_entry_set_max_length ( GTK_ENTRY ( gui - > key_entry ) , 23 ) ;
2026-03-18 09:10:40 -06:00
gtk_widget_set_size_request ( gui - > key_entry , 115 , 11 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , gui - > key_entry , 0 , 0 , 0 ) ;
mg_apply_emoji_fallback_widget ( gui - > key_entry ) ;
2026-03-18 09:10:40 -06:00
mg_apply_compact_mode_css ( gui - > key_entry ) ;
2026-01-15 18:35:27 -07:00
g_signal_connect ( G_OBJECT ( gui - > key_entry ) , " activate " ,
G_CALLBACK ( mg_key_entry_cb ) , NULL ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( gui - > key_entry ) , " key-press-event " ,
2026-03-14 17:15:59 -06:00
G_CALLBACK ( mg_entry_select_all ) , NULL ) ;
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_input_style )
mg_apply_entry_style ( gui - > key_entry ) ;
gui - > flag_l = mg_create_flagbutton ( _ ( " User Limit " ) , box , " l " ) ;
gui - > limit_entry = gtk_entry_new ( ) ;
gtk_widget_set_name ( gui - > limit_entry , " zoitechat-inputbox " ) ;
gtk_entry_set_max_length ( GTK_ENTRY ( gui - > limit_entry ) , 10 ) ;
2026-03-18 09:10:40 -06:00
gtk_widget_set_size_request ( gui - > limit_entry , 30 , 11 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , gui - > limit_entry , 0 , 0 , 0 ) ;
mg_apply_emoji_fallback_widget ( gui - > limit_entry ) ;
2026-03-18 09:10:40 -06:00
mg_apply_compact_mode_css ( gui - > limit_entry ) ;
2026-01-15 18:35:27 -07:00
g_signal_connect ( G_OBJECT ( gui - > limit_entry ) , " activate " ,
G_CALLBACK ( mg_limit_entry_cb ) , NULL ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( gui - > limit_entry ) , " key-press-event " ,
2026-03-14 17:15:59 -06:00
G_CALLBACK ( mg_entry_select_all ) , NULL ) ;
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_input_style )
mg_apply_entry_style ( gui - > limit_entry ) ;
2011-02-24 04:14:30 +01:00
}
/*static void
mg_create_link_buttons ( GtkWidget * box , gpointer userdata )
{
2026-01-30 15:27:01 -07:00
gtkutil_button ( box , ICON_TAB_CLOSE , _ ( " Close this tab/window " ) ,
2026-01-15 18:35:27 -07:00
mg_x_click_cb , userdata , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! userdata )
2026-01-30 15:27:01 -07:00
gtkutil_button ( box , ICON_TAB_DETACH , _ ( " Attach/Detach this tab " ) ,
2026-01-15 18:35:27 -07:00
mg_link_cb , userdata , 0 ) ;
2011-02-24 04:14:30 +01:00
} */
static void
mg_dialog_button_cb ( GtkWidget * wid , char * cmd )
{
2026-01-15 18:35:27 -07:00
/* the longest cmd is 12, and the longest nickname is 64 */
char buf [ 128 ] ;
char * host = " " ;
char * topic ;
2026-03-14 19:57:42 -06:00
char * topic_text ;
GtkTextBuffer * topic_buffer ;
GtkTextIter start ;
GtkTextIter end ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! current_sess )
return ;
2011-02-24 04:14:30 +01:00
2026-03-14 19:57:42 -06:00
topic_buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( current_sess - > gui - > topic_entry ) ) ;
gtk_text_buffer_get_bounds ( topic_buffer , & start , & end ) ;
topic_text = gtk_text_buffer_get_text ( topic_buffer , & start , & end , FALSE ) ;
topic = strrchr ( topic_text , ' @ ' ) ;
2026-01-15 18:35:27 -07:00
if ( topic )
host = topic + 1 ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
auto_insert ( buf , sizeof ( buf ) , cmd , 0 , 0 , " " , " " , " " ,
server_get_network ( current_sess - > server , TRUE ) , host , " " ,
current_sess - > channel , " " ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
handle_command ( current_sess , buf , TRUE ) ;
2026-03-14 19:57:42 -06:00
g_free ( topic_text ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* dirty trick to avoid auto-selection */
SPELL_ENTRY_SET_EDITABLE ( current_sess - > gui - > input_box , FALSE ) ;
gtk_widget_grab_focus ( current_sess - > gui - > input_box ) ;
SPELL_ENTRY_SET_EDITABLE ( current_sess - > gui - > input_box , TRUE ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_dialog_button ( GtkWidget * box , char * name , char * cmd )
{
2026-01-15 18:35:27 -07:00
GtkWidget * wid ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
wid = gtk_button_new_with_label ( name ) ;
gtk_box_pack_start ( GTK_BOX ( box ) , wid , FALSE , FALSE , 0 ) ;
g_signal_connect ( G_OBJECT ( wid ) , " clicked " ,
G_CALLBACK ( mg_dialog_button_cb ) , cmd ) ;
gtk_widget_set_size_request ( wid , - 1 , 0 ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_dialogbuttons ( GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
struct popup * pop ;
GSList * list = dlgbutton_list ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
while ( list )
{
pop = list - > data ;
if ( pop - > cmd [ 0 ] )
mg_dialog_button ( box , pop - > name , pop - > cmd ) ;
list = list - > next ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_create_topicbar ( session * sess , GtkWidget * box )
{
2026-03-18 09:10:40 -06:00
GtkWidget * vbox , * hbox , * mode_hbox , * topic , * bbox ;
session_gui * gui = sess - > gui ;
2011-02-24 04:14:30 +01:00
2026-03-18 09:10:40 -06:00
gui - > topic_bar = vbox = mg_box_new ( GTK_ORIENTATION_VERTICAL , FALSE , 0 ) ;
gtk_box_pack_start ( GTK_BOX ( box ) , vbox , 0 , 0 , 0 ) ;
hbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , hbox , 0 , 0 , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! gui - > is_tab )
sess - > res - > tab = NULL ;
2011-02-24 04:14:30 +01:00
2026-03-14 19:57:42 -06:00
gui - > topic_entry = topic = gtk_text_view_new ( ) ;
2026-01-15 18:35:27 -07:00
gtk_widget_set_name ( topic , " zoitechat-inputbox " ) ;
2026-03-14 19:57:42 -06:00
gtk_text_view_set_wrap_mode ( GTK_TEXT_VIEW ( topic ) , GTK_WRAP_WORD_CHAR ) ;
gtk_text_view_set_left_margin ( GTK_TEXT_VIEW ( topic ) , 4 ) ;
gtk_text_view_set_right_margin ( GTK_TEXT_VIEW ( topic ) , 4 ) ;
2026-02-01 14:04:56 -07:00
gtk_box_pack_start ( GTK_BOX ( hbox ) , topic , TRUE , TRUE , 0 ) ;
2026-01-15 18:35:27 -07:00
mg_apply_emoji_fallback_widget ( topic ) ;
2026-03-14 19:57:42 -06:00
gtk_widget_add_events ( topic , GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK ) ;
g_signal_connect ( G_OBJECT ( topic ) , " key-press-event " ,
G_CALLBACK ( mg_topic_key_press_cb ) , NULL ) ;
g_signal_connect ( G_OBJECT ( topic ) , " button-release-event " ,
G_CALLBACK ( mg_topic_button_release_cb ) , NULL ) ;
g_signal_connect ( G_OBJECT ( topic ) , " motion-notify-event " ,
G_CALLBACK ( mg_topic_motion_cb ) , NULL ) ;
g_signal_connect ( G_OBJECT ( topic ) , " leave-notify-event " ,
G_CALLBACK ( mg_topic_leave_cb ) , NULL ) ;
2011-02-24 04:14:30 +01:00
2026-03-18 09:10:40 -06:00
gui - > dialogbutton_box = bbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , bbox , 0 , 0 , 0 ) ;
mg_create_dialogbuttons ( bbox ) ;
mode_hbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , mode_hbox , 0 , 0 , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-03-18 09:10:40 -06:00
gui - > topicbutton_box = bbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
gtk_box_pack_end ( GTK_BOX ( mode_hbox ) , bbox , 0 , 0 , 0 ) ;
mg_create_chanmodebuttons ( gui , bbox ) ;
2011-02-24 04:14:30 +01:00
}
/* check if a word is clickable */
static int
2013-01-02 14:50:26 -08:00
mg_word_check ( GtkWidget * xtext , char * word )
2011-02-24 04:14:30 +01:00
{
2026-01-15 18:35:27 -07:00
session * sess = current_sess ;
int ret ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
ret = url_check_word ( word ) ;
if ( ret = = 0 & & sess - > type = = SESS_DIALOG )
return WORD_DIALOG ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return ret ;
2011-02-24 04:14:30 +01:00
}
/* mouse click inside text area */
static void
mg_word_clicked ( GtkWidget * xtext , char * word , GdkEventButton * even )
{
2026-01-15 18:35:27 -07:00
session * sess = current_sess ;
int word_type = 0 , start , end ;
char * tmp ;
if ( word )
{
word_type = mg_word_check ( xtext , word ) ;
url_last ( & start , & end ) ;
}
if ( even - > button = = 1 ) /* left button */
{
if ( word = = NULL )
{
mg_focus ( sess ) ;
return ;
}
if ( ( even - > state & 13 ) = = prefs . hex_gui_url_mod )
{
switch ( word_type )
{
case WORD_URL :
case WORD_HOST6 :
case WORD_HOST :
word [ end ] = 0 ;
fe_open_url ( word + start ) ;
}
}
return ;
}
if ( even - > button = = 2 )
{
if ( sess - > type = = SESS_DIALOG )
menu_middlemenu ( sess , even ) ;
else if ( even - > type = = GDK_2BUTTON_PRESS )
userlist_select ( sess , word ) ;
return ;
}
if ( word = = NULL )
return ;
switch ( word_type )
{
case 0 :
case WORD_PATH :
menu_middlemenu ( sess , even ) ;
break ;
case WORD_URL :
case WORD_HOST6 :
case WORD_HOST :
word [ end ] = 0 ;
word + = start ;
menu_urlmenu ( even , word ) ;
break ;
case WORD_NICK :
word [ end ] = 0 ;
word + = start ;
menu_nickmenu ( sess , even , word , FALSE ) ;
break ;
case WORD_CHANNEL :
word [ end ] = 0 ;
word + = start ;
menu_chanmenu ( sess , even , word ) ;
break ;
case WORD_EMAIL :
word [ end ] = 0 ;
word + = start ;
tmp = g_strdup_printf ( " mailto:%s " , word + ( ispunct ( * word ) ? 1 : 0 ) ) ;
menu_urlmenu ( even , tmp ) ;
g_free ( tmp ) ;
break ;
case WORD_DIALOG :
menu_nickmenu ( sess , even , sess - > channel , FALSE ) ;
break ;
}
2011-02-24 04:14:30 +01:00
}
void
mg_update_xtext ( GtkWidget * wid )
{
2026-01-15 18:35:27 -07:00
GtkXText * xtext = GTK_XTEXT ( wid ) ;
2026-01-15 19:58:12 -07:00
const gchar * font_name ;
2026-01-17 21:39:45 -07:00
XTextColor xtext_palette [ XTEXT_COLS ] ;
2011-02-24 04:14:30 +01:00
2026-03-04 23:28:01 -07:00
theme_get_xtext_colors_for_widget ( wid , xtext_palette , XTEXT_COLS ) ;
2026-01-17 21:39:45 -07:00
gtk_xtext_set_palette ( xtext , xtext_palette ) ;
2026-01-15 18:35:27 -07:00
gtk_xtext_set_max_lines ( xtext , prefs . hex_text_max_lines ) ;
gtk_xtext_set_background ( xtext , channelwin_pix ) ;
gtk_xtext_set_wordwrap ( xtext , prefs . hex_text_wordwrap ) ;
gtk_xtext_set_show_marker ( xtext , prefs . hex_text_show_marker ) ;
gtk_xtext_set_show_separator ( xtext , prefs . hex_text_indent ? prefs . hex_text_show_sep : 0 ) ;
gtk_xtext_set_indent ( xtext , prefs . hex_text_indent ) ;
2011-02-24 04:14:30 +01:00
2026-03-11 01:49:14 -06:00
font_name = * prefs . hex_text_font
2026-01-15 19:58:12 -07:00
? prefs . hex_text_font
: " Sans 10 " ;
if ( ! gtk_xtext_set_font ( xtext , ( char * ) font_name ) )
2026-01-15 18:35:27 -07:00
{
fe_message ( " Failed to open any font. I'm out of here! " , FE_MSG_WAIT | FE_MSG_ERROR ) ;
exit ( 1 ) ;
}
gtk_xtext_refresh ( xtext ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_textarea ( session * sess , GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
GtkWidget * inbox , * vbox , * frame ;
GtkXText * xtext ;
2026-01-17 21:39:45 -07:00
XTextColor xtext_palette [ XTEXT_COLS ] ;
2026-01-15 18:35:27 -07:00
session_gui * gui = sess - > gui ;
static const GtkTargetEntry dnd_targets [ ] =
{
{ " text/uri-list " , 0 , 1 }
} ;
static const GtkTargetEntry dnd_dest_targets [ ] =
{
2026-01-25 16:13:47 -07:00
{ " ZOITECHAT_CHANVIEW " , GTK_TARGET_SAME_APP , 75 } ,
{ " ZOITECHAT_USERLIST " , GTK_TARGET_SAME_APP , 75 }
2026-01-15 18:35:27 -07:00
} ;
2026-01-19 20:11:07 -07:00
vbox = mg_box_new ( GTK_ORIENTATION_VERTICAL , FALSE , 0 ) ;
2026-02-01 14:04:56 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , vbox , TRUE , TRUE , 0 ) ;
2026-01-19 20:11:07 -07:00
inbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 2 ) ;
2026-02-01 14:04:56 -07:00
gtk_box_pack_start ( GTK_BOX ( vbox ) , inbox , TRUE , TRUE , 0 ) ;
2026-01-15 18:35:27 -07:00
frame = gtk_frame_new ( NULL ) ;
gtk_frame_set_shadow_type ( GTK_FRAME ( frame ) , GTK_SHADOW_IN ) ;
2026-02-01 14:04:56 -07:00
gtk_box_pack_start ( GTK_BOX ( inbox ) , frame , TRUE , TRUE , 0 ) ;
2026-01-15 18:35:27 -07:00
2026-03-04 23:28:01 -07:00
theme_get_xtext_colors_for_widget ( frame , xtext_palette , XTEXT_COLS ) ;
2026-01-17 21:39:45 -07:00
gui - > xtext = gtk_xtext_new ( xtext_palette , TRUE ) ;
2026-01-15 18:35:27 -07:00
xtext = GTK_XTEXT ( gui - > xtext ) ;
gtk_xtext_set_max_indent ( xtext , prefs . hex_text_max_indent ) ;
gtk_xtext_set_thin_separator ( xtext , prefs . hex_text_thin_sep ) ;
gtk_xtext_set_urlcheck_function ( xtext , mg_word_check ) ;
gtk_xtext_set_max_lines ( xtext , prefs . hex_text_max_lines ) ;
gtk_container_add ( GTK_CONTAINER ( frame ) , GTK_WIDGET ( xtext ) ) ;
mg_update_xtext ( GTK_WIDGET ( xtext ) ) ;
g_signal_connect ( G_OBJECT ( xtext ) , " word_click " ,
G_CALLBACK ( mg_word_clicked ) , NULL ) ;
2026-01-30 17:44:30 -07:00
gui - > vscrollbar = gtk_scrollbar_new ( GTK_ORIENTATION_VERTICAL ,
GTK_XTEXT ( xtext ) - > adj ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( inbox ) , gui - > vscrollbar , FALSE , TRUE , 0 ) ;
gtk_drag_dest_set ( gui - > vscrollbar , 5 , dnd_dest_targets , 2 ,
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( gui - > vscrollbar ) , " drag-begin " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_drag_begin_cb ) , NULL ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( gui - > vscrollbar ) , " drag-drop " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_drag_drop_cb ) , NULL ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( gui - > vscrollbar ) , " drag-motion " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_drag_motion_cb ) , gui - > vscrollbar ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( gui - > vscrollbar ) , " drag-end " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_drag_end_cb ) , NULL ) ;
gtk_drag_dest_set ( gui - > xtext , GTK_DEST_DEFAULT_ALL , dnd_targets , 1 ,
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( gui - > xtext ) , " drag-data-received " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_dialog_dnd_drop ) , NULL ) ;
2011-02-24 04:14:30 +01:00
}
static GtkWidget *
mg_create_infoframe ( GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
GtkWidget * frame , * label , * hbox ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
frame = gtk_frame_new ( 0 ) ;
gtk_frame_set_shadow_type ( ( GtkFrame * ) frame , GTK_SHADOW_OUT ) ;
2026-02-01 14:04:56 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , frame , FALSE , TRUE , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-19 20:11:07 -07:00
hbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
2026-01-15 18:35:27 -07:00
gtk_container_add ( GTK_CONTAINER ( frame ) , hbox ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
label = gtk_label_new ( NULL ) ;
2026-02-01 14:04:56 -07:00
gtk_box_pack_start ( GTK_BOX ( hbox ) , label , TRUE , TRUE , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return label ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_meters ( session_gui * gui , GtkWidget * parent_box )
{
2026-01-15 18:35:27 -07:00
GtkWidget * infbox , * wid , * box ;
2011-02-24 04:14:30 +01:00
2026-01-19 20:11:07 -07:00
gui - > meter_box = infbox = box = mg_box_new ( GTK_ORIENTATION_VERTICAL , FALSE , 1 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( parent_box ) , box , 0 , 0 , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ( prefs . hex_gui_lagometer & 2 ) | | ( prefs . hex_gui_throttlemeter & 2 ) )
{
2026-01-19 20:11:07 -07:00
infbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , infbox , 0 , 0 , 0 ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_lagometer & 1 )
{
gui - > lagometer = wid = gtk_progress_bar_new ( ) ;
2011-02-24 04:14:30 +01:00
# ifdef WIN32
2026-01-15 18:35:27 -07:00
gtk_widget_set_size_request ( wid , 1 , 10 ) ;
2011-02-24 04:14:30 +01:00
# else
2026-01-15 18:35:27 -07:00
gtk_widget_set_size_request ( wid , 1 , 8 ) ;
2011-02-24 04:14:30 +01:00
# endif
2026-01-15 18:35:27 -07:00
wid = gtk_event_box_new ( ) ;
gtk_container_add ( GTK_CONTAINER ( wid ) , gui - > lagometer ) ;
gtk_box_pack_start ( GTK_BOX ( box ) , wid , 0 , 0 , 0 ) ;
}
if ( prefs . hex_gui_lagometer & 2 )
{
gui - > laginfo = wid = mg_create_infoframe ( infbox ) ;
gtk_label_set_text ( ( GtkLabel * ) wid , " Lag " ) ;
}
if ( prefs . hex_gui_throttlemeter & 1 )
{
gui - > throttlemeter = wid = gtk_progress_bar_new ( ) ;
2011-02-24 04:14:30 +01:00
# ifdef WIN32
2026-01-15 18:35:27 -07:00
gtk_widget_set_size_request ( wid , 1 , 10 ) ;
2011-02-24 04:14:30 +01:00
# else
2026-01-15 18:35:27 -07:00
gtk_widget_set_size_request ( wid , 1 , 8 ) ;
2011-02-24 04:14:30 +01:00
# endif
2026-01-15 18:35:27 -07:00
wid = gtk_event_box_new ( ) ;
gtk_container_add ( GTK_CONTAINER ( wid ) , gui - > throttlemeter ) ;
gtk_box_pack_start ( GTK_BOX ( box ) , wid , 0 , 0 , 0 ) ;
}
if ( prefs . hex_gui_throttlemeter & 2 )
{
gui - > throttleinfo = wid = mg_create_infoframe ( infbox ) ;
gtk_label_set_text ( ( GtkLabel * ) wid , " Throttle " ) ;
}
2011-02-24 04:14:30 +01:00
}
void
mg_update_meters ( session_gui * gui )
{
2026-01-15 18:35:27 -07:00
gtk_widget_destroy ( gui - > meter_box ) ;
gui - > lagometer = NULL ;
gui - > laginfo = NULL ;
gui - > throttlemeter = NULL ;
gui - > throttleinfo = NULL ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_create_meters ( gui , gui - > button_box_parent ) ;
gtk_widget_show_all ( gui - > meter_box ) ;
2011-02-24 04:14:30 +01:00
}
2026-03-02 19:42:48 -07:00
static void
mg_theme_apply_userlist_style ( session_gui * gui )
{
const PangoFontDescription * font = NULL ;
if ( ! gui | | ! gui - > user_tree )
return ;
if ( input_style )
font = input_style - > font_desc ;
theme_manager_apply_userlist_style ( gui - > user_tree ,
theme_manager_get_userlist_palette_behavior ( font ) ) ;
}
static void
mg_theme_userlist_changed ( const ThemeChangedEvent * event , gpointer userdata )
{
session_gui * gui = userdata ;
if ( ! theme_changed_event_has_reason ( event , THEME_CHANGED_REASON_USERLIST ) & &
! theme_changed_event_has_reason ( event , THEME_CHANGED_REASON_PALETTE ) & &
! theme_changed_event_has_reason ( event , THEME_CHANGED_REASON_WIDGET_STYLE ) & &
! theme_changed_event_has_reason ( event , THEME_CHANGED_REASON_MODE ) & &
! theme_changed_event_has_reason ( event , THEME_CHANGED_REASON_THEME_PACK ) )
return ;
mg_theme_apply_userlist_style ( gui ) ;
}
static void
mg_theme_window_changed ( const ThemeChangedEvent * event , gpointer userdata )
{
session_gui * gui = userdata ;
if ( ! theme_changed_event_has_reason ( event , THEME_CHANGED_REASON_MODE ) & &
! theme_changed_event_has_reason ( event , THEME_CHANGED_REASON_THEME_PACK ) & &
! theme_changed_event_has_reason ( event , THEME_CHANGED_REASON_WIDGET_STYLE ) )
return ;
if ( gui )
theme_manager_apply_to_window ( gui - > window ) ;
}
static void
mg_theme_userlist_destroy_cb ( GtkWidget * widget , gpointer userdata )
{
session_gui * gui = userdata ;
( void ) widget ;
if ( ! gui )
return ;
if ( gui - > theme_userlist_listener_id )
{
theme_listener_unregister ( gui - > theme_userlist_listener_id ) ;
gui - > theme_userlist_listener_id = 0 ;
}
}
static void
mg_theme_window_destroy_cb ( GtkWidget * widget , gpointer userdata )
{
session_gui * gui = userdata ;
( void ) widget ;
if ( ! gui )
return ;
theme_manager_detach_window ( gui - > window ) ;
if ( gui - > theme_window_listener_id )
{
theme_listener_unregister ( gui - > theme_window_listener_id ) ;
gui - > theme_window_listener_id = 0 ;
}
}
2011-02-24 04:14:30 +01:00
static void
mg_create_userlist ( session_gui * gui , GtkWidget * box )
{
2026-03-18 09:06:10 -06:00
GtkWidget * ulist , * vbox ;
2011-02-24 04:14:30 +01:00
2026-01-19 20:11:07 -07:00
vbox = mg_box_new ( GTK_ORIENTATION_VERTICAL , FALSE , 1 ) ;
2026-02-01 14:04:56 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , vbox , TRUE , TRUE , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gui - > namelistinfo = gtk_label_new ( NULL ) ;
2026-03-15 18:21:01 -06:00
gtk_label_set_xalign ( GTK_LABEL ( gui - > namelistinfo ) , 0.0f ) ;
gtk_widget_set_halign ( gui - > namelistinfo , GTK_ALIGN_START ) ;
2026-03-18 09:06:10 -06:00
if ( prefs . hex_gui_ulist_count )
gtk_box_pack_start ( GTK_BOX ( vbox ) , gui - > namelistinfo , 0 , 0 , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gui - > user_tree = ulist = userlist_create ( vbox ) ;
2011-02-24 04:14:30 +01:00
2026-03-02 19:42:48 -07:00
if ( ! gui - > theme_userlist_listener_id )
gui - > theme_userlist_listener_id = theme_listener_register ( " maingui.userlist " , mg_theme_userlist_changed , gui ) ;
g_signal_connect ( G_OBJECT ( ulist ) , " destroy " , G_CALLBACK ( mg_theme_userlist_destroy_cb ) , gui ) ;
mg_theme_apply_userlist_style ( gui ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_create_meters ( gui , vbox ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gui - > button_box_parent = vbox ;
gui - > button_box = mg_create_userlistbuttons ( vbox ) ;
2011-02-24 04:14:30 +01:00
}
2012-11-30 17:22:12 -05:00
static void
mg_vpane_cb ( GtkPaned * pane , GParamSpec * param , session_gui * gui )
{
2026-01-15 18:35:27 -07:00
prefs . hex_gui_pane_divider_position = gtk_paned_get_position ( pane ) ;
2012-11-30 17:22:12 -05:00
}
2011-02-24 04:14:30 +01:00
static void
mg_leftpane_cb ( GtkPaned * pane , GParamSpec * param , session_gui * gui )
{
2026-01-15 18:35:27 -07:00
prefs . hex_gui_pane_left_size = gtk_paned_get_position ( pane ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_rightpane_cb ( GtkPaned * pane , GParamSpec * param , session_gui * gui )
{
2026-01-15 18:35:27 -07:00
int handle_size ;
GtkAllocation allocation ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_style_get ( GTK_WIDGET ( pane ) , " handle-size " , & handle_size , NULL ) ;
/* record the position from the RIGHT side */
gtk_widget_get_allocation ( GTK_WIDGET ( pane ) , & allocation ) ;
prefs . hex_gui_pane_right_size = allocation . width - gtk_paned_get_position ( pane ) - handle_size ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_add_pane_signals ( session_gui * gui )
{
2026-01-15 18:35:27 -07:00
g_signal_connect ( G_OBJECT ( gui - > hpane_right ) , " notify::position " ,
G_CALLBACK ( mg_rightpane_cb ) , gui ) ;
g_signal_connect ( G_OBJECT ( gui - > hpane_left ) , " notify::position " ,
G_CALLBACK ( mg_leftpane_cb ) , gui ) ;
g_signal_connect ( G_OBJECT ( gui - > vpane_left ) , " notify::position " ,
G_CALLBACK ( mg_vpane_cb ) , gui ) ;
g_signal_connect ( G_OBJECT ( gui - > vpane_right ) , " notify::position " ,
G_CALLBACK ( mg_vpane_cb ) , gui ) ;
return FALSE ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_center ( session * sess , session_gui * gui , GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
GtkWidget * vbox , * hbox , * book ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* sep between top and bottom of left side */
2026-01-30 17:44:30 -07:00
gui - > vpane_left = gtk_paned_new ( GTK_ORIENTATION_VERTICAL ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* sep between top and bottom of right side */
2026-01-30 17:44:30 -07:00
gui - > vpane_right = gtk_paned_new ( GTK_ORIENTATION_VERTICAL ) ;
2011-02-24 04:14:30 +01:00
2026-01-30 19:02:51 -07:00
/* sep between left and xtext */
gui - > hpane_left = gtk_paned_new ( GTK_ORIENTATION_HORIZONTAL ) ;
2026-02-15 22:31:45 -07:00
gtk_paned_set_wide_handle ( GTK_PANED ( gui - > hpane_left ) , FALSE ) ;
2026-01-30 19:02:51 -07:00
gtk_paned_set_position ( GTK_PANED ( gui - > hpane_left ) , prefs . hex_gui_pane_left_size ) ;
2011-02-24 04:14:30 +01:00
2026-01-30 19:02:51 -07:00
/* sep between xtext and right side */
gui - > hpane_right = gtk_paned_new ( GTK_ORIENTATION_HORIZONTAL ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_win_swap )
{
2026-03-14 19:43:01 -06:00
gtk_paned_pack2 ( GTK_PANED ( gui - > hpane_left ) , gui - > vpane_left , FALSE , FALSE ) ;
2026-02-16 02:01:21 -07:00
gtk_paned_pack1 ( GTK_PANED ( gui - > hpane_left ) , gui - > hpane_right , TRUE , TRUE ) ;
2026-01-15 18:35:27 -07:00
}
else
{
2026-03-14 19:43:01 -06:00
gtk_paned_pack1 ( GTK_PANED ( gui - > hpane_left ) , gui - > vpane_left , FALSE , FALSE ) ;
2026-02-16 02:01:21 -07:00
gtk_paned_pack2 ( GTK_PANED ( gui - > hpane_left ) , gui - > hpane_right , TRUE , TRUE ) ;
2026-01-15 18:35:27 -07:00
}
2026-03-15 18:21:01 -06:00
gtk_paned_pack2 ( GTK_PANED ( gui - > hpane_right ) , gui - > vpane_right , FALSE , TRUE ) ;
2011-02-24 04:14:30 +01:00
2026-02-01 14:04:56 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , gui - > hpane_left , TRUE , TRUE , 0 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gui - > note_book = book = gtk_notebook_new ( ) ;
gtk_notebook_set_show_tabs ( GTK_NOTEBOOK ( book ) , FALSE ) ;
gtk_notebook_set_show_border ( GTK_NOTEBOOK ( book ) , FALSE ) ;
gtk_paned_pack1 ( GTK_PANED ( gui - > hpane_right ) , book , TRUE , TRUE ) ;
2011-02-24 04:14:30 +01:00
2026-01-19 20:11:07 -07:00
hbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
2026-01-15 18:35:27 -07:00
gtk_paned_pack1 ( GTK_PANED ( gui - > vpane_right ) , hbox , FALSE , TRUE ) ;
mg_create_userlist ( gui , hbox ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gui - > user_box = hbox ;
2011-02-24 04:14:30 +01:00
2026-01-19 20:11:07 -07:00
vbox = mg_box_new ( GTK_ORIENTATION_VERTICAL , FALSE , 3 ) ;
2026-01-15 18:35:27 -07:00
gtk_notebook_append_page ( GTK_NOTEBOOK ( book ) , vbox , NULL ) ;
mg_create_topicbar ( sess , vbox ) ;
2013-07-24 20:47:01 -04:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_search_pos )
{
mg_create_search ( sess , vbox ) ;
mg_create_textarea ( sess , vbox ) ;
}
else
{
mg_create_textarea ( sess , vbox ) ;
mg_create_search ( sess , vbox ) ;
}
2013-07-24 20:47:01 -04:00
2026-01-15 18:35:27 -07:00
mg_create_entry ( sess , vbox ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_add_pane_signals ( gui ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_change_nick ( int cancel , char * text , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
char buf [ 256 ] ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! cancel )
{
g_snprintf ( buf , sizeof ( buf ) , " nick %s " , text ) ;
handle_command ( current_sess , buf , FALSE ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_nickclick_cb ( GtkWidget * button , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
fe_get_str ( _ ( " Enter new nickname: " ) , current_sess - > server - > nick ,
mg_change_nick , ( void * ) 1 ) ;
2011-02-24 04:14:30 +01:00
}
/* make sure chanview and userlist positions are sane */
static void
mg_sanitize_positions ( int * cv , int * ul )
{
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_tab_layout = = 2 )
{
/* treeview can't be on TOP or BOTTOM */
if ( * cv = = POS_TOP | | * cv = = POS_BOTTOM )
* cv = POS_TOPLEFT ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* userlist can't be on TOP or BOTTOM */
if ( * ul = = POS_TOP | | * ul = = POS_BOTTOM )
* ul = POS_TOPRIGHT ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* can't have both in the same place */
if ( * cv = = * ul )
{
* cv = POS_TOPRIGHT ;
if ( * ul = = POS_TOPRIGHT )
* cv = POS_BOTTOMRIGHT ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_place_userlist_and_chanview_real ( session_gui * gui , GtkWidget * userlist , GtkWidget * chanview )
{
2026-01-15 18:35:27 -07:00
int unref_userlist = FALSE ;
int unref_chanview = FALSE ;
/* first, remove userlist/treeview from their containers */
if ( userlist & & gtk_widget_get_parent ( userlist ) )
{
g_object_ref ( userlist ) ;
gtk_container_remove ( GTK_CONTAINER ( gtk_widget_get_parent ( userlist ) ) , userlist ) ;
unref_userlist = TRUE ;
}
if ( chanview & & gtk_widget_get_parent ( chanview ) )
{
g_object_ref ( chanview ) ;
gtk_container_remove ( GTK_CONTAINER ( gtk_widget_get_parent ( chanview ) ) , chanview ) ;
unref_chanview = TRUE ;
}
if ( chanview )
{
/* incase the previous pos was POS_HIDDEN */
gtk_widget_show ( chanview ) ;
2026-01-19 19:26:29 -07:00
gtk_widget_set_margin_top ( chanview , 0 ) ;
gtk_widget_set_margin_bottom ( chanview , 0 ) ;
2026-01-15 18:35:27 -07:00
/* then place them back in their new positions */
switch ( prefs . hex_gui_tab_pos )
{
case POS_TOPLEFT :
gtk_paned_pack1 ( GTK_PANED ( gui - > vpane_left ) , chanview , FALSE , TRUE ) ;
break ;
case POS_BOTTOMLEFT :
gtk_paned_pack2 ( GTK_PANED ( gui - > vpane_left ) , chanview , FALSE , TRUE ) ;
break ;
case POS_TOPRIGHT :
gtk_paned_pack1 ( GTK_PANED ( gui - > vpane_right ) , chanview , FALSE , TRUE ) ;
break ;
case POS_BOTTOMRIGHT :
gtk_paned_pack2 ( GTK_PANED ( gui - > vpane_right ) , chanview , FALSE , TRUE ) ;
break ;
case POS_TOP :
2026-01-19 19:26:29 -07:00
gtk_widget_set_margin_bottom ( chanview , GUI_SPACING - 1 ) ;
2026-01-30 16:04:50 -07:00
gtk_widget_set_hexpand ( chanview , FALSE ) ;
gtk_widget_set_vexpand ( chanview , FALSE ) ;
gtk_widget_set_halign ( chanview , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( chanview , GTK_ALIGN_FILL ) ;
2026-01-19 19:26:29 -07:00
gtk_grid_attach ( GTK_GRID ( gui - > main_table ) , chanview ,
1 , 1 , 1 , 1 ) ;
2026-01-15 18:35:27 -07:00
break ;
case POS_HIDDEN :
gtk_widget_hide ( chanview ) ;
/* always attach it to something to avoid ref_count=0 */
if ( prefs . hex_gui_ulist_pos = = POS_TOP )
2026-01-30 16:14:35 -07:00
{
2026-01-30 16:04:50 -07:00
gtk_widget_set_hexpand ( chanview , FALSE ) ;
gtk_widget_set_vexpand ( chanview , FALSE ) ;
gtk_widget_set_halign ( chanview , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( chanview , GTK_ALIGN_FILL ) ;
2026-01-19 19:26:29 -07:00
gtk_grid_attach ( GTK_GRID ( gui - > main_table ) , chanview ,
1 , 3 , 1 , 1 ) ;
2026-01-30 16:14:35 -07:00
}
2026-01-15 18:35:27 -07:00
else
2026-01-30 16:14:35 -07:00
{
2026-01-30 16:04:50 -07:00
gtk_widget_set_hexpand ( chanview , FALSE ) ;
gtk_widget_set_vexpand ( chanview , FALSE ) ;
gtk_widget_set_halign ( chanview , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( chanview , GTK_ALIGN_FILL ) ;
2026-01-19 19:26:29 -07:00
gtk_grid_attach ( GTK_GRID ( gui - > main_table ) , chanview ,
1 , 1 , 1 , 1 ) ;
2026-01-30 16:14:35 -07:00
}
2026-01-15 18:35:27 -07:00
break ;
default : /* POS_BOTTOM */
2026-01-19 19:26:29 -07:00
gtk_widget_set_margin_top ( chanview , 3 ) ;
2026-01-30 16:04:50 -07:00
gtk_widget_set_hexpand ( chanview , FALSE ) ;
gtk_widget_set_vexpand ( chanview , FALSE ) ;
gtk_widget_set_halign ( chanview , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( chanview , GTK_ALIGN_FILL ) ;
2026-01-19 19:26:29 -07:00
gtk_grid_attach ( GTK_GRID ( gui - > main_table ) , chanview ,
1 , 3 , 1 , 1 ) ;
2026-01-15 18:35:27 -07:00
}
}
if ( userlist )
{
switch ( prefs . hex_gui_ulist_pos )
{
case POS_TOPLEFT :
gtk_paned_pack1 ( GTK_PANED ( gui - > vpane_left ) , userlist , FALSE , TRUE ) ;
break ;
case POS_BOTTOMLEFT :
gtk_paned_pack2 ( GTK_PANED ( gui - > vpane_left ) , userlist , FALSE , TRUE ) ;
break ;
case POS_BOTTOMRIGHT :
gtk_paned_pack2 ( GTK_PANED ( gui - > vpane_right ) , userlist , FALSE , TRUE ) ;
break ;
/*case POS_HIDDEN:
break ; */ /* Hide using the VIEW menu instead */
default : /* POS_TOPRIGHT */
gtk_paned_pack1 ( GTK_PANED ( gui - > vpane_right ) , userlist , FALSE , TRUE ) ;
}
}
if ( mg_is_userlist_and_tree_combined ( ) & & prefs . hex_gui_pane_divider_position ! = 0 )
{
gtk_paned_set_position ( GTK_PANED ( gui - > vpane_left ) , prefs . hex_gui_pane_divider_position ) ;
gtk_paned_set_position ( GTK_PANED ( gui - > vpane_right ) , prefs . hex_gui_pane_divider_position ) ;
}
if ( unref_chanview )
g_object_unref ( chanview ) ;
if ( unref_userlist )
g_object_unref ( userlist ) ;
mg_hide_empty_boxes ( gui ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_place_userlist_and_chanview ( session_gui * gui )
{
2026-01-15 18:35:27 -07:00
GtkOrientation orientation ;
GtkWidget * chanviewbox = NULL ;
int pos ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_sanitize_positions ( & prefs . hex_gui_tab_pos , & prefs . hex_gui_ulist_pos ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( gui - > chanview )
{
pos = prefs . hex_gui_tab_pos ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
orientation = chanview_get_orientation ( gui - > chanview ) ;
if ( ( pos = = POS_BOTTOM | | pos = = POS_TOP ) & & orientation = = GTK_ORIENTATION_VERTICAL )
chanview_set_orientation ( gui - > chanview , FALSE ) ;
else if ( ( pos = = POS_TOPLEFT | | pos = = POS_BOTTOMLEFT | | pos = = POS_TOPRIGHT | | pos = = POS_BOTTOMRIGHT ) & & orientation = = GTK_ORIENTATION_HORIZONTAL )
chanview_set_orientation ( gui - > chanview , TRUE ) ;
chanviewbox = chanview_get_box ( gui - > chanview ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_place_userlist_and_chanview_real ( gui , gui - > user_box , chanviewbox ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_change_layout ( int type )
{
2026-01-15 18:35:27 -07:00
if ( mg_gui )
{
/* put tabs at the bottom */
if ( type = = 0 & & prefs . hex_gui_tab_pos ! = POS_BOTTOM & & prefs . hex_gui_tab_pos ! = POS_TOP )
prefs . hex_gui_tab_pos = POS_BOTTOM ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_place_userlist_and_chanview ( mg_gui ) ;
chanview_set_impl ( mg_gui - > chanview , type ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_inputbox_rightclick ( GtkEntry * entry , GtkWidget * menu )
{
2026-01-15 18:35:27 -07:00
mg_create_color_menu ( menu , NULL ) ;
}
/* ------------------------------------------------------------------------- *
2026-02-22 15:11:35 -07:00
* Emoji font handling
2026-01-15 18:35:27 -07:00
*
* Goal : prefer color emoji fonts when available , without changing existing
* font size / style / weight , and without breaking user - configured fonts .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static const char * mg_emoji_family_fallback =
2026-01-16 23:18:12 -07:00
# ifdef G_OS_WIN32
" Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji, Apple Color Emoji, Twemoji Mozilla, EmojiOne Color " ;
# else
2026-01-15 18:35:27 -07:00
" Noto Color Emoji, Segoe UI Emoji, Apple Color Emoji, Twemoji Mozilla, EmojiOne Color " ;
2026-01-16 23:18:12 -07:00
# endif
2026-01-15 18:35:27 -07:00
2026-02-17 23:07:17 -07:00
static const char *
mg_find_available_icon_name ( const char * const * icon_names )
{
GtkIconTheme * theme ;
int i ;
theme = gtk_icon_theme_get_default ( ) ;
if ( ! theme | | ! icon_names )
return NULL ;
for ( i = 0 ; icon_names [ i ] ! = NULL ; i + + )
{
2026-03-13 12:39:55 -06:00
int action ;
2026-02-17 23:07:17 -07:00
if ( gtk_icon_theme_has_icon ( theme , icon_names [ i ] ) )
return icon_names [ i ] ;
2026-03-13 12:39:55 -06:00
if ( icon_resolver_menu_action_from_name ( icon_names [ i ] , & action ) )
{
char * resource_path = icon_resolver_resolve_path ( ICON_RESOLVER_ROLE_MENU_ACTION , action , GTK_ICON_SIZE_MENU , " menu " , ICON_RESOLVER_THEME_SYSTEM , NULL ) ;
if ( resource_path )
{
g_free ( resource_path ) ;
return icon_names [ i ] ;
}
}
2026-02-17 23:07:17 -07:00
}
return NULL ;
}
2026-01-15 18:35:27 -07:00
static gboolean
mg_family_already_has_emoji ( const gchar * family )
{
if ( ! family | | ! * family )
return FALSE ;
/* cheap but effective */
return ( strstr ( family , " Noto Color Emoji " ) ! = NULL ) | |
( strstr ( family , " Segoe UI Emoji " ) ! = NULL ) | |
( strstr ( family , " Apple Color Emoji " ) ! = NULL ) | |
( strstr ( family , " Twemoji " ) ! = NULL ) | |
( strstr ( family , " EmojiOne " ) ! = NULL ) ;
}
static PangoFontDescription *
mg_fontdesc_with_fallback ( const PangoFontDescription * base_desc , gboolean emoji_first )
{
PangoFontDescription * desc ;
const gchar * base_family ;
gchar * family_list ;
if ( ! base_desc )
return NULL ;
desc = pango_font_description_copy ( base_desc ) ;
base_family = pango_font_description_get_family ( desc ) ;
if ( mg_family_already_has_emoji ( base_family ) )
return desc ;
if ( emoji_first )
{
family_list = g_strdup_printf ( " %s, %s " ,
mg_emoji_family_fallback ,
( base_family & & * base_family ) ? base_family : " Sans " ) ;
}
else
{
family_list = g_strdup_printf ( " %s, %s " ,
( base_family & & * base_family ) ? base_family : " Sans " ,
mg_emoji_family_fallback ) ;
}
pango_font_description_set_family ( desc , family_list ) ;
g_free ( family_list ) ;
return desc ;
}
static void
mg_apply_emoji_fallback_widget ( GtkWidget * widget )
{
PangoFontDescription * desc ;
2026-01-30 17:28:09 -07:00
GtkStyleContext * context ;
2026-03-11 01:49:14 -06:00
PangoFontDescription * base_desc = NULL ;
2026-01-15 18:35:27 -07:00
if ( ! widget )
return ;
2026-01-30 17:28:09 -07:00
context = gtk_widget_get_style_context ( widget ) ;
if ( ! context )
return ;
2026-03-11 01:49:14 -06:00
gtk_style_context_get ( context , GTK_STATE_FLAG_NORMAL ,
" font " , & base_desc ,
NULL ) ;
2026-01-30 17:28:09 -07:00
if ( ! base_desc )
return ;
desc = mg_fontdesc_with_fallback ( base_desc , FALSE ) ;
2026-03-11 01:49:14 -06:00
pango_font_description_free ( base_desc ) ;
2026-01-15 18:35:27 -07:00
if ( ! desc )
return ;
2026-03-11 01:49:14 -06:00
mg_apply_font_css ( widget , desc , " zoitechat-emoji-font " ,
" zoitechat-emoji-font-provider " ) ;
2026-01-15 18:35:27 -07:00
pango_font_description_free ( desc ) ;
}
2013-07-24 20:47:01 -04:00
/* Search bar adapted from Conspire's by William Pitcock */
2026-01-15 18:35:27 -07:00
# define SEARCH_CHANGE 1
# define SEARCH_NEXT 2
# define SEARCH_PREVIOUS 3
# define SEARCH_REFRESH 4
2013-07-24 20:47:01 -04:00
static void
search_handle_event ( int search_type , session * sess )
{
2026-01-15 18:35:27 -07:00
textentry * last ;
const gchar * text = NULL ;
gtk_xtext_search_flags flags ;
GError * err = NULL ;
gboolean backwards = FALSE ;
/* When just typing show most recent first */
if ( search_type = = SEARCH_PREVIOUS | | search_type = = SEARCH_CHANGE )
backwards = TRUE ;
flags = ( ( prefs . hex_text_search_case_match = = 1 ? case_match : 0 ) |
( backwards ? backward : 0 ) |
( prefs . hex_text_search_highlight_all = = 1 ? highlight : 0 ) |
( prefs . hex_text_search_follow = = 1 ? follow : 0 ) |
( prefs . hex_text_search_regexp = = 1 ? regexp : 0 ) ) ;
if ( search_type ! = SEARCH_REFRESH )
text = gtk_entry_get_text ( GTK_ENTRY ( sess - > gui - > shentry ) ) ;
last = gtk_xtext_search ( GTK_XTEXT ( sess - > gui - > xtext ) , text , flags , & err ) ;
if ( err )
{
2026-01-30 15:27:01 -07:00
gtk_entry_set_icon_from_icon_name ( GTK_ENTRY ( sess - > gui - > shentry ) , GTK_ENTRY_ICON_SECONDARY , ICON_ENTRY_ERROR ) ;
2026-01-15 18:35:27 -07:00
gtk_entry_set_icon_tooltip_text ( GTK_ENTRY ( sess - > gui - > shentry ) , GTK_ENTRY_ICON_SECONDARY , _ ( err - > message ) ) ;
g_error_free ( err ) ;
}
else if ( ! last )
{
if ( text & & text [ 0 ] = = 0 ) /* empty string, no error */
{
2026-01-23 12:57:52 -07:00
gtk_entry_set_icon_from_icon_name ( GTK_ENTRY ( sess - > gui - > shentry ) , GTK_ENTRY_ICON_SECONDARY , NULL ) ;
2026-01-15 18:35:27 -07:00
}
else
{
/* Either end of search or not found, try again to wrap if only end */
last = gtk_xtext_search ( GTK_XTEXT ( sess - > gui - > xtext ) , text , flags , & err ) ;
if ( ! last ) /* Not found error */
{
2026-01-30 15:27:01 -07:00
gtk_entry_set_icon_from_icon_name ( GTK_ENTRY ( sess - > gui - > shentry ) , GTK_ENTRY_ICON_SECONDARY , ICON_ENTRY_ERROR ) ;
2026-01-15 18:35:27 -07:00
gtk_entry_set_icon_tooltip_text ( GTK_ENTRY ( sess - > gui - > shentry ) , GTK_ENTRY_ICON_SECONDARY , _ ( " No results found. " ) ) ;
}
}
}
else
{
2026-01-23 12:57:52 -07:00
gtk_entry_set_icon_from_icon_name ( GTK_ENTRY ( sess - > gui - > shentry ) , GTK_ENTRY_ICON_SECONDARY , NULL ) ;
2026-01-15 18:35:27 -07:00
}
2013-07-24 20:47:01 -04:00
}
static void
search_handle_change ( GtkWidget * wid , session * sess )
{
2026-01-15 18:35:27 -07:00
search_handle_event ( SEARCH_CHANGE , sess ) ;
2013-07-24 20:47:01 -04:00
}
static void
search_handle_refresh ( GtkWidget * wid , session * sess )
{
2026-01-15 18:35:27 -07:00
search_handle_event ( SEARCH_REFRESH , sess ) ;
2013-07-24 20:47:01 -04:00
}
void
mg_search_handle_previous ( GtkWidget * wid , session * sess )
{
2026-01-15 18:35:27 -07:00
search_handle_event ( SEARCH_PREVIOUS , sess ) ;
2013-07-24 20:47:01 -04:00
}
void
mg_search_handle_next ( GtkWidget * wid , session * sess )
{
2026-01-15 18:35:27 -07:00
search_handle_event ( SEARCH_NEXT , sess ) ;
2013-07-24 20:47:01 -04:00
}
static void
search_set_option ( GtkToggleButton * but , guint * pref )
{
2026-01-15 18:35:27 -07:00
* pref = gtk_toggle_button_get_active ( but ) ;
2026-03-16 21:55:36 -06:00
if ( ! save_config ( ) )
fe_message ( _ ( " Could not save zoitechat.conf. " ) , FE_MSG_WARN ) ;
2013-07-24 20:47:01 -04:00
}
void
mg_search_toggle ( session * sess )
{
2026-01-15 18:35:27 -07:00
if ( gtk_widget_get_visible ( sess - > gui - > shbox ) )
{
gtk_widget_hide ( sess - > gui - > shbox ) ;
gtk_widget_grab_focus ( sess - > gui - > input_box ) ;
gtk_entry_set_text ( GTK_ENTRY ( sess - > gui - > shentry ) , " " ) ;
}
else
{
/* Reset search state */
2026-01-23 12:57:52 -07:00
gtk_entry_set_icon_from_icon_name ( GTK_ENTRY ( sess - > gui - > shentry ) , GTK_ENTRY_ICON_SECONDARY , NULL ) ;
2013-07-24 20:47:01 -04:00
2026-01-15 18:35:27 -07:00
/* Show and focus */
gtk_widget_show ( sess - > gui - > shbox ) ;
gtk_widget_grab_focus ( sess - > gui - > shentry ) ;
}
2013-07-24 20:47:01 -04:00
}
static gboolean
search_handle_esc ( GtkWidget * win , GdkEventKey * key , session * sess )
{
2026-01-15 18:35:27 -07:00
if ( key - > keyval = = GDK_KEY_Escape )
mg_search_toggle ( sess ) ;
return FALSE ;
2013-07-24 20:47:01 -04:00
}
static void
mg_create_search ( session * sess , GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
GtkWidget * entry , * label , * next , * previous , * highlight , * matchcase , * regex , * close ;
session_gui * gui = sess - > gui ;
2026-01-19 20:11:07 -07:00
gui - > shbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 5 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , gui - > shbox , FALSE , FALSE , 0 ) ;
close = gtk_button_new ( ) ;
2026-02-18 00:32:42 -07:00
gtk_button_set_image ( GTK_BUTTON ( close ) , gtkutil_image_new_from_stock ( ICON_TAB_CLOSE , GTK_ICON_SIZE_MENU ) ) ;
2026-01-15 18:35:27 -07:00
gtk_button_set_relief ( GTK_BUTTON ( close ) , GTK_RELIEF_NONE ) ;
gtk_widget_set_can_focus ( close , FALSE ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > shbox ) , close , FALSE , FALSE , 0 ) ;
g_signal_connect_swapped ( G_OBJECT ( close ) , " clicked " , G_CALLBACK ( mg_search_toggle ) , sess ) ;
label = gtk_label_new ( _ ( " Find: " ) ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > shbox ) , label , FALSE , FALSE , 0 ) ;
gui - > shentry = entry = gtk_entry_new ( ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > shbox ) , entry , FALSE , FALSE , 0 ) ;
gtk_widget_set_size_request ( gui - > shentry , 180 , - 1 ) ;
mg_apply_emoji_fallback_widget ( entry ) ;
gui - > search_changed_signal = g_signal_connect ( G_OBJECT ( entry ) , " changed " , G_CALLBACK ( search_handle_change ) , sess ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( entry ) , " key-press-event " , G_CALLBACK ( search_handle_esc ) , sess ) ;
2026-01-15 18:35:27 -07:00
g_signal_connect ( G_OBJECT ( entry ) , " activate " , G_CALLBACK ( mg_search_handle_next ) , sess ) ;
gtk_entry_set_icon_activatable ( GTK_ENTRY ( entry ) , GTK_ENTRY_ICON_SECONDARY , FALSE ) ;
gtk_entry_set_icon_tooltip_text ( GTK_ENTRY ( sess - > gui - > shentry ) , GTK_ENTRY_ICON_SECONDARY , _ ( " Search hit end or not found. " ) ) ;
previous = gtk_button_new ( ) ;
2026-02-18 00:32:42 -07:00
gtk_button_set_image ( GTK_BUTTON ( previous ) , gtkutil_image_new_from_stock ( ICON_TAB_PREVIOUS , GTK_ICON_SIZE_MENU ) ) ;
2026-01-15 18:35:27 -07:00
gtk_button_set_relief ( GTK_BUTTON ( previous ) , GTK_RELIEF_NONE ) ;
gtk_widget_set_can_focus ( previous , FALSE ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > shbox ) , previous , FALSE , FALSE , 0 ) ;
g_signal_connect ( G_OBJECT ( previous ) , " clicked " , G_CALLBACK ( mg_search_handle_previous ) , sess ) ;
next = gtk_button_new ( ) ;
2026-02-18 00:32:42 -07:00
gtk_button_set_image ( GTK_BUTTON ( next ) , gtkutil_image_new_from_stock ( ICON_TAB_NEXT , GTK_ICON_SIZE_MENU ) ) ;
2026-01-15 18:35:27 -07:00
gtk_button_set_relief ( GTK_BUTTON ( next ) , GTK_RELIEF_NONE ) ;
gtk_widget_set_can_focus ( next , FALSE ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > shbox ) , next , FALSE , FALSE , 0 ) ;
g_signal_connect ( G_OBJECT ( next ) , " clicked " , G_CALLBACK ( mg_search_handle_next ) , sess ) ;
highlight = gtk_check_button_new_with_mnemonic ( _ ( " _Highlight all " ) ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( highlight ) , prefs . hex_text_search_highlight_all ) ;
gtk_widget_set_can_focus ( highlight , FALSE ) ;
g_signal_connect ( G_OBJECT ( highlight ) , " toggled " , G_CALLBACK ( search_set_option ) , & prefs . hex_text_search_highlight_all ) ;
g_signal_connect ( G_OBJECT ( highlight ) , " toggled " , G_CALLBACK ( search_handle_refresh ) , sess ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > shbox ) , highlight , FALSE , FALSE , 0 ) ;
gtk_widget_set_tooltip_text ( highlight , _ ( " Highlight all occurrences, and underline the current occurrence. " ) ) ;
matchcase = gtk_check_button_new_with_mnemonic ( _ ( " Mat_ch case " ) ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( matchcase ) , prefs . hex_text_search_case_match ) ;
gtk_widget_set_can_focus ( matchcase , FALSE ) ;
g_signal_connect ( G_OBJECT ( matchcase ) , " toggled " , G_CALLBACK ( search_set_option ) , & prefs . hex_text_search_case_match ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > shbox ) , matchcase , FALSE , FALSE , 0 ) ;
gtk_widget_set_tooltip_text ( matchcase , _ ( " Perform a case-sensitive search. " ) ) ;
regex = gtk_check_button_new_with_mnemonic ( _ ( " _Regex " ) ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( regex ) , prefs . hex_text_search_regexp ) ;
gtk_widget_set_can_focus ( regex , FALSE ) ;
g_signal_connect ( G_OBJECT ( regex ) , " toggled " , G_CALLBACK ( search_set_option ) , & prefs . hex_text_search_regexp ) ;
gtk_box_pack_start ( GTK_BOX ( gui - > shbox ) , regex , FALSE , FALSE , 0 ) ;
gtk_widget_set_tooltip_text ( regex , _ ( " Regard search string as a regular expression. " ) ) ;
2013-07-24 20:47:01 -04:00
}
2011-02-24 04:14:30 +01:00
static void
mg_create_entry ( session * sess , GtkWidget * box )
{
2026-02-17 15:23:03 -07:00
GtkWidget * hbox , * but , * entry ;
2026-01-15 18:35:27 -07:00
session_gui * gui = sess - > gui ;
2026-02-17 23:07:17 -07:00
const char * emoji_fallback_icon_names [ ] = {
" face-smile-symbolic " ,
" face-smile " ,
" insert-emoticon-symbolic " ,
" insert-emoticon " ,
2026-02-18 01:30:45 -07:00
" zc-menu-emoji " ,
2026-02-17 23:07:17 -07:00
NULL
} ;
const char * emoji_fallback_icon_name ;
2026-01-15 18:35:27 -07:00
2026-01-19 20:11:07 -07:00
hbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( box ) , hbox , 0 , 0 , 0 ) ;
2026-01-19 20:11:07 -07:00
gui - > nick_box = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( hbox ) , gui - > nick_box , 0 , 0 , 0 ) ;
gui - > nick_label = but = gtk_button_new_with_label ( sess - > server - > nick ) ;
gtk_button_set_relief ( GTK_BUTTON ( but ) , GTK_RELIEF_NONE ) ;
gtk_widget_set_can_focus ( but , FALSE ) ;
gtk_box_pack_end ( GTK_BOX ( gui - > nick_box ) , but , 0 , 0 , 0 ) ;
g_signal_connect ( G_OBJECT ( but ) , " clicked " ,
G_CALLBACK ( mg_nickclick_cb ) , NULL ) ;
gui - > input_box = entry = sexy_spell_entry_new ( ) ;
sexy_spell_entry_set_checked ( ( SexySpellEntry * ) entry , prefs . hex_gui_input_spell ) ;
sexy_spell_entry_set_parse_attributes ( ( SexySpellEntry * ) entry , prefs . hex_gui_input_attr ) ;
gtk_entry_set_max_length ( GTK_ENTRY ( gui - > input_box ) , 0 ) ;
g_signal_connect ( G_OBJECT ( entry ) , " activate " ,
G_CALLBACK ( mg_inputbox_cb ) , gui ) ;
g_signal_connect ( G_OBJECT ( entry ) , " changed " ,
G_CALLBACK ( key_check_replace_on_change ) , NULL ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , entry , TRUE , TRUE , 0 ) ;
gtk_widget_set_name ( entry , " zoitechat-inputbox " ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( entry ) , " key-press-event " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( key_handle_key_press ) , NULL ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( entry ) , " focus-in-event " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_inputbox_focus ) , gui ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( entry ) , " populate-popup " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_inputbox_rightclick ) , NULL ) ;
g_signal_connect ( G_OBJECT ( entry ) , " word-check " ,
G_CALLBACK ( mg_spellcheck_cb ) , NULL ) ;
gtk_widget_grab_focus ( entry ) ;
if ( prefs . hex_gui_input_style )
mg_apply_entry_style ( entry ) ;
2026-02-17 15:23:03 -07:00
g_object_set ( G_OBJECT ( entry ) , " show-emoji-icon " , TRUE , NULL ) ;
2026-02-17 23:07:17 -07:00
if ( gtk_entry_get_icon_storage_type ( GTK_ENTRY ( entry ) , GTK_ENTRY_ICON_SECONDARY ) = = GTK_IMAGE_EMPTY )
{
emoji_fallback_icon_name = mg_find_available_icon_name ( emoji_fallback_icon_names ) ;
if ( emoji_fallback_icon_name )
gtk_entry_set_icon_from_icon_name ( GTK_ENTRY ( entry ) , GTK_ENTRY_ICON_SECONDARY , emoji_fallback_icon_name ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
mg_switch_tab_cb ( chanview * cv , chan * ch , int tag , gpointer ud )
{
2026-01-15 18:35:27 -07:00
chan * old ;
session * sess = ud ;
old = active_tab ;
active_tab = ch ;
if ( tag = = TAG_IRC )
{
if ( active_tab ! = old )
{
if ( old & & current_tab )
mg_unpopulate ( current_tab ) ;
mg_populate ( sess ) ;
}
} else if ( old ! = active_tab )
{
/* userdata for non-irc tabs is actually the GtkBox */
mg_show_generic_tab ( ud ) ;
if ( ! mg_is_userlist_and_tree_combined ( ) )
mg_userlist_showhide ( current_sess , FALSE ) ; /* hide */
}
2011-02-24 04:14:30 +01:00
}
/* compare two tabs (for tab sorting function) */
static int
mg_tabs_compare ( session * a , session * b )
{
2026-01-15 18:35:27 -07:00
/* server tabs always go first */
if ( a - > type = = SESS_SERVER )
return - 1 ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* then channels */
if ( a - > type = = SESS_CHANNEL & & b - > type ! = SESS_CHANNEL )
return - 1 ;
if ( a - > type ! = SESS_CHANNEL & & b - > type = = SESS_CHANNEL )
return 1 ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return g_ascii_strcasecmp ( a - > channel , b - > channel ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_tabs ( session_gui * gui )
{
2026-01-15 18:35:27 -07:00
gboolean use_icons = FALSE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* if any one of these PNGs exist, the chanview will create
* the extra column for icons . */
if ( prefs . hex_gui_tab_icons & & ( pix_tree_channel | | pix_tree_dialog | | pix_tree_server | | pix_tree_util ) )
{
use_icons = TRUE ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gui - > chanview = chanview_new ( prefs . hex_gui_tab_layout , prefs . hex_gui_tab_trunc ,
prefs . hex_gui_tab_sort , use_icons ,
2026-02-16 13:58:10 -07:00
input_style ? input_style - > font_desc : NULL
2026-01-30 18:39:49 -07:00
) ;
2026-01-15 18:35:27 -07:00
chanview_set_callbacks ( gui - > chanview , mg_switch_tab_cb , mg_xbutton_cb ,
mg_tab_contextmenu_cb , ( void * ) mg_tabs_compare ) ;
mg_place_userlist_and_chanview ( gui ) ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_tabwin_focus_cb ( GtkWindow * win , GdkEventFocus * event , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
current_sess = current_tab ;
if ( current_sess )
{
gtk_xtext_check_marker_visibility ( GTK_XTEXT ( current_sess - > gui - > xtext ) ) ;
plugin_emit_dummy_print ( current_sess , " Focus Window " ) ;
}
unflash_window ( GTK_WIDGET ( win ) ) ;
return FALSE ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_topwin_focus_cb ( GtkWindow * win , GdkEventFocus * event , session * sess )
{
2026-01-15 18:35:27 -07:00
current_sess = sess ;
if ( ! sess - > server - > server_session )
sess - > server - > server_session = sess ;
gtk_xtext_check_marker_visibility ( GTK_XTEXT ( current_sess - > gui - > xtext ) ) ;
unflash_window ( GTK_WIDGET ( win ) ) ;
plugin_emit_dummy_print ( sess , " Focus Window " ) ;
return FALSE ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_menu ( session_gui * gui , GtkWidget * table , int away_state )
{
2026-01-15 18:35:27 -07:00
GtkAccelGroup * accel_group ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
accel_group = gtk_accel_group_new ( ) ;
gtk_window_add_accel_group ( GTK_WINDOW ( gtk_widget_get_toplevel ( table ) ) ,
accel_group ) ;
g_object_unref ( accel_group ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gui - > menu = menu_create_main ( accel_group , TRUE , away_state , ! gui - > is_tab ,
gui - > menu_item ) ;
2026-01-19 19:26:29 -07:00
gtk_widget_set_hexpand ( gui - > menu , TRUE ) ;
2026-01-30 16:04:50 -07:00
gtk_widget_set_vexpand ( gui - > menu , FALSE ) ;
2026-01-19 19:26:29 -07:00
gtk_widget_set_halign ( gui - > menu , GTK_ALIGN_FILL ) ;
2026-01-30 16:04:50 -07:00
gtk_widget_set_valign ( gui - > menu , GTK_ALIGN_FILL ) ;
gtk_grid_attach ( GTK_GRID ( table ) , gui - > menu , 0 , 0 , 3 , 1 ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_irctab ( session * sess , GtkWidget * table )
{
2026-01-15 18:35:27 -07:00
GtkWidget * vbox ;
session_gui * gui = sess - > gui ;
2011-02-24 04:14:30 +01:00
2026-01-19 20:11:07 -07:00
vbox = mg_box_new ( GTK_ORIENTATION_VERTICAL , FALSE , 0 ) ;
2026-01-19 19:26:29 -07:00
gtk_widget_set_hexpand ( vbox , TRUE ) ;
gtk_widget_set_vexpand ( vbox , TRUE ) ;
2026-01-30 16:04:50 -07:00
gtk_widget_set_halign ( vbox , GTK_ALIGN_FILL ) ;
gtk_widget_set_valign ( vbox , GTK_ALIGN_FILL ) ;
gtk_grid_attach ( GTK_GRID ( table ) , vbox , 1 , 2 , 1 , 1 ) ;
2026-01-15 18:35:27 -07:00
mg_create_center ( sess , gui , vbox ) ;
2011-02-24 04:14:30 +01:00
}
static void
mg_create_topwindow ( session * sess )
{
GtkWidget * win ;
GtkWidget * table ;
2026-01-16 22:07:01 -07:00
# ifdef G_OS_WIN32
GdkWindow * parent_win ;
# endif
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > type = = SESS_DIALOG )
win = gtkutil_window_new ( " ZoiteChat " , NULL ,
prefs . hex_gui_dialog_width , prefs . hex_gui_dialog_height , 0 ) ;
else
win = gtkutil_window_new ( " ZoiteChat " , NULL ,
prefs . hex_gui_win_width ,
prefs . hex_gui_win_height , 0 ) ;
sess - > gui - > window = win ;
gtk_container_set_border_width ( GTK_CONTAINER ( win ) , GUI_BORDER ) ;
2026-03-11 01:49:14 -06:00
gtk_widget_set_opacity ( win , ( prefs . hex_gui_transparency / 255. ) ) ;
2011-02-24 04:14:30 +01:00
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( win ) , " focus-in-event " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_topwin_focus_cb ) , sess ) ;
g_signal_connect ( G_OBJECT ( win ) , " destroy " ,
G_CALLBACK ( mg_topdestroy_cb ) , sess ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( win ) , " configure-event " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_configure_cb ) , sess ) ;
2011-02-24 04:14:30 +01:00
2026-01-19 19:26:29 -07:00
table = gtk_grid_new ( ) ;
/* spacing under the menubar */
gtk_grid_set_row_spacing ( GTK_GRID ( table ) , GUI_SPACING ) ;
/* left and right borders */
gtk_grid_set_column_spacing ( GTK_GRID ( table ) , 1 ) ;
2026-01-15 18:35:27 -07:00
gtk_container_add ( GTK_CONTAINER ( win ) , table ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_create_irctab ( sess , table ) ;
mg_create_menu ( sess - > gui , table , sess - > server - > is_away ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > res - > buffer = = NULL )
{
sess - > res - > buffer = gtk_xtext_buffer_new ( GTK_XTEXT ( sess - > gui - > xtext ) ) ;
gtk_xtext_buffer_show ( GTK_XTEXT ( sess - > gui - > xtext ) , sess - > res - > buffer , TRUE ) ;
gtk_xtext_set_time_stamp ( sess - > res - > buffer , prefs . hex_stamp_text ) ;
sess - > res - > user_model = userlist_create_model ( sess ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
userlist_show ( sess ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_show_all ( table ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_hide_menu )
gtk_widget_hide ( sess - > gui - > menu ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* Will be shown when needed */
gtk_widget_hide ( sess - > gui - > topic_bar ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! prefs . hex_gui_ulist_buttons )
gtk_widget_hide ( sess - > gui - > button_box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! prefs . hex_gui_input_nick )
gtk_widget_hide ( sess - > gui - > nick_box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_hide ( sess - > gui - > shbox ) ;
2013-07-24 20:47:01 -04:00
2026-01-15 18:35:27 -07:00
mg_decide_userlist ( sess , FALSE ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > type = = SESS_DIALOG )
{
/* hide the chan-mode buttons */
gtk_widget_hide ( sess - > gui - > topicbutton_box ) ;
} else
{
gtk_widget_hide ( sess - > gui - > dialogbutton_box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! prefs . hex_gui_mode_buttons )
gtk_widget_hide ( sess - > gui - > topicbutton_box ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_place_userlist_and_chanview ( sess - > gui ) ;
2011-02-24 04:14:30 +01:00
gtk_widget_show ( win ) ;
2026-03-02 19:42:48 -07:00
if ( ! sess - > gui - > theme_window_listener_id )
sess - > gui - > theme_window_listener_id = theme_listener_register ( " maingui.window " , mg_theme_window_changed , sess - > gui ) ;
g_signal_connect ( G_OBJECT ( win ) , " destroy " , G_CALLBACK ( mg_theme_window_destroy_cb ) , sess - > gui ) ;
theme_manager_attach_window ( win ) ;
2026-01-16 22:07:01 -07:00
# ifdef G_OS_WIN32
parent_win = gtk_widget_get_window ( win ) ;
gdk_window_add_filter ( parent_win , mg_win32_filter , NULL ) ;
# endif
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_tabwindow_de_cb ( GtkWidget * widget , GdkEvent * event , gpointer user_data )
{
2026-01-15 18:35:27 -07:00
GSList * list ;
session * sess ;
GtkWindow * win = GTK_WINDOW ( gtk_widget_get_toplevel ( widget ) ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_tray_close & & gtkutil_tray_icon_supported ( win ) & & tray_toggle_visibility ( FALSE ) )
return TRUE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* check for remaining toplevel windows */
list = sess_list ;
while ( list )
{
sess = list - > data ;
if ( ! sess - > gui - > is_tab )
return FALSE ;
list = list - > next ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_open_quit_dialog ( TRUE ) ;
return TRUE ;
2011-02-24 04:14:30 +01:00
}
2017-11-14 21:47:17 +01:00
# ifdef G_OS_WIN32
static GdkFilterReturn
2026-01-16 22:07:01 -07:00
mg_win32_filter ( GdkXEvent * xevent , GdkEvent * event , gpointer data )
2017-11-14 21:47:17 +01:00
{
2026-01-15 18:35:27 -07:00
MSG * msg = ( MSG * ) xevent ;
2017-11-14 21:47:17 +01:00
2026-01-16 22:07:01 -07:00
if ( ! msg )
return GDK_FILTER_CONTINUE ;
2017-11-14 21:47:17 +01:00
if ( msg - > message = = WM_TIMECHANGE )
{
_tzset ( ) ;
2026-01-16 22:07:01 -07:00
return GDK_FILTER_CONTINUE ;
}
2026-01-18 10:46:41 -07:00
if ( msg - > message = = WM_SETTINGCHANGE | | msg - > message = = WM_THEMECHANGED )
{
2026-03-02 19:42:48 -07:00
theme_manager_refresh_auto_mode ( ) ;
2026-01-18 10:46:41 -07:00
return GDK_FILTER_CONTINUE ;
}
2026-01-16 22:07:01 -07:00
if ( msg - > message = = WM_COPYDATA )
{
COPYDATASTRUCT * copy_data = ( COPYDATASTRUCT * ) msg - > lParam ;
if ( copy_data & & copy_data - > lpData & & copy_data - > cbData > 0 & & current_sess )
{
char * command = g_strndup ( ( const char * ) copy_data - > lpData , copy_data - > cbData ) ;
if ( command )
{
2026-02-18 13:28:17 -07:00
if ( strcmp ( command , " __WIN32_TASKBAR_TOGGLE__ " ) = = 0 )
{
if ( gtk_widget_get_visible ( current_sess - > gui - > window ) )
fe_ctrl_gui ( current_sess , FE_GUI_ICONIFY , 0 ) ;
else
fe_ctrl_gui ( current_sess , FE_GUI_SHOW , 0 ) ;
}
else
{
handle_command ( current_sess , command , FALSE ) ;
}
2026-01-16 22:07:01 -07:00
g_free ( command ) ;
return GDK_FILTER_REMOVE ;
}
}
2017-11-14 21:47:17 +01:00
}
2026-02-16 03:05:43 -07:00
if ( msg - > message = = WM_MOUSEWHEEL | | msg - > message = = WM_MOUSEHWHEEL )
{
POINT cursor_pos ;
HWND hover_hwnd ;
DWORD hover_pid = 0 ;
if ( ! GetCursorPos ( & cursor_pos ) )
return GDK_FILTER_CONTINUE ;
hover_hwnd = WindowFromPoint ( cursor_pos ) ;
if ( ! hover_hwnd | | hover_hwnd = = msg - > hwnd )
return GDK_FILTER_CONTINUE ;
GetWindowThreadProcessId ( hover_hwnd , & hover_pid ) ;
if ( hover_pid ! = GetCurrentProcessId ( ) )
return GDK_FILTER_CONTINUE ;
PostMessage ( hover_hwnd , msg - > message , msg - > wParam , msg - > lParam ) ;
return GDK_FILTER_REMOVE ;
}
2026-01-15 18:35:27 -07:00
return GDK_FILTER_CONTINUE ;
2017-11-14 21:47:17 +01:00
}
# endif
2011-02-24 04:14:30 +01:00
static void
mg_create_tabwindow ( session * sess )
{
2026-01-15 18:35:27 -07:00
GtkWidget * win ;
GtkWidget * table ;
2017-11-14 21:47:17 +01:00
# ifdef G_OS_WIN32
2026-01-15 18:35:27 -07:00
GdkWindow * parent_win ;
2017-11-14 21:47:17 +01:00
# endif
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
win = gtkutil_window_new ( " ZoiteChat " , NULL , prefs . hex_gui_win_width ,
prefs . hex_gui_win_height , 0 ) ;
sess - > gui - > window = win ;
gtk_window_move ( GTK_WINDOW ( win ) , prefs . hex_gui_win_left ,
prefs . hex_gui_win_top ) ;
if ( prefs . hex_gui_win_state )
gtk_window_maximize ( GTK_WINDOW ( win ) ) ;
if ( prefs . hex_gui_win_fullscreen )
gtk_window_fullscreen ( GTK_WINDOW ( win ) ) ;
2026-03-11 01:49:14 -06:00
gtk_widget_set_opacity ( win , ( prefs . hex_gui_transparency / 255. ) ) ;
2026-01-15 18:35:27 -07:00
gtk_container_set_border_width ( GTK_CONTAINER ( win ) , GUI_BORDER ) ;
2011-02-24 04:14:30 +01:00
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( win ) , " delete-event " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_tabwindow_de_cb ) , 0 ) ;
g_signal_connect ( G_OBJECT ( win ) , " destroy " ,
G_CALLBACK ( mg_tabwindow_kill_cb ) , 0 ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( win ) , " focus-in-event " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_tabwin_focus_cb ) , NULL ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( win ) , " configure-event " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_configure_cb ) , NULL ) ;
2026-03-17 10:30:54 -06:00
g_signal_connect ( G_OBJECT ( win ) , " window-state-event " ,
2026-01-15 18:35:27 -07:00
G_CALLBACK ( mg_windowstate_cb ) , NULL ) ;
2011-02-24 04:14:30 +01:00
2026-01-19 19:26:29 -07:00
sess - > gui - > main_table = table = gtk_grid_new ( ) ;
/* spacing under the menubar */
gtk_grid_set_row_spacing ( GTK_GRID ( table ) , GUI_SPACING ) ;
/* left and right borders */
gtk_grid_set_column_spacing ( GTK_GRID ( table ) , 1 ) ;
2026-01-15 18:35:27 -07:00
gtk_container_add ( GTK_CONTAINER ( win ) , table ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_create_irctab ( sess , table ) ;
mg_create_tabs ( sess - > gui ) ;
mg_create_menu ( sess - > gui , table , sess - > server - > is_away ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_focus ( sess ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_show_all ( table ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_hide_menu )
gtk_widget_hide ( sess - > gui - > menu ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_decide_userlist ( sess , FALSE ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* Will be shown when needed */
gtk_widget_hide ( sess - > gui - > topic_bar ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! prefs . hex_gui_mode_buttons )
gtk_widget_hide ( sess - > gui - > topicbutton_box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! prefs . hex_gui_ulist_buttons )
gtk_widget_hide ( sess - > gui - > button_box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! prefs . hex_gui_input_nick )
gtk_widget_hide ( sess - > gui - > nick_box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_hide ( sess - > gui - > shbox ) ;
2013-07-24 20:47:01 -04:00
2026-01-15 18:35:27 -07:00
mg_place_userlist_and_chanview ( sess - > gui ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_show ( win ) ;
2026-03-02 19:42:48 -07:00
if ( ! sess - > gui - > theme_window_listener_id )
sess - > gui - > theme_window_listener_id = theme_listener_register ( " maingui.window " , mg_theme_window_changed , sess - > gui ) ;
g_signal_connect ( G_OBJECT ( win ) , " destroy " , G_CALLBACK ( mg_theme_window_destroy_cb ) , sess - > gui ) ;
theme_manager_attach_window ( win ) ;
2017-11-14 21:47:17 +01:00
# ifdef G_OS_WIN32
parent_win = gtk_widget_get_window ( win ) ;
2026-01-16 22:07:01 -07:00
gdk_window_add_filter ( parent_win , mg_win32_filter , NULL ) ;
2017-11-14 21:47:17 +01:00
# endif
2011-02-24 04:14:30 +01:00
}
void
mg_apply_setup ( void )
{
2026-01-15 18:35:27 -07:00
GSList * list = sess_list ;
session * sess ;
int done_main = FALSE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
mg_create_tab_colors ( ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
while ( list )
{
sess = list - > data ;
gtk_xtext_set_time_stamp ( sess - > res - > buffer , prefs . hex_stamp_text ) ;
( ( xtext_buffer * ) sess - > res - > buffer ) - > needs_recalc = TRUE ;
if ( ! sess - > gui - > is_tab | | ! done_main )
mg_place_userlist_and_chanview ( sess - > gui ) ;
if ( sess - > gui - > is_tab )
done_main = TRUE ;
list = list - > next ;
}
2011-02-24 04:14:30 +01:00
}
static chan *
mg_add_generic_tab ( char * name , char * title , void * family , GtkWidget * box )
{
2026-01-15 18:35:27 -07:00
chan * ch ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_notebook_append_page ( GTK_NOTEBOOK ( mg_gui - > note_book ) , box , NULL ) ;
gtk_widget_show ( box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
ch = chanview_add ( mg_gui - > chanview , name , NULL , box , TRUE , TAG_UTIL , pix_tree_util ) ;
chan_set_color ( ch , plain_list ) ;
2016-07-24 21:10:34 -04:00
2026-01-15 18:35:27 -07:00
g_object_set_data_full ( G_OBJECT ( box ) , " title " , g_strdup ( title ) , g_free ) ;
g_object_set_data ( G_OBJECT ( box ) , " ch " , ch ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_tab_newtofront )
chan_focus ( ch ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return ch ;
2011-02-24 04:14:30 +01:00
}
void
fe_buttons_update ( session * sess )
{
2026-01-15 18:35:27 -07:00
session_gui * gui = sess - > gui ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_destroy ( gui - > button_box ) ;
gui - > button_box = mg_create_userlistbuttons ( gui - > button_box_parent ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( prefs . hex_gui_ulist_buttons )
gtk_widget_show ( sess - > gui - > button_box ) ;
else
gtk_widget_hide ( sess - > gui - > button_box ) ;
2011-02-24 04:14:30 +01:00
}
void
fe_clear_channel ( session * sess )
{
2026-01-15 18:35:27 -07:00
char tbuf [ CHANLEN + 6 ] ;
session_gui * gui = sess - > gui ;
if ( sess - > gui - > is_tab )
{
if ( sess - > waitchannel [ 0 ] )
{
if ( prefs . hex_gui_tab_trunc > 2 & & g_utf8_strlen ( sess - > waitchannel , - 1 ) > prefs . hex_gui_tab_trunc )
{
/* truncate long channel names */
tbuf [ 0 ] = ' ( ' ;
strcpy ( tbuf + 1 , sess - > waitchannel ) ;
g_utf8_offset_to_pointer ( tbuf , prefs . hex_gui_tab_trunc ) [ 0 ] = 0 ;
strcat ( tbuf , " ..) " ) ;
} else
{
sprintf ( tbuf , " (%s) " , sess - > waitchannel ) ;
}
}
else
strcpy ( tbuf , _ ( " <none> " ) ) ;
chan_rename ( sess - > res - > tab , tbuf , prefs . hex_gui_tab_trunc ) ;
}
if ( ! sess - > gui - > is_tab | | sess = = current_tab )
{
2026-03-14 19:57:42 -06:00
gtk_text_buffer_set_text (
gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( gui - > topic_entry ) ) , " " , - 1 ) ;
2026-01-15 18:35:27 -07:00
if ( gui - > op_xpm )
{
gtk_widget_destroy ( gui - > op_xpm ) ;
gui - > op_xpm = 0 ;
}
} else
{
if ( sess - > res - > topic_text )
{
g_free ( sess - > res - > topic_text ) ;
sess - > res - > topic_text = NULL ;
}
}
2011-02-24 04:14:30 +01:00
}
void
fe_set_nonchannel ( session * sess , int state )
{
}
void
fe_dlgbuttons_update ( session * sess )
{
2026-01-15 18:35:27 -07:00
GtkWidget * box ;
session_gui * gui = sess - > gui ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_destroy ( gui - > dialogbutton_box ) ;
2011-02-24 04:14:30 +01:00
2026-01-19 20:11:07 -07:00
gui - > dialogbutton_box = box = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( gui - > topic_bar ) , box , 0 , 0 , 0 ) ;
gtk_box_reorder_child ( GTK_BOX ( gui - > topic_bar ) , box , 3 ) ;
mg_create_dialogbuttons ( box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_widget_show_all ( box ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( current_tab & & current_tab - > type ! = SESS_DIALOG )
gtk_widget_hide ( current_tab - > gui - > dialogbutton_box ) ;
2011-02-24 04:14:30 +01:00
}
void
fe_update_mode_buttons ( session * sess , char mode , char sign )
{
2026-01-15 18:35:27 -07:00
int state , i ;
if ( sign = = ' + ' )
state = TRUE ;
else
state = FALSE ;
for ( i = 0 ; i < NUM_FLAG_WIDS - 1 ; i + + )
{
if ( chan_flags [ i ] = = mode )
{
if ( ! sess - > gui - > is_tab | | sess = = current_tab )
{
ignore_chanmode = TRUE ;
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( sess - > gui - > flag_wid [ i ] ) ) ! = state )
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( sess - > gui - > flag_wid [ i ] ) , state ) ;
ignore_chanmode = FALSE ;
} else
{
sess - > res - > flag_wid_state [ i ] = state ;
}
return ;
}
}
2011-02-24 04:14:30 +01:00
}
void
fe_set_nick ( server * serv , char * newnick )
{
2026-01-15 18:35:27 -07:00
GSList * list = sess_list ;
session * sess ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
while ( list )
{
sess = list - > data ;
if ( sess - > server = = serv )
{
if ( current_tab = = sess | | ! sess - > gui - > is_tab )
gtk_button_set_label ( GTK_BUTTON ( sess - > gui - > nick_label ) , newnick ) ;
}
list = list - > next ;
}
2011-02-24 04:14:30 +01:00
}
void
fe_set_away ( server * serv )
{
2026-01-15 18:35:27 -07:00
GSList * list = sess_list ;
session * sess ;
while ( list )
{
sess = list - > data ;
if ( sess - > server = = serv )
{
if ( ! sess - > gui - > is_tab | | sess = = current_tab )
{
menu_set_away ( sess - > gui , serv - > is_away ) ;
/* gray out my nickname */
mg_set_myself_away ( sess - > gui , serv - > is_away ) ;
}
}
list = list - > next ;
}
2011-02-24 04:14:30 +01:00
}
void
fe_set_channel ( session * sess )
{
2026-01-15 18:35:27 -07:00
if ( sess - > res - > tab ! = NULL )
chan_rename ( sess - > res - > tab , sess - > channel , prefs . hex_gui_tab_trunc ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_changui_new ( session * sess , restore_gui * res , int tab , int focus )
{
2026-01-15 18:35:27 -07:00
int first_run = FALSE ;
session_gui * gui ;
if ( res = = NULL )
{
res = g_new0 ( restore_gui , 1 ) ;
}
sess - > res = res ;
if ( sess - > server - > front_session = = NULL )
{
sess - > server - > front_session = sess ;
}
if ( ! tab )
{
gui = g_new0 ( session_gui , 1 ) ;
gui - > is_tab = FALSE ;
sess - > gui = gui ;
mg_create_topwindow ( sess ) ;
fe_set_title ( sess ) ;
return ;
}
if ( mg_gui = = NULL )
{
first_run = TRUE ;
gui = & static_mg_gui ;
memset ( gui , 0 , sizeof ( session_gui ) ) ;
gui - > is_tab = TRUE ;
sess - > gui = gui ;
mg_create_tabwindow ( sess ) ;
mg_gui = gui ;
parent_window = gui - > window ;
} else
{
sess - > gui = gui = mg_gui ;
gui - > is_tab = TRUE ;
}
mg_add_chan ( sess ) ;
if ( first_run | | ( prefs . hex_gui_tab_newtofront = = FOCUS_NEW_ONLY_ASKED & & focus )
| | prefs . hex_gui_tab_newtofront = = FOCUS_NEW_ALL )
chan_focus ( res - > tab ) ;
2011-02-24 04:14:30 +01:00
}
GtkWidget *
mg_create_generic_tab ( char * name , char * title , int force_toplevel ,
2026-01-15 18:35:27 -07:00
int link_buttons ,
void * close_callback , void * userdata ,
int width , int height , GtkWidget * * vbox_ret ,
void * family )
{
GtkWidget * vbox , * win ;
if ( prefs . hex_gui_tab_pos = = POS_HIDDEN & & prefs . hex_gui_tab_utils )
prefs . hex_gui_tab_utils = 0 ;
if ( force_toplevel | | ! prefs . hex_gui_tab_utils )
{
win = gtkutil_window_new ( title , name , width , height , 2 ) ;
2026-01-19 20:11:07 -07:00
vbox = mg_box_new ( GTK_ORIENTATION_VERTICAL , FALSE , 0 ) ;
2026-01-15 18:35:27 -07:00
* vbox_ret = vbox ;
gtk_container_add ( GTK_CONTAINER ( win ) , vbox ) ;
gtk_widget_show ( vbox ) ;
if ( close_callback )
g_signal_connect ( G_OBJECT ( win ) , " destroy " ,
G_CALLBACK ( close_callback ) , userdata ) ;
return win ;
}
2026-01-19 20:11:07 -07:00
vbox = mg_box_new ( GTK_ORIENTATION_VERTICAL , FALSE , 2 ) ;
2026-01-15 18:35:27 -07:00
g_object_set_data ( G_OBJECT ( vbox ) , " w " , GINT_TO_POINTER ( width ) ) ;
g_object_set_data ( G_OBJECT ( vbox ) , " h " , GINT_TO_POINTER ( height ) ) ;
gtk_container_set_border_width ( GTK_CONTAINER ( vbox ) , 3 ) ;
* vbox_ret = vbox ;
if ( close_callback )
g_signal_connect ( G_OBJECT ( vbox ) , " destroy " ,
G_CALLBACK ( close_callback ) , userdata ) ;
mg_add_generic_tab ( name , title , family , vbox ) ;
/* if (link_buttons)
{
2026-01-19 20:11:07 -07:00
hbox = mg_box_new ( GTK_ORIENTATION_HORIZONTAL , FALSE , 0 ) ;
2026-01-15 18:35:27 -07:00
gtk_box_pack_start ( GTK_BOX ( vbox ) , hbox , 0 , 0 , 0 ) ;
mg_create_link_buttons ( hbox , ch ) ;
gtk_widget_show ( hbox ) ;
} */
return vbox ;
2011-02-24 04:14:30 +01:00
}
void
mg_move_tab ( session * sess , int delta )
{
2026-01-15 18:35:27 -07:00
if ( sess - > gui - > is_tab )
chan_move ( sess - > res - > tab , delta ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_move_tab_family ( session * sess , int delta )
{
2026-01-15 18:35:27 -07:00
if ( sess - > gui - > is_tab )
chan_move_family ( sess - > res - > tab , delta ) ;
2011-02-24 04:14:30 +01:00
}
void
mg_set_title ( GtkWidget * vbox , char * title ) /* for non-irc tab/window only */
{
2026-01-15 18:35:27 -07:00
char * old ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
old = g_object_get_data ( G_OBJECT ( vbox ) , " title " ) ;
if ( old )
{
g_object_set_data_full ( G_OBJECT ( vbox ) , " title " , g_strdup ( title ) , g_free ) ;
} else
{
gtk_window_set_title ( GTK_WINDOW ( vbox ) , title ) ;
}
2011-02-24 04:14:30 +01:00
}
void
fe_server_callback ( server * serv )
{
2026-01-15 18:35:27 -07:00
joind_close ( serv ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( serv - > gui - > chanlist_window )
mg_close_gen ( NULL , serv - > gui - > chanlist_window ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( serv - > gui - > rawlog_window )
mg_close_gen ( NULL , serv - > gui - > rawlog_window ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
g_free ( serv - > gui ) ;
2011-02-24 04:14:30 +01:00
}
/* called when a session is being killed */
void
fe_session_callback ( session * sess )
{
2026-01-15 18:35:27 -07:00
gtk_xtext_buffer_free ( sess - > res - > buffer ) ;
g_object_unref ( G_OBJECT ( sess - > res - > user_model ) ) ;
2026-03-02 19:42:48 -07:00
if ( sess - > res - > user_row_refs )
g_hash_table_destroy ( sess - > res - > user_row_refs ) ;
2016-07-24 21:14:11 -04:00
2026-01-15 18:35:27 -07:00
if ( sess - > res - > banlist & & sess - > res - > banlist - > window )
mg_close_gen ( NULL , sess - > res - > banlist - > window ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
g_free ( sess - > res - > input_text ) ;
g_free ( sess - > res - > topic_text ) ;
g_free ( sess - > res - > limit_text ) ;
g_free ( sess - > res - > key_text ) ;
g_free ( sess - > res - > queue_text ) ;
g_free ( sess - > res - > queue_tip ) ;
g_free ( sess - > res - > lag_text ) ;
g_free ( sess - > res - > lag_tip ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > gui - > bartag )
fe_timeout_remove ( sess - > gui - > bartag ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( sess - > gui ! = & static_mg_gui )
g_free ( sess - > gui ) ;
g_free ( sess - > res ) ;
2011-02-24 04:14:30 +01:00
}
/* ===== DRAG AND DROP STUFF ===== */
static gboolean
is_child_of ( GtkWidget * widget , GtkWidget * parent )
{
2026-01-15 18:35:27 -07:00
while ( widget )
{
if ( gtk_widget_get_parent ( widget ) = = parent )
return TRUE ;
widget = gtk_widget_get_parent ( widget ) ;
}
return FALSE ;
2011-02-24 04:14:30 +01:00
}
static void
mg_handle_drop ( GtkWidget * widget , int y , int * pos , int * other_pos )
{
2026-01-15 18:35:27 -07:00
int height ;
session_gui * gui = current_sess - > gui ;
height = gdk_window_get_height ( gtk_widget_get_window ( widget ) ) ;
if ( y < height / 2 )
{
if ( is_child_of ( widget , gui - > vpane_left ) )
* pos = 1 ; /* top left */
else
* pos = 3 ; /* top right */
}
else
{
if ( is_child_of ( widget , gui - > vpane_left ) )
* pos = 2 ; /* bottom left */
else
* pos = 4 ; /* bottom right */
}
/* both in the same pos? must move one */
if ( * pos = = * other_pos )
{
switch ( * other_pos )
{
case 1 :
* other_pos = 2 ;
break ;
case 2 :
* other_pos = 1 ;
break ;
case 3 :
* other_pos = 4 ;
break ;
case 4 :
* other_pos = 3 ;
break ;
}
}
mg_place_userlist_and_chanview ( gui ) ;
2011-02-24 04:14:30 +01:00
}
static gboolean
mg_is_gui_target ( GdkDragContext * context )
{
2026-01-15 18:35:27 -07:00
char * target_name ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
if ( ! context | | ! gdk_drag_context_list_targets ( context ) | | ! gdk_drag_context_list_targets ( context ) - > data )
return FALSE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
target_name = gdk_atom_name ( gdk_drag_context_list_targets ( context ) - > data ) ;
if ( target_name )
{
2026-01-25 16:13:47 -07:00
/* if it's not ZOITECHAT_CHANVIEW or ZOITECHAT_USERLIST */
2026-01-15 18:35:27 -07:00
/* we should ignore it. */
if ( target_name [ 0 ] ! = ' H ' )
{
g_free ( target_name ) ;
return FALSE ;
}
g_free ( target_name ) ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return TRUE ;
2011-02-24 04:14:30 +01:00
}
/* this begin callback just creates an nice of the source */
gboolean
mg_drag_begin_cb ( GtkWidget * widget , GdkDragContext * context , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
int width , height ;
GdkPixbuf * pix , * pix2 ;
2026-01-17 17:34:38 -07:00
GdkWindow * window ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
/* ignore file drops */
if ( ! mg_is_gui_target ( context ) )
return FALSE ;
2011-02-24 04:14:30 +01:00
2026-01-17 17:34:38 -07:00
window = gtk_widget_get_window ( widget ) ;
width = gdk_window_get_width ( window ) ;
height = gdk_window_get_height ( window ) ;
2011-02-24 04:14:30 +01:00
2026-01-17 17:34:38 -07:00
pix = mg_pixbuf_from_window ( window , width , height ) ;
2026-01-17 21:56:00 -07:00
if ( ! pix )
return FALSE ;
2026-01-15 18:35:27 -07:00
pix2 = gdk_pixbuf_scale_simple ( pix , width * 4 / 5 , height / 2 , GDK_INTERP_HYPER ) ;
g_object_unref ( pix ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
gtk_drag_set_icon_pixbuf ( context , pix2 , 0 , 0 ) ;
g_object_set_data ( G_OBJECT ( widget ) , " ico " , pix2 ) ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return TRUE ;
2011-02-24 04:14:30 +01:00
}
void
mg_drag_end_cb ( GtkWidget * widget , GdkDragContext * context , gpointer userdata )
{
2026-01-15 18:35:27 -07:00
/* ignore file drops */
if ( ! mg_is_gui_target ( context ) )
return ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
g_object_unref ( g_object_get_data ( G_OBJECT ( widget ) , " ico " ) ) ;
2011-02-24 04:14:30 +01:00
}
/* drop complete */
gboolean
mg_drag_drop_cb ( GtkWidget * widget , GdkDragContext * context , int x , int y , guint time , gpointer user_data )
{
2026-01-15 18:35:27 -07:00
/* ignore file drops */
if ( ! mg_is_gui_target ( context ) )
return FALSE ;
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
switch ( gdk_drag_context_get_selected_action ( context ) )
{
case GDK_ACTION_MOVE :
/* from userlist */
mg_handle_drop ( widget , y , & prefs . hex_gui_ulist_pos , & prefs . hex_gui_tab_pos ) ;
break ;
case GDK_ACTION_COPY :
/* from tree - we use GDK_ACTION_COPY for the tree */
mg_handle_drop ( widget , y , & prefs . hex_gui_tab_pos , & prefs . hex_gui_ulist_pos ) ;
break ;
default :
return FALSE ;
}
2011-02-24 04:14:30 +01:00
2026-01-15 18:35:27 -07:00
return TRUE ;
2011-02-24 04:14:30 +01:00
}
/* draw highlight rectangle in the destination */
gboolean
mg_drag_motion_cb ( GtkWidget * widget , GdkDragContext * context , int x , int y , guint time , gpointer scbar )
{
2026-01-17 21:56:00 -07:00
XTextColor col ;
2026-01-17 16:19:56 -07:00
cairo_t * cr ;
2026-03-11 01:49:14 -06:00
GdkDrawingContext * draw_context ;
cairo_region_t * region ;
2026-01-15 18:35:27 -07:00
int half , width , height ;
int ox , oy ;
2026-01-17 17:34:38 -07:00
GdkWindow * window ;
2026-01-15 18:35:27 -07:00
GtkAllocation allocation ;
/* ignore file drops */
if ( ! mg_is_gui_target ( context ) )
return FALSE ;
if ( scbar ) /* scrollbar */
{
gtk_widget_get_allocation ( widget , & allocation ) ;
ox = allocation . x ;
oy = allocation . y ;
width = allocation . width ;
height = allocation . height ;
2026-01-17 17:34:38 -07:00
window = gtk_widget_get_window ( widget ) ;
2026-01-15 18:35:27 -07:00
}
else
{
ox = oy = 0 ;
2026-01-17 17:34:38 -07:00
window = gtk_widget_get_window ( widget ) ;
width = gdk_window_get_width ( window ) ;
height = gdk_window_get_height ( window ) ;
2026-01-15 18:35:27 -07:00
}
2026-01-17 21:56:00 -07:00
col . red = ( double ) rand ( ) / ( double ) RAND_MAX ;
col . green = ( double ) rand ( ) / ( double ) RAND_MAX ;
col . blue = ( double ) rand ( ) / ( double ) RAND_MAX ;
col . alpha = 1.0 ;
2026-03-11 01:49:14 -06:00
region = cairo_region_create ( ) ;
cairo_region_union_rectangle ( region , & ( cairo_rectangle_int_t ) { 0 , 0 , width , height } ) ;
draw_context = gdk_window_begin_draw_frame ( window , region ) ;
cairo_region_destroy ( region ) ;
cr = gdk_drawing_context_get_cairo_context ( draw_context ) ;
2026-01-17 16:19:56 -07:00
cairo_set_operator ( cr , CAIRO_OPERATOR_XOR ) ;
2026-01-17 16:46:21 -07:00
mg_set_source_color ( cr , & col ) ;
2026-01-17 16:19:56 -07:00
cairo_set_line_width ( cr , 1.0 ) ;
2026-01-15 18:35:27 -07:00
half = height / 2 ;
2011-02-24 04:14:30 +01:00
#if 0
2026-01-15 18:35:27 -07:00
/* are both tree/userlist on the same side? */
2026-01-17 16:34:38 -07:00
GtkPaned * paned ;
2026-01-31 11:36:47 -07:00
GtkWidget * parent ;
GtkWidget * grandparent ;
parent = gtk_widget_get_parent ( widget ) ;
grandparent = parent ? gtk_widget_get_parent ( parent ) : NULL ;
paned = grandparent ? GTK_PANED ( grandparent ) : NULL ;
if ( paned ! = NULL & &
gtk_paned_get_child1 ( paned ) ! = NULL & &
gtk_paned_get_child2 ( paned ) ! = NULL )
2026-01-15 18:35:27 -07:00
{
2026-01-17 16:34:38 -07:00
cairo_rectangle ( cr , 1 + ox , 2 + oy , width - 3 , height - 4 ) ;
cairo_rectangle ( cr , 0 + ox , 1 + oy , width - 1 , height - 2 ) ;
cairo_stroke ( cr ) ;
cairo_destroy ( cr ) ;
2026-01-15 18:35:27 -07:00
return TRUE ;
}
2011-02-24 04:14:30 +01:00
# endif
2026-01-15 18:35:27 -07:00
if ( y < half )
{
2026-01-17 16:19:56 -07:00
cairo_rectangle ( cr , 1 + ox , 2 + oy , width - 3 , half - 4 ) ;
cairo_rectangle ( cr , 0 + ox , 1 + oy , width - 1 , half - 2 ) ;
cairo_stroke ( cr ) ;
2026-01-15 18:35:27 -07:00
gtk_widget_queue_draw_area ( widget , ox , half + oy , width , height - half ) ;
}
else
{
2026-01-17 16:19:56 -07:00
cairo_rectangle ( cr , 0 + ox , half + 1 + oy , width - 1 , half - 2 ) ;
cairo_rectangle ( cr , 1 + ox , half + 2 + oy , width - 3 , half - 4 ) ;
cairo_stroke ( cr ) ;
2026-01-15 18:35:27 -07:00
gtk_widget_queue_draw_area ( widget , ox , oy , width , half ) ;
}
2026-03-11 01:49:14 -06:00
gdk_window_end_draw_frame ( window , draw_context ) ;
2026-01-15 18:35:27 -07:00
return TRUE ;
2011-02-24 04:14:30 +01:00
}