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