📄 xtext.c
字号:
/* X-Chat * Copyright (C) 1998 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 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * ========================================================================= * * xtext, the text widget used by X-Chat. * * By Peter Zelezny <zed@linux.com>. * Some functions used from Zvt and Eterm (transparency stuff). * */#define XCHAT /* using xchat */#define REFRESH_TIMEOUT 20#define WORDWRAP_LIMIT 24#define TINT_VALUE 195 /* 195/255 of the brightness. */#define MOTION_MONITOR 1 /* URL hilights. */#define MARGIN 2 /* dont touch. */#define SMOOTH_SCROLL#ifdef XCHAT#include "../../config.h" /* can define USE_XLIB here */#else#define USE_XLIB#endif#include <string.h>#include <ctype.h>#include <stdlib.h>#include <time.h>#include <gtk/gtkmain.h>#include <gtk/gtksignal.h>#include <gtk/gtkselection.h>#ifdef USE_XLIB#include <gdk/gdkx.h>#include <X11/Xlib.h>#include <X11/Xatom.h>#endif#include "xtext.h"#ifdef USE_GDK_PIXBUF#include <gdk-pixbuf/gdk-pixbuf.h>#endif#undef GTK_WIDGET#define GTK_WIDGET(n) ((GtkWidget*)n)#undef GTK_OBJECT#define GTK_OBJECT(n) ((GtkObject*)n)#undef GTK_OBJECT_CLASS#define GTK_OBJECT_CLASS(n) ((GtkObjectClass*)n)#ifdef WIN32#define charlen(str) g_utf8_skip[*(guchar *)(str)]#else#define charlen(str) mblen(str, MB_CUR_MAX)#endif/* is delimiter */#define is_del(c) \ (c == ' ' || c == '\n' || c == ')' || c == '(' || \ c == '>' || c == '<' || c == ATTR_RESET || c == ATTR_BOLD || c == 0)static GtkWidgetClass *parent_class = NULL;enum{ WORD_CLICK, LAST_SIGNAL};/* values for selection info */enum{ TARGET_STRING, TARGET_TEXT, TARGET_COMPOUND_TEXT};static guint xtext_signals[LAST_SIGNAL] = { 0 };#ifdef XCHATchar *nocasestrstr (char *text, char *tofind); /* util.c */int get_stamp_str (time_t, char *, int);#endifstatic void gtk_xtext_render_page (GtkXText * xtext);static void gtk_xtext_calc_lines (GtkXText * xtext, int);#ifdef USE_XLIBstatic void gtk_xtext_load_trans (GtkXText * xtext);static void gtk_xtext_free_trans (GtkXText * xtext);#endifstatic textentry *gtk_xtext_nth (GtkXText * xtext, textentry * start_ent, int line, int *subline);static gint gtk_xtext_selection_kill (GtkWidget * widget, GdkEventSelection * event);static void gtk_xtext_selection_get (GtkWidget * widget, GtkSelectionData * selection_data_ptr, guint info, guint time);static int gtk_xtext_text_width (GtkXText * xtext, unsigned char *text, int len, int *mb_ret);static void gtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext);static void gtk_xtext_draw_sep (GtkXText * xtext, int height);static void gtk_xtext_render_ents (GtkXText * xtext, textentry *, textentry *, int);static void gtk_xtext_recalc_widths (GtkXText * xtext, int);static void gtk_xtext_fix_indent (GtkXText * xtext);static char *gtk_xtext_conv_color (unsigned char *text, int len, char *outbuf, int *newlen, int fonttype);static guint gtk_xtext_get_type (void);/* some utility functions first */#ifndef XCHAT /* xchat has this in util.c */static char *nocasestrstr (char *s, char *wanted){ register const size_t len = strlen (wanted); if (len == 0) return (char *)s; while (toupper(*s) != toupper(*wanted) || strncasecmp (s, wanted, len)) if (*s++ == '\0') return (char *)NULL; return (char *)s; }#endifstatic voidxtext_set_fg (GdkGC *gc, gulong pixel){ GdkColor col; col.pixel = pixel; gdk_gc_set_foreground (gc, &col);}static voidxtext_set_bg (GdkGC *gc, gulong pixel){ GdkColor col; col.pixel = pixel; gdk_gc_set_background (gc, &col);}static voidgtk_xtext_init (GtkXText * xtext){ xtext->old_value = -1; xtext->pixmap = NULL; xtext->text_first = NULL; xtext->text_last = NULL; xtext->last_ent_start = NULL; xtext->last_ent_end = NULL; xtext->io_tag = 0; xtext->add_io_tag = 0; xtext->scroll_tag = 0;/* xtext->frozen = 0;*/ xtext->num_lines = 0; xtext->max_lines = 0; xtext->col_back = 19; xtext->col_fore = 18; xtext->nc = 0; xtext->pixel_offset = 0; xtext->scrollbar_down = TRUE; xtext->bold = FALSE; xtext->underline = FALSE; xtext->reverse = FALSE; xtext->time_stamp = FALSE; xtext->font = NULL; xtext->error_function = NULL; xtext->urlcheck_function = NULL; xtext->color_paste = FALSE; xtext->skip_fills = FALSE; xtext->skip_border_fills = FALSE; xtext->skip_stamp = FALSE; xtext->do_underline_fills_only = FALSE; xtext->jump_out_early = FALSE; xtext->recycle = FALSE; xtext->tint_red = xtext->tint_green = xtext->tint_blue = TINT_VALUE; xtext->adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 0, 1, 0, 0); gtk_object_ref ((GtkObject *) xtext->adj); gtk_object_sink ((GtkObject *) xtext->adj); gtk_signal_connect (GTK_OBJECT (xtext->adj), "value_changed", GTK_SIGNAL_FUNC (gtk_xtext_adjustment_changed), xtext); gtk_signal_connect (GTK_OBJECT (xtext), "selection_clear_event", GTK_SIGNAL_FUNC (gtk_xtext_selection_kill), xtext); { static const GtkTargetEntry targets[] = { { "STRING", 0, TARGET_STRING }, { "TEXT", 0, TARGET_TEXT }, { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT } }; static const gint n_targets = sizeof (targets) / sizeof (targets[0]); gtk_selection_add_targets (GTK_WIDGET (xtext), GDK_SELECTION_PRIMARY, targets, n_targets); gtk_selection_add_targets (GTK_WIDGET (xtext), gdk_atom_intern ("CLIPBOARD", FALSE), targets, n_targets); } gtk_signal_connect (GTK_OBJECT (xtext), "selection_get", GTK_SIGNAL_FUNC (gtk_xtext_selection_get), xtext);}static voidgtk_xtext_adjustment_set (GtkXText * xtext, int fire_signal){ GtkAdjustment *adj = xtext->adj; adj->lower = 0; adj->upper = xtext->num_lines; adj->page_size = (GTK_WIDGET (xtext)->allocation.height - xtext->font->descent) / xtext->fontsize; adj->page_increment = adj->page_size; if (adj->value > adj->upper - adj->page_size) adj->value = adj->upper - adj->page_size; if (fire_signal) gtk_adjustment_changed (adj);}static gintgtk_xtext_adjustment_timeout (GtkXText * xtext){ gtk_xtext_render_page (xtext); xtext->io_tag = 0; return 0;}static voidgtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext){/* if (xtext->frozen) return;*/#ifdef SMOOTH_SCROLL if (xtext->old_value != xtext->adj->value)#else if ((int) xtext->old_value != (int) xtext->adj->value)#endif { if (xtext->adj->value >= xtext->adj->upper - xtext->adj->page_size) xtext->scrollbar_down = TRUE; else xtext->scrollbar_down = FALSE; if (xtext->adj->value + 1 == xtext->old_value || xtext->adj->value - 1 == xtext->old_value) /* clicked an arrow? */ { if (xtext->io_tag) { g_source_remove (xtext->io_tag); xtext->io_tag = 0; } gtk_xtext_render_page (xtext); } else { if (!xtext->io_tag) xtext->io_tag = g_timeout_add (REFRESH_TIMEOUT, (GSourceFunc) gtk_xtext_adjustment_timeout, xtext); } } xtext->old_value = adj->value;}GtkWidget *gtk_xtext_new (int indent, int separator){ GtkXText *xtext; xtext = gtk_type_new (gtk_xtext_get_type ()); xtext->indent = indent; xtext->separator = separator; xtext->wordwrap = FALSE; return GTK_WIDGET (xtext);}static voidgtk_xtext_destroy (GtkObject * object){ GtkXText *xtext = GTK_XTEXT (object); textentry *ent, *next; if (xtext->add_io_tag) { g_source_remove (xtext->add_io_tag); xtext->add_io_tag = 0; } if (xtext->scroll_tag) { g_source_remove (xtext->scroll_tag); xtext->scroll_tag = 0; } if (xtext->io_tag) { g_source_remove (xtext->io_tag); xtext->io_tag = 0; } if (xtext->pixmap) {#ifdef USE_XLIB if (xtext->transparent) gtk_xtext_free_trans (xtext); else#endif gdk_pixmap_unref (xtext->pixmap); xtext->pixmap = NULL; } if (xtext->font) { gdk_font_unref (xtext->font); xtext->font = NULL; } if (xtext->adj) { gtk_signal_disconnect_by_data (GTK_OBJECT (xtext->adj), xtext); gtk_object_unref (GTK_OBJECT (xtext->adj)); xtext->adj = NULL; } if (xtext->bgc) { gdk_gc_destroy (xtext->bgc); xtext->bgc = NULL; } if (xtext->fgc) { gdk_gc_destroy (xtext->fgc); xtext->fgc = NULL; } if (xtext->light_gc) { gdk_gc_destroy (xtext->light_gc); xtext->light_gc = NULL; } if (xtext->dark_gc) { gdk_gc_destroy (xtext->dark_gc); xtext->dark_gc = NULL; } if (xtext->hand_cursor) { gdk_cursor_destroy (xtext->hand_cursor); xtext->hand_cursor = NULL; } ent = xtext->text_first; while (ent) { next = ent->next; free (ent); ent = next; } xtext->text_first = NULL; if (GTK_OBJECT_CLASS (parent_class)->destroy) (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);}static voidgtk_xtext_realize (GtkWidget * widget){ GtkXText *xtext; GdkWindowAttr attributes; GdkGCValues val; GdkColor col; GdkColormap *cmap; GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); xtext = GTK_XTEXT (widget); attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK#ifdef MOTION_MONITOR | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK;#else | GDK_POINTER_MOTION_MASK;#endif cmap = gtk_widget_get_colormap (widget); attributes.colormap = cmap; attributes.visual = gtk_widget_get_visual (widget); widget->window = gdk_window_new (widget->parent->window, &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP); gdk_window_set_user_data (widget->window, widget); 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); /* for the separator bar (light) */ col.red = 0xffff; col.green = 0xffff; col.blue = 0xffff; gdk_color_alloc (cmap, &col); gdk_gc_set_foreground (xtext->light_gc, &col); /* for the separator bar (dark) */ col.red = 0x8e38; col.green = 0x8e38; col.blue = 0x9f38; gdk_color_alloc (cmap, &col); gdk_gc_set_foreground (xtext->dark_gc, &col); if (xtext->fonttype != FONT_SET && xtext->font != NULL) gdk_gc_set_font (xtext->fgc, xtext->font); xtext_set_fg (xtext->fgc, xtext->palette[18]); xtext_set_bg (xtext->fgc, xtext->palette[19]); xtext_set_fg (xtext->bgc, xtext->palette[19]);#ifdef USE_XLIB if (xtext->transparent) { gtk_xtext_load_trans (xtext); } else if (xtext->pixmap) { gdk_gc_set_tile (xtext->bgc, xtext->pixmap); gdk_gc_set_ts_origin (xtext->bgc, 0, 0); gdk_gc_set_fill (xtext->bgc, GDK_TILED); }#else if (xtext->pixmap) { gdk_gc_set_tile (xtext->bgc, xtext->pixmap); gdk_gc_set_ts_origin (xtext->bgc, 0, 0); gdk_gc_set_fill (xtext->bgc, GDK_TILED); }#endif xtext->hand_cursor = gdk_cursor_new (GDK_HAND1); gdk_window_set_back_pixmap (widget->window, NULL, FALSE); /* draw directly to window */ xtext->draw_buf = widget->window; if (xtext->auto_indent) xtext->indent = 1;}static voidgtk_xtext_size_request (GtkWidget * widget, GtkRequisition * requisition){ requisition->width = GTK_XTEXT (widget)->fontwidth['Z'] * 20; requisition->height = (GTK_XTEXT (widget)->fontsize * 10) + 3;}static voidgtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation){ GtkXText *xtext = GTK_XTEXT (widget); if (allocation->width == widget->allocation.width && allocation->height == widget->allocation.height) return; widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget)) { gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); gtk_xtext_calc_lines (xtext, FALSE);#ifdef USE_XLIB if (xtext->transparent && xtext->shaded) { gtk_xtext_free_trans (xtext); gtk_xtext_load_trans (xtext); }#endif }}static gintgtk_xtext_idle (GtkXText *xtext){ xtext->skip_exposure = FALSE; return FALSE;}static voidgtk_xtext_draw (GtkWidget * widget, GdkRectangle * area){ GtkXText *xtext = GTK_XTEXT (widget);#ifdef USE_XLIB int x, y; if (xtext->transparent) { gdk_window_get_origin (widget->window, &x, &y); /* update transparency only if it moved */ if (xtext->last_win_x != x || xtext->last_win_y != y) { xtext->last_win_x = x; xtext->last_win_y = y; if (xtext->shaded) { xtext->recycle = TRUE; gtk_xtext_load_trans (xtext); xtext->recycle = FALSE; } else { gtk_xtext_free_trans (xtext); gtk_xtext_load_trans (xtext); } } }#endif gtk_xtext_render_page (xtext); if (xtext->scrollbar_down) gtk_adjustment_set_value (xtext->adj, xtext->adj->upper - xtext->adj->page_size); xtext->skip_exposure = TRUE; gtk_idle_add ((GtkFunction)gtk_xtext_idle, xtext);}static intgtk_xtext_selection_clear (GtkXText * xtext){ textentry *ent; int ret = 0; ent = xtext->last_ent_start; while (ent) { if (ent->mark_start != -1) ret = 1; ent->mark_start = -1; ent->mark_end = -1; if (ent == xtext->last_ent_end) break; ent = ent->next; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -