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