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