Initial git import
[sxemacs] / src / ui / Gtk / gtk-xemacs.c
1 /* gtk-xemacs.c
2 **
3 ** Description: A widget to encapsulate a XEmacs 'text widget'
4 **
5 ** Created by: William M. Perry
6 ** Copyright (c) 2000 William M. Perry <wmperry@gnu.org>
7 **
8 */
9
10 #include <config.h>
11
12 #include "lisp.h"
13 #include "console-gtk.h"
14 #include "objects-gtk.h"
15 #include "gtk-xemacs.h"
16 #include "ui/window.h"
17 #include "ui/faces.h"
18
19 extern Lisp_Object Vmodeline_face;
20 extern Lisp_Object Vscrollbar_on_left_p;
21
22 EXFUN(Fmake_image_instance, 4);
23
24 static void gtk_xemacs_class_init(GtkXEmacsClass * klass);
25 static void gtk_xemacs_init(GtkXEmacs * xemacs);
26 static void gtk_xemacs_size_allocate(GtkWidget * widget,
27                                      GtkAllocation * allocaction);
28 static void gtk_xemacs_draw(GtkWidget * widget, GdkRectangle * area);
29 static void gtk_xemacs_paint(GtkWidget * widget, GdkRectangle * area);
30 static void gtk_xemacs_size_request(GtkWidget * widget,
31                                     GtkRequisition * requisition);
32 static void gtk_xemacs_realize(GtkWidget * widget);
33 static void gtk_xemacs_style_set(GtkWidget * widget, GtkStyle * previous_style);
34 static gint gtk_xemacs_expose(GtkWidget * widget, GdkEventExpose * event);
35
36 guint gtk_xemacs_get_type(void)
37 {
38         static guint xemacs_type = 0;
39
40         if (!xemacs_type) {
41                 static const GtkTypeInfo xemacs_info = {
42                         "GtkXEmacs",
43                         sizeof(GtkXEmacs),
44                         sizeof(GtkXEmacsClass),
45                         (GtkClassInitFunc) gtk_xemacs_class_init,
46                         (GtkObjectInitFunc) gtk_xemacs_init,
47                         /* reserved_1 */ NULL,
48                         /* reserved_2 */ NULL,
49                         (GtkClassInitFunc) NULL,
50                 };
51
52                 xemacs_type =
53                     gtk_type_unique(gtk_fixed_get_type(), &xemacs_info);
54         }
55
56         return xemacs_type;
57 }
58
59 static GtkWidgetClass *parent_class;
60
61 extern gint emacs_gtk_button_event_handler(GtkWidget * widget,
62                                            GdkEventButton * event);
63 extern gint emacs_gtk_key_event_handler(GtkWidget * widget,
64                                         GdkEventKey * event);
65 extern gint emacs_gtk_motion_event_handler(GtkWidget * widget,
66                                            GdkEventMotion * event);
67
68 static void gtk_xemacs_class_init(GtkXEmacsClass * class)
69 {
70         GtkWidgetClass *widget_class;
71
72         widget_class = (GtkWidgetClass *) class;
73         parent_class = (GtkWidgetClass *) gtk_type_class(gtk_fixed_get_type());
74
75         widget_class->size_allocate = gtk_xemacs_size_allocate;
76         widget_class->size_request = gtk_xemacs_size_request;
77         widget_class->draw = gtk_xemacs_draw;
78         widget_class->expose_event = gtk_xemacs_expose;
79         widget_class->realize = gtk_xemacs_realize;
80         widget_class->button_press_event = emacs_gtk_button_event_handler;
81         widget_class->button_release_event = emacs_gtk_button_event_handler;
82         widget_class->key_press_event = emacs_gtk_key_event_handler;
83         widget_class->key_release_event = emacs_gtk_key_event_handler;
84         widget_class->motion_notify_event = emacs_gtk_motion_event_handler;
85         widget_class->style_set = gtk_xemacs_style_set;
86 }
87
88 static void gtk_xemacs_init(GtkXEmacs * xemacs)
89 {
90         GTK_WIDGET_SET_FLAGS(xemacs, GTK_CAN_FOCUS);
91 }
92
93 GtkWidget *gtk_xemacs_new(struct frame *f)
94 {
95         GtkXEmacs *xemacs;
96
97         xemacs = gtk_type_new(gtk_xemacs_get_type());
98         xemacs->f = f;
99
100         return GTK_WIDGET(xemacs);
101 }
102
103 static void __nuke_background_items(GtkWidget * widget)
104 {
105         /* This bit of voodoo is here to get around the annoying flicker
106            when GDK tries to futz with our background pixmap as well as
107            XEmacs doing it
108
109            We do NOT set the background of this widget window, that way
110            there is NO flickering, etc.  The downside is the XEmacs frame
111            appears as 'seethru' when XEmacs is too busy to redraw the
112            frame.
113
114            Well, wait, we do... otherwise there sre weird 'seethru' areas
115            even when XEmacs does a full redisplay.  Most noticable in some
116            areas of the modeline, or in the right-hand-side of the window
117            between the scrollbar ad n the edge of the window.
118          */
119         if (widget->window) {
120                 gdk_window_set_back_pixmap(widget->window, NULL, 0);
121                 gdk_window_set_back_pixmap(widget->parent->window, NULL, 0);
122                 gdk_window_set_background(widget->parent->window,
123                                           &widget->style->bg[GTK_STATE_NORMAL]);
124                 gdk_window_set_background(widget->window,
125                                           &widget->style->bg[GTK_STATE_NORMAL]);
126         }
127 }
128
129 extern Lisp_Object xemacs_gtk_convert_color(GdkColor * c, GtkWidget * w);
130
131 /* From objects-gtk.c */
132 extern Lisp_Object __get_gtk_font_truename(GdkFont * gdk_font, int expandp);
133
134 #define convert_font(f) __get_gtk_font_truename (f, 0)
135
136 static void smash_face_fallbacks(struct frame *f, GtkStyle * style)
137 {
138 #define FROB(face,prop,slot) do {                                                       \
139                                 Lisp_Object fallback = Qnil;                            \
140                                 Lisp_Object specifier = Fget (face, prop, Qnil);        \
141                                 struct Lisp_Specifier *sp = NULL;                       \
142                                 if (NILP (specifier)) continue;                         \
143                                 sp = XSPECIFIER (specifier);                            \
144                                 fallback = sp->fallback;                                \
145                                 if (EQ (Fcar (Fcar (Fcar (fallback))), Qgtk))           \
146                                         fallback = XCDR (fallback);                     \
147                                 if (! NILP (slot))                                      \
148                                         fallback = acons (list1 (Qgtk),                 \
149                                                                   slot,                 \
150                                                                   fallback);            \
151                                 set_specifier_fallback (specifier, fallback);           \
152                              } while (0);
153 #define FROB_FACE(face,fg_slot,bg_slot) \
154 do {                                                                                    \
155         FROB (face, Qforeground, xemacs_gtk_convert_color (&style->fg_slot[GTK_STATE_NORMAL], FRAME_GTK_SHELL_WIDGET (f)));     \
156         FROB (face, Qbackground, xemacs_gtk_convert_color (&style->bg_slot[GTK_STATE_NORMAL], FRAME_GTK_SHELL_WIDGET (f)));     \
157         if (style->rc_style && style->rc_style->bg_pixmap_name[GTK_STATE_NORMAL])       \
158         {                                                                               \
159                 FROB (Vdefault_face, Qbackground_pixmap,                                \
160                         Fmake_image_instance (build_string (style->rc_style->bg_pixmap_name[GTK_STATE_NORMAL]), \
161                                           f->device, Qnil, make_int (5)));                      \
162         }                                                                               \
163         else                                                                            \
164         {                                                                               \
165                 FROB (Vdefault_face, Qbackground_pixmap, Qnil);                         \
166         }                                                                               \
167 } while (0)
168
169         FROB(Vdefault_face, Qfont, convert_font(style->font));
170         FROB_FACE(Vdefault_face, fg, bg);
171         FROB_FACE(Vgui_element_face, text, mid);
172
173 #undef FROB
174 #undef FROB_FACE
175 }
176
177 #ifdef HAVE_SCROLLBARS
178 static void smash_scrollbar_specifiers(struct frame *f, GtkStyle * style)
179 {
180         Lisp_Object frame;
181         int slider_size = 0;
182         int hsize, vsize;
183         GtkRangeClass *klass;
184
185         XSETFRAME(frame, f);
186
187         klass = (GtkRangeClass *) gtk_type_class(GTK_TYPE_SCROLLBAR);
188         slider_size = klass->slider_width;
189         hsize = slider_size + (style->klass->ythickness * 2);
190         vsize = slider_size + (style->klass->xthickness * 2);
191
192         style = gtk_style_attach(style,
193                                  GTK_WIDGET(DEVICE_GTK_APP_SHELL
194                                             (XDEVICE(FRAME_DEVICE(f))))->
195                                  window);
196
197         Fadd_spec_to_specifier(Vscrollbar_width, make_int(vsize), frame, Qnil,
198                                Qnil);
199         Fadd_spec_to_specifier(Vscrollbar_height, make_int(hsize), frame, Qnil,
200                                Qnil);
201 }
202 #else
203 #define smash_scrollbar_specifiers(x,y)
204 #endif                          /* HAVE_SCROLLBARS */
205
206 static void gtk_xemacs_realize(GtkWidget * widget)
207 {
208         parent_class->realize(widget);
209         gtk_xemacs_style_set(widget, gtk_widget_get_style(widget));
210 }
211
212 static void gtk_xemacs_style_set(GtkWidget * widget, GtkStyle * previous_style)
213 {
214         GtkStyle *new_style = gtk_widget_get_style(widget);
215         GtkXEmacs *x = GTK_XEMACS(widget);
216
217         parent_class->style_set(widget, previous_style);
218
219         if (x->f) {
220                 __nuke_background_items(widget);
221 #if 0
222                 smash_face_fallbacks(x->f, new_style);
223 #endif
224                 smash_scrollbar_specifiers(x->f, new_style);
225         }
226 }
227
228 static void
229 gtk_xemacs_size_request(GtkWidget * widget, GtkRequisition * requisition)
230 {
231         GtkXEmacs *x = GTK_XEMACS(widget);
232         struct frame *f = GTK_XEMACS_FRAME(x);
233         int width, height;
234
235         if (f) {
236                 char_to_pixel_size(f, FRAME_WIDTH(f), FRAME_HEIGHT(f),
237                                    &width, &height);
238                 requisition->width = width;
239                 requisition->height = height;
240         } else {
241                 parent_class->size_request(widget, requisition);
242         }
243 }
244
245 /* Assign a size and position to the child widgets.  This differs from the
246    super class method in that for all widgets except the scrollbars the size
247    and position are not caclulated here.  This is because these widgets have
248    this function performed for them by the redisplay code (see
249    gtk_map_subwindow()). If the superclass method is called then the widgets
250    can change size and position as the two pieces of code move the widgets at
251    random.
252 */
253 static void
254 gtk_xemacs_size_allocate(GtkWidget * widget, GtkAllocation * allocation)
255 {
256         GtkXEmacs *x = GTK_XEMACS(widget);
257         GtkFixed *fixed = GTK_FIXED(widget);
258         struct frame *f = GTK_XEMACS_FRAME(x);
259         int columns, rows;
260         GList *children;
261         guint16 border_width;
262
263         widget->allocation = *allocation;
264         if (GTK_WIDGET_REALIZED(widget))
265                 gdk_window_move_resize(widget->window,
266                                        allocation->x,
267                                        allocation->y,
268                                        allocation->width, allocation->height);
269
270         border_width = GTK_CONTAINER(fixed)->border_width;
271
272         children = fixed->children;
273         while (children) {
274                 GtkFixedChild *child = children->data;
275                 children = children->next;
276
277                 /*
278                    Scrollbars are the only widget that is managed by GTK.  See
279                    comments in gtk_create_scrollbar_instance().
280                  */
281                 if (GTK_WIDGET_VISIBLE(child->widget) &&
282                     gtk_type_is_a(GTK_OBJECT_TYPE(child->widget),
283                                   GTK_TYPE_SCROLLBAR)) {
284                         GtkAllocation child_allocation;
285                         GtkRequisition child_requisition;
286
287                         gtk_widget_get_child_requisition(child->widget,
288                                                          &child_requisition);
289                         child_allocation.x = child->x + border_width;
290                         child_allocation.y = child->y + border_width;
291                         child_allocation.width = child_requisition.width;
292                         child_allocation.height = child_requisition.height;
293                         gtk_widget_size_allocate(child->widget,
294                                                  &child_allocation);
295                 }
296         }
297
298         if (f) {
299                 f->pixwidth = allocation->width;
300                 f->pixheight = allocation->height;
301
302                 pixel_to_char_size(f,
303                                    allocation->width,
304                                    allocation->height, &columns, &rows);
305
306                 change_frame_size(f, rows, columns, 1);
307         }
308 }
309
310 static void gtk_xemacs_paint(GtkWidget * widget, GdkRectangle * area)
311 {
312         GtkXEmacs *x = GTK_XEMACS(widget);
313         struct frame *f = GTK_XEMACS_FRAME(x);
314
315         if (GTK_WIDGET_DRAWABLE(widget))
316                 gtk_redraw_exposed_area(f, area->x, area->y, area->width,
317                                         area->height);
318 }
319
320 static void gtk_xemacs_draw(GtkWidget * widget, GdkRectangle * area)
321 {
322         GtkFixed *fixed = GTK_FIXED(widget);
323         GtkFixedChild *child;
324         GdkRectangle child_area;
325         GList *children;
326
327         /* I need to manually iterate over the children instead of just
328            chaining to parent_class->draw() because it calls
329            gtk_fixed_paint() directly, which clears the background window,
330            which causes A LOT of flashing. */
331
332         if (GTK_WIDGET_DRAWABLE(widget)) {
333                 gtk_xemacs_paint(widget, area);
334
335                 children = fixed->children;
336
337                 while (children) {
338                         child = children->data;
339                         children = children->next;
340                         /* #### This is what causes the scrollbar flickering!
341                            Evidently the scrollbars pretty much take care of drawing
342                            themselves in most cases.  Then we come along and tell them
343                            to redraw again!
344
345                            But if we just leave it out, then they do not get drawn
346                            correctly the first time!
347
348                            Scrollbar flickering has been greatly helped by the
349                            optimizations in scrollbar-gtk.c /
350                            gtk_update_scrollbar_instance_status (), so this is not that
351                            big a deal anymore.
352                          */
353                         if (gtk_widget_intersect
354                             (child->widget, area, &child_area)) {
355                                 gtk_widget_draw(child->widget, &child_area);
356                         }
357                 }
358         }
359 }
360
361 static gint gtk_xemacs_expose(GtkWidget * widget, GdkEventExpose * event)
362 {
363         GtkXEmacs *x = GTK_XEMACS(widget);
364         struct frame *f = GTK_XEMACS_FRAME(x);
365         GdkRectangle *a = &event->area;
366
367         if (GTK_WIDGET_DRAWABLE(widget)) {
368                 /* This takes care of drawing the scrollbars, etc */
369                 parent_class->expose_event(widget, event);
370
371                 /* Now draw the actual frame data */
372                 if (!check_for_ignored_expose
373                     (f, a->x, a->y, a->width, a->height)
374                     && !find_matching_subwindow(f, a->x, a->y, a->width,
375                                                 a->height))
376                         gtk_redraw_exposed_area(f, a->x, a->y, a->width,
377                                                 a->height);
378                 return (TRUE);
379         }
380
381         return FALSE;
382 }
383
384 Lisp_Object xemacs_gtk_convert_color(GdkColor * c, GtkWidget * w)
385 {
386         char color_buf[255];
387
388         sprintf(color_buf, "#%04x%04x%04x", c->red, c->green, c->blue);
389
390         return (build_string(color_buf));
391 }