518684b335781c4d627d32bbef409428ebfeb370
[sxemacs] / src / ui / Gtk / toolbar-gtk.c
1 /* toolbar implementation -- X interface.
2    Copyright (C) 1995 Board of Trustees, University of Illinois.
3    Copyright (C) 1995 Sun Microsystems, Inc.
4    Copyright (C) 1995, 1996 Ben Wing.
5    Copyright (C) 1996 Chuck Thompson.
6
7 This file is part of SXEmacs
8
9 SXEmacs is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 SXEmacs is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
21
22
23 /* Synched up with: Not in FSF. */
24
25 #include <config.h>
26 #include "lisp.h"
27
28 #include "console-gtk.h"
29 #include "glyphs-gtk.h"
30 #include "objects-gtk.h"
31 #include "gtk-xemacs.h"
32 #include "gccache-gtk.h"
33
34 #include "ui/faces.h"
35 #include "ui/frame.h"
36 #include "ui/toolbar.h"
37 #include "ui/window.h"
38
39 extern GdkGC *gtk_get_gc(struct device *d, Lisp_Object font, Lisp_Object fg,
40                          Lisp_Object bg, Lisp_Object bg_pmap,
41                          Lisp_Object lwidth);
42
43 static GdkGC *get_toolbar_gc(struct frame *f)
44 {
45         Lisp_Object fg, bg;
46         Lisp_Object frame;
47
48         XSETFRAME(frame, f);
49
50         fg = Fspecifier_instance(Fget(Vtoolbar_face, Qforeground, Qnil), frame,
51                                  Qnil, Qnil);
52         bg = Fspecifier_instance(Fget(Vtoolbar_face, Qbackground, Qnil), frame,
53                                  Qnil, Qnil);
54
55         /* Need to swap the foreground/background here or most themes look bug ugly */
56         return (gtk_get_gc(XDEVICE(FRAME_DEVICE(f)), Qnil, bg, fg, Qnil, Qnil));
57 }
58
59 static void
60 gtk_draw_blank_toolbar_button(struct frame *f, int x, int y, int width,
61                               int height, int threed, int border_width,
62                               int vertical)
63 {
64         GtkXEmacs *ef = GTK_XEMACS(FRAME_GTK_TEXT_WIDGET(f));
65         int sx = x, sy = y, swidth = width, sheight = height;
66         GdkWindow *x_win = GTK_WIDGET(ef)->window;
67         GdkGC *background_gc = get_toolbar_gc(f);
68
69         if (vertical) {
70                 sx += border_width;
71                 swidth -= 2 * border_width;
72         } else {
73                 sy += border_width;
74                 sheight -= 2 * border_width;
75         }
76
77         /* Blank the entire area. */
78         gdk_draw_rectangle(x_win, background_gc, TRUE, sx, sy, swidth, sheight);
79
80         /* Draw the outline. */
81         if (threed)
82                 gtk_output_shadows(f, sx, sy, swidth, sheight, 2);
83
84         /* Do the border */
85         gdk_draw_rectangle(x_win, background_gc, TRUE, x, y,
86                            (vertical ? border_width : width),
87                            (vertical ? height : border_width));
88         gdk_draw_rectangle(x_win, background_gc, TRUE,
89                            (vertical ? sx + swidth : x),
90                            (vertical ? y : sy + sheight),
91                            (vertical ? border_width : width),
92                            (vertical ? height : border_width));
93 }
94
95 static void gtk_output_toolbar_button(struct frame *f, Lisp_Object button)
96 {
97         int shadow_thickness = 2;
98         int x_adj, y_adj, width_adj, height_adj;
99         GdkWindow *x_win = FRAME_GTK_TEXT_WIDGET(f)->window;
100         GdkGC *background_gc = get_toolbar_gc(f);
101         Lisp_Object instance, frame, window, glyph;
102         struct toolbar_button *tb = XTOOLBAR_BUTTON(button);
103         struct Lisp_Image_Instance *p;
104         struct window *w;
105         int vertical = tb->vertical;
106         int border_width = tb->border_width;
107
108         if (vertical) {
109                 x_adj = border_width;
110                 width_adj = -2 * border_width;
111                 y_adj = 0;
112                 height_adj = 0;
113         } else {
114                 x_adj = 0;
115                 width_adj = 0;
116                 y_adj = border_width;
117                 height_adj = -2 * border_width;
118         }
119
120         XSETFRAME(frame, f);
121         window = FRAME_LAST_NONMINIBUF_WINDOW(f);
122         w = XWINDOW(window);
123
124         glyph = get_toolbar_button_glyph(w, tb);
125
126         if (tb->enabled) {
127                 if (tb->down) {
128                         shadow_thickness = -2;
129                 } else {
130                         shadow_thickness = 2;
131                 }
132         } else {
133                 shadow_thickness = 0;
134         }
135
136         background_gc = get_toolbar_gc(f);
137
138         /* Clear the entire area. */
139         gdk_draw_rectangle(x_win, background_gc, TRUE,
140                            tb->x + x_adj,
141                            tb->y + y_adj,
142                            tb->width + width_adj, tb->height + height_adj);
143
144         /* Draw the outline. */
145         if (shadow_thickness)
146                 gtk_output_shadows(f, tb->x + x_adj, tb->y + y_adj,
147                                    tb->width + width_adj,
148                                    tb->height + height_adj, shadow_thickness);
149
150         /* Do the border. */
151         gdk_draw_rectangle(x_win, background_gc, TRUE, tb->x, tb->y,
152                            (vertical ? border_width : tb->width),
153                            (vertical ? tb->height : border_width));
154
155         gdk_draw_rectangle(x_win, background_gc, TRUE,
156                            (vertical ? tb->x + tb->width -
157                             border_width : tb->x),
158                            (vertical ? tb->y : tb->y + tb->height -
159                             border_width),
160                            (vertical ? border_width : tb->width),
161                            (vertical ? tb->height : border_width));
162
163         background_gc = get_toolbar_gc(f);
164
165         /* #### It is currently possible for users to trash us by directly
166            changing the toolbar glyphs.  Avoid crashing in that case. */
167         if (GLYPHP(glyph))
168                 instance = glyph_image_instance(glyph, window, ERROR_ME_NOT, 1);
169         else
170                 instance = Qnil;
171
172         if (IMAGE_INSTANCEP(instance)) {
173                 int width = tb->width + width_adj - shadow_thickness * 2;
174                 int height = tb->height + height_adj - shadow_thickness * 2;
175                 int x_offset = x_adj + shadow_thickness;
176                 int y_offset = y_adj + shadow_thickness;
177
178                 p = XIMAGE_INSTANCE(instance);
179
180                 if (IMAGE_INSTANCE_PIXMAP_TYPE_P(p)) {
181                         if (width > (int)IMAGE_INSTANCE_PIXMAP_WIDTH(p)) {
182                                 x_offset +=
183                                     ((int)
184                                      (width - IMAGE_INSTANCE_PIXMAP_WIDTH(p))
185                                      / 2);
186                                 width = IMAGE_INSTANCE_PIXMAP_WIDTH(p);
187                         }
188                         if (height > (int)IMAGE_INSTANCE_PIXMAP_HEIGHT(p)) {
189                                 y_offset +=
190                                     ((int)
191                                      (height - IMAGE_INSTANCE_PIXMAP_HEIGHT(p))
192                                      / 2);
193                                 height = IMAGE_INSTANCE_PIXMAP_HEIGHT(p);
194                         }
195
196                         gtk_output_gdk_pixmap(f, XIMAGE_INSTANCE(instance),
197                                               tb->x + x_offset,
198                                               tb->y + y_offset, 0, 0, 0, 0,
199                                               width, height, 0, 0, 0,
200                                               background_gc);
201                 } else if (IMAGE_INSTANCE_TYPE(p) == IMAGE_TEXT) {
202                         /* #### We need to make the face used configurable. */
203                         struct face_cachel *cachel =
204                             WINDOW_FACE_CACHEL(w, DEFAULT_INDEX);
205                         struct display_line dl;
206                         Lisp_Object string = IMAGE_INSTANCE_TEXT_STRING(p);
207                         unsigned char charsets[NUM_LEADING_BYTES];
208                         Emchar_dynarr *buf;
209                         struct font_metric_info fm;
210
211                         /* This could be true if we were called via the Expose event
212                            handler.  Mark the button as dirty and return
213                            immediately. */
214                         if (f->window_face_cache_reset) {
215                                 tb->dirty = 1;
216                                 MARK_TOOLBAR_CHANGED;
217                                 return;
218                         }
219                         buf = Dynarr_new(Emchar);
220                         convert_bufbyte_string_into_emchar_dynarr
221                             (XSTRING_DATA(string), XSTRING_LENGTH(string), buf);
222                         find_charsets_in_emchar_string(charsets,
223                                                        Dynarr_atp(buf, 0),
224                                                        Dynarr_length(buf));
225                         ensure_face_cachel_complete(cachel, window, charsets);
226                         face_cachel_charset_font_metric_info(cachel, charsets,
227                                                              &fm);
228
229                         dl.ascent = fm.ascent;
230                         dl.descent = fm.descent;
231                         dl.ypos = tb->y + y_offset + fm.ascent;
232
233                         if (fm.ascent + fm.descent <= height) {
234                                 dl.ypos +=
235                                     (height - fm.ascent - fm.descent) / 2;
236                                 dl.clip = 0;
237                         } else {
238                                 dl.clip = fm.ascent + fm.descent - height;
239                         }
240
241                         gtk_output_string(w, &dl, buf, tb->x + x_offset, 0, 0,
242                                           width, DEFAULT_INDEX, 0, 0, 0, 0);
243                         Dynarr_free(buf);
244                 }
245
246                 /* We silently ignore the image if it isn't a pixmap or text. */
247         }
248
249         tb->dirty = 0;
250 }
251
252 static int
253 gtk_get_button_size(struct frame *f, Lisp_Object window,
254                     struct toolbar_button *tb, int vert, int pos)
255 {
256         int shadow_thickness = 2;
257         int size;
258
259         if (tb->blank) {
260                 if (!NILP(tb->down_glyph))
261                         size = XINT(tb->down_glyph);
262                 else
263                         size = DEFAULT_TOOLBAR_BLANK_SIZE;
264         } else {
265                 struct window *w = XWINDOW(window);
266                 Lisp_Object glyph = get_toolbar_button_glyph(w, tb);
267
268                 /* Unless, of course, the user has done something stupid like
269                    change the glyph out from under us.  Use a blank placeholder
270                    in that case. */
271                 if (NILP(glyph))
272                         return XINT(f->toolbar_size[pos]);
273
274                 if (vert)
275                         size = glyph_height(glyph, window);
276                 else
277                         size = glyph_width(glyph, window);
278         }
279
280         if (!size) {
281                 /* If the glyph doesn't have a size we'll insert a blank
282                    placeholder instead. */
283                 return XINT(f->toolbar_size[pos]);
284         }
285
286         size += shadow_thickness * 2;
287
288         return (size);
289 }
290
291 #define GTK_OUTPUT_BUTTONS_LOOP(left)                                   \
292   do {                                                                  \
293     while (!NILP (button))                                              \
294       {                                                                 \
295         struct toolbar_button *tb = XTOOLBAR_BUTTON (button);           \
296         int size, height, width;                                        \
297                                                                         \
298         if (left && tb->pushright)                                      \
299           break;                                                        \
300                                                                         \
301         size = gtk_get_button_size (f, window, tb, vert, pos);          \
302                                                                         \
303         if (vert)                                                       \
304           {                                                             \
305             width = bar_width;                                          \
306             if (y + size > max_pixpos)                                  \
307               height = max_pixpos - y;                                  \
308             else                                                        \
309               height = size;                                            \
310           }                                                             \
311         else                                                            \
312           {                                                             \
313             if (x + size > max_pixpos)                                  \
314               width = max_pixpos - x;                                   \
315             else                                                        \
316               width = size;                                             \
317             height = bar_height;                                        \
318           }                                                             \
319                                                                         \
320         if (tb->x != x                                                  \
321             || tb->y != y                                               \
322             || tb->width != width                                       \
323             || tb->height != height                                     \
324             || tb->dirty                                                \
325             || f->clear) /* This is clearly necessary. */               \
326           {                                                             \
327             if (width && height)                                        \
328               {                                                         \
329                 tb->x = x;                                              \
330                 tb->y = y;                                              \
331                 tb->width = width;                                      \
332                 tb->height = height;                                    \
333                 tb->border_width = border_width;                        \
334                 tb->vertical = vert;                                    \
335                                                                         \
336                 if (tb->blank || NILP (tb->up_glyph))                   \
337                   {                                                     \
338                     int threed = (EQ (Qt, tb->up_glyph) ? 1 : 0);       \
339                     gtk_draw_blank_toolbar_button (f, x, y, width,      \
340                                                  height, threed,        \
341                                                  border_width, vert);   \
342                   }                                                     \
343                 else                                                    \
344                   gtk_output_toolbar_button (f, button);                \
345               }                                                         \
346           }                                                             \
347                                                                         \
348         if (vert)                                                       \
349           y += height;                                                  \
350         else                                                            \
351           x += width;                                                   \
352                                                                         \
353         if ((vert && y == max_pixpos) || (!vert && x == max_pixpos))    \
354           button = Qnil;                                                \
355         else                                                            \
356           button = tb->next;                                            \
357       }                                                                 \
358   } while (0)
359
360 #define SET_TOOLBAR_WAS_VISIBLE_FLAG(frame, pos, flag)                  \
361   do {                                                                  \
362     switch (pos)                                                        \
363       {                                                                 \
364       case TOP_TOOLBAR:                                                 \
365         (frame)->top_toolbar_was_visible = flag;                        \
366         break;                                                          \
367       case BOTTOM_TOOLBAR:                                              \
368         (frame)->bottom_toolbar_was_visible = flag;                     \
369         break;                                                          \
370       case LEFT_TOOLBAR:                                                \
371         (frame)->left_toolbar_was_visible = flag;                       \
372         break;                                                          \
373       case RIGHT_TOOLBAR:                                               \
374         (frame)->right_toolbar_was_visible = flag;                      \
375         break;                                                          \
376       default:                                                          \
377         abort ();                                                       \
378       }                                                                 \
379   } while (0)
380
381 static void gtk_output_toolbar(struct frame *f, enum toolbar_pos pos)
382 {
383         int x, y, bar_width, bar_height, vert;
384         int max_pixpos, right_size, right_start, blank_size;
385         int border_width = FRAME_REAL_TOOLBAR_BORDER_WIDTH(f, pos);
386         Lisp_Object button, window;
387         GdkWindow *x_win = FRAME_GTK_TEXT_WIDGET(f)->window;
388         GdkGC *background_gc = get_toolbar_gc(f);
389
390         get_toolbar_coords(f, pos, &x, &y, &bar_width, &bar_height, &vert, 1);
391         window = FRAME_LAST_NONMINIBUF_WINDOW(f);
392
393         /* Do the border */
394         gdk_draw_rectangle(x_win, background_gc, TRUE, x, y,
395                            (vert ? bar_width : border_width),
396                            (vert ? border_width : bar_height));
397         gdk_draw_rectangle(x_win, background_gc, TRUE,
398                            (vert ? x : x + bar_width - border_width),
399                            (vert ? y + bar_height - border_width : y),
400                            (vert ? bar_width : border_width),
401                            (vert ? border_width : bar_height));
402
403         if (vert) {
404                 max_pixpos = y + bar_height - border_width;
405                 y += border_width;
406         } else {
407                 max_pixpos = x + bar_width - border_width;
408                 x += border_width;
409         }
410
411         button = FRAME_TOOLBAR_BUTTONS(f, pos);
412         right_size = 0;
413
414         /* First loop over all of the buttons to determine how much room we
415            need for left hand and right hand buttons.  This loop will also
416            make sure that all instances are instantiated so when we actually
417            output them they will come up immediately. */
418         while (!NILP(button)) {
419                 struct toolbar_button *tb = XTOOLBAR_BUTTON(button);
420                 int size = gtk_get_button_size(f, window, tb, vert, pos);
421
422                 if (tb->pushright)
423                         right_size += size;
424
425                 button = tb->next;
426         }
427
428         button = FRAME_TOOLBAR_BUTTONS(f, pos);
429
430         /* Loop over the left buttons, updating and outputting them. */
431         GTK_OUTPUT_BUTTONS_LOOP(1);
432
433         /* Now determine where the right buttons start. */
434         right_start = max_pixpos - right_size;
435         if (right_start < (vert ? y : x))
436                 right_start = (vert ? y : x);
437
438         /* Output the blank which goes from the end of the left buttons to
439            the start of the right. */
440         blank_size = right_start - (vert ? y : x);
441         if (blank_size) {
442                 int height, width;
443
444                 if (vert) {
445                         width = bar_width;
446                         height = blank_size;
447                 } else {
448                         width = blank_size;
449                         height = bar_height;
450                 }
451
452                 /*
453                  * Use a 3D pushright separator only if there isn't a toolbar
454                  * border.  A flat separator meshes with the border and looks
455                  * better.
456                  */
457                 gtk_draw_blank_toolbar_button(f, x, y, width, height,
458                                               !border_width, border_width,
459                                               vert);
460
461                 if (vert)
462                         y += height;
463                 else
464                         x += width;
465         }
466
467         /* Loop over the right buttons, updating and outputting them. */
468         GTK_OUTPUT_BUTTONS_LOOP(0);
469
470         if (!vert) {
471                 Lisp_Object frame;
472
473                 XSETFRAME(frame, f);
474                 redisplay_clear_region(frame,
475                                        DEFAULT_INDEX, FRAME_PIXWIDTH(f) - 1, y,
476                                        1, bar_height);
477         }
478
479         SET_TOOLBAR_WAS_VISIBLE_FLAG(f, pos, 1);
480
481         gdk_flush();
482 }
483
484 static void
485 gtk_clear_toolbar(struct frame *f, enum toolbar_pos pos, int thickness_change)
486 {
487         Lisp_Object frame;
488         int x, y, width, height, vert;
489
490         get_toolbar_coords(f, pos, &x, &y, &width, &height, &vert, 1);
491         XSETFRAME(frame, f);
492
493         /* The thickness_change parameter is used by the toolbar resize routines
494            to clear any excess toolbar if the size shrinks. */
495         if (thickness_change < 0) {
496                 if (pos == LEFT_TOOLBAR || pos == RIGHT_TOOLBAR) {
497                         x = x + width + thickness_change;
498                         width = -thickness_change;
499                 } else {
500                         y = y + height + thickness_change;
501                         height = -thickness_change;
502                 }
503         }
504
505         SET_TOOLBAR_WAS_VISIBLE_FLAG(f, pos, 0);
506
507         redisplay_clear_region(frame, DEFAULT_INDEX, x, y, width, height);
508         gdk_flush();
509 }
510
511 static void gtk_output_frame_toolbars(struct frame *f)
512 {
513         assert(FRAME_GTK_P(f));
514
515         if (FRAME_REAL_TOP_TOOLBAR_VISIBLE(f))
516                 gtk_output_toolbar(f, TOP_TOOLBAR);
517         if (FRAME_REAL_BOTTOM_TOOLBAR_VISIBLE(f))
518                 gtk_output_toolbar(f, BOTTOM_TOOLBAR);
519         if (FRAME_REAL_LEFT_TOOLBAR_VISIBLE(f))
520                 gtk_output_toolbar(f, LEFT_TOOLBAR);
521         if (FRAME_REAL_RIGHT_TOOLBAR_VISIBLE(f))
522                 gtk_output_toolbar(f, RIGHT_TOOLBAR);
523 }
524
525 static void gtk_clear_frame_toolbars(struct frame *f)
526 {
527         assert(FRAME_GTK_P(f));
528
529         if (f->top_toolbar_was_visible && !FRAME_REAL_TOP_TOOLBAR_VISIBLE(f))
530                 gtk_clear_toolbar(f, TOP_TOOLBAR, 0);
531         if (f->bottom_toolbar_was_visible
532             && !FRAME_REAL_BOTTOM_TOOLBAR_VISIBLE(f))
533                 gtk_clear_toolbar(f, BOTTOM_TOOLBAR, 0);
534         if (f->left_toolbar_was_visible && !FRAME_REAL_LEFT_TOOLBAR_VISIBLE(f))
535                 gtk_clear_toolbar(f, LEFT_TOOLBAR, 0);
536         if (f->right_toolbar_was_visible
537             && !FRAME_REAL_RIGHT_TOOLBAR_VISIBLE(f))
538                 gtk_clear_toolbar(f, RIGHT_TOOLBAR, 0);
539 }
540
541 static void
542 gtk_redraw_exposed_toolbar(struct frame *f, enum toolbar_pos pos, int x, int y,
543                            int width, int height)
544 {
545         int bar_x, bar_y, bar_width, bar_height, vert;
546         Lisp_Object button = FRAME_TOOLBAR_BUTTONS(f, pos);
547
548         get_toolbar_coords(f, pos, &bar_x, &bar_y, &bar_width, &bar_height,
549                            &vert, 1);
550
551         if (((y + height) < bar_y) || (y > (bar_y + bar_height)))
552                 return;
553         if (((x + width) < bar_x) || (x > (bar_x + bar_width)))
554                 return;
555
556         while (!NILP(button)) {
557                 struct toolbar_button *tb = XTOOLBAR_BUTTON(button);
558
559                 if (vert) {
560                         if (((tb->y + tb->height) > y)
561                             && (tb->y < (y + height)))
562                                 tb->dirty = 1;
563
564                         /* If this is true we have gone past the exposed region. */
565                         if (tb->y > (y + height))
566                                 break;
567                 } else {
568                         if (((tb->x + tb->width) > x) && (tb->x < (x + width)))
569                                 tb->dirty = 1;
570
571                         /* If this is true we have gone past the exposed region. */
572                         if (tb->x > (x + width))
573                                 break;
574                 }
575
576                 button = tb->next;
577         }
578
579         /* Even if none of the buttons is in the area, the blank region at
580            the very least must be because the first thing we did is verify
581            that some portion of the toolbar is in the exposed region. */
582         gtk_output_toolbar(f, pos);
583 }
584
585 static void
586 gtk_redraw_exposed_toolbars(struct frame *f, int x, int y, int width,
587                             int height)
588 {
589         assert(FRAME_GTK_P(f));
590
591         if (FRAME_REAL_TOP_TOOLBAR_VISIBLE(f))
592                 gtk_redraw_exposed_toolbar(f, TOP_TOOLBAR, x, y, width, height);
593
594         if (FRAME_REAL_BOTTOM_TOOLBAR_VISIBLE(f))
595                 gtk_redraw_exposed_toolbar(f, BOTTOM_TOOLBAR, x, y, width,
596                                            height);
597
598         if (FRAME_REAL_LEFT_TOOLBAR_VISIBLE(f))
599                 gtk_redraw_exposed_toolbar(f, LEFT_TOOLBAR, x, y, width,
600                                            height);
601
602         if (FRAME_REAL_RIGHT_TOOLBAR_VISIBLE(f))
603                 gtk_redraw_exposed_toolbar(f, RIGHT_TOOLBAR, x, y, width,
604                                            height);
605 }
606
607 static void gtk_redraw_frame_toolbars(struct frame *f)
608 {
609         /* There are certain startup paths that lead to update_EmacsFrame in
610            faces.c being called before a new frame is fully initialized.  In
611            particular before we have actually mapped it.  That routine can
612            call this one.  So, we need to make sure that the frame is
613            actually ready before we try and draw all over it. */
614
615         if (GTK_WIDGET_REALIZED(FRAME_GTK_TEXT_WIDGET(f)))
616                 gtk_redraw_exposed_toolbars(f, 0, 0, FRAME_PIXWIDTH(f),
617                                             FRAME_PIXHEIGHT(f));
618 }
619 \f
620 static void gtk_initialize_frame_toolbars(struct frame *f)
621 {
622 }
623
624 /* This only calls one function but we go ahead and create this in
625    case we ever do decide that we need to do more work. */
626 static void gtk_free_frame_toolbars(struct frame *f)
627 {
628 }
629 \f
630 /************************************************************************/
631 /*                            initialization                            */
632 /************************************************************************/
633
634 void console_type_create_toolbar_gtk(void)
635 {
636         CONSOLE_HAS_METHOD(gtk, output_frame_toolbars);
637         CONSOLE_HAS_METHOD(gtk, clear_frame_toolbars);
638         CONSOLE_HAS_METHOD(gtk, initialize_frame_toolbars);
639         CONSOLE_HAS_METHOD(gtk, free_frame_toolbars);
640         CONSOLE_HAS_METHOD(gtk, output_toolbar_button);
641         CONSOLE_HAS_METHOD(gtk, redraw_exposed_toolbars);
642         CONSOLE_HAS_METHOD(gtk, redraw_frame_toolbars);
643 }