2 Copyright (c) 1997 Douglas Keller
4 This file is part of SXEmacs
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 /* Synched up with: Not in FSF. */
25 * Version: 1.337 (Sun Apr 13 04:52:10 1997)
27 * Written by Douglas Keller <dkeller@vnet.ibm.com>
39 #include <X11/Xutil.h>
40 #include <X11/extensions/shape.h>
42 #include "xintrinsic.h"
44 #include "balloon_help.h"
47 #define max(x,y) (x>y?x:y)
53 #define MARGIN_WIDTH 4
54 #define POINTER_OFFSET 8
55 #define BORDER_WIDTH 2
56 #define BORDER_WIDTH_HALF 1
58 #define CONE_HEIGHT 20
61 #define SHAPE_CONE_TOP (1<<0)
62 #define SHAPE_CONE_LEFT (1<<1)
63 #define SHAPE_CONE_TOP_LEFT (SHAPE_CONE_TOP | SHAPE_CONE_LEFT)
64 #define SHAPE_CONE_TOP_RIGHT (SHAPE_CONE_TOP)
65 #define SHAPE_CONE_BOTTOM_LEFT (SHAPE_CONE_LEFT)
66 #define SHAPE_CONE_BOTTOM_RIGHT (0)
67 #define SHAPE_CONE_FREE (-1)
69 static Display *b_dpy;
71 static XFontStruct *b_fontStruct;
78 static bool b_winMapped;
81 static int b_maskWidth, b_maskHeight;
84 static const char *b_text;
85 static int b_width, b_height;
87 static XtIntervalId b_timer;
88 static unsigned long b_delay;
90 static int b_screenWidth, b_screenHeight;
92 static int b_lastShape;
94 /*============================================================================
96 ============================================================================*/
99 create_gc(Display * dpy, Window win, unsigned long fg, unsigned long bg,
100 XFontStruct * fontStruct)
107 gcv.font = fontStruct->fid;
108 gcv.join_style = JoinMiter;
109 gcv.line_width = BORDER_WIDTH;
111 mask = GCFont | GCBackground | GCForeground | GCJoinStyle | GCLineWidth;
113 return XCreateGC(dpy, win, mask, &gcv);
116 static void destroy_gc(Display * dpy, GC gc)
123 /*============================================================================
125 ============================================================================*/
127 static Window create_window(Display * dpy, unsigned long bg)
130 XSetWindowAttributes attr;
131 unsigned long attr_mask;
133 attr_mask = CWOverrideRedirect | CWBackPixel | CWSaveUnder;
134 attr.override_redirect = True;
135 attr.background_pixel = bg;
136 attr.save_under = True;
140 DefaultRootWindow(dpy),
143 CopyFromParent, InputOutput, CopyFromParent,
146 XSelectInput(dpy, win,
147 SubstructureRedirectMask |
148 SubstructureNotifyMask |
149 ExposureMask | EnterWindowMask | LeaveWindowMask);
153 static void destroy_window(Display * dpy, Window win)
156 XDestroyWindow(dpy, win);
160 /*============================================================================
162 ============================================================================*/
164 static void get_pointer_xy(Display * dpy, int *x_return, int *y_return)
170 XQueryPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)), &dummy_win,
171 &dummy_win, x_return, y_return, &dummy, &dummy, &mask);
174 /*============================================================================
176 ============================================================================*/
178 static void create_pixmap_mask(int width, int height)
181 b_maskHeight = height;
182 b_mask = XCreatePixmap(b_dpy, b_win, width, height, 1);
185 static void destroy_pixmap_mask(void)
187 XFreePixmap(b_dpy, b_mask);
190 static void grow_pixmap_mask(int width, int height)
192 if (width > b_maskWidth || height > b_maskHeight) {
193 destroy_pixmap_mask();
194 create_pixmap_mask(width, height);
198 /*============================================================================
200 ============================================================================*/
203 text_extent(XFontStruct * fontStruct, const char *text, int len,
204 int *width, int *height)
209 XTextExtents(fontStruct, text, len, &dummy, &dummy, &dummy, &extent);
211 *width = extent.width;
212 *height = fontStruct->ascent + fontStruct->descent;
216 get_text_size(Display * dpy, XFontStruct * fontStruct, const char *text,
217 int *max_width, int *max_height)
224 *max_width = *max_height = 0;
227 while ((end = strchr(start, '\n'))) {
228 text_extent(fontStruct, start, end - start, &width, &height);
229 *max_width = max(width, *max_width);
230 *max_height += height;
234 text_extent(fontStruct, start, strlen(start), &width, &height);
235 *max_width = max(width, *max_width);
236 *max_height += height;
239 *max_width = max(*max_width, CONE_WIDTH / 2 * 3);
244 draw_text(Display * dpy, Window win, GC gc, XFontStruct * fontStruct,
245 int x, int y, const char *text)
251 y += fontStruct->ascent;
253 font_height = fontStruct->ascent + fontStruct->descent;
256 while ((end = strchr(start, '\n'))) {
257 XDrawString(dpy, win, gc, x, y, start, end - start);
262 XDrawString(dpy, win, gc, x, y, start, strlen(start));
265 /*============================================================================
267 ============================================================================*/
270 get_shape(int last_shape, int x, int y, int width, int height,
271 int screen_width, int screen_height)
273 /* Can we use last_shape? */
274 if (((last_shape == SHAPE_CONE_TOP_LEFT) &&
275 (x + width < screen_width) && (y + height < screen_height)) ||
276 ((last_shape == SHAPE_CONE_TOP_RIGHT) &&
277 (x - width > 0) && (y + height < screen_height)) ||
278 ((last_shape == SHAPE_CONE_BOTTOM_LEFT) &&
279 (x + width < screen_width) && (y - height > 0)) ||
280 ((last_shape == SHAPE_CONE_BOTTOM_RIGHT) &&
281 (x - width > 0) && (y - height > 0)))
284 /* Try to pick a shape that will not get changed,
285 e.g. if top left quadrant, top_left */
286 return (x < screen_width / 2) ?
289 2 ? SHAPE_CONE_TOP_LEFT : SHAPE_CONE_BOTTOM_LEFT) : (y <
295 SHAPE_CONE_BOTTOM_RIGHT);
298 static void make_mask(int shape, int x, int y, int width, int height)
302 grow_pixmap_mask(width, height);
305 XSetForeground(b_dpy, b_maskGC, 0);
306 XFillRectangle(b_dpy, b_mask, b_maskGC, 0, 0, width, height);
308 /* Enable text area */
309 XSetForeground(b_dpy, b_maskGC, 1);
310 XFillRectangle(b_dpy, b_mask, b_maskGC, 0,
311 shape & SHAPE_CONE_TOP ? CONE_HEIGHT : 0, width,
312 height - CONE_HEIGHT);
314 /* Enable for cone area */
316 (shape & SHAPE_CONE_LEFT) ? CONE_WIDTH / 2 : width -
319 (shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : height - CONE_HEIGHT;
320 cone[1].x = (shape & SHAPE_CONE_LEFT) ? 0 : width;
321 cone[1].y = (shape & SHAPE_CONE_TOP) ? 0 : height;
322 cone[2].x = (shape & SHAPE_CONE_LEFT) ? CONE_WIDTH : width - CONE_WIDTH;
324 (shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : height - CONE_HEIGHT;
326 XFillPolygon(b_dpy, b_mask, b_maskGC, cone, 3, Nonconvex,
331 static void show_help(XtPointer data, XtIntervalId * id)
337 if (id == NULL || ((id && b_timer) && b_text)) {
341 get_text_size(b_dpy, b_fontStruct, b_text, &b_width, &b_height);
342 b_width += 2 * MARGIN_WIDTH + 2 * BORDER_WIDTH;
343 b_height += 2 * MARGIN_WIDTH + 2 * BORDER_WIDTH + CONE_HEIGHT;
346 get_pointer_xy(b_dpy, &x, &y);
349 shape = get_shape(b_lastShape, x, y, b_width, b_height,
350 b_screenWidth, b_screenHeight);
352 x += (shape & SHAPE_CONE_LEFT) ? POINTER_OFFSET :
354 y += (shape & SHAPE_CONE_TOP) ? POINTER_OFFSET :
357 /* make sure it is still ok with offset */
359 get_shape(shape, x, y, b_width, b_height, b_screenWidth,
364 make_mask(shape, x, y, b_width, b_height);
366 XShapeCombineMask(b_dpy, b_win, ShapeBounding, 0, 0, b_mask,
369 XMoveResizeWindow(b_dpy, b_win,
370 (shape & SHAPE_CONE_LEFT) ? x : x - b_width,
371 (shape & SHAPE_CONE_TOP) ? y : y - b_height,
374 XClearWindow(b_dpy, b_win);
376 XMapRaised(b_dpy, b_win);
379 draw_text(b_dpy, b_win, b_gc, b_fontStruct,
380 BORDER_WIDTH + MARGIN_WIDTH,
381 BORDER_WIDTH + MARGIN_WIDTH +
382 ((shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : 0), b_text);
385 /* shine- top left */
386 border[0].x = 0 + BORDER_WIDTH_HALF;
388 ((shape & SHAPE_CONE_TOP) ? b_height : b_height -
389 CONE_HEIGHT) - BORDER_WIDTH_HALF;
390 border[1].x = 0 + BORDER_WIDTH_HALF;
392 ((shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : 0) +
394 border[2].x = b_width - BORDER_WIDTH_HALF;
395 border[2].y = border[1].y;
396 XDrawLines(b_dpy, b_win, b_shineGC, border, 3, CoordModeOrigin);
398 /* shadow- bottom right */
399 border[0].x = 0 + BORDER_WIDTH_HALF;
401 ((shape & SHAPE_CONE_TOP) ? b_height : b_height -
402 CONE_HEIGHT) - BORDER_WIDTH_HALF;
403 border[1].x = b_width - BORDER_WIDTH_HALF;
404 border[1].y = border[0].y;
405 border[2].x = b_width - BORDER_WIDTH_HALF;
407 ((shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : 0) +
409 XDrawLines(b_dpy, b_win, b_shadowGC, border, 3,
413 if (SHAPE_CONE_TOP_LEFT == shape) {
414 XClearArea(b_dpy, b_win,
415 CONE_WIDTH / 2 + BORDER_WIDTH,
417 CONE_WIDTH / 2 - BORDER_WIDTH,
418 BORDER_WIDTH, False);
419 XDrawLine(b_dpy, b_win, b_shadowGC,
422 CONE_WIDTH / 2 + BORDER_WIDTH_HALF,
424 XDrawLine(b_dpy, b_win, b_shineGC,
427 CONE_WIDTH - BORDER_WIDTH_HALF, CONE_HEIGHT);
428 } else if (SHAPE_CONE_TOP_RIGHT == shape) {
429 XClearArea(b_dpy, b_win,
430 b_width - CONE_WIDTH + BORDER_WIDTH,
432 CONE_WIDTH / 2 - BORDER_WIDTH,
433 BORDER_WIDTH, False);
434 XDrawLine(b_dpy, b_win, b_shadowGC,
437 b_width - CONE_WIDTH / 2 - BORDER_WIDTH_HALF,
439 XDrawLine(b_dpy, b_win, b_shineGC,
442 b_width - CONE_WIDTH + BORDER_WIDTH_HALF,
444 } else if (SHAPE_CONE_BOTTOM_LEFT == shape) {
445 XClearArea(b_dpy, b_win,
446 CONE_WIDTH / 2 + BORDER_WIDTH,
447 b_height - CONE_HEIGHT - BORDER_WIDTH,
448 CONE_WIDTH / 2 - BORDER_WIDTH,
449 BORDER_WIDTH, False);
450 XDrawLine(b_dpy, b_win, b_shadowGC,
453 CONE_WIDTH, b_height - 1 - CONE_HEIGHT);
454 XDrawLine(b_dpy, b_win, b_shineGC,
457 CONE_WIDTH / 2 + BORDER_WIDTH,
458 b_height - 1 - CONE_HEIGHT);
459 } else if (SHAPE_CONE_BOTTOM_RIGHT == shape) {
460 XClearArea(b_dpy, b_win,
461 b_width - 1 - CONE_WIDTH + BORDER_WIDTH,
462 b_height - CONE_HEIGHT - BORDER_WIDTH,
463 CONE_WIDTH / 2 - BORDER_WIDTH - 1,
464 BORDER_WIDTH, False);
465 XDrawLine(b_dpy, b_win, b_shadowGC,
468 b_width - 1 - CONE_WIDTH,
469 b_height - 1 - CONE_HEIGHT);
470 XDrawLine(b_dpy, b_win, b_shineGC,
473 b_width - 1 - CONE_WIDTH / 2 - BORDER_WIDTH,
474 b_height - 1 - CONE_HEIGHT);
480 /*============================================================================
482 ============================================================================*/
484 static void balloon_help_destroy(void)
486 assert(b_dpy != NULL);
489 destroy_window(b_dpy, b_win);
490 destroy_gc(b_dpy, b_gc);
492 destroy_gc(b_dpy, b_shineGC);
493 destroy_gc(b_dpy, b_shadowGC);
495 destroy_pixmap_mask();
496 destroy_gc(b_dpy, b_maskGC);
499 XtRemoveTimeOut(b_timer);
503 balloon_help_create(Display * dpy,
504 Pixel fg, Pixel bg, Pixel shine, Pixel shadow,
508 balloon_help_destroy();
514 b_win = create_window(dpy, bg);
515 b_gc = create_gc(dpy, b_win, fg, bg, b_fontStruct);
517 b_shineGC = create_gc(dpy, b_win, shine, bg, b_fontStruct);
518 b_shadowGC = create_gc(dpy, b_win, shadow, bg, b_fontStruct);
520 create_pixmap_mask(1, 1);
521 b_maskGC = create_gc(dpy, b_mask, bg, fg, b_fontStruct);
527 b_screenWidth = DisplayWidth(b_dpy, DefaultScreen(b_dpy));
528 b_screenHeight = DisplayHeight(b_dpy, DefaultScreen(b_dpy));
530 b_lastShape = SHAPE_CONE_FREE;
533 void balloon_help_set_delay(unsigned long milliseconds)
535 b_delay = milliseconds;
538 void balloon_help_show(const char *text)
540 assert(b_dpy != NULL);
542 /* We don't copy the text */
544 b_lastShape = SHAPE_CONE_FREE;
547 /* If help is already being shown, don't delay just update */
548 show_help(NULL, NULL);
551 XtAppAddTimeOut(XtDisplayToApplicationContext(b_dpy),
552 b_delay, show_help, NULL);
556 void balloon_help_hide(void)
558 assert(b_dpy != NULL);
561 XUnmapWindow(b_dpy, b_win);
564 XtRemoveTimeOut(b_timer);
569 void balloon_help_move_to_pointer(void)
571 assert(b_dpy != NULL);
575 int shape = b_lastShape;
577 get_pointer_xy(b_dpy, &x, &y);
579 x += (shape & SHAPE_CONE_LEFT) ? POINTER_OFFSET :
581 y += (shape & SHAPE_CONE_TOP) ? POINTER_OFFSET :
585 get_shape(shape, x, y, b_width, b_height, b_screenWidth,
588 if (shape == b_lastShape) {
589 XMoveWindow(b_dpy, b_win,
590 shape & SHAPE_CONE_LEFT ? x : x - b_width,
591 shape & SHAPE_CONE_TOP ? y : y - b_height);
593 /* text would be off screen, rebuild with new shape */
594 b_lastShape = SHAPE_CONE_FREE;
595 show_help(NULL, NULL);