Merge pull request #29 from ZoiteChat/gtk-ciaro

Gtk ciaro
This commit is contained in:
deepend-tildeclub
2026-01-18 02:11:54 -07:00
committed by GitHub
19 changed files with 896 additions and 374 deletions

View File

@@ -437,7 +437,7 @@ const struct prefs vars[] =
{"gui_tab_dialogs", P_OFFINT (hex_gui_tab_dialogs), TYPE_BOOL}, {"gui_tab_dialogs", P_OFFINT (hex_gui_tab_dialogs), TYPE_BOOL},
{"gui_tab_dots", P_OFFINT (hex_gui_tab_dots), TYPE_BOOL}, {"gui_tab_dots", P_OFFINT (hex_gui_tab_dots), TYPE_BOOL},
{"gui_tab_icons", P_OFFINT (hex_gui_tab_icons), TYPE_BOOL}, {"gui_tab_icons", P_OFFINT (hex_gui_tab_icons), TYPE_BOOL},
{"gui_dark_mode", P_OFFINT (hex_gui_dark_mode), TYPE_BOOL}, {"gui_dark_mode", P_OFFINT (hex_gui_dark_mode), TYPE_INT},
{"gui_tab_layout", P_OFFINT (hex_gui_tab_layout), TYPE_INT}, {"gui_tab_layout", P_OFFINT (hex_gui_tab_layout), TYPE_INT},
{"gui_tab_middleclose", P_OFFINT (hex_gui_tab_middleclose), TYPE_BOOL}, {"gui_tab_middleclose", P_OFFINT (hex_gui_tab_middleclose), TYPE_BOOL},
{"gui_tab_newtofront", P_OFFINT (hex_gui_tab_newtofront), TYPE_INT}, {"gui_tab_newtofront", P_OFFINT (hex_gui_tab_newtofront), TYPE_INT},

View File

@@ -83,6 +83,10 @@ gboolean zoitechat_import_theme (const char *path, GError **error);
#define USERNAMELEN 10 #define USERNAMELEN 10
#define HIDDEN_CHAR 8 /* invisible character for xtext */ #define HIDDEN_CHAR 8 /* invisible character for xtext */
#define ZOITECHAT_DARK_MODE_AUTO 0
#define ZOITECHAT_DARK_MODE_DARK 1
#define ZOITECHAT_DARK_MODE_LIGHT 2
struct nbexec struct nbexec
{ {
int myfd; int myfd;

View File

@@ -117,7 +117,11 @@ cv_tree_init (chanview *cv)
view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (cv->store)); view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (cv->store));
gtk_widget_set_name (view, "zoitechat-tree"); gtk_widget_set_name (view, "zoitechat-tree");
if (cv->style) if (cv->style)
gtk_widget_set_style (view, cv->style); {
gtk_widget_modify_base (view, GTK_STATE_NORMAL, &cv->style->base[GTK_STATE_NORMAL]);
gtk_widget_modify_text (view, GTK_STATE_NORMAL, &cv->style->text[GTK_STATE_NORMAL]);
gtk_widget_modify_font (view, cv->style->font_desc);
}
/*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[COL_BG]);*/ /*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[COL_BG]);*/
gtk_widget_set_can_focus (view, FALSE); gtk_widget_set_can_focus (view, FALSE);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);

View File

@@ -122,7 +122,7 @@ chanview_apply_theme (chanview *cv)
return; return;
w = GTK_WIDGET (tv->tree); w = GTK_WIDGET (tv->tree);
if (prefs.hex_gui_dark_mode) if (fe_dark_mode_is_enabled () || prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_LIGHT)
{ {
gtk_widget_modify_base (w, GTK_STATE_NORMAL, &colors[COL_BG]); gtk_widget_modify_base (w, GTK_STATE_NORMAL, &colors[COL_BG]);
gtk_widget_modify_text (w, GTK_STATE_NORMAL, &colors[COL_FG]); gtk_widget_modify_text (w, GTK_STATE_NORMAL, &colors[COL_FG]);

View File

@@ -58,7 +58,7 @@
#include <canberra.h> #include <canberra.h>
#endif #endif
GdkPixmap *channelwin_pix; cairo_surface_t *channelwin_pix;
#ifdef USE_LIBCANBERRA #ifdef USE_LIBCANBERRA
static ca_context *ca_con; static ca_context *ca_con;
@@ -270,6 +270,88 @@ static const char adwaita_workaround_rc[] =
"}" "}"
"widget \"*.zoitechat-inputbox\" style \"zoitechat-input-workaround\""; "widget \"*.zoitechat-inputbox\" style \"zoitechat-input-workaround\"";
static gboolean
fe_system_prefers_dark (void)
{
GtkSettings *settings = gtk_settings_get_default ();
gboolean prefer_dark = FALSE;
char *theme_name = NULL;
if (!settings)
return FALSE;
if (g_object_class_find_property (G_OBJECT_GET_CLASS (settings),
"gtk-application-prefer-dark-theme"))
{
g_object_get (settings, "gtk-application-prefer-dark-theme", &prefer_dark, NULL);
}
if (!prefer_dark)
{
g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
if (theme_name)
{
char *lower = g_ascii_strdown (theme_name, -1);
if (g_str_has_suffix (lower, "-dark") || g_strrstr (lower, "dark"))
prefer_dark = TRUE;
g_free (lower);
g_free (theme_name);
}
}
return prefer_dark;
}
static gboolean auto_dark_mode_enabled = FALSE;
static void
fe_auto_dark_mode_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data)
{
gboolean enabled;
(void) settings;
(void) pspec;
(void) data;
if (prefs.hex_gui_dark_mode != ZOITECHAT_DARK_MODE_AUTO)
return;
enabled = fe_system_prefers_dark ();
if (enabled == auto_dark_mode_enabled)
return;
auto_dark_mode_enabled = enabled;
palette_apply_dark_mode (enabled);
setup_apply_real (0, TRUE, FALSE, FALSE);
}
void
fe_set_auto_dark_mode_state (gboolean enabled)
{
auto_dark_mode_enabled = enabled;
}
gboolean
fe_dark_mode_is_enabled_for (unsigned int mode)
{
switch (mode)
{
case ZOITECHAT_DARK_MODE_DARK:
return TRUE;
case ZOITECHAT_DARK_MODE_LIGHT:
return FALSE;
case ZOITECHAT_DARK_MODE_AUTO:
default:
return fe_system_prefers_dark ();
}
}
gboolean
fe_dark_mode_is_enabled (void)
{
return fe_dark_mode_is_enabled_for (prefs.hex_gui_dark_mode);
}
GtkStyle * GtkStyle *
create_input_style (GtkStyle *style) create_input_style (GtkStyle *style)
{ {
@@ -316,8 +398,10 @@ create_input_style (GtkStyle *style)
void void
fe_init (void) fe_init (void)
{ {
GtkSettings *settings;
palette_load (); palette_load ();
palette_apply_dark_mode (prefs.hex_gui_dark_mode); palette_apply_dark_mode (fe_dark_mode_is_enabled ());
key_init (); key_init ();
pixmaps_init (); pixmaps_init ();
@@ -326,6 +410,16 @@ fe_init (void)
#endif #endif
channelwin_pix = pixmap_load_from_file (prefs.hex_text_background); channelwin_pix = pixmap_load_from_file (prefs.hex_text_background);
input_style = create_input_style (gtk_style_new ()); input_style = create_input_style (gtk_style_new ());
settings = gtk_settings_get_default ();
if (settings)
{
auto_dark_mode_enabled = fe_system_prefers_dark ();
g_signal_connect (settings, "notify::gtk-application-prefer-dark-theme",
G_CALLBACK (fe_auto_dark_mode_changed), NULL);
g_signal_connect (settings, "notify::gtk-theme-name",
G_CALLBACK (fe_auto_dark_mode_changed), NULL);
}
} }
#ifdef HAVE_GTK_MAC #ifdef HAVE_GTK_MAC

View File

@@ -30,6 +30,7 @@
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <cairo.h>
#ifdef HAVE_GTK_MAC #ifdef HAVE_GTK_MAC
#include <gtkosxapplication.h> #include <gtkosxapplication.h>
@@ -178,8 +179,12 @@ typedef struct session_gui
} session_gui; } session_gui;
extern GdkPixmap *channelwin_pix; extern cairo_surface_t *channelwin_pix;
extern GdkPixmap *dialogwin_pix; extern cairo_surface_t *dialogwin_pix;
gboolean fe_dark_mode_is_enabled (void);
gboolean fe_dark_mode_is_enabled_for (unsigned int mode);
void fe_set_auto_dark_mode_state (gboolean enabled);
#define SPELL_ENTRY_GET_TEXT(e) ((char *)(gtk_entry_get_text (GTK_ENTRY(e)))) #define SPELL_ENTRY_GET_TEXT(e) ((char *)(gtk_entry_get_text (GTK_ENTRY(e))))
#define SPELL_ENTRY_SET_TEXT(e,txt) gtk_entry_set_text(GTK_ENTRY(e),txt) #define SPELL_ENTRY_SET_TEXT(e,txt) gtk_entry_set_text(GTK_ENTRY(e),txt)

View File

@@ -803,6 +803,7 @@ key_dialog_show ()
GtkWidget *vbox, *box; GtkWidget *vbox, *box;
GtkWidget *view, *xtext; GtkWidget *view, *xtext;
GtkListStore *store; GtkListStore *store;
XTextColor xtext_palette[XTEXT_COLS];
char buf[128]; char buf[128];
if (key_dialog) if (key_dialog)
@@ -816,7 +817,8 @@ key_dialog_show ()
NULL, 600, 360, &vbox, 0); NULL, 600, 360, &vbox, 0);
view = key_dialog_treeview_new (vbox); view = key_dialog_treeview_new (vbox);
xtext = gtk_xtext_new (colors, 0); palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
xtext = gtk_xtext_new (xtext_palette, 0);
gtk_box_pack_start (GTK_BOX (vbox), xtext, FALSE, TRUE, 2); gtk_box_pack_start (GTK_BOX (vbox), xtext, FALSE, TRUE, 2);
gtk_xtext_set_font (GTK_XTEXT (xtext), prefs.hex_text_font); gtk_xtext_set_font (GTK_XTEXT (xtext), prefs.hex_text_font);

View File

@@ -95,7 +95,7 @@ gtkutil_check_file (char *filename, struct file_req *freq)
} }
else else
{ {
GFileInfo *fi = g_file_query_info (file, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, G_FILE_QUERY_INFO_NONE, NULL, NULL); GFileInfo *fi = g_file_query_info (file, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
if (fi != NULL) if (fi != NULL)
{ {

View File

@@ -21,7 +21,10 @@
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h> #include <gdk/gdkkeysyms.h>
#include <gdk/gdkcairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "../common/zoitechat.h" #include "../common/zoitechat.h"
#include "../common/fe.h" #include "../common/fe.h"
@@ -82,6 +85,136 @@ static void mg_emoji_button_cb (GtkWidget *widget, session_gui *gui);
static GtkWidget *mg_create_emoji_menu (session_gui *gui); static GtkWidget *mg_create_emoji_menu (session_gui *gui);
static void mg_emoji_insert_cb (GtkMenuItem *item, session_gui *gui); static void mg_emoji_insert_cb (GtkMenuItem *item, session_gui *gui);
static inline void
mg_set_source_color (cairo_t *cr, const XTextColor *color)
{
cairo_set_source_rgba (cr, color->red, color->green, color->blue, color->alpha);
}
static inline guint16
mg_color_component_to_pango (double value)
{
if (value < 0.0)
value = 0.0;
if (value > 1.0)
value = 1.0;
return (guint16)(value * 65535.0 + 0.5);
}
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);
}
static GdkPixbuf *
mg_pixbuf_from_window (GdkWindow *window, int width, int height)
{
int src_width;
int src_height;
cairo_surface_t *surface;
cairo_t *cr;
GdkPixbuf *pixbuf;
if (!window)
return NULL;
src_width = gdk_window_get_width (window);
src_height = gdk_window_get_height (window);
if (width <= 0 || height <= 0)
{
width = src_width;
height = src_height;
}
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;
}
static void mg_create_entry (session *sess, GtkWidget *box); static void mg_create_entry (session *sess, GtkWidget *box);
static void mg_create_search (session *sess, GtkWidget *box); static void mg_create_search (session *sess, GtkWidget *box);
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
@@ -106,7 +239,7 @@ static PangoAttrList *newmsg_list;
static PangoAttrList *plain_list = NULL; static PangoAttrList *plain_list = NULL;
static PangoAttrList * static PangoAttrList *
mg_attr_list_create (GdkColor *col, int size) mg_attr_list_create (const XTextColor *col, int size)
{ {
PangoAttribute *attr; PangoAttribute *attr;
PangoAttrList *list; PangoAttrList *list;
@@ -115,7 +248,10 @@ mg_attr_list_create (GdkColor *col, int size)
if (col) if (col)
{ {
attr = pango_attr_foreground_new (col->red, col->green, col->blue); 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));
attr->start_index = 0; attr->start_index = 0;
attr->end_index = 0xffff; attr->end_index = 0xffff;
pango_attr_list_insert (list, attr); pango_attr_list_insert (list, attr);
@@ -135,6 +271,8 @@ mg_attr_list_create (GdkColor *col, int size)
static void static void
mg_create_tab_colors (void) mg_create_tab_colors (void)
{ {
XTextColor gui_palette[MAX_COL + 1];
if (plain_list) if (plain_list)
{ {
pango_attr_list_unref (plain_list); pango_attr_list_unref (plain_list);
@@ -144,11 +282,12 @@ mg_create_tab_colors (void)
pango_attr_list_unref (away_list); pango_attr_list_unref (away_list);
} }
palette_get_xtext_colors (gui_palette, G_N_ELEMENTS (gui_palette));
plain_list = mg_attr_list_create (NULL, prefs.hex_gui_tab_small); plain_list = mg_attr_list_create (NULL, prefs.hex_gui_tab_small);
newdata_list = mg_attr_list_create (&colors[COL_NEW_DATA], prefs.hex_gui_tab_small); newdata_list = mg_attr_list_create (&gui_palette[COL_NEW_DATA], prefs.hex_gui_tab_small);
nickseen_list = mg_attr_list_create (&colors[COL_HILIGHT], prefs.hex_gui_tab_small); nickseen_list = mg_attr_list_create (&gui_palette[COL_HILIGHT], prefs.hex_gui_tab_small);
newmsg_list = mg_attr_list_create (&colors[COL_NEW_MSG], prefs.hex_gui_tab_small); newmsg_list = mg_attr_list_create (&gui_palette[COL_NEW_MSG], prefs.hex_gui_tab_small);
away_list = mg_attr_list_create (&colors[COL_AWAY], FALSE); away_list = mg_attr_list_create (&gui_palette[COL_AWAY], FALSE);
} }
static void static void
@@ -2314,8 +2453,10 @@ mg_update_xtext (GtkWidget *wid)
{ {
GtkXText *xtext = GTK_XTEXT (wid); GtkXText *xtext = GTK_XTEXT (wid);
const gchar *font_name; const gchar *font_name;
XTextColor xtext_palette[XTEXT_COLS];
gtk_xtext_set_palette (xtext, colors); palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
gtk_xtext_set_palette (xtext, xtext_palette);
gtk_xtext_set_max_lines (xtext, prefs.hex_text_max_lines); gtk_xtext_set_max_lines (xtext, prefs.hex_text_max_lines);
gtk_xtext_set_background (xtext, channelwin_pix); gtk_xtext_set_background (xtext, channelwin_pix);
gtk_xtext_set_wordwrap (xtext, prefs.hex_text_wordwrap); gtk_xtext_set_wordwrap (xtext, prefs.hex_text_wordwrap);
@@ -2340,6 +2481,7 @@ mg_create_textarea (session *sess, GtkWidget *box)
{ {
GtkWidget *inbox, *vbox, *frame; GtkWidget *inbox, *vbox, *frame;
GtkXText *xtext; GtkXText *xtext;
XTextColor xtext_palette[XTEXT_COLS];
session_gui *gui = sess->gui; session_gui *gui = sess->gui;
static const GtkTargetEntry dnd_targets[] = static const GtkTargetEntry dnd_targets[] =
{ {
@@ -2360,7 +2502,8 @@ mg_create_textarea (session *sess, GtkWidget *box)
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (inbox), frame); gtk_container_add (GTK_CONTAINER (inbox), frame);
gui->xtext = gtk_xtext_new (colors, TRUE); palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
gui->xtext = gtk_xtext_new (xtext_palette, TRUE);
xtext = GTK_XTEXT (gui->xtext); xtext = GTK_XTEXT (gui->xtext);
gtk_xtext_set_max_indent (xtext, prefs.hex_text_max_indent); 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_thin_separator (xtext, prefs.hex_text_thin_sep);
@@ -2496,7 +2639,7 @@ mg_create_userlist (session_gui *gui, GtkWidget *box)
if (prefs.hex_gui_ulist_style) if (prefs.hex_gui_ulist_style)
{ {
gtk_widget_set_style (ulist, input_style); gtk_widget_modify_font (ulist, input_style->font_desc);
} }
/* /*
@@ -2507,7 +2650,7 @@ mg_create_userlist (session_gui *gui, GtkWidget *box)
* - When "Dark mode" is enabled, we also force the user list to use the * - When "Dark mode" is enabled, we also force the user list to use the
* palette colors so it doesn't stay blindingly white on GTK2 themes. * palette colors so it doesn't stay blindingly white on GTK2 themes.
*/ */
if (prefs.hex_gui_ulist_style || prefs.hex_gui_dark_mode) if (prefs.hex_gui_ulist_style || fe_dark_mode_is_enabled ())
{ {
gtk_widget_modify_base (ulist, GTK_STATE_NORMAL, &colors[COL_BG]); gtk_widget_modify_base (ulist, GTK_STATE_NORMAL, &colors[COL_BG]);
gtk_widget_modify_text (ulist, GTK_STATE_NORMAL, &colors[COL_FG]); gtk_widget_modify_text (ulist, GTK_STATE_NORMAL, &colors[COL_FG]);
@@ -4053,18 +4196,20 @@ gboolean
mg_drag_begin_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata) mg_drag_begin_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata)
{ {
int width, height; int width, height;
GdkColormap *cmap;
GdkPixbuf *pix, *pix2; GdkPixbuf *pix, *pix2;
GdkWindow *window;
/* ignore file drops */ /* ignore file drops */
if (!mg_is_gui_target (context)) if (!mg_is_gui_target (context))
return FALSE; return FALSE;
cmap = gtk_widget_get_colormap (widget); window = gtk_widget_get_window (widget);
width = gdk_window_get_width (gtk_widget_get_window (widget)); width = gdk_window_get_width (window);
height = gdk_window_get_height (gtk_widget_get_window (widget)); height = gdk_window_get_height (window);
pix = gdk_pixbuf_get_from_drawable (NULL, gtk_widget_get_window (widget), cmap, 0, 0, 0, 0, width, height); pix = mg_pixbuf_from_window (window, width, height);
if (!pix)
return FALSE;
pix2 = gdk_pixbuf_scale_simple (pix, width * 4 / 5, height / 2, GDK_INTERP_HYPER); pix2 = gdk_pixbuf_scale_simple (pix, width * 4 / 5, height / 2, GDK_INTERP_HYPER);
g_object_unref (pix); g_object_unref (pix);
@@ -4115,12 +4260,11 @@ mg_drag_drop_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint
gboolean gboolean
mg_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer scbar) mg_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer scbar)
{ {
GdkGC *gc; XTextColor col;
GdkColor col; cairo_t *cr;
GdkGCValues val;
int half, width, height; int half, width, height;
int ox, oy; int ox, oy;
GdkDrawable *draw; GdkWindow *window;
GtkAllocation allocation; GtkAllocation allocation;
/* ignore file drops */ /* ignore file drops */
@@ -4134,55 +4278,57 @@ mg_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, gui
oy = allocation.y; oy = allocation.y;
width = allocation.width; width = allocation.width;
height = allocation.height; height = allocation.height;
draw = gtk_widget_get_window (widget); window = gtk_widget_get_window (widget);
} }
else else
{ {
ox = oy = 0; ox = oy = 0;
width = gdk_window_get_width (gtk_widget_get_window (widget)); window = gtk_widget_get_window (widget);
height = gdk_window_get_height (gtk_widget_get_window (widget)); width = gdk_window_get_width (window);
draw = gtk_widget_get_window (widget); height = gdk_window_get_height (window);
} }
val.subwindow_mode = GDK_INCLUDE_INFERIORS; col.red = (double)rand () / (double)RAND_MAX;
val.graphics_exposures = 0; col.green = (double)rand () / (double)RAND_MAX;
val.function = GDK_XOR; col.blue = (double)rand () / (double)RAND_MAX;
col.alpha = 1.0;
gc = gdk_gc_new_with_values (gtk_widget_get_window (widget), &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW | GDK_GC_FUNCTION); cr = gdk_cairo_create (window);
col.red = rand() % 0xffff; cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
col.green = rand() % 0xffff; mg_set_source_color (cr, &col);
col.blue = rand() % 0xffff; cairo_set_line_width (cr, 1.0);
gdk_colormap_alloc_color (gtk_widget_get_colormap (widget), &col, FALSE, TRUE);
gdk_gc_set_foreground (gc, &col);
half = height / 2; half = height / 2;
#if 0 #if 0
/* are both tree/userlist on the same side? */ /* are both tree/userlist on the same side? */
GtkPaned *paned;
paned = (GtkPaned *)widget->parent->parent; paned = (GtkPaned *)widget->parent->parent;
if (paned->child1 != NULL && paned->child2 != NULL) if (paned->child1 != NULL && paned->child2 != NULL)
{ {
gdk_draw_rectangle (draw, gc, 0, 1, 2, width - 3, height - 4); cairo_rectangle (cr, 1 + ox, 2 + oy, width - 3, height - 4);
gdk_draw_rectangle (draw, gc, 0, 0, 1, width - 1, height - 2); cairo_rectangle (cr, 0 + ox, 1 + oy, width - 1, height - 2);
g_object_unref (gc); cairo_stroke (cr);
cairo_destroy (cr);
return TRUE; return TRUE;
} }
#endif #endif
if (y < half) if (y < half)
{ {
gdk_draw_rectangle (draw, gc, FALSE, 1 + ox, 2 + oy, width - 3, half - 4); cairo_rectangle (cr, 1 + ox, 2 + oy, width - 3, half - 4);
gdk_draw_rectangle (draw, gc, FALSE, 0 + ox, 1 + oy, width - 1, half - 2); cairo_rectangle (cr, 0 + ox, 1 + oy, width - 1, half - 2);
cairo_stroke (cr);
gtk_widget_queue_draw_area (widget, ox, half + oy, width, height - half); gtk_widget_queue_draw_area (widget, ox, half + oy, width, height - half);
} }
else else
{ {
gdk_draw_rectangle (draw, gc, FALSE, 0 + ox, half + 1 + oy, width - 1, half - 2); cairo_rectangle (cr, 0 + ox, half + 1 + oy, width - 1, half - 2);
gdk_draw_rectangle (draw, gc, FALSE, 1 + ox, half + 2 + oy, width - 3, half - 4); cairo_rectangle (cr, 1 + ox, half + 2 + oy, width - 3, half - 4);
cairo_stroke (cr);
gtk_widget_queue_draw_area (widget, ox, oy, width, half); gtk_widget_queue_draw_area (widget, ox, oy, width, half);
} }
g_object_unref (gc); cairo_destroy (cr);
return TRUE; return TRUE;
} }

View File

@@ -38,6 +38,18 @@
#include "../common/cfgfiles.h" #include "../common/cfgfiles.h"
#include "../common/typedef.h" #include "../common/typedef.h"
static XTextColor
palette_color_from_gdk (const GdkColor *color)
{
XTextColor result;
result.red = color->red / 65535.0;
result.green = color->green / 65535.0;
result.blue = color->blue / 65535.0;
result.alpha = 1.0;
return result;
}
GdkColor colors[] = { GdkColor colors[] = {
/* colors for xtext */ /* colors for xtext */
@@ -143,6 +155,18 @@ static const GdkColor dark_colors[MAX_COL + 1] = {
{0, 0xf4f4, 0x4747, 0x4747}, /* 41 COL_SPELL (spellcheck underline) */ {0, 0xf4f4, 0x4747, 0x4747}, /* 41 COL_SPELL (spellcheck underline) */
}; };
void
palette_get_xtext_colors (XTextColor *palette, size_t palette_len)
{
size_t i;
size_t count = palette_len < G_N_ELEMENTS (colors) ? palette_len : G_N_ELEMENTS (colors);
for (i = 0; i < count; i++)
{
palette[i] = palette_color_from_gdk (&colors[i]);
}
}
void void
palette_user_set_color (int idx, const GdkColor *col) palette_user_set_color (int idx, const GdkColor *col)
{ {
@@ -287,20 +311,21 @@ palette_save (void)
char prefname[256]; char prefname[256];
const GdkColor *lightpal = colors; const GdkColor *lightpal = colors;
const GdkColor *darkpal = NULL; const GdkColor *darkpal = NULL;
gboolean dark_mode_active = fe_dark_mode_is_enabled ();
/* If we're currently in dark mode, keep colors.conf's legacy keys as the user's light palette. */ /* If we're currently in dark mode, keep colors.conf's legacy keys as the user's light palette. */
if (prefs.hex_gui_dark_mode && user_colors_valid) if (dark_mode_active && user_colors_valid)
lightpal = user_colors; lightpal = user_colors;
/* If we're currently in light mode, ensure the snapshot stays in sync. */ /* If we're currently in light mode, ensure the snapshot stays in sync. */
if (!prefs.hex_gui_dark_mode) if (!dark_mode_active)
{ {
memcpy (user_colors, colors, sizeof (user_colors)); memcpy (user_colors, colors, sizeof (user_colors));
user_colors_valid = TRUE; user_colors_valid = TRUE;
} }
/* If dark mode is enabled but we haven't snapshotted a custom dark palette yet, capture it now. */ /* If dark mode is enabled but we haven't snapshotted a custom dark palette yet, capture it now. */
if (prefs.hex_gui_dark_mode && !dark_user_colors_valid) if (dark_mode_active && !dark_user_colors_valid)
{ {
memcpy (dark_user_colors, colors, sizeof (dark_user_colors)); memcpy (dark_user_colors, colors, sizeof (dark_user_colors));
dark_user_colors_valid = TRUE; dark_user_colors_valid = TRUE;
@@ -308,7 +333,7 @@ palette_save (void)
if (dark_user_colors_valid) if (dark_user_colors_valid)
darkpal = dark_user_colors; darkpal = dark_user_colors;
else if (prefs.hex_gui_dark_mode) else if (dark_mode_active)
darkpal = colors; /* current dark palette (likely defaults) */ darkpal = colors; /* current dark palette (likely defaults) */
fh = zoitechat_open_file ("colors.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE); fh = zoitechat_open_file ("colors.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
@@ -399,4 +424,3 @@ palette_apply_dark_mode (gboolean enable)
return changed; return changed;
} }

View File

@@ -20,6 +20,10 @@
#ifndef HEXCHAT_PALETTE_H #ifndef HEXCHAT_PALETTE_H
#define HEXCHAT_PALETTE_H #define HEXCHAT_PALETTE_H
#include <stddef.h>
#include "xtext-color.h"
extern GdkColor colors[]; extern GdkColor colors[];
#define COL_MARK_FG 32 #define COL_MARK_FG 32
@@ -59,4 +63,6 @@ void palette_dark_set_color (int idx, const GdkColor *col);
*/ */
gboolean palette_apply_dark_mode (gboolean enable); gboolean palette_apply_dark_mode (gboolean enable);
void palette_get_xtext_colors (XTextColor *palette, size_t palette_len);
#endif #endif

View File

@@ -28,6 +28,7 @@
#include <gio/gio.h> #include <gio/gio.h>
#include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf.h>
#include <cairo.h>
GdkPixbuf *pix_ulist_voice; GdkPixbuf *pix_ulist_voice;
GdkPixbuf *pix_ulist_halfop; GdkPixbuf *pix_ulist_halfop;
@@ -49,26 +50,89 @@ GdkPixbuf *pix_tree_util;
GdkPixbuf *pix_book; GdkPixbuf *pix_book;
GdkPixbuf *pix_zoitechat; GdkPixbuf *pix_zoitechat;
static GdkPixmap * static cairo_surface_t *
pixbuf_to_cairo_surface (GdkPixbuf *pixbuf)
{
cairo_surface_t *surface;
gboolean has_alpha;
int width;
int height;
int src_stride;
int dest_stride;
int n_channels;
const guchar *src_pixels;
unsigned char *dest_pixels;
int x;
int y;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
n_channels = gdk_pixbuf_get_n_channels (pixbuf);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
{
cairo_surface_destroy (surface);
return NULL;
}
src_stride = gdk_pixbuf_get_rowstride (pixbuf);
src_pixels = gdk_pixbuf_get_pixels (pixbuf);
dest_stride = cairo_image_surface_get_stride (surface);
dest_pixels = cairo_image_surface_get_data (surface);
for (y = 0; y < height; y++)
{
const guchar *src_row = src_pixels + (y * src_stride);
guint32 *dest_row = (guint32 *)(dest_pixels + (y * dest_stride));
for (x = 0; x < width; x++)
{
const guchar *src = src_row + (x * n_channels);
guchar alpha = has_alpha ? src[3] : 0xff;
guchar red = src[0];
guchar green = src[1];
guchar blue = src[2];
guchar premul_red = (guchar)((red * alpha + 127) / 255);
guchar premul_green = (guchar)((green * alpha + 127) / 255);
guchar premul_blue = (guchar)((blue * alpha + 127) / 255);
dest_row[x] = ((guint32)alpha << 24) |
((guint32)premul_red << 16) |
((guint32)premul_green << 8) |
((guint32)premul_blue);
}
}
cairo_surface_mark_dirty (surface);
return surface;
}
static cairo_surface_t *
pixmap_load_from_file_real (char *file) pixmap_load_from_file_real (char *file)
{ {
GdkPixbuf *img; GdkPixbuf *img;
GdkPixmap *pixmap; cairo_surface_t *surface;
img = gdk_pixbuf_new_from_file (file, 0); img = gdk_pixbuf_new_from_file (file, 0);
if (!img) if (!img)
return NULL; return NULL;
gdk_pixbuf_render_pixmap_and_mask (img, &pixmap, NULL, 128);
surface = pixbuf_to_cairo_surface (img);
g_object_unref (img); g_object_unref (img);
return pixmap; return surface;
} }
GdkPixmap * cairo_surface_t *
pixmap_load_from_file (char *filename) pixmap_load_from_file (char *filename)
{ {
char buf[256]; char buf[256];
GdkPixmap *pix; cairo_surface_t *pix;
if (filename[0] == '\0') if (filename[0] == '\0')
return NULL; return NULL;

View File

@@ -20,6 +20,8 @@
#ifndef HEXCHAT_PIXMAPS_H #ifndef HEXCHAT_PIXMAPS_H
#define HEXCHAT_PIXMAPS_H #define HEXCHAT_PIXMAPS_H
#include <cairo.h>
extern GdkPixbuf *pix_ulist_voice; extern GdkPixbuf *pix_ulist_voice;
extern GdkPixbuf *pix_ulist_halfop; extern GdkPixbuf *pix_ulist_halfop;
extern GdkPixbuf *pix_ulist_op; extern GdkPixbuf *pix_ulist_op;
@@ -40,7 +42,7 @@ extern GdkPixbuf *pix_tree_util;
extern GdkPixbuf *pix_book; extern GdkPixbuf *pix_book;
extern GdkPixbuf *pix_zoitechat; extern GdkPixbuf *pix_zoitechat;
extern GdkPixmap *pixmap_load_from_file (char *file); extern cairo_surface_t *pixmap_load_from_file (char *file);
extern void pixmaps_init (void); extern void pixmaps_init (void);
#endif #endif

View File

@@ -101,6 +101,7 @@ void
open_rawlog (struct server *serv) open_rawlog (struct server *serv)
{ {
GtkWidget *bbox, *scrolledwindow, *vbox; GtkWidget *bbox, *scrolledwindow, *vbox;
XTextColor xtext_palette[XTEXT_COLS];
char tbuf[256]; char tbuf[256];
if (serv->gui->rawlog_window) if (serv->gui->rawlog_window)
@@ -120,7 +121,8 @@ open_rawlog (struct server *serv)
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (vbox), scrolledwindow); gtk_container_add (GTK_CONTAINER (vbox), scrolledwindow);
serv->gui->rawlog_textlist = gtk_xtext_new (colors, 0); palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
serv->gui->rawlog_textlist = gtk_xtext_new (xtext_palette, 0);
gtk_container_add (GTK_CONTAINER (scrolledwindow), serv->gui->rawlog_textlist); gtk_container_add (GTK_CONTAINER (scrolledwindow), serv->gui->rawlog_textlist);
gtk_xtext_set_font (GTK_XTEXT (serv->gui->rawlog_textlist), prefs.hex_text_font); gtk_xtext_set_font (GTK_XTEXT (serv->gui->rawlog_textlist), prefs.hex_text_font);
GTK_XTEXT (serv->gui->rawlog_textlist)->ignore_hidden = 1; GTK_XTEXT (serv->gui->rawlog_textlist)->ignore_hidden = 1;

View File

@@ -58,7 +58,6 @@ static int last_selected_row = 0; /* sound row */
static gboolean color_change; static gboolean color_change;
static struct zoitechatprefs setup_prefs; static struct zoitechatprefs setup_prefs;
static GSList *color_selector_widgets; static GSList *color_selector_widgets;
static GtkWidget *dark_mode_toggle_widget;
static GtkWidget *cancel_button; static GtkWidget *cancel_button;
static GtkWidget *font_dialog = NULL; static GtkWidget *font_dialog = NULL;
void setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd); void setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd);
@@ -348,22 +347,30 @@ static const setting tabs_settings[] =
static const setting color_settings[] = static const setting color_settings[] =
{ {
{ST_TOGGLE, N_("Messages"), P_OFFINTNL(hex_text_stripcolor_msg), 0, 0, 0}, {ST_TOGGLE, N_("Messages"), P_OFFINTNL(hex_text_stripcolor_msg), 0, 0, 0},
{ST_TOGGLE, N_("Scrollback"), P_OFFINTNL(hex_text_stripcolor_replay), 0, 0, 0}, {ST_TOGGLE, N_("Scrollback"), P_OFFINTNL(hex_text_stripcolor_replay), 0, 0, 0},
{ST_TOGGLE, N_("Topic"), P_OFFINTNL(hex_text_stripcolor_topic), 0, 0, 0}, {ST_TOGGLE, N_("Topic"), P_OFFINTNL(hex_text_stripcolor_topic), 0, 0, 0},
{ST_END, 0, 0, 0, 0, 0} {ST_END, 0, 0, 0, 0, 0}
};
static const char *const dark_mode_modes[] =
{
N_("Auto (system)"),
N_("Dark"),
N_("Light"),
NULL
}; };
static const setting dark_mode_setting = static const setting dark_mode_setting =
{ {
ST_TOGGLE, ST_MENU,
N_("Enable dark mode"), N_("Dark mode:"),
P_OFFINTNL(hex_gui_dark_mode), P_OFFINTNL(hex_gui_dark_mode),
N_("Applies ZoiteChat's built-in dark palette to the chat buffer, channel list, and user list.\n" N_("Choose how ZoiteChat selects its color palette for the chat buffer, channel list, and user list.\n"
"This includes message colors, selection colors, and interface highlights.\n"), "This includes message colors, selection colors, and interface highlights.\n"),
0, dark_mode_modes,
0 0
}; };
static const char *const dccaccept[] = static const char *const dccaccept[] =
@@ -1440,12 +1447,65 @@ setup_color_selectors_set_sensitive (gboolean sensitive)
} }
static void static void
setup_dark_mode_ui_toggle_cb (GtkToggleButton *but, gpointer userdata) setup_dark_mode_menu_cb (GtkWidget *cbox, const setting *set)
{ {
(void) but; setup_menu_cb (cbox, set);
(void) userdata; /* Keep color selectors usable even when dark mode is enabled. */
/* Keep color selectors usable even when dark mode is enabled. */ setup_color_selectors_set_sensitive (TRUE);
setup_color_selectors_set_sensitive (TRUE); }
static GtkWidget *
setup_create_dark_mode_menu (GtkWidget *table, int row, const setting *set)
{
GtkWidget *wid, *cbox, *box;
const char **text = (const char **)set->list;
int i;
wid = gtk_label_new (_(set->label));
gtk_misc_set_alignment (GTK_MISC (wid), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), wid, 2, 3, row, row + 1,
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
cbox = gtk_combo_box_text_new ();
for (i = 0; text[i]; i++)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cbox), _(text[i]));
gtk_combo_box_set_active (GTK_COMBO_BOX (cbox),
setup_get_int (&setup_prefs, set) - set->extra);
g_signal_connect (G_OBJECT (cbox), "changed",
G_CALLBACK (setup_dark_mode_menu_cb), (gpointer)set);
box = gtk_hbox_new (0, 0);
gtk_box_pack_start (GTK_BOX (box), cbox, 0, 0, 0);
gtk_table_attach (GTK_TABLE (table), box, 3, 4, row, row + 1,
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
return cbox;
}
static void
setup_color_button_apply (GtkWidget *button, const GdkColor *color)
{
GtkWidget *target = g_object_get_data (G_OBJECT (button), "zoitechat-color-box");
GtkStateType states[] = {
GTK_STATE_NORMAL,
GTK_STATE_PRELIGHT,
GTK_STATE_ACTIVE,
GTK_STATE_SELECTED,
GTK_STATE_INSENSITIVE
};
guint i;
GtkWidget *apply_widget = GTK_IS_WIDGET (target) ? target : button;
for (i = 0; i < G_N_ELEMENTS (states); i++)
gtk_widget_modify_bg (apply_widget, states[i], color);
if (apply_widget != button)
for (i = 0; i < G_N_ELEMENTS (states); i++)
gtk_widget_modify_bg (button, states[i], color);
gtk_widget_queue_draw (button);
} }
static void static void
@@ -1454,8 +1514,6 @@ setup_color_ok_cb (GtkWidget *button, GtkWidget *dialog)
GtkColorSelectionDialog *cdialog = GTK_COLOR_SELECTION_DIALOG (dialog); GtkColorSelectionDialog *cdialog = GTK_COLOR_SELECTION_DIALOG (dialog);
GdkColor *col; GdkColor *col;
GdkColor old_color; GdkColor old_color;
GtkStyle *style;
col = g_object_get_data (G_OBJECT (button), "c"); col = g_object_get_data (G_OBJECT (button), "c");
old_color = *col; old_color = *col;
@@ -1473,19 +1531,16 @@ setup_color_ok_cb (GtkWidget *button, GtkWidget *dialog)
gdk_colormap_alloc_color (gtk_widget_get_colormap (button), col, TRUE, TRUE); gdk_colormap_alloc_color (gtk_widget_get_colormap (button), col, TRUE, TRUE);
style = gtk_style_new (); setup_color_button_apply (button, col);
style->bg[0] = *col;
gtk_widget_set_style (button, style);
g_object_unref (style);
/* is this line correct?? */ /* is this line correct?? */
gdk_colormap_free_colors (gtk_widget_get_colormap (button), &old_color, 1); gdk_colormap_free_colors (gtk_widget_get_colormap (button), &old_color, 1);
/* Persist custom colors for the palette the user is editing. */ /* Persist custom colors for the palette the user is editing. */
if (setup_prefs.hex_gui_dark_mode) if (fe_dark_mode_is_enabled_for (setup_prefs.hex_gui_dark_mode))
palette_dark_set_color ((int)(col - colors), col); palette_dark_set_color ((int)(col - colors), col);
else else
palette_user_set_color ((int)(col - colors), col); palette_user_set_color ((int)(col - colors), col);
gtk_widget_destroy (dialog); gtk_widget_destroy (dialog);
} }
@@ -1528,26 +1583,41 @@ static void
setup_create_color_button (GtkWidget *table, int num, int row, int col) setup_create_color_button (GtkWidget *table, int num, int row, int col)
{ {
GtkWidget *but; GtkWidget *but;
GtkStyle *style; GtkWidget *label;
GtkWidget *box;
GtkWidget *alignment;
char buf[64]; char buf[64];
if (num > 31) if (num > 31)
strcpy (buf, "<span size=\"x-small\"> </span>"); strcpy (buf, "<span size=\"x-small\">&#x2007;&#x2007;</span>");
else if (num < 10)
sprintf (buf, "<span size=\"x-small\">&#x2007;%d</span>", num);
else else
/* 12345678901 23456789 01 23456789 */ /* 12345678901 23456789 01 23456789 */
sprintf (buf, "<span size=\"x-small\">%d</span>", num); sprintf (buf, "<span size=\"x-small\">%d</span>", num);
but = gtk_button_new_with_label (" "); but = gtk_button_new ();
gtk_label_set_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (but))), buf); label = gtk_label_new (" ");
gtk_label_set_markup (GTK_LABEL (label), buf);
box = gtk_event_box_new ();
gtk_event_box_set_visible_window (GTK_EVENT_BOX (box), TRUE);
gtk_container_add (GTK_CONTAINER (box), label);
gtk_container_add (GTK_CONTAINER (but), box);
alignment = gtk_bin_get_child (GTK_BIN (but));
if (GTK_IS_ALIGNMENT (alignment))
{
gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 1.0, 1.0);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 0, 0);
}
gtk_widget_show (label);
gtk_widget_show (box);
/* win32 build uses this to turn off themeing */ /* win32 build uses this to turn off themeing */
g_object_set_data (G_OBJECT (but), "zoitechat-color", (gpointer)1); g_object_set_data (G_OBJECT (but), "zoitechat-color", (gpointer)1);
g_object_set_data (G_OBJECT (but), "zoitechat-color-box", box);
gtk_table_attach (GTK_TABLE (table), but, col, col+1, row, row+1, gtk_table_attach (GTK_TABLE (table), but, col, col+1, row, row+1,
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_SHRINK, GTK_SHRINK, 0, 0);
g_signal_connect (G_OBJECT (but), "clicked", g_signal_connect (G_OBJECT (but), "clicked",
G_CALLBACK (setup_color_cb), GINT_TO_POINTER (num)); G_CALLBACK (setup_color_cb), GINT_TO_POINTER (num));
style = gtk_style_new (); setup_color_button_apply (but, &colors[num]);
style->bg[GTK_STATE_NORMAL] = colors[num];
gtk_widget_set_style (but, style);
g_object_unref (style);
/* Track all color selector widgets (used for dark mode UI behavior). */ /* Track all color selector widgets (used for dark mode UI behavior). */
color_selector_widgets = g_slist_prepend (color_selector_widgets, but); color_selector_widgets = g_slist_prepend (color_selector_widgets, but);
@@ -1580,11 +1650,10 @@ setup_create_other_color (char *text, int num, int row, GtkWidget *tab)
static GtkWidget * static GtkWidget *
setup_create_color_page (void) setup_create_color_page (void)
{ {
color_selector_widgets = NULL; color_selector_widgets = NULL;
dark_mode_toggle_widget = NULL;
GtkWidget *tab, *box, *label; GtkWidget *tab, *box, *label;
int i; int i;
box = gtk_vbox_new (FALSE, 0); box = gtk_vbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (box), 6); gtk_container_set_border_width (GTK_CONTAINER (box), 6);
@@ -1625,15 +1694,13 @@ setup_create_color_page (void)
setup_create_other_color (_("New data:"), COL_NEW_DATA, 9, tab); setup_create_other_color (_("New data:"), COL_NEW_DATA, 9, tab);
setup_create_other_colorR (_("Marker line:"), COL_MARKER, 9, tab); setup_create_other_colorR (_("Marker line:"), COL_MARKER, 9, tab);
setup_create_other_color (_("New message:"), COL_NEW_MSG, 10, tab); setup_create_other_color (_("New message:"), COL_NEW_MSG, 10, tab);
setup_create_other_colorR (_("Away user:"), COL_AWAY, 10, tab); setup_create_other_colorR (_("Away user:"), COL_AWAY, 10, tab);
setup_create_other_color (_("Highlight:"), COL_HILIGHT, 11, tab); setup_create_other_color (_("Highlight:"), COL_HILIGHT, 11, tab);
setup_create_other_colorR (_("Spell checker:"), COL_SPELL, 11, tab); setup_create_other_colorR (_("Spell checker:"), COL_SPELL, 11, tab);
dark_mode_toggle_widget = setup_create_toggleL (tab, 13, &dark_mode_setting); setup_create_dark_mode_menu (tab, 13, &dark_mode_setting);
g_signal_connect (G_OBJECT (dark_mode_toggle_widget), "toggled", setup_color_selectors_set_sensitive (TRUE);
G_CALLBACK (setup_dark_mode_ui_toggle_cb), NULL); setup_create_header (tab, 15, N_("Color Stripping"));
setup_color_selectors_set_sensitive (TRUE);
setup_create_header (tab, 15, N_("Color Stripping"));
/* label = gtk_label_new (_("Strip colors from:")); /* label = gtk_label_new (_("Strip colors from:"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
@@ -1809,9 +1876,9 @@ setup_theme_apply_cb (GtkWidget *button, gpointer user_data)
g_unlink (events_dest); g_unlink (events_dest);
} }
palette_load (); palette_load ();
palette_apply_dark_mode (prefs.hex_gui_dark_mode); palette_apply_dark_mode (fe_dark_mode_is_enabled ());
color_change = TRUE; color_change = TRUE;
setup_apply_real (0, TRUE, FALSE, FALSE); setup_apply_real (0, TRUE, FALSE, FALSE);
setup_theme_show_message (GTK_MESSAGE_INFO, _("Theme applied. Some changes may require a restart to take full effect.")); setup_theme_show_message (GTK_MESSAGE_INFO, _("Theme applied. Some changes may require a restart to take full effect."));
@@ -2372,15 +2439,15 @@ setup_apply_to_sess (session_gui *gui)
chanview_apply_theme ((chanview *) gui->chanview); chanview_apply_theme ((chanview *) gui->chanview);
if (prefs.hex_gui_ulist_style) if (prefs.hex_gui_ulist_style)
gtk_widget_set_style (gui->user_tree, input_style); gtk_widget_modify_font (gui->user_tree, input_style->font_desc);
if (prefs.hex_gui_ulist_style || prefs.hex_gui_dark_mode) if (prefs.hex_gui_ulist_style || fe_dark_mode_is_enabled ())
{ {
gtk_widget_modify_base (gui->user_tree, GTK_STATE_NORMAL, &colors[COL_BG]); gtk_widget_modify_base (gui->user_tree, GTK_STATE_NORMAL, &colors[COL_BG]);
if (prefs.hex_gui_dark_mode) if (fe_dark_mode_is_enabled ())
gtk_widget_modify_text (gui->user_tree, GTK_STATE_NORMAL, &colors[COL_FG]); gtk_widget_modify_text (gui->user_tree, GTK_STATE_NORMAL, &colors[COL_FG]);
else else
gtk_widget_modify_text (gui->user_tree, GTK_STATE_NORMAL, NULL); gtk_widget_modify_text (gui->user_tree, GTK_STATE_NORMAL, NULL);
} }
else else
{ {
@@ -2449,7 +2516,7 @@ setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd)
if (new_pix) if (new_pix)
{ {
if (channelwin_pix) if (channelwin_pix)
g_object_unref (channelwin_pix); cairo_surface_destroy (channelwin_pix);
channelwin_pix = pixmap_load_from_file (prefs.hex_text_background); channelwin_pix = pixmap_load_from_file (prefs.hex_text_background);
} }
@@ -2579,14 +2646,17 @@ setup_apply (struct zoitechatprefs *pr)
* "Dark mode" applies ZoiteChat's built-in dark palette to the chat views. * "Dark mode" applies ZoiteChat's built-in dark palette to the chat views.
* *
* IMPORTANT: don't short-circuit this call. * IMPORTANT: don't short-circuit this call.
* We MUST run palette_apply_dark_mode() when the toggle changes, otherwise * We MUST run palette_apply_dark_mode() when the setting changes, otherwise
* the preference flips but the palette stays the same (aka: "nothing happens"). * the preference flips but the palette stays the same (aka: "nothing happens").
*/ */
{ {
gboolean pal_changed = palette_apply_dark_mode (prefs.hex_gui_dark_mode); gboolean pal_changed = palette_apply_dark_mode (fe_dark_mode_is_enabled_for (prefs.hex_gui_dark_mode));
if (prefs.hex_gui_dark_mode != old_dark_mode || pal_changed) if (prefs.hex_gui_dark_mode != old_dark_mode || pal_changed)
color_change = TRUE; color_change = TRUE;
} }
if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO)
fe_set_auto_dark_mode_state (fe_dark_mode_is_enabled_for (ZOITECHAT_DARK_MODE_AUTO));
#ifdef WIN32 #ifdef WIN32
/* merge hex_font_main and hex_font_alternative into hex_font_normal */ /* merge hex_font_main and hex_font_alternative into hex_font_normal */
@@ -2683,12 +2753,11 @@ setup_close_cb (GtkWidget *win, GtkWidget **swin)
{ {
*swin = NULL; *swin = NULL;
if (color_selector_widgets) if (color_selector_widgets)
{ {
g_slist_free (color_selector_widgets); g_slist_free (color_selector_widgets);
color_selector_widgets = NULL; color_selector_widgets = NULL;
dark_mode_toggle_widget = NULL; }
}
if (font_dialog) if (font_dialog)
{ {

View File

@@ -438,6 +438,7 @@ void
pevent_dialog_show () pevent_dialog_show ()
{ {
GtkWidget *vbox, *hbox, *wid, *pane; GtkWidget *vbox, *hbox, *wid, *pane;
XTextColor xtext_palette[XTEXT_COLS];
if (pevent_dialog) if (pevent_dialog)
{ {
@@ -462,7 +463,8 @@ pevent_dialog_show ()
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (wid), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (wid), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
gtk_box_pack_start (GTK_BOX (vbox), wid, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), wid, FALSE, TRUE, 0);
pevent_dialog_twid = gtk_xtext_new (colors, 0); palette_get_xtext_colors (xtext_palette, XTEXT_COLS);
pevent_dialog_twid = gtk_xtext_new (xtext_palette, 0);
gtk_widget_set_sensitive (pevent_dialog_twid, FALSE); gtk_widget_set_sensitive (pevent_dialog_twid, FALSE);
gtk_widget_set_size_request (pevent_dialog_twid, -1, 75); gtk_widget_set_size_request (pevent_dialog_twid, -1, 75);
gtk_container_add (GTK_CONTAINER (wid), pevent_dialog_twid); gtk_container_add (GTK_CONTAINER (wid), pevent_dialog_twid);

20
src/fe-gtk/xtext-color.h Normal file
View File

@@ -0,0 +1,20 @@
/* ZoiteChat
*
* 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.
*/
#ifndef HEXCHAT_XTEXT_COLOR_H
#define HEXCHAT_XTEXT_COLOR_H
typedef struct
{
double red;
double green;
double blue;
double alpha;
} XTextColor;
#endif

View File

@@ -54,13 +54,16 @@
#include <windows.h> #include <windows.h>
#include <io.h> #include <io.h>
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include <gdk/gdkcairo.h>
#include <gdk/gdkwin32.h> #include <gdk/gdkwin32.h>
#else #else
#include <unistd.h> #include <unistd.h>
#include <gdk/gdkcairo.h>
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h> #include <gdk/gdkx.h>
#endif #endif
#endif #endif
#include <pango/pangocairo.h>
/* is delimiter */ /* is delimiter */
#define is_del(c) \ #define is_del(c) \
@@ -147,7 +150,109 @@ static void gtk_xtext_search_fini (xtext_buffer *);
static gboolean gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr); static gboolean gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr);
static char * gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent, int *ret_off, int *ret_len, GSList **slp); static char * gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent, int *ret_off, int *ret_len, GSList **slp);
#define xtext_draw_bg(xt,x,y,w,h) gdk_draw_rectangle(xt->draw_buf, xt->bgc, 1, x, y, w, h); static inline void
xtext_set_source_color (cairo_t *cr, const XTextColor *color, gdouble alpha)
{
cairo_set_source_rgba (cr, color->red, color->green,
color->blue, color->alpha * alpha);
}
static cairo_surface_t *
xtext_surface_from_window (GdkWindow *window)
{
cairo_surface_t *surface = NULL;
int width;
int height;
cairo_t *cr;
if (!window)
return NULL;
width = gdk_window_get_width (window);
height = gdk_window_get_height (window);
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);
return surface;
}
static cairo_t *
xtext_create_context (GtkXText *xtext)
{
if (xtext->draw_surface)
return cairo_create (xtext->draw_surface);
return gdk_cairo_create (xtext->draw_window);
}
static inline void
xtext_draw_rectangle (GtkXText *xtext, cairo_t *cr, const XTextColor *color, int x, int y, int width, int height)
{
cairo_save (cr);
xtext_set_source_color (cr, color, 1.0);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_rectangle (cr, (double)x, (double)y, (double)width, (double)height);
cairo_fill (cr);
cairo_restore (cr);
}
static inline void
xtext_draw_line (GtkXText *xtext, cairo_t *cr, const XTextColor *color, int x1, int y1, int x2, int y2)
{
cairo_save (cr);
/* Disable antialiasing for crispy 1-pixel lines */
cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
xtext_set_source_color (cr, color, 1.0);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_set_line_width (cr, 1.0);
cairo_move_to (cr, x1, y1);
cairo_line_to (cr, x2, y2);
cairo_stroke (cr);
cairo_restore (cr);
}
static inline void
xtext_draw_bg_offset (GtkXText *xtext, int x, int y, int width, int height, int tile_x, int tile_y)
{
cairo_t *cr = xtext_create_context (xtext);
if (xtext->background_surface)
{
cairo_set_source_surface (cr, xtext->background_surface, tile_x, tile_y);
cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
cairo_rectangle (cr, (double)x, (double)y, (double)width, (double)height);
cairo_fill (cr);
}
else
{
xtext_draw_rectangle (xtext, cr, &xtext->bgc, x, y, width, height);
}
cairo_destroy (cr);
}
static inline void
xtext_draw_bg (GtkXText *xtext, int x, int y, int width, int height)
{
xtext_draw_bg_offset (xtext, x, y, width, height, xtext->ts_x, xtext->ts_y);
}
/* ======================================= */ /* ======================================= */
/* ============ PANGO BACKEND ============ */ /* ============ PANGO BACKEND ============ */
@@ -354,77 +459,53 @@ backend_get_text_width_slp (GtkXText *xtext, guchar *str, GSList *slp)
return width; return width;
} }
/* simplified version of gdk_draw_layout_line_with_colors() */
static void static void
xtext_draw_layout_line (GdkDrawable *drawable, backend_draw_text_emph (GtkXText *xtext, gboolean dofill, int x, int y,
GdkGC *gc,
gint x,
gint y,
PangoLayoutLine *line)
{
GSList *tmp_list = line->runs;
PangoRectangle logical_rect;
gint x_off = 0;
while (tmp_list)
{
PangoLayoutRun *run = tmp_list->data;
pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
NULL, &logical_rect);
gdk_draw_glyphs (drawable, gc, run->item->analysis.font,
x + x_off / PANGO_SCALE, y, run->glyphs);
x_off += logical_rect.width;
tmp_list = tmp_list->next;
}
}
static void
backend_draw_text_emph (GtkXText *xtext, int dofill, GdkGC *gc, int x, int y,
char *str, int len, int str_width, int emphasis) char *str, int len, int str_width, int emphasis)
{ {
GdkGCValues val; cairo_t *cr;
GdkColor col;
PangoLayoutLine *line; PangoLayoutLine *line;
cr = xtext_create_context (xtext);
pango_layout_set_attributes (xtext->layout, attr_lists[emphasis]); pango_layout_set_attributes (xtext->layout, attr_lists[emphasis]);
pango_layout_set_text (xtext->layout, str, len); pango_layout_set_text (xtext->layout, str, len);
if (dofill) if (dofill)
{ {
gdk_gc_get_values (gc, &val); xtext_draw_rectangle (xtext, cr, &xtext->bgc, x, y -
col.pixel = val.background.pixel;
gdk_gc_set_foreground (gc, &col);
gdk_draw_rectangle (xtext->draw_buf, gc, 1, x, y -
xtext->font->ascent, str_width, xtext->fontsize); xtext->font->ascent, str_width, xtext->fontsize);
col.pixel = val.foreground.pixel;
gdk_gc_set_foreground (gc, &col);
} }
line = pango_layout_get_lines (xtext->layout)->data; xtext_set_source_color (cr, &xtext->fgc, 1.0);
line = pango_layout_get_line_readonly (xtext->layout, 0);
xtext_draw_layout_line (xtext->draw_buf, gc, x, y, line); cairo_save (cr);
cairo_move_to (cr, x, y);
pango_cairo_show_layout_line (cr, line);
cairo_restore (cr);
cairo_destroy (cr);
} }
static void static void
xtext_set_fg (GtkXText *xtext, GdkGC *gc, int index) xtext_set_fg (GtkXText *xtext, int index)
{ {
gdk_gc_set_foreground (gc, &xtext->palette[index]); xtext->fgc = xtext->palette[index];
} }
static void static void
xtext_set_bg (GtkXText *xtext, GdkGC *gc, int index) xtext_set_bg (GtkXText *xtext, int index)
{ {
gdk_gc_set_background (gc, &xtext->palette[index]); xtext->bgc = xtext->palette[index];
} }
static void static void
gtk_xtext_init (GtkXText * xtext) gtk_xtext_init (GtkXText * xtext)
{ {
xtext->pixmap = NULL; xtext->background_surface = NULL;
xtext->draw_window = NULL;
xtext->draw_surface = NULL;
xtext->io_tag = 0; xtext->io_tag = 0;
xtext->add_io_tag = 0; xtext->add_io_tag = 0;
xtext->scroll_tag = 0; xtext->scroll_tag = 0;
@@ -545,7 +626,7 @@ gtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext)
} }
GtkWidget * GtkWidget *
gtk_xtext_new (GdkColor palette[], int separator) gtk_xtext_new (const XTextColor *palette, int separator)
{ {
GtkXText *xtext; GtkXText *xtext;
@@ -584,10 +665,10 @@ gtk_xtext_destroy (GtkObject * object)
xtext->io_tag = 0; xtext->io_tag = 0;
} }
if (xtext->pixmap) if (xtext->background_surface)
{ {
g_object_unref (xtext->pixmap); cairo_surface_destroy (xtext->background_surface);
xtext->pixmap = NULL; xtext->background_surface = NULL;
} }
if (xtext->font) if (xtext->font)
@@ -605,41 +686,6 @@ gtk_xtext_destroy (GtkObject * object)
xtext->adj = NULL; xtext->adj = NULL;
} }
if (xtext->bgc)
{
g_object_unref (xtext->bgc);
xtext->bgc = NULL;
}
if (xtext->fgc)
{
g_object_unref (xtext->fgc);
xtext->fgc = NULL;
}
if (xtext->light_gc)
{
g_object_unref (xtext->light_gc);
xtext->light_gc = NULL;
}
if (xtext->dark_gc)
{
g_object_unref (xtext->dark_gc);
xtext->dark_gc = NULL;
}
if (xtext->thin_gc)
{
g_object_unref (xtext->thin_gc);
xtext->thin_gc = NULL;
}
if (xtext->marker_gc)
{
g_object_unref (xtext->marker_gc);
xtext->marker_gc = NULL;
}
if (xtext->hand_cursor) if (xtext->hand_cursor)
{ {
@@ -680,9 +726,6 @@ gtk_xtext_realize (GtkWidget * widget)
{ {
GtkXText *xtext; GtkXText *xtext;
GdkWindowAttr attributes; GdkWindowAttr attributes;
GdkGCValues val;
GdkColor col;
GdkColormap *cmap;
gtk_widget_set_realized (widget, TRUE); gtk_widget_set_realized (widget, TRUE);
xtext = GTK_XTEXT (widget); xtext = GTK_XTEXT (widget);
@@ -697,8 +740,7 @@ gtk_xtext_realize (GtkWidget * widget)
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK; | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK;
cmap = gtk_widget_get_colormap (widget); attributes.colormap = gtk_widget_get_colormap (widget);
attributes.colormap = cmap;
attributes.visual = gtk_widget_get_visual (widget); attributes.visual = gtk_widget_get_visual (widget);
widget->window = gdk_window_new (widget->parent->window, &attributes, widget->window = gdk_window_new (widget->parent->window, &attributes,
@@ -709,60 +751,42 @@ gtk_xtext_realize (GtkWidget * widget)
xtext->depth = gdk_window_get_visual (widget->window)->depth; xtext->depth = gdk_window_get_visual (widget->window)->depth;
val.subwindow_mode = GDK_INCLUDE_INFERIORS;
val.graphics_exposures = 0;
xtext->bgc = gdk_gc_new_with_values (widget->window, &val,
GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
xtext->fgc = gdk_gc_new_with_values (widget->window, &val,
GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
xtext->light_gc = gdk_gc_new_with_values (widget->window, &val,
GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
xtext->dark_gc = gdk_gc_new_with_values (widget->window, &val,
GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
xtext->thin_gc = gdk_gc_new_with_values (widget->window, &val,
GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
xtext->marker_gc = gdk_gc_new_with_values (widget->window, &val,
GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
/* for the separator bar (light) */ /* for the separator bar (light) */
col.red = 0xffff; col.green = 0xffff; col.blue = 0xffff; xtext->light_gc.red = 1.0;
gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE); xtext->light_gc.green = 1.0;
gdk_gc_set_foreground (xtext->light_gc, &col); xtext->light_gc.blue = 1.0;
xtext->light_gc.alpha = 1.0;
/* for the separator bar (dark) */ /* for the separator bar (dark) */
col.red = 0x1111; col.green = 0x1111; col.blue = 0x1111; xtext->dark_gc.red = 0x1111 / 65535.0;
gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE); xtext->dark_gc.green = 0x1111 / 65535.0;
gdk_gc_set_foreground (xtext->dark_gc, &col); xtext->dark_gc.blue = 0x1111 / 65535.0;
xtext->dark_gc.alpha = 1.0;
/* for the separator bar (thinline) */ /* for the separator bar (thinline) */
col.red = 0x8e38; col.green = 0x8e38; col.blue = 0x9f38; xtext->thin_gc.red = 0x8e38 / 65535.0;
gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE); xtext->thin_gc.green = 0x8e38 / 65535.0;
gdk_gc_set_foreground (xtext->thin_gc, &col); xtext->thin_gc.blue = 0x9f38 / 65535.0;
xtext->thin_gc.alpha = 1.0;
/* for the marker bar (marker) */ /* for the marker bar (marker) */
gdk_gc_set_foreground (xtext->marker_gc, &xtext->palette[XTEXT_MARKER]); xtext->marker_gc = xtext->palette[XTEXT_MARKER];
xtext_set_fg (xtext, xtext->fgc, XTEXT_FG); xtext_set_fg (xtext, XTEXT_FG);
xtext_set_bg (xtext, xtext->fgc, XTEXT_BG); xtext_set_bg (xtext, XTEXT_BG);
xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
/* draw directly to window */ /* draw directly to window */
xtext->draw_buf = widget->window; xtext->draw_window = widget->window;
if (xtext->pixmap) if (xtext->background_surface)
{ {
gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
xtext->ts_x = xtext->ts_y = 0; xtext->ts_x = xtext->ts_y = 0;
gdk_gc_set_fill (xtext->bgc, GDK_TILED);
} }
xtext->hand_cursor = gdk_cursor_new_for_display (gdk_window_get_display (widget->window), GDK_HAND1); xtext->hand_cursor = gdk_cursor_new_for_display (gdk_window_get_display (widget->window), GDK_HAND1);
xtext->resize_cursor = gdk_cursor_new_for_display (gdk_window_get_display (widget->window), GDK_LEFT_SIDE); xtext->resize_cursor = gdk_cursor_new_for_display (gdk_window_get_display (widget->window), GDK_LEFT_SIDE);
gdk_window_set_back_pixmap (widget->window, NULL, FALSE); gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
widget->style = gtk_style_attach (widget->style, widget->window);
backend_init (xtext); backend_init (xtext);
} }
@@ -971,7 +995,7 @@ static void
gtk_xtext_draw_sep (GtkXText * xtext, int y) gtk_xtext_draw_sep (GtkXText * xtext, int y)
{ {
int x, height; int x, height;
GdkGC *light, *dark; cairo_t *cr;
if (y == -1) if (y == -1)
{ {
@@ -985,31 +1009,37 @@ gtk_xtext_draw_sep (GtkXText * xtext, int y)
/* draw the separator line */ /* draw the separator line */
if (xtext->separator && xtext->buffer->indent) if (xtext->separator && xtext->buffer->indent)
{ {
light = xtext->light_gc; const XTextColor *light = &xtext->light_gc;
dark = xtext->dark_gc; const XTextColor *dark = &xtext->dark_gc;
cr = xtext_create_context (xtext);
x = xtext->buffer->indent - ((xtext->space_width + 1) / 2); x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
if (x < 1) if (x < 1)
{
cairo_destroy (cr);
return; return;
}
if (xtext->thinline) if (xtext->thinline)
{ {
if (xtext->moving_separator) if (xtext->moving_separator)
gdk_draw_line (xtext->draw_buf, light, x, y, x, y + height); xtext_draw_line (xtext, cr, light, x, y, x, y + height);
else else
gdk_draw_line (xtext->draw_buf, xtext->thin_gc, x, y, x, y + height); xtext_draw_line (xtext, cr, &xtext->thin_gc, x, y, x, y + height);
} else } else
{ {
if (xtext->moving_separator) if (xtext->moving_separator)
{ {
gdk_draw_line (xtext->draw_buf, light, x - 1, y, x - 1, y + height); xtext_draw_line (xtext, cr, light, x - 1, y, x - 1, y + height);
gdk_draw_line (xtext->draw_buf, dark, x, y, x, y + height); xtext_draw_line (xtext, cr, dark, x, y, x, y + height);
} else } else
{ {
gdk_draw_line (xtext->draw_buf, dark, x - 1, y, x - 1, y + height); xtext_draw_line (xtext, cr, dark, x - 1, y, x - 1, y + height);
gdk_draw_line (xtext->draw_buf, light, x, y, x, y + height); xtext_draw_line (xtext, cr, light, x, y, x, y + height);
} }
} }
cairo_destroy (cr);
} }
} }
@@ -1017,6 +1047,7 @@ static void
gtk_xtext_draw_marker (GtkXText * xtext, textentry * ent, int y) gtk_xtext_draw_marker (GtkXText * xtext, textentry * ent, int y)
{ {
int x, width, render_y; int x, width, render_y;
cairo_t *cr;
if (!xtext->marker) return; if (!xtext->marker) return;
@@ -1033,7 +1064,9 @@ gtk_xtext_draw_marker (GtkXText * xtext, textentry * ent, int y)
x = 0; x = 0;
width = GTK_WIDGET (xtext)->allocation.width; width = GTK_WIDGET (xtext)->allocation.width;
gdk_draw_line (xtext->draw_buf, xtext->marker_gc, x, render_y, x + width, render_y); cr = xtext_create_context (xtext);
xtext_draw_line (xtext, cr, &xtext->marker_gc, x, render_y, x + width, render_y);
cairo_destroy (cr);
if (gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (xtext))))) if (gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (xtext)))))
{ {
@@ -2551,11 +2584,13 @@ gtk_xtext_text_width (GtkXText *xtext, unsigned char *text, int len)
static int static int
gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str, gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
int len, GdkGC *gc, int *emphasis) int len, int *emphasis)
{ {
int str_width, dofill; int str_width, dofill;
GdkDrawable *pix = NULL; cairo_surface_t *surface = NULL;
int dest_x = 0, dest_y = 0; int dest_x = 0, dest_y = 0;
int tile_x = xtext->ts_x;
int tile_y = xtext->ts_y;
if (xtext->dont_render || len < 1 || xtext->hidden) if (xtext->dont_render || len < 1 || xtext->hidden)
return 0; return 0;
@@ -2579,39 +2614,40 @@ gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
goto dounder; goto dounder;
} }
pix = gdk_pixmap_new (xtext->draw_buf, str_width, xtext->fontsize, xtext->depth); surface = gdk_window_create_similar_surface (GTK_WIDGET (xtext)->window,
if (pix) CAIRO_CONTENT_COLOR_ALPHA, str_width, xtext->fontsize);
if (surface)
{ {
dest_x = x; dest_x = x;
dest_y = y - xtext->font->ascent; dest_y = y - xtext->font->ascent;
tile_x = xtext->ts_x - x;
gdk_gc_set_ts_origin (xtext->bgc, xtext->ts_x - x, xtext->ts_y - dest_y); tile_y = xtext->ts_y - dest_y;
x = 0; x = 0;
y = xtext->font->ascent; y = xtext->font->ascent;
xtext->draw_buf = pix; xtext->draw_surface = surface;
} }
dofill = TRUE; dofill = TRUE;
/* backcolor is always handled by XDrawImageString */ /* backcolor is always handled by XDrawImageString */
if (!xtext->backcolor && xtext->pixmap) if (!xtext->backcolor && xtext->background_surface)
{ {
/* draw the background pixmap behind the text - CAUSES FLICKER HERE!! */ /* draw the background surface behind the text - CAUSES FLICKER HERE!! */
xtext_draw_bg (xtext, x, y - xtext->font->ascent, str_width, xtext_draw_bg_offset (xtext, x, y - xtext->font->ascent, str_width,
xtext->fontsize); xtext->fontsize, tile_x, tile_y);
dofill = FALSE; /* already drawn the background */ dofill = FALSE; /* already drawn the background */
} }
backend_draw_text_emph (xtext, dofill, gc, x, y, str, len, str_width, *emphasis); backend_draw_text_emph (xtext, dofill, x, y, str, len, str_width, *emphasis);
if (pix) if (surface)
{ {
GdkRectangle clip; GdkRectangle clip;
GdkRectangle dest; GdkRectangle dest;
cairo_t *cr;
gdk_gc_set_ts_origin (xtext->bgc, xtext->ts_x, xtext->ts_y); xtext->draw_surface = NULL;
xtext->draw_buf = GTK_WIDGET (xtext)->window;
clip.x = xtext->clip_x; clip.x = xtext->clip_x;
clip.y = xtext->clip_y; clip.y = xtext->clip_y;
clip.width = xtext->clip_x2 - xtext->clip_x; clip.width = xtext->clip_x2 - xtext->clip_x;
@@ -2624,24 +2660,36 @@ gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
if (gdk_rectangle_intersect (&clip, &dest, &dest)) if (gdk_rectangle_intersect (&clip, &dest, &dest))
/* dump the DB to window, but only within the clip_x/x2/y/y2 */ /* dump the DB to window, but only within the clip_x/x2/y/y2 */
gdk_draw_drawable (xtext->draw_buf, xtext->bgc, pix, {
dest.x - dest_x, dest.y - dest_y, cr = xtext_create_context (xtext);
dest.x, dest.y, dest.width, dest.height); cairo_save (cr);
g_object_unref (pix); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface (cr, surface, dest_x, dest_y);
cairo_rectangle (cr, dest.x, dest.y, dest.width, dest.height);
cairo_fill (cr);
cairo_restore (cr);
cairo_destroy (cr);
}
cairo_surface_destroy (surface);
} }
if (xtext->strikethrough) if (xtext->strikethrough)
{ {
cairo_t *cr;
/* pango_attr_strikethrough_new does not render in the custom widget so we need to reinvent the wheel */ /* pango_attr_strikethrough_new does not render in the custom widget so we need to reinvent the wheel */
y = dest_y + (xtext->fontsize / 2); y = dest_y + (xtext->fontsize / 2);
gdk_draw_line (xtext->draw_buf, gc, dest_x, y, dest_x + str_width - 1, y); cr = xtext_create_context (xtext);
xtext_draw_line (xtext, cr, &xtext->fgc, dest_x, y, dest_x + str_width - 1, y);
cairo_destroy (cr);
} }
if (xtext->underline) if (xtext->underline)
{ {
dounder: dounder:
{
cairo_t *cr;
if (pix) if (surface)
y = dest_y + xtext->font->ascent + 1; y = dest_y + xtext->font->ascent + 1;
else else
{ {
@@ -2649,7 +2697,10 @@ dounder:
dest_x = x; dest_x = x;
} }
/* draw directly to window, it's out of the range of our DB */ /* draw directly to window, it's out of the range of our DB */
gdk_draw_line (xtext->draw_buf, gc, dest_x, y, dest_x + str_width - 1, y); cr = xtext_create_context (xtext);
xtext_draw_line (xtext, cr, &xtext->fgc, dest_x, y, dest_x + str_width - 1, y);
cairo_destroy (cr);
}
} }
return str_width; return str_width;
@@ -2668,9 +2719,9 @@ gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
{ {
xtext->backcolor = FALSE; xtext->backcolor = FALSE;
if (xtext->col_fore != XTEXT_FG) if (xtext->col_fore != XTEXT_FG)
xtext_set_fg (xtext, xtext->fgc, XTEXT_FG); xtext_set_fg (xtext, XTEXT_FG);
if (xtext->col_back != XTEXT_BG) if (xtext->col_back != XTEXT_BG)
xtext_set_bg (xtext, xtext->fgc, XTEXT_BG); xtext_set_bg (xtext, XTEXT_BG);
} }
xtext->col_fore = XTEXT_FG; xtext->col_fore = XTEXT_FG;
xtext->col_back = XTEXT_BG; xtext->col_back = XTEXT_BG;
@@ -2738,14 +2789,13 @@ gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off)
/* render a single line, which WONT wrap, and parse mIRC colors */ /* render a single line, which WONT wrap, and parse mIRC colors */
#define RENDER_FLUSH x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, emphasis) #define RENDER_FLUSH x += gtk_xtext_render_flush (xtext, x, y, pstr, j, emphasis)
static int static int
gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent, gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
unsigned char *str, int len, int win_width, int indent, unsigned char *str, int len, int win_width, int indent,
int line, int left_only, int *x_size_ret, int *emphasis) int line, int left_only, int *x_size_ret, int *emphasis)
{ {
GdkGC *gc;
int i = 0, x = indent, j = 0; int i = 0, x = indent, j = 0;
unsigned char *pstr = str; unsigned char *pstr = str;
int col_num, tmp; int col_num, tmp;
@@ -2760,13 +2810,11 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
offset = str - ent->str; offset = str - ent->str;
gc = xtext->fgc; /* our foreground GC */
if (ent->mark_start != -1 && if (ent->mark_start != -1 &&
ent->mark_start <= i + offset && ent->mark_end > i + offset) ent->mark_start <= i + offset && ent->mark_end > i + offset)
{ {
xtext_set_bg (xtext, gc, XTEXT_MARK_BG); xtext_set_bg (xtext, XTEXT_MARK_BG);
xtext_set_fg (xtext, gc, XTEXT_MARK_FG); xtext_set_fg (xtext, XTEXT_MARK_FG);
xtext->backcolor = TRUE; xtext->backcolor = TRUE;
mark = TRUE; mark = TRUE;
} }
@@ -2840,7 +2888,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
col_num = col_num % XTEXT_MIRC_COLS; col_num = col_num % XTEXT_MIRC_COLS;
xtext->col_fore = col_num; xtext->col_fore = col_num;
if (!mark) if (!mark)
xtext_set_fg (xtext, gc, col_num); xtext_set_fg (xtext, col_num);
} }
} else } else
{ {
@@ -2870,7 +2918,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
else else
xtext->backcolor = TRUE; xtext->backcolor = TRUE;
if (!mark) if (!mark)
xtext_set_bg (xtext, gc, col_num); xtext_set_bg (xtext, col_num);
xtext->col_back = col_num; xtext->col_back = col_num;
} else } else
{ {
@@ -2880,7 +2928,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
if (col_num > XTEXT_MAX_COLOR) if (col_num > XTEXT_MAX_COLOR)
col_num = col_num % XTEXT_MIRC_COLS; col_num = col_num % XTEXT_MIRC_COLS;
if (!mark) if (!mark)
xtext_set_fg (xtext, gc, col_num); xtext_set_fg (xtext, col_num);
xtext->col_fore = col_num; xtext->col_fore = col_num;
} }
xtext->parsing_backcolor = FALSE; xtext->parsing_backcolor = FALSE;
@@ -2904,14 +2952,14 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
{ {
if (k & GTK_MATCH_CUR) if (k & GTK_MATCH_CUR)
{ {
xtext_set_bg (xtext, gc, XTEXT_MARK_BG); xtext_set_bg (xtext, XTEXT_MARK_BG);
xtext_set_fg (xtext, gc, XTEXT_MARK_FG); xtext_set_fg (xtext, XTEXT_MARK_FG);
xtext->backcolor = TRUE; xtext->backcolor = TRUE;
srch_mark = TRUE; srch_mark = TRUE;
} else } else
{ {
xtext_set_bg (xtext, gc, xtext->col_back); xtext_set_bg (xtext, xtext->col_back);
xtext_set_fg (xtext, gc, xtext->col_fore); xtext_set_fg (xtext, xtext->col_fore);
xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE; xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE;
srch_mark = FALSE; srch_mark = FALSE;
} }
@@ -2921,15 +2969,15 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
xtext->underline = (k & GTK_MATCH_CUR)? TRUE: FALSE; xtext->underline = (k & GTK_MATCH_CUR)? TRUE: FALSE;
if (k & (GTK_MATCH_START | GTK_MATCH_MID)) if (k & (GTK_MATCH_START | GTK_MATCH_MID))
{ {
xtext_set_bg (xtext, gc, XTEXT_MARK_BG); xtext_set_bg (xtext, XTEXT_MARK_BG);
xtext_set_fg (xtext, gc, XTEXT_MARK_FG); xtext_set_fg (xtext, XTEXT_MARK_FG);
xtext->backcolor = TRUE; xtext->backcolor = TRUE;
srch_mark = TRUE; srch_mark = TRUE;
} }
if (k & GTK_MATCH_END) if (k & GTK_MATCH_END)
{ {
xtext_set_bg (xtext, gc, xtext->col_back); xtext_set_bg (xtext, xtext->col_back);
xtext_set_fg (xtext, gc, xtext->col_fore); xtext_set_fg (xtext, xtext->col_fore);
xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE; xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE;
srch_mark = FALSE; srch_mark = FALSE;
xtext->underline = FALSE; xtext->underline = FALSE;
@@ -2952,8 +3000,8 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
xtext->col_back = tmp; xtext->col_back = tmp;
if (!mark) if (!mark)
{ {
xtext_set_fg (xtext, gc, xtext->col_fore); xtext_set_fg (xtext, xtext->col_fore);
xtext_set_bg (xtext, gc, xtext->col_back); xtext_set_bg (xtext, xtext->col_back);
} }
if (xtext->col_back != XTEXT_BG) if (xtext->col_back != XTEXT_BG)
xtext->backcolor = TRUE; xtext->backcolor = TRUE;
@@ -3040,7 +3088,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
/* have we been told to stop rendering at this point? */ /* have we been told to stop rendering at this point? */
if (xtext->jump_out_offset > 0 && xtext->jump_out_offset <= (i + offset)) if (xtext->jump_out_offset > 0 && xtext->jump_out_offset <= (i + offset))
{ {
gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, emphasis); gtk_xtext_render_flush (xtext, x, y, pstr, j, emphasis);
ret = 0; /* skip the rest of the lines, we're done. */ ret = 0; /* skip the rest of the lines, we're done. */
j = 0; j = 0;
break; break;
@@ -3074,8 +3122,8 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
RENDER_FLUSH; RENDER_FLUSH;
pstr += j; pstr += j;
j = 0; j = 0;
xtext_set_bg (xtext, gc, XTEXT_MARK_BG); xtext_set_bg (xtext, XTEXT_MARK_BG);
xtext_set_fg (xtext, gc, XTEXT_MARK_FG); xtext_set_fg (xtext, XTEXT_MARK_FG);
xtext->backcolor = TRUE; xtext->backcolor = TRUE;
if (srch_underline) if (srch_underline)
{ {
@@ -3090,8 +3138,8 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
RENDER_FLUSH; RENDER_FLUSH;
pstr += j; pstr += j;
j = 0; j = 0;
xtext_set_bg (xtext, gc, xtext->col_back); xtext_set_bg (xtext, xtext->col_back);
xtext_set_fg (xtext, gc, xtext->col_fore); xtext_set_fg (xtext, xtext->col_fore);
if (xtext->col_back != XTEXT_BG) if (xtext->col_back != XTEXT_BG)
xtext->backcolor = TRUE; xtext->backcolor = TRUE;
else else
@@ -3106,8 +3154,8 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
if (mark || srch_mark) if (mark || srch_mark)
{ {
xtext_set_bg (xtext, gc, xtext->col_back); xtext_set_bg (xtext, xtext->col_back);
xtext_set_fg (xtext, gc, xtext->col_fore); xtext_set_fg (xtext, xtext->col_fore);
if (xtext->col_back != XTEXT_BG) if (xtext->col_back != XTEXT_BG)
xtext->backcolor = TRUE; xtext->backcolor = TRUE;
else else
@@ -3431,7 +3479,7 @@ gtk_xtext_render_line (GtkXText * xtext, textentry * ent, int line,
} }
void void
gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[]) gtk_xtext_set_palette (GtkXText * xtext, const XTextColor *palette)
{ {
int i; int i;
@@ -3442,11 +3490,10 @@ gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[])
if (gtk_widget_get_realized (GTK_WIDGET(xtext))) if (gtk_widget_get_realized (GTK_WIDGET(xtext)))
{ {
xtext_set_fg (xtext, xtext->fgc, XTEXT_FG); xtext_set_fg (xtext, XTEXT_FG);
xtext_set_bg (xtext, xtext->fgc, XTEXT_BG); xtext_set_bg (xtext, XTEXT_BG);
xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
gdk_gc_set_foreground (xtext->marker_gc, &xtext->palette[XTEXT_MARKER]); xtext->marker_gc = xtext->palette[XTEXT_MARKER];
} }
xtext->col_fore = XTEXT_FG; xtext->col_fore = XTEXT_FG;
xtext->col_back = XTEXT_BG; xtext->col_back = XTEXT_BG;
@@ -3530,37 +3577,23 @@ gtk_xtext_set_font (GtkXText *xtext, char *name)
} }
void void
gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap) gtk_xtext_set_background (GtkXText * xtext, cairo_surface_t *surface)
{ {
GdkGCValues val; if (xtext->background_surface)
if (xtext->pixmap)
{ {
g_object_unref (xtext->pixmap); cairo_surface_destroy (xtext->background_surface);
xtext->pixmap = NULL; xtext->background_surface = NULL;
} }
dontscroll (xtext->buffer); dontscroll (xtext->buffer);
xtext->pixmap = pixmap; if (surface)
{
xtext->background_surface = cairo_surface_reference (surface);
}
if (pixmap != 0) if (surface && gtk_widget_get_realized (GTK_WIDGET(xtext)))
{ {
g_object_ref (pixmap); xtext->ts_x = xtext->ts_y = 0;
if (gtk_widget_get_realized (GTK_WIDGET(xtext)))
{
gdk_gc_set_tile (xtext->bgc, pixmap);
gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
xtext->ts_x = xtext->ts_y = 0;
gdk_gc_set_fill (xtext->bgc, GDK_TILED);
}
} else if (gtk_widget_get_realized (GTK_WIDGET(xtext)))
{
g_object_unref (xtext->bgc);
val.subwindow_mode = GDK_INCLUDE_INFERIORS;
val.graphics_exposures = 0;
xtext->bgc = gdk_gc_new_with_values (GTK_WIDGET (xtext)->window,
&val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
} }
} }
@@ -3812,7 +3845,8 @@ gtk_xtext_render_page (GtkXText * xtext)
if (xtext->buffer->indent < MARGIN) if (xtext->buffer->indent < MARGIN)
xtext->buffer->indent = MARGIN; /* 2 pixels is our left margin */ xtext->buffer->indent = MARGIN; /* 2 pixels is our left margin */
gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height); width = gdk_window_get_width (GTK_WIDGET (xtext)->window);
height = gdk_window_get_height (GTK_WIDGET (xtext)->window);
if (width < 34 || height < xtext->fontsize || width < xtext->buffer->indent + 32) if (width < 34 || height < xtext->fontsize || width < xtext->buffer->indent + 32)
return; return;
@@ -3837,30 +3871,70 @@ gtk_xtext_render_page (GtkXText * xtext)
xtext->buffer->last_pixel_pos = pos; xtext->buffer->last_pixel_pos = pos;
#ifndef __APPLE__ #ifndef __APPLE__
if (!xtext->pixmap && abs (overlap) < height) if (!xtext->background_surface && abs (overlap) < height)
{ {
GdkRectangle area; GdkRectangle area;
cairo_t *cr;
int src_x = 0;
int src_y = 0;
int dest_x = 0;
int dest_y = 0;
int copy_height = 0;
/* so the obscured regions are exposed */
gdk_gc_set_exposures (xtext->fgc, TRUE);
if (overlap < 1) /* DOWN */ if (overlap < 1) /* DOWN */
{ {
int remainder; int remainder;
cairo_surface_t *surface;
gdk_draw_drawable (xtext->draw_buf, xtext->fgc, xtext->draw_buf, src_y = -overlap;
0, -overlap, 0, 0, width, height + overlap); dest_y = 0;
copy_height = height + overlap;
cr = xtext_create_context (xtext);
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
surface = xtext_surface_from_window (GTK_WIDGET (xtext)->window);
if (!surface)
{
cairo_restore (cr);
cairo_destroy (cr);
goto full_redraw;
}
cairo_set_source_surface (cr, surface, dest_x - src_x, dest_y - src_y);
cairo_surface_destroy (surface);
cairo_rectangle (cr, dest_x, dest_y, width, copy_height);
cairo_fill (cr);
cairo_restore (cr);
cairo_destroy (cr);
remainder = ((height - xtext->font->descent) % xtext->fontsize) + remainder = ((height - xtext->font->descent) % xtext->fontsize) +
xtext->font->descent; xtext->font->descent;
area.y = (height + overlap) - remainder; area.y = (height + overlap) - remainder;
area.height = remainder - overlap; area.height = remainder - overlap;
} else } else
{ {
gdk_draw_drawable (xtext->draw_buf, xtext->fgc, xtext->draw_buf, cairo_surface_t *surface;
0, 0, 0, overlap, width, height - overlap);
src_y = 0;
dest_y = overlap;
copy_height = height - overlap;
cr = xtext_create_context (xtext);
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
surface = xtext_surface_from_window (GTK_WIDGET (xtext)->window);
if (!surface)
{
cairo_restore (cr);
cairo_destroy (cr);
goto full_redraw;
}
cairo_set_source_surface (cr, surface, dest_x - src_x, dest_y - src_y);
cairo_surface_destroy (surface);
cairo_rectangle (cr, dest_x, dest_y, width, copy_height);
cairo_fill (cr);
cairo_restore (cr);
cairo_destroy (cr);
area.y = 0; area.y = 0;
area.height = overlap; area.height = overlap;
} }
gdk_gc_set_exposures (xtext->fgc, FALSE);
if (area.height > 0) if (area.height > 0)
{ {
@@ -3873,6 +3947,7 @@ gtk_xtext_render_page (GtkXText * xtext)
} }
#endif #endif
full_redraw:
width -= MARGIN; width -= MARGIN;
lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1; lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1;

View File

@@ -21,6 +21,8 @@
#define HEXCHAT_XTEXT_H #define HEXCHAT_XTEXT_H
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <cairo.h>
#include "xtext-color.h"
#define GTK_TYPE_XTEXT (gtk_xtext_get_type ()) #define GTK_TYPE_XTEXT (gtk_xtext_get_type ())
#define GTK_XTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_XTEXT, GtkXText)) #define GTK_XTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_XTEXT, GtkXText))
@@ -130,8 +132,9 @@ struct _GtkXText
xtext_buffer *selection_buffer; xtext_buffer *selection_buffer;
GtkAdjustment *adj; GtkAdjustment *adj;
GdkPixmap *pixmap; /* 0 = use palette[19] */ cairo_surface_t *background_surface; /* 0 = use palette[19] */
GdkDrawable *draw_buf; /* points to ->window */ GdkWindow *draw_window; /* points to ->window */
cairo_surface_t *draw_surface; /* temporary surface for offscreen draws */
GdkCursor *hand_cursor; GdkCursor *hand_cursor;
GdkCursor *resize_cursor; GdkCursor *resize_cursor;
@@ -142,13 +145,13 @@ struct _GtkXText
int last_win_h; int last_win_h;
int last_win_w; int last_win_w;
GdkGC *bgc; /* backing pixmap */ XTextColor bgc; /* text background color */
GdkGC *fgc; /* text foreground color */ XTextColor fgc; /* text foreground color */
GdkGC *light_gc; /* sep bar */ XTextColor light_gc; /* sep bar */
GdkGC *dark_gc; XTextColor dark_gc;
GdkGC *thin_gc; XTextColor thin_gc;
GdkGC *marker_gc; XTextColor marker_gc;
GdkColor palette[XTEXT_COLS]; XTextColor palette[XTEXT_COLS];
gint io_tag; /* for delayed refresh events */ gint io_tag; /* for delayed refresh events */
gint add_io_tag; /* "" when adding new text */ gint add_io_tag; /* "" when adding new text */
@@ -252,15 +255,15 @@ struct _GtkXTextClass
void (*set_scroll_adjustments) (GtkXText *xtext, GtkAdjustment *hadj, GtkAdjustment *vadj); void (*set_scroll_adjustments) (GtkXText *xtext, GtkAdjustment *hadj, GtkAdjustment *vadj);
}; };
GtkWidget *gtk_xtext_new (GdkColor palette[], int separator); GtkWidget *gtk_xtext_new (const XTextColor *palette, int separator);
void gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len, time_t stamp); void gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len, time_t stamp);
void gtk_xtext_append_indent (xtext_buffer *buf, void gtk_xtext_append_indent (xtext_buffer *buf,
unsigned char *left_text, int left_len, unsigned char *left_text, int left_len,
unsigned char *right_text, int right_len, unsigned char *right_text, int right_len,
time_t stamp); time_t stamp);
int gtk_xtext_set_font (GtkXText *xtext, char *name); int gtk_xtext_set_font (GtkXText *xtext, char *name);
void gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap); void gtk_xtext_set_background (GtkXText * xtext, cairo_surface_t *surface);
void gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[]); void gtk_xtext_set_palette (GtkXText * xtext, const XTextColor *palette);
void gtk_xtext_clear (xtext_buffer *buf, int lines); void gtk_xtext_clear (xtext_buffer *buf, int lines);
void gtk_xtext_save (GtkXText * xtext, int fh); void gtk_xtext_save (GtkXText * xtext, int fh);
void gtk_xtext_refresh (GtkXText * xtext); void gtk_xtext_refresh (GtkXText * xtext);