1 /*
2 * This file is part of the XForms library package.
3 *
4 * XForms is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2.1, or
7 * (at your option) any later version.
8 *
9 * XForms is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with XForms. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19 /**
20 * \file canvas.c
21 *
22 * This file is part of the XForms library package.
23 * Copyright (c) 1996-2002 T.C. Zhao
24 * All rights reserved.
25 *.
26 * Class FL_CANVAS
27 *
28 * Not too much different from an app_win except geometry is managed
29 * by forms and has one of the forms as its parent.
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include "include/forms.h"
37 #include "flinternal.h"
38 #include "private/pcanvas.h"
39
40
41 /***************************************
42 * If yield_to_shortcut is set, every key press will have to
43 * be checked for all objects' shortcuts on the current form
44 ***************************************/
45
46 static int
handle_keyboard_special(FL_OBJECT * ob,XEvent * xev)47 handle_keyboard_special( FL_OBJECT * ob,
48 XEvent * xev )
49 {
50 unsigned char keybuf[ 127 ],
51 *ch;
52 KeySym keysym;
53 int kbuflen;
54 int ret = 0;
55
56 kbuflen = XLookupString( ( XKeyEvent * ) xev, ( char * ) keybuf,
57 sizeof keybuf, &keysym, 0 );
58
59 if ( IsModifierKey( keysym ) )
60 /* empty */ ;
61 else if ( kbuflen == 0 && keysym != None )
62 ret = fli_do_shortcut( ob->form, keysym,
63 xev->xkey.x, xev->xkey.y, xev );
64 else
65 for ( ch = keybuf; ch < keybuf + kbuflen && ob->form; ch++ )
66 ret = fli_do_shortcut( ob->form, *ch,
67 xev->xkey.x, xev->xkey.y, xev ) || ret;
68
69
70 return ret;
71 }
72
73
74 #define IsValidCanvas( ob ) ( ob && ( ob->objclass == FL_CANVAS \
75 || ob->objclass == FL_GLCANVAS ) )
76
77
78 /***************************************
79 * We have to intercept all events destined for the canvas.
80 * Must return 0 if canvas is used just like an arbitary application
81 * window. event processing routine calls the preemptive routine
82 * and continues dispatching events if preemptive handler returns 0.
83 ***************************************/
84
85 static int
canvas_event_intercept(XEvent * xev,void * vob)86 canvas_event_intercept( XEvent * xev,
87 void * vob )
88 {
89 FL_OBJECT *ob = vob;
90 FLI_CANVAS_SPEC *sp = ob->spec;
91
92 fli_xevent_name( "CanvasIntercept", xev );
93
94 if ( ! sp )
95 {
96 /* Must be Destroy Event, which is generated after FREEMEM. Probably
97 should block closewindow and handle the events there. Speed
98 penalty? */
99
100 return FL_PREEMPT;
101 }
102
103 if ( xev->type == DestroyNotify
104 && ! sp->canvas_handler[ xev->type ]
105 && sp->cleanup )
106 {
107 sp->cleanup( ob );
108 sp->window = None;
109 }
110
111 if ( xev->type == KeyPress
112 && sp->yield_to_shortcut
113 && handle_keyboard_special( ob, xev ) )
114 return FL_PREEMPT;
115
116 if ( xev->type != Expose
117 && xev->type != GraphicsExpose
118 && xev->type != ClientMessage
119 && ( ! ob->active || ob->form->deactivated ) )
120 return FL_PREEMPT;
121
122 if ( sp->canvas_handler[ xev->type ] )
123 {
124 if ( xev->type == Expose
125 && sp->activate
126 && ob->objclass == FL_GLCANVAS )
127 sp->activate( ob );
128
129 sp->canvas_handler[ xev->type ]( ob, sp->window, sp->w, sp->h,
130 xev, sp->user_data[ xev->type ] );
131 return FL_PREEMPT;
132 }
133
134 return FL_PREEMPT;
135 }
136
137
138 /********** Data Structure maint. ***************{ ***/
139
140 /***************************************
141 ***************************************/
142
143 static void
free_canvas(FL_OBJECT * ob)144 free_canvas( FL_OBJECT * ob )
145 {
146 FLI_CANVAS_SPEC *sp = ob->spec;
147
148 fli_unmap_canvas_window( ob );
149
150 /* Don't free the colormap if it is XForms' internal one */
151
152 if ( ! sp->keep_colormap && sp->colormap != fli_colormap( fl_vmode ) )
153 XFreeColormap( flx->display, sp->colormap );
154
155 fli_safe_free( ob->spec );
156 }
157
158
159 /***************************************
160 * Returns the window ID of the canvas window
161 ***************************************/
162
163 Window
fl_get_canvas_id(FL_OBJECT * ob)164 fl_get_canvas_id( FL_OBJECT * ob )
165 {
166 FLI_CANVAS_SPEC *sp = ob->spec;
167
168 #if FL_DEBUG >= ML_DEBUG
169 if ( ! IsValidCanvas( ob ) )
170 {
171 M_err( "fl_get_canvas_id", "%s not a canvas",
172 ( ob && ob->label ) ? ob->label : "" );
173 return None;
174 }
175 #endif
176
177 return sp->window;
178 }
179
180
181 /****** End of data struct. maint.************* }********/
182
183
184 /***************************************
185 ***************************************/
186
187 static void
BegWMColormap(FLI_CANVAS_SPEC * sp)188 BegWMColormap( FLI_CANVAS_SPEC * sp )
189 {
190
191 /* Set WM_COLORMAP property. Seems some versions of tvtwm have problems
192 with setting this property. This check simply works around the problem
193 (for most cases). */
194
195 if ( sp->colormap != fli_colormap( fl_vmode )
196 && ! XSetWMColormapWindows( flx->display, sp->parent,
197 &sp->window, 1 ) )
198 M_err( "BegWMColormap", "WM choked" );
199 }
200
201
202 /***************************************
203 ***************************************/
204
205 void
fl_set_canvas_attributes(FL_OBJECT * ob,unsigned int mask,XSetWindowAttributes * xswa)206 fl_set_canvas_attributes( FL_OBJECT * ob,
207 unsigned int mask,
208 XSetWindowAttributes * xswa )
209 {
210 FLI_CANVAS_SPEC *sp = ob->spec;
211
212 /* Must not allow adding/removing events. We take care of soliciting
213 events via canvas handler registrations */
214
215 if ( mask & CWEventMask )
216 {
217 M_err( "fl_set_canvas_attributes", "Changing Events not supported" );
218 mask &= ~ CWEventMask;
219 }
220
221 sp->user_mask = mask;
222 sp->user_xswa = *xswa;
223
224 /* Check if canvas is already active */
225
226 if ( sp->window )
227 {
228 XChangeWindowAttributes( flx->display, sp->window,
229 sp->user_mask, &sp->user_xswa );
230
231 if ( mask & CWColormap )
232 BegWMColormap( sp );
233 }
234 }
235
236
237 /***************************************
238 ***************************************/
239
240 void
fl_set_canvas_colormap(FL_OBJECT * ob,Colormap colormap)241 fl_set_canvas_colormap( FL_OBJECT * ob,
242 Colormap colormap )
243 {
244 FLI_CANVAS_SPEC *sp = ob->spec;
245
246 sp->colormap = sp->xswa.colormap = colormap;
247 sp->mask |= CWColormap;
248
249 if ( sp->window )
250 {
251 M_warn( "fl_set_canvas_colormap",
252 "Changing colormap for active window" );
253 XChangeWindowAttributes( flx->display, sp->window, sp->mask,
254 &sp->xswa );
255 BegWMColormap( sp );
256 }
257 }
258
259
260 /***************************************
261 ***************************************/
262
263 void
fl_share_canvas_colormap(FL_OBJECT * ob,Colormap colormap)264 fl_share_canvas_colormap( FL_OBJECT * ob,
265 Colormap colormap )
266 {
267 FLI_CANVAS_SPEC *sp = ob->spec;
268
269 sp->keep_colormap = 1;
270 fl_set_canvas_colormap( ob, colormap );
271 }
272
273
274 /***************************************
275 ***************************************/
276
277 Colormap
fl_get_canvas_colormap(FL_OBJECT * ob)278 fl_get_canvas_colormap( FL_OBJECT * ob )
279 {
280 return ( ( FLI_CANVAS_SPEC * ) ( ob->spec ) )->colormap;
281 }
282
283
284 /***************************************
285 ***************************************/
286
287 void
fl_set_canvas_visual(FL_OBJECT * obj,Visual * vi)288 fl_set_canvas_visual( FL_OBJECT * obj,
289 Visual * vi )
290 {
291 ( ( FLI_CANVAS_SPEC * ) ( obj->spec ) )->visual = vi;
292 }
293
294
295 /***************************************
296 ***************************************/
297
298 void
fl_set_canvas_depth(FL_OBJECT * obj,int depth)299 fl_set_canvas_depth( FL_OBJECT * obj,
300 int depth )
301 {
302 ( ( FLI_CANVAS_SPEC * ) ( obj->spec ) )->depth = depth;
303 }
304
305
306 /***************************************
307 ***************************************/
308
309 int
fl_get_canvas_depth(FL_OBJECT * obj)310 fl_get_canvas_depth( FL_OBJECT * obj )
311 {
312 return ( ( FLI_CANVAS_SPEC * ) ( obj->spec ) )->depth;
313 }
314
315
316 #define Moved( ob, sp ) ( ob->x != sp->x || ob->y != sp->y )
317 #define Resized( ob, sp ) ( ob->w != sp->w || ob->h != sp->h )
318
319
320 /***************************************
321 ***************************************/
322
323 static void
init_canvas(FL_OBJECT * ob)324 init_canvas( FL_OBJECT * ob )
325 {
326 FLI_CANVAS_SPEC *sp = ob->spec;
327 static int nc; /* number of canvases */
328 char name[ 32 ];
329
330 if ( ! sp->window || ! fl_winisvalid( sp->window ) )
331 {
332 /* Find the real parent of the canvas */
333
334 sp->parent = fl_get_real_object_window( ob );
335 sp->window = None;
336
337 if ( sp->parent == None )
338 {
339 M_err( "init_canvas", "Internal Error" );
340 exit( 0 );
341 }
342
343 if ( sp->init && sp->init( ob ) < 0 )
344 {
345 M_err( "init_canvas", "Unable to initialize canvas %s", ob->label );
346 return;
347 }
348
349 /* Create the window */
350
351 sp->window = XCreateWindow( flx->display, sp->parent,
352 ob->x, ob->y, ob->w, ob->h, 0,
353 sp->depth, InputOutput,
354 sp->visual, sp->mask, &sp->xswa );
355
356 if ( sp->user_mask )
357 XChangeWindowAttributes( flx->display, sp->window,
358 sp->user_mask, &sp->user_xswa );
359
360 #if FL_DEBUG >= ML_ERR
361 M_warn( "init_canvas", "Depth = %d colormap = 0x%lx, WinID = %ld",
362 sp->depth, sp->colormap, sp->window );
363 #endif
364
365 /* Take over event handling */
366
367 fli_set_preemptive_callback( sp->window, canvas_event_intercept, ob );
368
369 if ( sp->activate && sp->activate( ob ) < 0 )
370 {
371 M_err( "init_canvas", "Can't initialize canvas %s", ob->label );
372 return;
373 }
374
375 /* Record the name of the window */
376
377 if ( *ob->label )
378 XStoreName( flx->display, sp->window, ob->label );
379 else
380 {
381 sprintf( name, "flcanvas%d", nc++ );
382 XStoreName( flx->display, sp->window, name );
383 }
384
385 BegWMColormap( sp );
386
387 XMapWindow( flx->display, sp->window );
388
389 /* Save size */
390
391 sp->x = ob->x;
392 sp->y = ob->y;
393 sp->w = ob->w;
394 sp->h = ob->h;
395 }
396
397 /* Check if moved or resized */
398
399 if ( Moved( ob, sp ) || Resized( ob, sp ) )
400 {
401 M_warn( "init_canvas", "Canvas: WinMoved\n" );
402 XMoveResizeWindow( flx->display, sp->window, ob->x, ob->y,
403 ob->w, ob->h );
404 }
405
406 sp->x = ob->x;
407 sp->y = ob->y;
408 sp->w = ob->w;
409 sp->h = ob->h;
410
411 if ( ob->col1 != FL_NoColor )
412 XClearWindow( flx->display, sp->window );
413
414 sp->dec_type = fli_boxtype2frametype( ob->boxtype );
415 fl_draw_frame( sp->dec_type, ob->x, ob->y, ob->w, ob->h, ob->col2, ob->bw );
416 }
417
418
419 /***************************************
420 ***************************************/
421
422 FL_HANDLE_CANVAS
fl_add_canvas_handler(FL_OBJECT * ob,int ev,FL_HANDLE_CANVAS h,void * udata)423 fl_add_canvas_handler( FL_OBJECT * ob,
424 int ev,
425 FL_HANDLE_CANVAS h,
426 void * udata )
427 {
428 FL_HANDLE_CANVAS oldh = NULL;
429 FLI_CANVAS_SPEC *sp = ob->spec;
430 unsigned long emask = fli_xevent_to_mask( ev );
431
432 if ( ! IsValidCanvas( ob ) )
433 {
434 M_err( "fl_add_canvas_handler", "%s not canvas class",
435 ob ? ob->label : "" );
436 return NULL;
437 }
438
439 if ( ev < KeyPress )
440 {
441 M_err( "fl_add_canvas_handler", "Invalid event %d", ev );
442 return NULL;
443 }
444
445 if ( ev != 0 && ev < LASTEvent )
446 {
447 oldh = sp->canvas_handler[ ev ];
448 sp->canvas_handler[ ev ] = h;
449 sp->user_data[ ev ] = udata;
450 if ( ! sp->window )
451 sp->xswa.event_mask |= emask;
452 else
453 sp->xswa.event_mask = fl_addto_selected_xevent( sp->window, emask );
454 }
455
456 return oldh;
457 }
458
459
460 /***************************************
461 * Remove a particular handler for event ev. If ev is invalid,
462 * remove all handlers and their corresponding event mask
463 ***************************************/
464
465 void
fl_remove_canvas_handler(FL_OBJECT * ob,int ev,FL_HANDLE_CANVAS h FL_UNUSED_ARG)466 fl_remove_canvas_handler( FL_OBJECT * ob,
467 int ev,
468 FL_HANDLE_CANVAS h FL_UNUSED_ARG )
469 {
470 FLI_CANVAS_SPEC *sp = ob->spec;
471 unsigned long emask = fli_xevent_to_mask( ev );
472
473 if ( ev < 0 || ev >= LASTEvent )
474 {
475 M_err( "fl_remove_canvas_handler", "Invalid event %d", ev );
476 return;
477 }
478
479 sp->canvas_handler[ ev ] = NULL;
480
481 if ( ! sp->window )
482 {
483 if ( emask != 0 )
484 {
485 sp->xswa.event_mask &= ~emask;
486 sp->xswa.event_mask |= ExposureMask;
487 }
488 return;
489 }
490
491 /* Knock off the mask. Need to get Expose however */
492
493 if ( emask != 0 )
494 sp->xswa.event_mask = fl_remove_selected_xevent( sp->window, emask );
495 else if ( ev < 2 )
496 XSelectInput( flx->display, sp->window,
497 sp->xswa.event_mask = ExposureMask );
498
499 if ( ev == 0 )
500 {
501 for ( ; ev < LASTEvent; ev++ )
502 sp->canvas_handler[ ev ] = NULL;
503 }
504 }
505
506
507 /***************************************
508 * Handle canvas by calling registered handlers. There is not much
509 * to do as FL_DRAW typically will be followd by Expose on the
510 * canvas
511 ***************************************/
512
513 static int
handle_canvas(FL_OBJECT * ob,int event,FL_Coord mx FL_UNUSED_ARG,FL_Coord my FL_UNUSED_ARG,int key FL_UNUSED_ARG,void * xev FL_UNUSED_ARG)514 handle_canvas( FL_OBJECT * ob,
515 int event,
516 FL_Coord mx FL_UNUSED_ARG,
517 FL_Coord my FL_UNUSED_ARG,
518 int key FL_UNUSED_ARG,
519 void * xev FL_UNUSED_ARG )
520 {
521 FLI_CANVAS_SPEC *sp = ob->spec;
522
523 switch ( event )
524 {
525 case FL_ATTRIB :
526 ob->align = fl_to_outside_lalign( ob->align );
527 break;
528
529 case FL_DRAW:
530 if ( ob->col1 != FL_NoColor )
531 sp->xswa.background_pixel = fl_get_pixel( ob->col1 );
532 else
533 sp->xswa.background_pixel = None;
534 sp->mask |= CWBackPixel;
535 init_canvas( ob );
536 fl_draw_object_label_outside( ob );
537 break;
538
539 case FL_FREEMEM:
540 fli_hide_canvas( ob );
541 free_canvas( ob );
542 break;
543 }
544
545 return FL_RETURN_NONE;
546 }
547
548
549 /***************************************
550 ***************************************/
551
552 void
fli_hide_canvas(FL_OBJECT * ob)553 fli_hide_canvas( FL_OBJECT * ob )
554 {
555 FLI_CANVAS_SPEC *sp = ob->spec;
556
557 if ( sp->window && sp->cleanup )
558 sp->cleanup( ob );
559
560 /* If parent is unmapped, sp->window is also unmapped, must cleanup
561 canvas specific stuff before closing window */
562
563 if ( ob->visible && sp->window && ob->form && ob->form->window )
564 fl_winclose( sp->window );
565
566 sp->window = None;
567 }
568
569
570 /***************************************
571 ***************************************/
572
573 FL_OBJECT *
fl_create_generic_canvas(int canvas_class,int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)574 fl_create_generic_canvas( int canvas_class,
575 int type,
576 FL_Coord x,
577 FL_Coord y,
578 FL_Coord w,
579 FL_Coord h,
580 const char * label )
581 {
582 FL_OBJECT *ob;
583 FLI_CANVAS_SPEC *sp;
584 int vmode = fl_vmode;
585 int i;
586
587 ob = fl_make_object( canvas_class, type, x, y, w, h, label, handle_canvas );
588 ob->boxtype = FL_CANVAS_BOXTYPE;
589 ob->col1 = FL_NoColor; /* indicates no background */
590 ob->col2 = FL_BLACK;
591
592 sp = ob->spec = fl_calloc( 1, sizeof *sp );
593 sp->xswa.border_pixel = 0;
594 sp->xswa.event_mask = ExposureMask | StructureNotifyMask;
595 sp->xswa.do_not_propagate_mask = 0;
596 sp->mask = CWColormap | CWEventMask | CWBorderPixel | CWDontPropagate;
597
598 if ( ! fli_no_connection )
599 {
600 sp->visual = fli_visual( vmode );
601 sp->depth = fli_depth( vmode );
602 sp->colormap = sp->xswa.colormap = fli_colormap( vmode );
603 sp->gc = fl_state[ vmode ].gc[ 7 ]; /* NOT USED */
604 }
605
606 sp->winname = NULL;
607 sp->window = sp->parent = sp->swindow = None;
608 sp->init = sp->activate = sp->cleanup = NULL;
609 sp->last_active = NULL;
610 sp->context = NULL;
611 for ( i = 0; i < LASTEvent; i++ )
612 {
613 sp->canvas_handler[ i ] = NULL;
614 sp->user_data[ i ] = NULL;
615 }
616
617 fl_canvas_yield_to_shortcut( ob, 1 );
618
619 return ob;
620 }
621
622
623 /***************************************
624 ***************************************/
625
626 FL_OBJECT *
fl_create_canvas(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)627 fl_create_canvas( int type,
628 FL_Coord x,
629 FL_Coord y,
630 FL_Coord w,
631 FL_Coord h,
632 const char * label )
633 {
634 return fl_create_generic_canvas( FL_CANVAS, type, x, y, w, h, label );
635 }
636
637
638 /***************************************
639 ***************************************/
640
641 FL_OBJECT *
fl_add_canvas(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)642 fl_add_canvas( int type,
643 FL_Coord x,
644 FL_Coord y,
645 FL_Coord w,
646 FL_Coord h,
647 const char * label )
648 {
649 FL_OBJECT *ob = fl_create_canvas( type, x, y, w, h, label );
650
651 fl_add_object( fl_current_form, ob );
652 return ob;
653 }
654
655
656 /***************************************
657 ***************************************/
658
659 void
fl_modify_canvas_prop(FL_OBJECT * obj,FL_MODIFY_CANVAS_PROP init,FL_MODIFY_CANVAS_PROP activate,FL_MODIFY_CANVAS_PROP cleanup)660 fl_modify_canvas_prop( FL_OBJECT * obj,
661 FL_MODIFY_CANVAS_PROP init,
662 FL_MODIFY_CANVAS_PROP activate,
663 FL_MODIFY_CANVAS_PROP cleanup )
664 {
665 FLI_CANVAS_SPEC *sp = obj->spec;
666
667 sp->init = init;
668 sp->activate = activate;
669 sp->cleanup = cleanup;
670 }
671
672
673 /***************************************
674 ***************************************/
675
676 void
fl_canvas_yield_to_shortcut(FL_OBJECT * ob,int yes)677 fl_canvas_yield_to_shortcut( FL_OBJECT * ob,
678 int yes )
679 {
680 FLI_CANVAS_SPEC *sp = ob->spec;
681 unsigned int emask = KeyPressMask;
682
683 if ( ( sp->yield_to_shortcut = yes ) )
684 {
685 if ( ! sp->window )
686 sp->xswa.event_mask |= emask;
687 else
688 sp->xswa.event_mask =
689 fl_addto_selected_xevent( sp->window, emask );
690 }
691 else if ( ! sp->canvas_handler[ KeyPress ] )
692 {
693 if ( ! sp->window )
694 sp->xswa.event_mask &= ~emask;
695 else
696 sp->xswa.event_mask =
697 fl_remove_selected_xevent( sp->window, emask );
698 }
699 }
700
701
702 /***************************************
703 * Clear the canvas to the background color. If no background is
704 * defined use black.
705 ***************************************/
706
707 void
fl_clear_canvas(FL_OBJECT * ob)708 fl_clear_canvas( FL_OBJECT * ob )
709 {
710 Window win;
711
712 if ( ! ob || ! ( win = FL_ObjWin( ob ) ) )
713 return;
714
715 if ( ob->col1 != FL_NoColor )
716 XClearWindow( flx->display, win );
717 else
718 {
719 fl_winset( win );
720 fl_rectf( ob->x, ob->y, ob->w, ob->h, FL_BLACK );
721 }
722 }
723
724
725 /***************************************
726 * Called when a form that contains a canvas is getting hidden
727 ***************************************/
728
729 void
fli_unmap_canvas_window(FL_OBJECT * ob)730 fli_unmap_canvas_window( FL_OBJECT * ob )
731 {
732 FLI_CANVAS_SPEC *sp = ob->spec;
733
734 if ( ob->visible && sp->window && ob->form && ob->form->window )
735 fl_winclose( sp->window );
736
737 sp->window = None;
738 }
739
740
741 /*
742 * Local variables:
743 * tab-width: 4
744 * indent-tabs-mode: nil
745 * End:
746 */
747