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