comparison src/toolbar-common.c @ 713:c2c346111c9a

[xemacs-hg @ 2001-12-23 01:11:00 by wmperry] New common redisplay-based toolbar code.
author wmperry
date Sun, 23 Dec 2001 01:11:00 +0000
parents
children 02339d4ebed4
comparison
equal deleted inserted replaced
712:2d3184c89f71 713:c2c346111c9a
1 /* toolbar implementation -- Generic redisplay 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 "faces.h"
30 #include "frame.h"
31 #include "toolbar.h"
32 #include "window.h"
33
34 /* Only a very few things need to differ based on the toolkit used.
35 **
36 ** Some of the routines used assert(FRAME_yyy_P(f)) checks, this is
37 ** now abstracted into __INTERNAL_APPROPRIATENESS_CHECK(). When we
38 ** add new window systems that use this code, we should either add a
39 ** new case here, or just remove the checks completely.
40 **
41 ** At least for X & GTK redraw_frame_toolbars() might end up getting
42 ** called before we are completely initialized. To avoid this, we use
43 ** the __INTERNAL_MAPPED_P(f) macro, that should return 0 if we should
44 ** not draw the toolbars yet. When we add new window systems that use
45 ** this code, we should add a new case here, if they need it.
46 **
47 ** When clearing the toolbar, it is nice to flush the drawing queue.
48 ** Use __INTERNAL_FLUSH to do this. It is passed a device.
49 */
50 #if defined(HAVE_GTK)
51 #include "console-gtk.h"
52 #define __INTERNAL_MAPPED_P(f) GTK_WIDGET_REALIZED (FRAME_GTK_TEXT_WIDGET (f))
53 #define __INTERNAL_FLUSH(d) gdk_flush()
54 #define __INTERNAL_APPROPRIATENESS_CHECK(f) assert(FRAME_GTK_P (f))
55 #elif defined(HAVE_X_WINDOWS)
56 #include "console-x.h"
57 #define __INTERNAL_MAPPED_P(f) XtIsRealized (FRAME_X_SHELL_WIDGET (f))
58 #define __INTERNAL_APPROPRIATENESS_CHECK(f) assert(FRAME_X_P (f))
59 #define __INTERNAL_FLUSH(d) XFlush (DEVICE_X_DISPLAY (d))
60 #else
61 #define __INTERNAL_MAPPED_P(f) abort()
62 #define __INTERNAL_APPROPRIATENESS_CHECK(f) abort()
63 #define __INTERNAL_FLUSH(f) abort()
64 #endif
65
66 #include "toolbar-common.h"
67
68 static void __prepare_button_area (struct frame *f,
69 struct toolbar_button *tb)
70 {
71 int sx = tb->x;
72 int sy = tb->y;
73 int swidth = tb->width;
74 int sheight = tb->height;
75 int border_width = tb->border_width;
76 int x_adj, width_adj, y_adj, height_adj;
77 struct device *d = XDEVICE (f->device);
78 Lisp_Object window = FRAME_LAST_NONMINIBUF_WINDOW (f);
79 struct window *w = XWINDOW (window);
80 int shadow_thickness = 2;
81 face_index toolbar_findex;
82
83 if (tb->vertical)
84 {
85 x_adj = border_width;
86 width_adj = - 2 * border_width;
87 y_adj = height_adj = 0;
88 }
89 else
90 {
91 x_adj = width_adj = 0;
92 y_adj = border_width;
93 height_adj = - 2 * border_width;
94 }
95
96 toolbar_findex = get_builtin_face_cache_index (w, Vtoolbar_face);
97
98 /* Blank toolbar buttons that should be 3d will have EQ(tb->up_glyph, Qt)
99 ** Blank toolbar buttons that should be flat will have NILP (tb->up_glyph)
100 **
101 ** Real toolbar buttons will check tb->enabled && tb->down
102 */
103 if (EQ (Qt, tb->up_glyph))
104 {
105 shadow_thickness = 2;
106 }
107 else if (NILP (tb->up_glyph))
108 {
109 shadow_thickness = 0;
110 }
111 else
112 {
113 if (tb->enabled)
114 {
115 if (tb->down)
116 shadow_thickness = -2;
117 else
118 shadow_thickness = 2;
119 }
120 else
121 {
122 shadow_thickness = 0;
123 }
124 }
125
126 /* Blank the entire area. */
127 redisplay_clear_region (window, toolbar_findex,
128 sx + x_adj, sy + y_adj,
129 swidth + width_adj,
130 sheight + height_adj);
131
132 /* Draw the outline. */
133 if (shadow_thickness)
134 {
135 MAYBE_DEVMETH (d, bevel_area,
136 (w, toolbar_findex, sx + x_adj,
137 sy + y_adj, swidth + width_adj,
138 sheight + height_adj, abs(shadow_thickness),
139 EDGE_ALL, (shadow_thickness < 0) ? EDGE_BEVEL_IN : EDGE_BEVEL_OUT));
140 }
141
142 /* Handle the borders... */
143 redisplay_clear_region (window, toolbar_findex,
144 sx, sy,
145 (tb->vertical ? border_width : swidth),
146 (tb->vertical ? sheight : border_width));
147 redisplay_clear_region (window, toolbar_findex,
148 (tb->vertical ? sx + swidth : sx),
149 (tb->vertical ? sy : sy + sheight),
150 (tb->vertical ? border_width : swidth),
151 (tb->vertical ? sheight : border_width));
152 }
153
154 #define common_draw_blank_toolbar_button(f,tb) __prepare_button_area (f,tb)
155
156 void
157 common_output_toolbar_button (struct frame *f, Lisp_Object button)
158 {
159 int shadow_thickness = 2;
160 int x_adj, y_adj, width_adj, height_adj;
161 struct device *d = XDEVICE (f->device);
162 Lisp_Object instance, frame, window, glyph;
163 struct toolbar_button *tb = XTOOLBAR_BUTTON (button);
164 struct Lisp_Image_Instance *p;
165 struct window *w;
166 int vertical = tb->vertical;
167 int border_width = tb->border_width;
168 face_index toolbar_findex;
169
170 if (vertical)
171 {
172 x_adj = border_width;
173 width_adj = - 2 * border_width;
174 y_adj = 0;
175 height_adj = 0;
176 }
177 else
178 {
179 x_adj = 0;
180 width_adj = 0;
181 y_adj = border_width;
182 height_adj = - 2 * border_width;
183 }
184
185 XSETFRAME (frame, f);
186 window = FRAME_LAST_NONMINIBUF_WINDOW (f);
187 w = XWINDOW (window);
188
189 glyph = get_toolbar_button_glyph (w, tb);
190
191 if (tb->enabled)
192 {
193 if (tb->down)
194 {
195 shadow_thickness = -2;
196 }
197 else
198 {
199 shadow_thickness = 2;
200 }
201 }
202 else
203 {
204 shadow_thickness = 0;
205 }
206
207 toolbar_findex = get_builtin_face_cache_index (w, Vtoolbar_face);
208
209 __prepare_button_area (f, tb);
210
211 /* #### It is currently possible for users to trash us by directly
212 changing the toolbar glyphs. Avoid crashing in that case. */
213 if (GLYPHP (glyph))
214 instance = glyph_image_instance (glyph, window, ERROR_ME_NOT, 1);
215 else
216 instance = Qnil;
217
218 if (IMAGE_INSTANCEP (instance))
219 {
220 int width = tb->width + width_adj - shadow_thickness * 2;
221 int height = tb->height + height_adj - shadow_thickness * 2;
222 int x_offset = x_adj + shadow_thickness;
223 int y_offset = y_adj + shadow_thickness;
224
225 p = XIMAGE_INSTANCE (instance);
226
227 if (IMAGE_INSTANCE_PIXMAP_TYPE_P (p))
228 {
229 struct display_box db;
230 struct display_glyph_area dga;
231
232 if (width > (int) IMAGE_INSTANCE_PIXMAP_WIDTH (p))
233 {
234 x_offset += ((int) (width - IMAGE_INSTANCE_PIXMAP_WIDTH (p))
235 / 2);
236 width = IMAGE_INSTANCE_PIXMAP_WIDTH (p);
237 }
238 if (height > (int) IMAGE_INSTANCE_PIXMAP_HEIGHT (p))
239 {
240 y_offset += ((int) (height - IMAGE_INSTANCE_PIXMAP_HEIGHT (p))
241 / 2);
242 height = IMAGE_INSTANCE_PIXMAP_HEIGHT (p);
243 }
244
245 /* Draw exactly in the area specified... */
246 db.xpos = tb->x + x_offset;
247 db.ypos = tb->y + y_offset;
248 db.width = width;
249 db.height = height;
250
251 /* Display the whole glyph */
252 dga.xoffset = 0;
253 dga.yoffset = 0;
254 dga.width = width;
255 dga.height = height;
256
257 redisplay_output_pixmap (w, instance,
258 &db, &dga,
259 toolbar_findex, 0, 0, 0, 0);
260 }
261 else if (IMAGE_INSTANCE_TYPE (p) == IMAGE_TEXT)
262 {
263 /* #### We need to make the face used configurable. */
264 struct face_cachel *cachel =
265 WINDOW_FACE_CACHEL (w, DEFAULT_INDEX);
266 struct display_line dl;
267 Lisp_Object string = IMAGE_INSTANCE_TEXT_STRING (p);
268 unsigned char charsets[NUM_LEADING_BYTES];
269 Emchar_dynarr *buf;
270 struct font_metric_info fm;
271
272 /* This could be true if we were called via the Expose event
273 handler. Mark the button as dirty and return
274 immediately. */
275 if (f->window_face_cache_reset)
276 {
277 tb->dirty = 1;
278 MARK_TOOLBAR_CHANGED;
279 return;
280 }
281 buf = Dynarr_new (Emchar);
282 convert_intbyte_string_into_emchar_dynarr
283 (XSTRING_DATA (string), XSTRING_LENGTH (string), buf);
284 find_charsets_in_emchar_string (charsets, Dynarr_atp (buf, 0),
285 Dynarr_length (buf));
286 ensure_face_cachel_complete (cachel, window, charsets);
287 face_cachel_charset_font_metric_info (cachel, charsets, &fm);
288
289 dl.ascent = fm.ascent;
290 dl.descent = fm.descent;
291 dl.ypos = tb->y + y_offset + fm.ascent;
292
293 if (fm.ascent + fm.descent <= height)
294 {
295 dl.ypos += (height - fm.ascent - fm.descent) / 2;
296 dl.clip = 0;
297 }
298 else
299 {
300 dl.clip = fm.ascent + fm.descent - height;
301 }
302
303 MAYBE_DEVMETH (d, output_string,
304 (w, &dl, buf, tb->x + x_offset, 0, 0, width,
305 toolbar_findex, 0, 0, 0, 0));
306 Dynarr_free (buf);
307 }
308
309 /* We silently ignore the image if it isn't a pixmap or text. */
310 }
311
312 tb->dirty = 0;
313 }
314
315 static int
316 common_get_button_size (struct frame *f, Lisp_Object window,
317 struct toolbar_button *tb, int vert, int pos)
318 {
319 int shadow_thickness = 2;
320 int size;
321
322 if (tb->blank)
323 {
324 if (!NILP (tb->down_glyph))
325 size = XINT (tb->down_glyph);
326 else
327 size = DEFAULT_TOOLBAR_BLANK_SIZE;
328 }
329 else
330 {
331 struct window *w = XWINDOW (window);
332 Lisp_Object glyph = get_toolbar_button_glyph (w, tb);
333
334 /* Unless, of course, the user has done something stupid like
335 change the glyph out from under us. Use a blank placeholder
336 in that case. */
337 if (NILP (glyph))
338 return XINT (f->toolbar_size[pos]);
339
340 if (vert)
341 size = glyph_height (glyph, window);
342 else
343 size = glyph_width (glyph, window);
344 }
345
346 if (!size)
347 {
348 /* If the glyph doesn't have a size we'll insert a blank
349 placeholder instead. */
350 return XINT (f->toolbar_size[pos]);
351 }
352
353 size += shadow_thickness * 2;
354
355 return (size);
356 }
357
358 #define COMMON_OUTPUT_BUTTONS_LOOP(left) \
359 do { \
360 while (!NILP (button)) \
361 { \
362 struct toolbar_button *tb = XTOOLBAR_BUTTON (button); \
363 int size, height, width; \
364 \
365 if (left && tb->pushright) \
366 break; \
367 \
368 size = common_get_button_size (f, window, tb, vert, pos); \
369 \
370 if (vert) \
371 { \
372 width = bar_width; \
373 if (y + size > max_pixpos) \
374 height = max_pixpos - y; \
375 else \
376 height = size; \
377 } \
378 else \
379 { \
380 if (x + size > max_pixpos) \
381 width = max_pixpos - x; \
382 else \
383 width = size; \
384 height = bar_height; \
385 } \
386 \
387 if (tb->x != x \
388 || tb->y != y \
389 || tb->width != width \
390 || tb->height != height \
391 || tb->dirty) \
392 { \
393 if (width && height) \
394 { \
395 tb->x = x; \
396 tb->y = y; \
397 tb->width = width; \
398 tb->height = height; \
399 tb->border_width = border_width; \
400 tb->vertical = vert; \
401 \
402 if (tb->blank || NILP (tb->up_glyph)) \
403 { \
404 common_draw_blank_toolbar_button (f, tb); \
405 } \
406 else \
407 common_output_toolbar_button (f, button); \
408 } \
409 } \
410 \
411 if (vert) \
412 y += height; \
413 else \
414 x += width; \
415 \
416 if ((vert && y == max_pixpos) || (!vert && x == max_pixpos)) \
417 button = Qnil; \
418 else \
419 button = tb->next; \
420 } \
421 } while (0)
422
423 #define SET_TOOLBAR_WAS_VISIBLE_FLAG(frame, pos, flag) \
424 do { \
425 switch (pos) \
426 { \
427 case TOP_TOOLBAR: \
428 (frame)->top_toolbar_was_visible = flag; \
429 break; \
430 case BOTTOM_TOOLBAR: \
431 (frame)->bottom_toolbar_was_visible = flag; \
432 break; \
433 case LEFT_TOOLBAR: \
434 (frame)->left_toolbar_was_visible = flag; \
435 break; \
436 case RIGHT_TOOLBAR: \
437 (frame)->right_toolbar_was_visible = flag; \
438 break; \
439 default: \
440 abort (); \
441 } \
442 } while (0)
443
444 static void
445 common_output_toolbar (struct frame *f, enum toolbar_pos pos)
446 {
447 int x, y, bar_width, bar_height, vert;
448 int max_pixpos, right_size, right_start, blank_size;
449 int border_width = FRAME_REAL_TOOLBAR_BORDER_WIDTH (f, pos);
450 Lisp_Object button, window;
451 face_index toolbar_findex;
452
453 get_toolbar_coords (f, pos, &x, &y, &bar_width, &bar_height, &vert, 1);
454 window = FRAME_LAST_NONMINIBUF_WINDOW (f);
455 toolbar_findex = get_builtin_face_cache_index (XWINDOW (window), Vtoolbar_face);
456
457 /* Do the border */
458 redisplay_clear_region (window, toolbar_findex,
459 x, y,
460 (vert ? bar_width : border_width),
461 (vert ? border_width : bar_height));
462 redisplay_clear_region (window, toolbar_findex,
463 (vert ? x : x + bar_width - border_width),
464 (vert ? y + bar_height - border_width : y),
465 (vert ? bar_width : border_width),
466 (vert ? border_width : bar_height));
467
468 if (vert)
469 {
470 max_pixpos = y + bar_height - border_width;
471 y += border_width;
472 }
473 else
474 {
475 max_pixpos = x + bar_width - border_width;
476 x += border_width;
477 }
478
479 button = FRAME_TOOLBAR_BUTTONS (f, pos);
480 right_size = 0;
481
482 /* First loop over all of the buttons to determine how much room we
483 need for left hand and right hand buttons. This loop will also
484 make sure that all instances are instantiated so when we actually
485 output them they will come up immediately. */
486 while (!NILP (button))
487 {
488 struct toolbar_button *tb = XTOOLBAR_BUTTON (button);
489 int size = common_get_button_size (f, window, tb, vert, pos);
490
491 if (tb->pushright)
492 right_size += size;
493
494 button = tb->next;
495 }
496
497 button = FRAME_TOOLBAR_BUTTONS (f, pos);
498
499 /* Loop over the left buttons, updating and outputting them. */
500 COMMON_OUTPUT_BUTTONS_LOOP (1);
501
502 /* Now determine where the right buttons start. */
503 right_start = max_pixpos - right_size;
504 if (right_start < (vert ? y : x))
505 right_start = (vert ? y : x);
506
507 /* Output the blank which goes from the end of the left buttons to
508 the start of the right. */
509 blank_size = right_start - (vert ? y : x);
510 if (blank_size)
511 {
512 int height, width;
513
514 if (vert)
515 {
516 width = bar_width;
517 height = blank_size;
518 }
519 else
520 {
521 width = blank_size;
522 height = bar_height;
523 }
524
525 /*
526 * Use a 3D pushright separator only if there isn't a toolbar
527 * border. A flat separator meshes with the border and looks
528 * better.
529 */
530 if (1)
531 {
532 struct toolbar_button tb;
533
534 tb.x = x;
535 tb.y = y;
536 tb.width = width;
537 tb.height = height;
538 tb.border_width = border_width;
539 tb.vertical = vert;
540 tb.enabled = 1;
541 tb.up_glyph = border_width ? Qt : Qnil;
542
543 __prepare_button_area (f, &tb);
544 }
545
546 if (vert)
547 y += height;
548 else
549 x += width;
550 }
551
552 /* Loop over the right buttons, updating and outputting them. */
553 COMMON_OUTPUT_BUTTONS_LOOP (0);
554
555 if (!vert)
556 {
557 Lisp_Object frame;
558
559 XSETFRAME (frame, f);
560 redisplay_clear_region (frame,
561 DEFAULT_INDEX, FRAME_PIXWIDTH (f) - 1, y, 1,
562 bar_height);
563 }
564
565 SET_TOOLBAR_WAS_VISIBLE_FLAG (f, pos, 1);
566 __INTERNAL_FLUSH (XDEVICE (f->device));
567 }
568
569 static void
570 common_clear_toolbar (struct frame *f, enum toolbar_pos pos, int thickness_change)
571 {
572 Lisp_Object frame;
573 int x, y, width, height, vert;
574
575 get_toolbar_coords (f, pos, &x, &y, &width, &height, &vert, 1);
576 XSETFRAME (frame, f);
577
578 /* The thickness_change parameter is used by the toolbar resize routines
579 to clear any excess toolbar if the size shrinks. */
580 if (thickness_change < 0)
581 {
582 if (pos == LEFT_TOOLBAR || pos == RIGHT_TOOLBAR)
583 {
584 x = x + width + thickness_change;
585 width = -thickness_change;
586 }
587 else
588 {
589 y = y + height + thickness_change;
590 height = -thickness_change;
591 }
592 }
593
594 SET_TOOLBAR_WAS_VISIBLE_FLAG (f, pos, 0);
595
596 redisplay_clear_region (frame, DEFAULT_INDEX, x, y, width, height);
597
598 __INTERNAL_FLUSH (XDEVICE (f->device));
599 }
600
601 void
602 common_output_frame_toolbars (struct frame *f)
603 {
604 __INTERNAL_APPROPRIATENESS_CHECK(f);
605
606 if (FRAME_REAL_TOP_TOOLBAR_VISIBLE (f))
607 common_output_toolbar (f, TOP_TOOLBAR);
608 else if (f->top_toolbar_was_visible)
609 common_clear_toolbar (f, TOP_TOOLBAR, 0);
610
611 if (FRAME_REAL_BOTTOM_TOOLBAR_VISIBLE (f))
612 common_output_toolbar (f, BOTTOM_TOOLBAR);
613 else if (f->bottom_toolbar_was_visible)
614 common_clear_toolbar (f, BOTTOM_TOOLBAR, 0);
615
616 if (FRAME_REAL_LEFT_TOOLBAR_VISIBLE (f))
617 common_output_toolbar (f, LEFT_TOOLBAR);
618 else if (f->left_toolbar_was_visible)
619 common_clear_toolbar (f, LEFT_TOOLBAR, 0);
620
621 if (FRAME_REAL_RIGHT_TOOLBAR_VISIBLE (f))
622 common_output_toolbar (f, RIGHT_TOOLBAR);
623 else if (f->right_toolbar_was_visible)
624 common_clear_toolbar (f, RIGHT_TOOLBAR, 0);
625 }
626
627 static void
628 common_redraw_exposed_toolbar (struct frame *f, enum toolbar_pos pos, int x, int y,
629 int width, int height)
630 {
631 int bar_x, bar_y, bar_width, bar_height, vert;
632 Lisp_Object button = FRAME_TOOLBAR_BUTTONS (f, pos);
633
634 get_toolbar_coords (f, pos, &bar_x, &bar_y, &bar_width, &bar_height,
635 &vert, 1);
636
637 if (((y + height) < bar_y) || (y > (bar_y + bar_height)))
638 return;
639 if (((x + width) < bar_x) || (x > (bar_x + bar_width)))
640 return;
641
642 while (!NILP (button))
643 {
644 struct toolbar_button *tb = XTOOLBAR_BUTTON (button);
645
646 if (vert)
647 {
648 if (((tb->y + tb->height) > y) && (tb->y < (y + height)))
649 tb->dirty = 1;
650
651 /* If this is true we have gone past the exposed region. */
652 if (tb->y > (y + height))
653 break;
654 }
655 else
656 {
657 if (((tb->x + tb->width) > x) && (tb->x < (x + width)))
658 tb->dirty = 1;
659
660 /* If this is true we have gone past the exposed region. */
661 if (tb->x > (x + width))
662 break;
663 }
664
665 button = tb->next;
666 }
667
668 /* Even if none of the buttons is in the area, the blank region at
669 the very least must be because the first thing we did is verify
670 that some portion of the toolbar is in the exposed region. */
671 common_output_toolbar (f, pos);
672 }
673
674 void
675 common_redraw_exposed_toolbars (struct frame *f, int x, int y, int width,
676 int height)
677 {
678 __INTERNAL_APPROPRIATENESS_CHECK(f);
679
680 if (FRAME_REAL_TOP_TOOLBAR_VISIBLE (f))
681 common_redraw_exposed_toolbar (f, TOP_TOOLBAR, x, y, width, height);
682
683 if (FRAME_REAL_BOTTOM_TOOLBAR_VISIBLE (f))
684 common_redraw_exposed_toolbar (f, BOTTOM_TOOLBAR, x, y, width, height);
685
686 if (FRAME_REAL_LEFT_TOOLBAR_VISIBLE (f))
687 common_redraw_exposed_toolbar (f, LEFT_TOOLBAR, x, y, width, height);
688
689 if (FRAME_REAL_RIGHT_TOOLBAR_VISIBLE (f))
690 common_redraw_exposed_toolbar (f, RIGHT_TOOLBAR, x, y, width, height);
691 }
692
693 void
694 common_redraw_frame_toolbars (struct frame *f)
695 {
696 /* There are certain startup paths that lead to update_EmacsFrame in
697 faces.c being called before a new frame is fully initialized. In
698 particular before we have actually mapped it. That routine can
699 call this one. So, we need to make sure that the frame is
700 actually ready before we try and draw all over it. */
701
702 if (__INTERNAL_MAPPED_P(f))
703 common_redraw_exposed_toolbars (f, 0, 0, FRAME_PIXWIDTH (f),
704 FRAME_PIXHEIGHT (f));
705 }
706