comparison src/toolbar-gtk.c @ 462:0784d089fdc9 r21-2-46

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