Mercurial > hg > xemacs-beta
comparison src/balloon_help.c @ 134:34a5b81f86ba r20-2b1
Import from CVS: tag r20-2b1
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:30:11 +0200 |
parents | |
children | b980b6286996 |
comparison
equal
deleted
inserted
replaced
133:b27e67717092 | 134:34a5b81f86ba |
---|---|
1 /* | |
2 * Balloon Help | |
3 * | |
4 * Version: 1.337 (Sun Apr 13 04:52:10 1997) | |
5 * | |
6 * Written by Douglas Keller <dkeller@vnet.ibm.com> | |
7 * | |
8 * | |
9 */ | |
10 | |
11 #include <string.h> | |
12 #include <stdio.h> | |
13 #include <stdlib.h> | |
14 #include <assert.h> | |
15 | |
16 #include <X11/Xlib.h> | |
17 #include <X11/Xutil.h> | |
18 #include <X11/extensions/shape.h> | |
19 | |
20 #include <X11/Intrinsic.h> | |
21 | |
22 #include "balloon_help.h" | |
23 | |
24 #define max(x,y) (x>y?x:y) | |
25 | |
26 #undef bool | |
27 #define bool int | |
28 | |
29 #define MARGIN_WIDTH 4 | |
30 #define POINTER_OFFSET 8 | |
31 #define BORDER_WIDTH 2 | |
32 #define BORDER_WIDTH_HALF 1 | |
33 | |
34 #define CONE_HEIGHT 20 | |
35 #define CONE_WIDTH 50 | |
36 | |
37 #define SHAPE_CONE_TOP (1<<0) | |
38 #define SHAPE_CONE_LEFT (1<<1) | |
39 #define SHAPE_CONE_TOP_LEFT (SHAPE_CONE_TOP | SHAPE_CONE_LEFT) | |
40 #define SHAPE_CONE_TOP_RIGHT (SHAPE_CONE_TOP) | |
41 #define SHAPE_CONE_BOTTOM_LEFT (SHAPE_CONE_LEFT) | |
42 #define SHAPE_CONE_BOTTOM_RIGHT (0) | |
43 #define SHAPE_CONE_FREE (-1) | |
44 | |
45 | |
46 static Display* b_dpy; | |
47 | |
48 static XFontStruct* b_fontStruct; | |
49 static GC b_gc; | |
50 | |
51 static GC b_shineGC; | |
52 static GC b_shadowGC; | |
53 | |
54 static Window b_win; | |
55 static bool b_winMapped; | |
56 | |
57 static Pixmap b_mask; | |
58 static int b_maskWidth, b_maskHeight; | |
59 static GC b_maskGC; | |
60 | |
61 static const char* b_text; | |
62 static int b_width, b_height; | |
63 | |
64 static int b_lastX, b_lastY; | |
65 | |
66 static XtIntervalId b_timer; | |
67 static unsigned long b_delay; | |
68 | |
69 static int b_screenWidth, b_screenHeight; | |
70 | |
71 static int b_lastShape; | |
72 | |
73 /*============================================================================ | |
74 | |
75 ============================================================================*/ | |
76 | |
77 static GC create_gc( Display* dpy, Window win, unsigned long fg, unsigned long bg, | |
78 XFontStruct* fontStruct ) | |
79 { | |
80 XGCValues gcv; | |
81 unsigned long mask; | |
82 | |
83 gcv.foreground = fg; | |
84 gcv.background = bg; | |
85 gcv.font = fontStruct->fid; | |
86 gcv.join_style = JoinMiter; | |
87 gcv.line_width = BORDER_WIDTH; | |
88 | |
89 mask = GCFont | GCBackground | GCForeground | GCJoinStyle | GCLineWidth; | |
90 | |
91 return XCreateGC( dpy, win, mask, &gcv ); | |
92 } | |
93 | |
94 static void destroy_gc( Display* dpy, GC gc ) | |
95 { | |
96 if( gc ) | |
97 { | |
98 XFreeGC( dpy, gc ); | |
99 } | |
100 } | |
101 | |
102 /*============================================================================ | |
103 | |
104 ============================================================================*/ | |
105 | |
106 static Window create_window( Display* dpy, unsigned long bg ) | |
107 { | |
108 Window win; | |
109 XSetWindowAttributes attr; | |
110 unsigned long attr_mask; | |
111 | |
112 attr_mask = CWOverrideRedirect | CWBackPixel | CWSaveUnder; | |
113 attr.override_redirect = True; | |
114 attr.background_pixel = bg; | |
115 attr.save_under = True; | |
116 | |
117 win = | |
118 XCreateWindow( dpy, | |
119 DefaultRootWindow( dpy ), | |
120 0, 0, 1, 1, | |
121 0, | |
122 CopyFromParent, InputOutput, CopyFromParent, | |
123 attr_mask, &attr ); | |
124 | |
125 XSelectInput( dpy, win, | |
126 SubstructureRedirectMask | | |
127 SubstructureNotifyMask | | |
128 ExposureMask | | |
129 EnterWindowMask | | |
130 LeaveWindowMask ); | |
131 return win; | |
132 } | |
133 | |
134 static void destroy_window( Display* dpy, Window win ) | |
135 { | |
136 if( win ) | |
137 { | |
138 XDestroyWindow( dpy, win ); | |
139 } | |
140 } | |
141 | |
142 /*============================================================================ | |
143 | |
144 ============================================================================*/ | |
145 | |
146 static void get_pointer_xy( Display* dpy, int* x_return, int* y_return ) | |
147 { | |
148 int dummy; | |
149 unsigned int mask; | |
150 Window dummy_win; | |
151 | |
152 XQueryPointer( dpy, RootWindow(dpy, DefaultScreen(dpy)), &dummy_win, &dummy_win, | |
153 x_return, y_return, &dummy, &dummy, &mask ); | |
154 } | |
155 | |
156 /*============================================================================ | |
157 | |
158 ============================================================================*/ | |
159 | |
160 static void create_pixmap_mask( int width, int height ) | |
161 { | |
162 b_maskWidth = width; | |
163 b_maskHeight = height; | |
164 b_mask = XCreatePixmap( b_dpy, b_win, width, height, 1 ); | |
165 } | |
166 | |
167 static void destroy_pixmap_mask( void ) | |
168 { | |
169 XFreePixmap( b_dpy, b_mask ); | |
170 } | |
171 | |
172 static void grow_pixmap_mask( int width, int height ) | |
173 { | |
174 if( width > b_maskWidth || height > b_maskHeight ) | |
175 { | |
176 destroy_pixmap_mask(); | |
177 create_pixmap_mask( width, height ); | |
178 } | |
179 } | |
180 | |
181 /*============================================================================ | |
182 | |
183 ============================================================================*/ | |
184 | |
185 static void text_extent( XFontStruct* fontStruct, const char* text, int len, | |
186 int* width, int* height ) | |
187 { | |
188 XCharStruct extent; | |
189 int dummy; | |
190 | |
191 XTextExtents( fontStruct, text, len, &dummy, &dummy, &dummy, &extent ); | |
192 | |
193 *width = extent.width; | |
194 *height = fontStruct->ascent + fontStruct->descent; | |
195 } | |
196 | |
197 static void get_text_size( Display* dpy, XFontStruct* fontStruct, const char* text, | |
198 int* max_width, int* max_height ) | |
199 { | |
200 int width; | |
201 int height; | |
202 const char* start; | |
203 const char* end; | |
204 | |
205 *max_width = *max_height = 0; | |
206 | |
207 start = text; | |
208 while( (end = strchr(start, '\n')) ) | |
209 { | |
210 text_extent( fontStruct, start, end - start, &width, &height ); | |
211 *max_width = max( width, *max_width ); | |
212 *max_height += height; | |
213 | |
214 start = end + 1; | |
215 } | |
216 text_extent( fontStruct, start, strlen(start), &width, &height ); | |
217 *max_width = max( width, *max_width ); | |
218 *max_height += height; | |
219 | |
220 /* Min width */ | |
221 *max_width = max( *max_width, CONE_WIDTH / 2 * 3 ); | |
222 | |
223 } | |
224 | |
225 static void draw_text( Display* dpy, Window win, GC gc, XFontStruct* fontStruct, | |
226 int x, int y, const char* text ) | |
227 { | |
228 const char* start; | |
229 const char* end; | |
230 int font_height; | |
231 | |
232 y += fontStruct->ascent; | |
233 | |
234 font_height = fontStruct->ascent + fontStruct->descent; | |
235 | |
236 start = text; | |
237 while( (end = strchr(start, '\n')) ) | |
238 { | |
239 XDrawString( dpy, win, gc, x, y, start, end - start ); | |
240 | |
241 start = end + 1; | |
242 y += font_height; | |
243 } | |
244 XDrawString( dpy, win, gc, x, y, start, strlen(start) ); | |
245 } | |
246 | |
247 /*============================================================================ | |
248 | |
249 ============================================================================*/ | |
250 | |
251 static int get_shape( int last_shape, int x, int y, int width, int height, | |
252 int screen_width, int screen_height ) | |
253 { | |
254 /* Can we use last_shape */ | |
255 if( SHAPE_CONE_TOP_LEFT == last_shape ) | |
256 { | |
257 if( (x + width < screen_width) && (y + height < screen_height) ) | |
258 { | |
259 return last_shape; | |
260 } | |
261 } | |
262 else if( SHAPE_CONE_TOP_RIGHT == last_shape ) | |
263 { | |
264 if( (x - width > 0) && (y + height < screen_height) ) | |
265 { | |
266 return last_shape; | |
267 } | |
268 } | |
269 else if( SHAPE_CONE_BOTTOM_LEFT == last_shape ) | |
270 { | |
271 if( (x + width < screen_width) && (y - height > 0) ) | |
272 { | |
273 return last_shape; | |
274 } | |
275 } | |
276 else if( SHAPE_CONE_BOTTOM_RIGHT == last_shape ) | |
277 { | |
278 if( (x - width > 0) && (y - height > 0) ) | |
279 { | |
280 return last_shape; | |
281 } | |
282 } | |
283 | |
284 /* Try to pick a shape that will not get changed, ie if top left quadrant, top_left */ | |
285 if( x < screen_width / 2 ) | |
286 { | |
287 if( y < screen_height / 2 ) | |
288 { | |
289 return SHAPE_CONE_TOP_LEFT; | |
290 } | |
291 else | |
292 { | |
293 return SHAPE_CONE_BOTTOM_LEFT; | |
294 } | |
295 } | |
296 else | |
297 { | |
298 if( y < screen_height / 2 ) | |
299 { | |
300 return SHAPE_CONE_TOP_RIGHT; | |
301 } | |
302 else | |
303 { | |
304 return SHAPE_CONE_BOTTOM_RIGHT; | |
305 } | |
306 } | |
307 | |
308 /* ### if width or height is greater than 1/2 the width or height then we might | |
309 run off the screen */ | |
310 | |
311 abort(); | |
312 | |
313 return 0; | |
314 } | |
315 | |
316 static void make_mask( int shape, int x, int y, int width, int height ) | |
317 { | |
318 XPoint cone[ 3 ]; | |
319 | |
320 grow_pixmap_mask( width, height ); | |
321 | |
322 /* Clear mask */ | |
323 XSetForeground( b_dpy, b_maskGC, 0 ); | |
324 XFillRectangle( b_dpy, b_mask, b_maskGC, | |
325 0, 0, width, height ); | |
326 | |
327 /* Enable text area */ | |
328 XSetForeground( b_dpy, b_maskGC, 1 ); | |
329 XFillRectangle( b_dpy, b_mask, b_maskGC, | |
330 0, shape & SHAPE_CONE_TOP ? CONE_HEIGHT : 0, width, height - CONE_HEIGHT ); | |
331 | |
332 /* Enable for cone area */ | |
333 cone[0].x = (shape & SHAPE_CONE_LEFT) ? CONE_WIDTH / 2 : width - (CONE_WIDTH / 2); | |
334 cone[0].y = (shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : height - CONE_HEIGHT; | |
335 cone[1].x = (shape & SHAPE_CONE_LEFT) ? 0 : width; | |
336 cone[1].y = (shape & SHAPE_CONE_TOP) ? 0 : height; | |
337 cone[2].x = (shape & SHAPE_CONE_LEFT) ? CONE_WIDTH : width - CONE_WIDTH; | |
338 cone[2].y = (shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : height - CONE_HEIGHT; | |
339 | |
340 XFillPolygon( b_dpy, b_mask, b_maskGC, cone, 3, Nonconvex, CoordModeOrigin ); | |
341 | |
342 } | |
343 | |
344 static void show_help( XtPointer data, XtIntervalId* id ) | |
345 { | |
346 int x, y; | |
347 int shape; | |
348 XPoint border[ 3 ]; | |
349 | |
350 if( id == NULL || (id && b_timer) && b_text ) | |
351 { | |
352 b_timer = None; | |
353 | |
354 /* size */ | |
355 get_text_size( b_dpy, b_fontStruct, b_text, &b_width, &b_height ); | |
356 b_width += 2 * MARGIN_WIDTH + 2 * BORDER_WIDTH; | |
357 b_height += 2 * MARGIN_WIDTH + 2 * BORDER_WIDTH + CONE_HEIGHT; | |
358 | |
359 /* origin */ | |
360 get_pointer_xy( b_dpy, &x, &y ); | |
361 | |
362 /* guess at shape */ | |
363 shape = get_shape( b_lastShape, x, y, b_width, b_height, b_screenWidth, b_screenHeight ); | |
364 | |
365 x += (shape & SHAPE_CONE_LEFT) ? POINTER_OFFSET : -POINTER_OFFSET; | |
366 y += (shape & SHAPE_CONE_TOP) ? POINTER_OFFSET : -POINTER_OFFSET; | |
367 | |
368 /* make sure it is still ok with offset */ | |
369 shape = get_shape( shape, x, y, b_width, b_height, b_screenWidth, b_screenHeight ); | |
370 | |
371 b_lastX = x; | |
372 b_lastY = y; | |
373 b_lastShape = shape; | |
374 | |
375 | |
376 make_mask( shape, x, y, b_width, b_height ); | |
377 | |
378 XShapeCombineMask( b_dpy, b_win, ShapeBounding, 0, 0, b_mask, ShapeSet ); | |
379 | |
380 XMoveResizeWindow( b_dpy, b_win, | |
381 (shape & SHAPE_CONE_LEFT) ? x : x - b_width, | |
382 (shape & SHAPE_CONE_TOP) ? y : y - b_height, | |
383 b_width, b_height ); | |
384 | |
385 XClearWindow( b_dpy, b_win ); | |
386 | |
387 XMapRaised( b_dpy, b_win ); | |
388 b_winMapped = True; | |
389 | |
390 draw_text( b_dpy, b_win, b_gc, b_fontStruct, | |
391 BORDER_WIDTH + MARGIN_WIDTH, | |
392 BORDER_WIDTH + MARGIN_WIDTH + ((shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : 0), | |
393 b_text ); | |
394 | |
395 /* 3d border */ | |
396 /* shine- top left */ | |
397 border[0].x = 0 + BORDER_WIDTH_HALF; | |
398 border[0].y = ((shape & SHAPE_CONE_TOP) ? b_height : b_height - CONE_HEIGHT) - BORDER_WIDTH_HALF; | |
399 border[1].x = 0 + BORDER_WIDTH_HALF; | |
400 border[1].y = ((shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : 0) + BORDER_WIDTH_HALF; | |
401 border[2].x = b_width - BORDER_WIDTH_HALF; | |
402 border[2].y = border[1].y; | |
403 XDrawLines( b_dpy, b_win, b_shineGC, border, 3, CoordModeOrigin ); | |
404 | |
405 /* shadow- bottom right */ | |
406 border[0].x = 0 + BORDER_WIDTH_HALF; | |
407 border[0].y = ((shape & SHAPE_CONE_TOP) ? b_height : b_height - CONE_HEIGHT) - BORDER_WIDTH_HALF; | |
408 border[1].x = b_width - BORDER_WIDTH_HALF; | |
409 border[1].y = border[0].y; | |
410 border[2].x = b_width - BORDER_WIDTH_HALF; | |
411 border[2].y = ((shape & SHAPE_CONE_TOP) ? CONE_HEIGHT : 0) + BORDER_WIDTH_HALF; | |
412 XDrawLines( b_dpy, b_win, b_shadowGC, border, 3, CoordModeOrigin ); | |
413 | |
414 /* cone */ | |
415 if( SHAPE_CONE_TOP_LEFT == shape ) | |
416 { | |
417 XClearArea( b_dpy, b_win, | |
418 CONE_WIDTH / 2 + BORDER_WIDTH, | |
419 CONE_HEIGHT, | |
420 CONE_WIDTH / 2 - BORDER_WIDTH, | |
421 BORDER_WIDTH, False ); | |
422 XDrawLine( b_dpy, b_win, b_shadowGC, | |
423 0, | |
424 0, | |
425 CONE_WIDTH / 2 + BORDER_WIDTH_HALF, | |
426 CONE_HEIGHT ); | |
427 XDrawLine( b_dpy, b_win, b_shineGC, | |
428 0, | |
429 0, | |
430 CONE_WIDTH - BORDER_WIDTH_HALF, | |
431 CONE_HEIGHT ); | |
432 } | |
433 else if( SHAPE_CONE_TOP_RIGHT == shape ) | |
434 { | |
435 XClearArea( b_dpy, b_win, | |
436 b_width - CONE_WIDTH + BORDER_WIDTH, | |
437 CONE_HEIGHT, | |
438 CONE_WIDTH / 2 - BORDER_WIDTH, | |
439 BORDER_WIDTH, False ); | |
440 XDrawLine( b_dpy, b_win, b_shadowGC, | |
441 b_width, | |
442 0, | |
443 b_width - CONE_WIDTH / 2 - BORDER_WIDTH_HALF, | |
444 CONE_HEIGHT ); | |
445 XDrawLine( b_dpy, b_win, b_shineGC, | |
446 b_width, | |
447 0, | |
448 b_width - CONE_WIDTH + BORDER_WIDTH_HALF, | |
449 CONE_HEIGHT ); | |
450 } | |
451 else if( SHAPE_CONE_BOTTOM_LEFT == shape ) | |
452 { | |
453 XClearArea( b_dpy, b_win, | |
454 CONE_WIDTH / 2 + BORDER_WIDTH, | |
455 b_height - CONE_HEIGHT - BORDER_WIDTH, | |
456 CONE_WIDTH / 2 - BORDER_WIDTH, | |
457 BORDER_WIDTH, False ); | |
458 XDrawLine( b_dpy, b_win, b_shadowGC, | |
459 0, | |
460 b_height - 1, | |
461 CONE_WIDTH, | |
462 b_height - 1 - CONE_HEIGHT ); | |
463 XDrawLine( b_dpy, b_win, b_shineGC, | |
464 0, | |
465 b_height - 1, | |
466 CONE_WIDTH / 2 + BORDER_WIDTH, | |
467 b_height - 1 - CONE_HEIGHT ); | |
468 } | |
469 else if( SHAPE_CONE_BOTTOM_RIGHT == shape ) | |
470 { | |
471 XClearArea( b_dpy, b_win, | |
472 b_width - 1 - CONE_WIDTH + BORDER_WIDTH, | |
473 b_height - CONE_HEIGHT - BORDER_WIDTH, | |
474 CONE_WIDTH / 2 - BORDER_WIDTH - 1, | |
475 BORDER_WIDTH, False ); | |
476 XDrawLine( b_dpy, b_win, b_shadowGC, | |
477 b_width - 1, | |
478 b_height - 1, | |
479 b_width - 1 - CONE_WIDTH, | |
480 b_height - 1 - CONE_HEIGHT ); | |
481 XDrawLine( b_dpy, b_win, b_shineGC, | |
482 b_width - 1, | |
483 b_height - 1, | |
484 b_width - 1 - CONE_WIDTH / 2 - BORDER_WIDTH, | |
485 b_height - 1 - CONE_HEIGHT); | |
486 } | |
487 } | |
488 | |
489 } | |
490 | |
491 /*============================================================================ | |
492 | |
493 ============================================================================*/ | |
494 | |
495 void balloon_help_create( Display* dpy, | |
496 Pixel fg, Pixel bg, Pixel shine, Pixel shadow, | |
497 XFontStruct* font ) | |
498 { | |
499 if( b_dpy ) balloon_help_destroy(); | |
500 | |
501 b_dpy = dpy; | |
502 | |
503 b_fontStruct = font; | |
504 | |
505 b_win = create_window( dpy, bg ); | |
506 b_gc = create_gc( dpy, b_win, fg, bg, b_fontStruct ); | |
507 | |
508 b_shineGC = create_gc( dpy, b_win, shine, bg, b_fontStruct ); | |
509 b_shadowGC = create_gc( dpy, b_win, shadow, bg, b_fontStruct ); | |
510 | |
511 create_pixmap_mask( 1, 1 ); | |
512 b_maskGC = create_gc( dpy, b_mask, bg, fg, b_fontStruct ); | |
513 | |
514 b_winMapped = False; | |
515 b_timer = None; | |
516 b_delay = 500; | |
517 | |
518 b_screenWidth = DisplayWidth( b_dpy, DefaultScreen(b_dpy) ); | |
519 b_screenHeight = DisplayHeight( b_dpy, DefaultScreen(b_dpy) ); | |
520 | |
521 b_lastShape = SHAPE_CONE_FREE; | |
522 } | |
523 | |
524 void balloon_help_destroy( void ) | |
525 { | |
526 assert( b_dpy != NULL ); | |
527 b_dpy = NULL; | |
528 | |
529 destroy_window( b_dpy, b_win ); | |
530 destroy_gc( b_dpy, b_gc ); | |
531 | |
532 destroy_gc( b_dpy, b_shineGC ); | |
533 destroy_gc( b_dpy, b_shadowGC ); | |
534 | |
535 destroy_pixmap_mask(); | |
536 destroy_gc( b_dpy, b_maskGC ); | |
537 | |
538 if( b_timer ) XtRemoveTimeOut( b_timer ); | |
539 } | |
540 | |
541 void balloon_help_set_delay( unsigned long milliseconds ) | |
542 { | |
543 b_delay = milliseconds; | |
544 } | |
545 | |
546 void balloon_help_show( const char* text ) | |
547 { | |
548 assert( b_dpy != NULL ); | |
549 | |
550 /* We don't copy the text */ | |
551 b_text = text; | |
552 b_lastShape = SHAPE_CONE_FREE; | |
553 | |
554 if( b_winMapped ) | |
555 { | |
556 /* If help is already being shown, don't delay just update */ | |
557 show_help( NULL, NULL ); | |
558 } | |
559 else | |
560 { | |
561 b_timer = | |
562 XtAppAddTimeOut( XtDisplayToApplicationContext(b_dpy), | |
563 b_delay, show_help, NULL ); | |
564 } | |
565 } | |
566 | |
567 void balloon_help_hide( void ) | |
568 { | |
569 assert( b_dpy != NULL ); | |
570 | |
571 b_text = NULL; | |
572 XUnmapWindow( b_dpy, b_win ); | |
573 b_winMapped = False; | |
574 if( b_timer ) | |
575 { | |
576 XtRemoveTimeOut( b_timer ); | |
577 b_timer = None; | |
578 } | |
579 } | |
580 | |
581 void balloon_help_move_to_pointer( void ) | |
582 { | |
583 assert( b_dpy != NULL ); | |
584 | |
585 if( b_winMapped ) | |
586 { | |
587 int x, y; | |
588 int shape = b_lastShape; | |
589 | |
590 get_pointer_xy( b_dpy, &x, &y ); | |
591 | |
592 x += (shape & SHAPE_CONE_LEFT) ? POINTER_OFFSET : -POINTER_OFFSET; | |
593 y += (shape & SHAPE_CONE_TOP) ? POINTER_OFFSET : -POINTER_OFFSET; | |
594 | |
595 shape = get_shape( shape, x, y, b_width, b_height, b_screenWidth, b_screenHeight ); | |
596 | |
597 if( shape == b_lastShape ) | |
598 { | |
599 b_lastX = x; | |
600 b_lastY = y; | |
601 | |
602 XMoveWindow( b_dpy, b_win, | |
603 shape & SHAPE_CONE_LEFT ? x : x - b_width, | |
604 shape & SHAPE_CONE_TOP ? y : y - b_height ); | |
605 } | |
606 else | |
607 { | |
608 /* text would be off screen, rebuild with new shape */ | |
609 b_lastShape = SHAPE_CONE_FREE; | |
610 show_help( NULL, NULL ); | |
611 } | |
612 } | |
613 } |