]> git.decadent.org.uk Git - ion3.git/blob - ioncore/frame.c
[svn-upgrade] Integrating new upstream version, ion3 (20070318)
[ion3.git] / ioncore / frame.c
1 /*
2  * ion/ioncore/frame.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <string.h>
13
14 #include <libtu/obj.h>
15 #include <libtu/objp.h>
16 #include <libtu/minmax.h>
17 #include <libtu/map.h>
18 #include <libmainloop/defer.h>
19
20 #include "common.h"
21 #include "window.h"
22 #include "global.h"
23 #include "rootwin.h"
24 #include "focus.h"
25 #include "event.h"
26 #include "attach.h"
27 #include "resize.h"
28 #include "tags.h"
29 #include "names.h"
30 #include "saveload.h"
31 #include "framep.h"
32 #include "frame-pointer.h"
33 #include "frame-draw.h"
34 #include "sizehint.h"
35 #include "extlconv.h"
36 #include "mplex.h"
37 #include "bindmaps.h"
38 #include "regbind.h"
39 #include "gr.h"
40 #include "llist.h"
41 #include "framedpholder.h"
42 #include "return.h"
43
44
45 extern bool frame_set_background(WFrame *frame, bool set_always);
46 extern void frame_initialise_gr(WFrame *frame);
47
48 static bool frame_initialise_titles(WFrame *frame);
49 static void frame_free_titles(WFrame *frame);
50
51 static void frame_add_mode_bindmaps(WFrame *frame);
52
53
54 WHook *frame_managed_changed_hook=NULL;
55
56 #define IS_FLOATING_MODE(FRAME) \
57     ((FRAME)->mode==FRAME_MODE_FLOATING || (FRAME)->mode==FRAME_MODE_TRANSIENT)
58 #define FORWARD_CWIN_RQGEOM(FRAME) IS_FLOATING_MODE(FRAME)
59 #define USE_MINMAX(FRAME) IS_FLOATING_MODE(FRAME)
60 #define DEST_EMPTY(FRAME) IS_FLOATING_MODE(FRAME)
61
62
63 /*{{{ Destroy/create frame */
64
65
66 bool frame_init(WFrame *frame, WWindow *parent, const WFitParams *fp,
67                 WFrameMode mode)
68 {
69     WRectangle mg;
70
71     frame->flags=0;
72     frame->saved_w=0;
73     frame->saved_h=0;
74     frame->saved_x=0;
75     frame->saved_y=0;
76     frame->tab_dragged_idx=-1;
77     frame->titles=NULL;
78     frame->titles_n=0;
79     frame->bar_h=0;
80     frame->bar_w=fp->g.w;
81     frame->tr_mode=GR_TRANSPARENCY_DEFAULT;
82     frame->brush=NULL;
83     frame->bar_brush=NULL;
84     frame->mode=mode;
85     frame->tab_min_w=0;
86     frame->bar_max_width_q=1.0;
87     
88     if(!mplex_init((WMPlex*)frame, parent, fp))
89         return FALSE;
90     
91     frame_initialise_gr(frame);
92     frame_initialise_titles(frame);
93     
94     region_add_bindmap((WRegion*)frame, ioncore_frame_bindmap);
95     region_add_bindmap((WRegion*)frame, ioncore_mplex_bindmap);
96     
97     frame_add_mode_bindmaps(frame);
98     
99     mplex_managed_geom((WMPlex*)frame, &mg);
100     
101     if(mg.h<=1)
102         frame->flags|=FRAME_SHADED;
103     
104     ((WRegion*)frame)->flags|=REGION_PLEASE_WARP;
105     
106     return TRUE;
107 }
108
109
110 WFrame *create_frame(WWindow *parent, const WFitParams *fp, WFrameMode mode)
111 {
112     CREATEOBJ_IMPL(WFrame, frame, (p, parent, fp, mode));
113 }
114
115
116 void frame_deinit(WFrame *frame)
117 {
118     frame_free_titles(frame);
119     frame_release_brushes(frame);
120     mplex_deinit((WMPlex*)frame);
121 }
122
123
124 /*}}}*/
125
126
127 /*{{{ Mode switching */
128
129
130 static void frame_add_mode_bindmaps(WFrame *frame)
131 {
132     WFrameMode mode=frame->mode;
133     
134     if(mode==FRAME_MODE_FLOATING){
135         region_add_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
136         region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
137         region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
138     }else if(mode==FRAME_MODE_TRANSIENT){
139         region_add_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
140         region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
141     }else{
142         /* mode==FRAME_MODE_TILED || mode==FRAME_MODE_TILED_ALT || mode==FRAME_MODE_UNKNOWN */
143         region_add_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
144         region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
145         region_add_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
146     } 
147 }
148
149
150 void frame_set_mode(WFrame *frame, WFrameMode mode)
151 {
152     if(frame->mode==mode)
153         return;
154     
155     frame_clear_shape(frame);
156     
157     frame_release_brushes(frame);
158     
159     region_remove_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
160     region_remove_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
161     region_remove_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
162     region_remove_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
163     region_remove_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
164     
165     frame->mode=mode;
166
167     frame_add_mode_bindmaps(frame);
168     
169     frame_initialise_gr(frame);
170     
171     mplex_fit_managed(&frame->mplex);
172     frame_recalc_bar(frame);
173     frame_set_background(frame, TRUE);
174 }
175
176
177 WFrameMode frame_mode(WFrame *frame)
178 {
179     return frame->mode;
180 }
181
182
183 StringIntMap frame_modes[]={
184     {"unknown", FRAME_MODE_UNKNOWN},
185     {"tiled", FRAME_MODE_TILED},
186     {"tiled-alt", FRAME_MODE_TILED_ALT},
187     {"floating", FRAME_MODE_FLOATING},
188     {"transient", FRAME_MODE_TRANSIENT},
189     END_STRINGINTMAP
190 };
191
192
193 /*EXTL_DOC
194  * Get frame mode.
195  */
196 EXTL_SAFE
197 EXTL_EXPORT_AS(WFrame, mode)
198 const char *frame_mode_extl(WFrame *frame)
199 {
200     return stringintmap_key(frame_modes, frame->mode, NULL);
201 }
202
203
204 /*EXTL_DOC
205  * Set frame mode.
206  */
207 EXTL_EXPORT_AS(WFrame, set_mode)
208 bool frame_set_mode_extl(WFrame *frame, const char *modestr)
209 {
210     WFrameMode mode;
211     int idx;
212     
213     idx=stringintmap_ndx(frame_modes, modestr);
214     if(idx<0)
215         return FALSE;
216     
217     frame_set_mode(frame, frame_modes[idx].value);
218     
219     return TRUE;
220 }
221
222
223 /*}}}*/
224
225
226 /*{{{ Tabs */
227
228
229 int frame_tab_at_x(WFrame *frame, int x)
230 {
231     WRectangle bg;
232     int tab, tx;
233     
234     frame_bar_geom(frame, &bg);
235     
236     if(x>=bg.x+bg.w || x<bg.x)
237         return -1;
238     
239     tx=bg.x;
240
241     for(tab=0; tab<FRAME_MCOUNT(frame); tab++){
242         tx+=frame_nth_tab_w(frame, tab);
243         if(x<tx)
244             break;
245     }
246     
247     return tab;
248 }
249
250
251 int frame_nth_tab_x(WFrame *frame, int n)
252 {
253     uint x=0;
254     int i;
255     
256     for(i=0; i<n; i++)
257         x+=frame_nth_tab_w(frame, i);
258     
259     return x;
260 }
261
262
263 static int frame_nth_tab_w_iw(WFrame *frame, int n, bool inner)
264 {
265     WRectangle bg;
266     GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
267     int m=FRAME_MCOUNT(frame);
268     uint w;
269     
270     frame_bar_geom(frame, &bg);
271
272     if(m==0)
273         m=1;
274
275     if(frame->bar_brush!=NULL)
276         grbrush_get_border_widths(frame->bar_brush, &bdw);
277     
278     /* Remove borders */
279     w=bg.w-bdw.left-bdw.right-(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)*(m-1);
280     
281     if(w<=0)
282         return 0;
283     
284     /* Get n:th tab's portion of free area */
285     w=(((n+1)*w)/m-(n*w)/m);
286     
287     /* Add n:th tab's borders back */
288     if(!inner){
289         w+=(n==0 ? bdw.left : bdw.tb_ileft);
290         w+=(n==m-1 ? bdw.right : bdw.tb_iright+bdw.spacing);
291     }
292             
293     return w;
294 }
295
296
297 int frame_nth_tab_w(WFrame *frame, int n)
298 {
299     return frame_nth_tab_w_iw(frame, n, FALSE);
300 }
301
302
303 int frame_nth_tab_iw(WFrame *frame, int n)
304 {
305     return frame_nth_tab_w_iw(frame, n, TRUE);
306 }
307
308
309
310 void frame_update_attr_nth(WFrame *frame, int i)
311 {
312     WRegion *reg;
313     
314     if(i<0 || i>=frame->titles_n)
315         return;
316
317     frame_update_attr(frame, i, mplex_mx_nth((WMPlex*)frame, i));
318 }
319
320
321 static void frame_update_attrs(WFrame *frame)
322 {
323     int i=0;
324     WRegion *sub;
325     WLListIterTmp tmp;
326     
327     FRAME_MX_FOR_ALL(sub, frame, tmp){
328         frame_update_attr(frame, i, sub);
329         i++;
330     }
331 }
332
333
334 static void frame_free_titles(WFrame *frame)
335 {
336     int i;
337     
338     if(frame->titles!=NULL){
339         for(i=0; i<frame->titles_n; i++){
340             if(frame->titles[i].text)
341                 free(frame->titles[i].text);
342             gr_stylespec_unalloc(&frame->titles[i].attr);
343         }
344         free(frame->titles);
345         frame->titles=NULL;
346     }
347     frame->titles_n=0;
348 }
349
350
351 static void do_init_title(WFrame *frame, int i, WRegion *sub)
352 {
353     frame->titles[i].text=NULL;
354     frame->titles[i].iw=frame_nth_tab_iw(frame, i);
355     
356     gr_stylespec_init(&frame->titles[i].attr);
357     
358     frame_update_attr(frame, i, sub);
359 }
360
361
362 static bool frame_initialise_titles(WFrame *frame)
363 {
364     int i, n=FRAME_MCOUNT(frame);
365     
366     frame_free_titles(frame);
367
368     if(n==0)
369         n=1;
370     
371     frame->titles=ALLOC_N(GrTextElem, n);
372     if(frame->titles==NULL)
373         return FALSE;
374     frame->titles_n=n;
375     
376     if(FRAME_MCOUNT(frame)==0){
377         do_init_title(frame, 0, NULL);
378     }else{
379         WLListIterTmp tmp;
380         WRegion *sub;
381         i=0;
382         FRAME_MX_FOR_ALL(sub, frame, tmp){
383             do_init_title(frame, i, sub);
384             i++;
385         }
386     }
387     
388     frame_recalc_bar(frame);
389
390     return TRUE;
391 }
392
393
394 /*}}}*/
395
396
397 /*{{{ Resize and reparent */
398
399
400 bool frame_fitrep(WFrame *frame, WWindow *par, const WFitParams *fp)
401 {
402     WRectangle old_geom, mg;
403     bool wchg=(REGION_GEOM(frame).w!=fp->g.w);
404     bool hchg=(REGION_GEOM(frame).h!=fp->g.h);
405     
406     old_geom=REGION_GEOM(frame);
407     
408     window_do_fitrep(&(frame->mplex.win), par, &(fp->g));
409
410     mplex_managed_geom((WMPlex*)frame, &mg);
411     
412     if(hchg){
413         if(mg.h<=1){
414             frame->flags|=(FRAME_SHADED|FRAME_SAVED_VERT);
415             frame->saved_y=old_geom.y;
416             frame->saved_h=old_geom.h;
417         }else{
418             frame->flags&=~FRAME_SHADED;
419         }
420         frame->flags&=~FRAME_MAXED_VERT;
421     }
422     
423     if(wchg){
424         if(mg.w<=1){
425             frame->flags|=(FRAME_MIN_HORIZ|FRAME_SAVED_HORIZ);
426             frame->saved_x=old_geom.x;
427             frame->saved_w=old_geom.w;
428         }else{
429             frame->flags&=~FRAME_MIN_HORIZ;
430         }
431         frame->flags&=~FRAME_MAXED_HORIZ;
432     }
433
434     if(wchg || hchg){
435         mplex_fit_managed((WMPlex*)frame);
436         mplex_size_changed((WMPlex*)frame, wchg, hchg);
437     }
438     
439     return TRUE;
440 }
441
442
443 void frame_size_hints(WFrame *frame, WSizeHints *hints_ret)
444 {
445     WRectangle subgeom;
446     WLListIterTmp tmp;
447     WRegion *sub;
448     int woff, hoff;
449     
450     mplex_managed_geom((WMPlex*)frame, &subgeom);
451     
452     woff=maxof(REGION_GEOM(frame).w-subgeom.w, 0);
453     hoff=maxof(REGION_GEOM(frame).h-subgeom.h, 0);
454
455     if(FRAME_CURRENT(frame)!=NULL){
456         region_size_hints(FRAME_CURRENT(frame), hints_ret);
457         if(!USE_MINMAX(frame)){
458             hints_ret->max_set=0;
459             hints_ret->min_set=0;
460             /*hints_ret->base_set=0;*/
461             hints_ret->aspect_set=0;
462             hints_ret->no_constrain=FALSE;
463             /*hints_ret->no_constrain=TRUE;*/
464         }
465     }else{
466         sizehints_clear(hints_ret);
467     }
468     
469     FRAME_MX_FOR_ALL(sub, frame, tmp){
470         sizehints_adjust_for(hints_ret, sub);
471     }
472     
473     if(!hints_ret->base_set){
474         hints_ret->base_width=0;
475         hints_ret->base_height=0;
476         hints_ret->base_set=TRUE;
477     }
478
479     if(!hints_ret->min_set){
480         hints_ret->min_width=0;
481         hints_ret->min_height=0;
482         hints_ret->min_set=TRUE;
483     }
484     
485     hints_ret->base_width+=woff;
486     hints_ret->base_height+=hoff;
487     hints_ret->max_width+=woff;
488     hints_ret->max_height+=hoff;
489     hints_ret->min_width+=woff;
490     hints_ret->min_height+=hoff;
491     
492     if(frame->barmode==FRAME_BAR_SHAPED){
493         int f=frame->flags&(FRAME_SHADED|FRAME_SHADED_TOGGLE);
494         
495         if(f==FRAME_SHADED || f==FRAME_SHADED_TOGGLE){
496             hints_ret->min_height=frame->bar_h;
497             hints_ret->max_height=frame->bar_h;
498             hints_ret->base_height=frame->bar_h;
499             if(!hints_ret->max_set){
500                 hints_ret->max_width=INT_MAX;
501                 hints_ret->max_set=TRUE;
502             }
503         }
504     }
505 }
506
507
508 /*}}}*/
509
510
511 /*{{{ Focus  */
512
513
514 void frame_inactivated(WFrame *frame)
515 {
516     window_draw((WWindow*)frame, FALSE);
517 }
518
519
520 void frame_activated(WFrame *frame)
521 {
522     window_draw((WWindow*)frame, FALSE);
523 }
524
525
526 void frame_quasiactivation(WFrame *frame, WRegion *reg, bool act)
527 {
528     bool was, is;
529     
530     was=(frame->quasiactive_count>0);
531     
532     frame->quasiactive_count=maxof(0, frame->quasiactive_count 
533                                        + (act ? 1 : -1));
534                                        
535     is=(frame->quasiactive_count>0);
536     
537     if(was!=is && !REGION_IS_ACTIVE(frame))
538         window_draw((WWindow*)frame, FALSE);
539 }
540
541
542 static bool actinact(WRegion *reg, bool act)
543 {
544     WPHolder *returnph=region_get_return(reg);
545     WFrame *frame;
546     
547     if(returnph==NULL || pholder_stale(returnph))
548         return FALSE;
549     
550     frame=OBJ_CAST(pholder_target(returnph), WFrame);
551     
552     if(frame!=NULL){
553         /* Ok, reg has return placeholder set to a frame: 
554          * do quasiactivation/inactivation 
555          */
556         frame_quasiactivation(frame, reg, act);
557     }
558     
559     return TRUE;
560 }
561
562
563 static bool activated(WRegion *reg)
564 {
565     return actinact(reg, TRUE);
566 }
567
568
569 static bool inactivated(WRegion *reg)
570 {
571     return actinact(reg, FALSE);
572 }
573
574
575 void ioncore_frame_quasiactivation_notify(WRegion *reg, 
576                                           WRegionNotify how)
577 {
578     if(how==ioncore_g.notifies.activated || 
579        how==ioncore_g.notifies.pseudoactivated){
580         activated(reg);
581     }else if(how==ioncore_g.notifies.inactivated ||
582              how==ioncore_g.notifies.pseudoinactivated){
583         inactivated(reg);
584     }else if(how==ioncore_g.notifies.set_return){
585         if(REGION_IS_ACTIVE(reg) || REGION_IS_PSEUDOACTIVE(reg))
586             activated(reg);
587     }else if(how==ioncore_g.notifies.unset_return){
588         if(REGION_IS_ACTIVE(reg) || REGION_IS_PSEUDOACTIVE(reg))
589             inactivated(reg);
590     }
591 }
592
593
594 /*}}}*/
595
596
597 /*{{{ Client window rqgeom */
598
599
600 static void frame_managed_rqgeom_absolute(WFrame *frame, WRegion *sub,
601                                           const WRQGeomParams *rq,
602                                           WRectangle *geomret)
603 {
604     if(!FORWARD_CWIN_RQGEOM(frame)){
605         region_managed_rqgeom_absolute_default((WRegion*)frame, sub, 
606                                                rq, geomret);
607     }else{
608         WRQGeomParams rq2=RQGEOMPARAMS_INIT;
609         int gravity=ForgetGravity;
610         WRectangle off;
611         WRegion *par;
612         
613         rq2.geom=rq->geom;
614         rq2.flags=rq->flags&(REGION_RQGEOM_WEAK_ALL
615                              |REGION_RQGEOM_TRYONLY
616                              |REGION_RQGEOM_ABSOLUTE);
617
618         if(rq->flags&REGION_RQGEOM_GRAVITY)
619             gravity=rq->gravity;
620     
621         mplex_managed_geom(&frame->mplex, &off);
622         off.x=-off.x;
623         off.y=-off.y;
624         off.w=REGION_GEOM(frame).w-off.w;
625         off.h=REGION_GEOM(frame).h-off.h;
626
627         rq2.geom.w=maxof(rq2.geom.w+off.w, 0);
628         rq2.geom.h=maxof(rq2.geom.h+off.h, 0);
629     
630         /*region_size_hints_correct((WRegion*)frame, &(geom.w), &(geom.h), TRUE);*/
631     
632         /* If WEAK_? is set, then geom.(x|y) is root-relative as it was not 
633          * requested by the client and clientwin_handle_configure_request has
634          * no better guess. Otherwise the coordinates are those requested by 
635          * the client (modulo borders/gravity) and we interpret them to be 
636          * root-relative coordinates for this frame modulo gravity.
637          */
638         if(rq->flags&REGION_RQGEOM_WEAK_X)
639             rq2.geom.x+=off.x;
640         else
641             rq2.geom.x+=xgravity_deltax(gravity, -off.x, off.x+off.w);
642         
643         if(rq->flags&REGION_RQGEOM_WEAK_Y)
644             rq2.geom.y+=off.y;
645         else
646             rq2.geom.y+=xgravity_deltay(gravity, -off.y, off.y+off.h);
647         
648         region_rqgeom((WRegion*)frame, &rq2, geomret);
649         
650         if(geomret!=NULL){
651             geomret->x-=off.x;
652             geomret->y-=off.y;
653             geomret->w-=off.w;
654             geomret->h-=off.h;
655         }
656     }
657 }
658
659
660 /*}}}*/
661
662
663 /*{{{ Frame recreate pholder stuff */
664
665
666 static WFramedPHolder *frame_make_recreate_pholder(WFrame *frame)
667 {
668     WPHolder *ph;
669     WFramedPHolder *fph;
670     WFramedParam fparam=FRAMEDPARAM_INIT;
671     
672     ph=region_make_return_pholder((WRegion*)frame);
673     
674     if(ph==NULL)
675         return NULL;
676         
677     fparam.mode=frame->mode;
678     
679     fph=create_framedpholder(ph, &fparam);
680     
681     if(fph==NULL){
682         destroy_obj((Obj*)ph);
683         return NULL;
684     }
685     
686     return fph;
687 }
688
689
690 static void mplex_flatten_phs(WMPlex *mplex)
691 {
692     WLListNode *node;
693     WLListIterTmp tmp;
694
695     FOR_ALL_NODES_ON_LLIST(node, mplex->mx_list, tmp){
696         WMPlexPHolder *last=(mplex->mx_phs==NULL ? NULL : mplex->mx_phs->prev);
697         mplex_move_phs(mplex, node, last, NULL);
698     }
699 }
700
701
702 static void frame_modify_pholders(WFrame *frame)
703 {
704     WFramedPHolder *fph;
705     WMPlexPHolder *phs, *ph;
706     
707     mplex_flatten_phs(&frame->mplex);
708     
709     if(frame->mplex.mx_phs==NULL)
710         return;
711     
712     fph=frame_make_recreate_pholder(frame);
713     
714     if(fph==NULL)
715         return;
716     
717     phs=frame->mplex.mx_phs;
718     frame->mplex.mx_phs=NULL;
719     
720     phs->recreate_pholder=fph;
721     
722     for(ph=phs; ph!=NULL; ph=ph->next)
723         watch_reset(&ph->mplex_watch);
724 }
725
726
727 /*}}}*/
728
729
730 /*{{{ Misc. */
731
732
733 bool frame_set_shaded(WFrame *frame, int sp)
734 {
735     bool set=(frame->flags&FRAME_SHADED);
736     bool nset=libtu_do_setparam(sp, set);
737     WRQGeomParams rq=RQGEOMPARAMS_INIT;
738     GrBorderWidths bdw;
739     int h;
740
741     if(!XOR(nset, set))
742         return nset;
743
744     rq.flags=REGION_RQGEOM_H_ONLY;
745     rq.geom=REGION_GEOM(frame);
746         
747     if(!nset){
748         if(!(frame->flags&FRAME_SAVED_VERT))
749             return FALSE;
750         rq.geom.h=frame->saved_h;
751     }else{
752         if(frame->barmode==FRAME_BAR_NONE){
753             return FALSE;
754         }else if(frame->barmode==FRAME_BAR_SHAPED){
755             rq.geom.h=frame->bar_h;
756         }else{
757             WRectangle tmp;
758             
759             frame_border_inner_geom(frame, &tmp);
760             
761             rq.geom.h=rq.geom.h-tmp.h;
762         }
763     }
764     
765     frame->flags|=FRAME_SHADED_TOGGLE;
766     
767     region_rqgeom((WRegion*)frame, &rq, NULL);
768     
769     frame->flags&=~FRAME_SHADED_TOGGLE;
770     
771     return (frame->flags&FRAME_SHADED);
772 }
773
774
775 /*EXTL_DOC
776  * Set shading state according to the parameter \var{how} 
777  * (set/unset/toggle). Resulting state is returned, which may not be
778  * what was requested.
779  */
780 EXTL_EXPORT_AS(WFrame, set_shaded)
781 bool frame_set_shaded_extl(WFrame *frame, const char *how)
782 {
783     return frame_set_shaded(frame, libtu_string_to_setparam(how));
784 }
785
786
787 /*EXTL_DOC
788  * Is \var{frame} shaded?
789  */
790 EXTL_SAFE
791 EXTL_EXPORT_MEMBER
792 bool frame_is_shaded(WFrame *frame)
793 {
794     return ((frame->flags&FRAME_SHADED)!=0);
795 }
796
797
798 bool frame_set_numbers(WFrame *frame, int sp)
799 {
800     bool set=frame->flags&FRAME_SHOW_NUMBERS;
801     bool nset=libtu_do_setparam(sp, set);
802     
803     if(XOR(nset, set)){
804         frame->flags^=FRAME_SHOW_NUMBERS;
805         frame_recalc_bar(frame);
806         frame_draw_bar(frame, TRUE);
807     }
808     
809     return frame->flags&FRAME_SHOW_NUMBERS;
810 }
811
812
813 /*EXTL_DOC
814  * Control whether tabs show numbers (set/unset/toggle). 
815  * Resulting state is returned, which may not be what was 
816  * requested.
817  */
818 EXTL_EXPORT_AS(WFrame, set_numbers)
819 bool frame_set_numbers_extl(WFrame *frame, const char *how)
820 {
821     return frame_set_numbers(frame, libtu_string_to_setparam(how));
822 }
823
824
825 /*EXTL_DOC
826  * Does \var{frame} show numbers for tabs?
827  */
828 bool frame_is_numbers(WFrame *frame)
829 {
830     return frame->flags&FRAME_SHOW_NUMBERS;
831 }
832
833
834 void frame_managed_notify(WFrame *frame, WRegion *sub, WRegionNotify how)
835 {
836     if(how==ioncore_g.notifies.activated ||
837        how==ioncore_g.notifies.inactivated ||
838        how==ioncore_g.notifies.name ||
839        how==ioncore_g.notifies.activity ||
840        how==ioncore_g.notifies.sub_activity ||
841        how==ioncore_g.notifies.tag){
842        
843         frame_update_attrs(frame);
844         frame_recalc_bar(frame);
845         frame_draw_bar(frame, FALSE);
846     }
847 }
848
849
850 static void frame_size_changed_default(WFrame *frame,
851                                        bool wchg, bool hchg)
852 {
853     int bar_w=frame->bar_w;
854     
855     if(wchg)
856         frame_recalc_bar(frame);
857     
858     if(frame->barmode==FRAME_BAR_SHAPED &&
859        ((!wchg && hchg) || (wchg && bar_w==frame->bar_w))){
860         frame_set_shape(frame);
861     }
862 }
863
864
865 static void frame_managed_changed(WFrame *frame, int mode, bool sw,
866                                   WRegion *reg)
867 {
868     bool need_draw=TRUE;
869     
870     if(mode!=MPLEX_CHANGE_SWITCHONLY)
871         frame_initialise_titles(frame);
872     else
873         frame_update_attrs(frame);
874
875     if(sw)
876         need_draw=!frame_set_background(frame, FALSE);
877     
878     if(need_draw)
879         frame_draw_bar(frame, mode!=MPLEX_CHANGE_SWITCHONLY);
880
881     mplex_call_changed_hook((WMPlex*)frame,
882                             frame_managed_changed_hook,
883                             mode, sw, reg);
884 }
885
886
887 WRegion *frame_managed_disposeroot(WFrame *frame, WRegion *reg)
888 {
889     if(DEST_EMPTY(frame) &&
890        frame->mplex.mgd!=NULL && 
891        frame->mplex.mgd->reg==reg && 
892        frame->mplex.mgd->next==NULL){
893         WRegion *tmp=region_disposeroot((WRegion*)frame);
894         return (tmp!=NULL ? tmp : reg);
895     }
896     
897     return reg;
898 }
899
900
901 int frame_default_index(WFrame *frame)
902 {
903     return ioncore_g.frame_default_index;
904 }
905
906
907 /*}}}*/
908
909
910 /*{{{ Save/load */
911
912
913 ExtlTab frame_get_configuration(WFrame *frame)
914 {
915     ExtlTab tab=mplex_get_configuration(&frame->mplex);
916     
917     extl_table_sets_i(tab, "mode", frame->mode);
918
919     if(frame->flags&FRAME_SAVED_VERT){
920         extl_table_sets_i(tab, "saved_y", frame->saved_y);
921         extl_table_sets_i(tab, "saved_h", frame->saved_h);
922     }
923
924     if(frame->flags&FRAME_SAVED_HORIZ){
925         extl_table_sets_i(tab, "saved_x", frame->saved_x);
926         extl_table_sets_i(tab, "saved_w", frame->saved_w);
927     }
928     
929     return tab;
930 }
931
932
933
934 void frame_do_load(WFrame *frame, ExtlTab tab)
935 {
936     int flags=0;
937     int p=0, s=0;
938     
939     if(extl_table_gets_i(tab, "saved_x", &p) &&
940        extl_table_gets_i(tab, "saved_w", &s)){
941         frame->saved_x=p;
942         frame->saved_w=s;
943         frame->flags|=FRAME_SAVED_HORIZ;
944     }
945
946     if(extl_table_gets_i(tab, "saved_y", &p) &&
947        extl_table_gets_i(tab, "saved_h", &s)){
948         frame->saved_y=p;
949         frame->saved_h=s;
950         frame->flags|=FRAME_SAVED_VERT;
951     }
952     
953     mplex_load_contents(&frame->mplex, tab);
954 }
955
956
957 WRegion *frame_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
958 {
959     int mode=FRAME_MODE_UNKNOWN;
960     WFrame *frame;
961     
962     extl_table_gets_i(tab, "mode", &mode);
963     
964     frame=create_frame(par, fp, mode);
965     
966     if(frame!=NULL)
967         frame_do_load(frame, tab);
968     
969     if(DEST_EMPTY(frame) && frame->mplex.mgd==NULL){
970         destroy_obj((Obj*)frame);
971         return NULL;
972     }
973     
974     return (WRegion*)frame;
975 }
976
977
978 /*}}}*/
979
980
981 /*{{{ Dynfuntab and class info */
982
983
984 static DynFunTab frame_dynfuntab[]={
985     {region_size_hints, frame_size_hints},
986
987     {mplex_managed_changed, frame_managed_changed},
988     {mplex_size_changed, frame_size_changed_default},
989     {region_managed_notify, frame_managed_notify},
990     
991     {region_activated, frame_activated},
992     {region_inactivated, frame_inactivated},
993
994     {(DynFun*)window_press, (DynFun*)frame_press},
995     
996     {(DynFun*)region_get_configuration,
997      (DynFun*)frame_get_configuration},
998
999     {window_draw, 
1000      frame_draw},
1001     
1002     {mplex_managed_geom, 
1003      frame_managed_geom},
1004
1005     {region_updategr, 
1006      frame_updategr},
1007
1008     {(DynFun*)region_fitrep,
1009      (DynFun*)frame_fitrep},
1010      
1011     {(DynFun*)region_managed_disposeroot,
1012      (DynFun*)frame_managed_disposeroot},
1013
1014     {region_managed_rqgeom_absolute, 
1015      frame_managed_rqgeom_absolute},
1016
1017     {(DynFun*)mplex_default_index,
1018      (DynFun*)frame_default_index},
1019     
1020     END_DYNFUNTAB
1021 };
1022                                        
1023
1024 EXTL_EXPORT
1025 IMPLCLASS(WFrame, WMPlex, frame_deinit, frame_dynfuntab);
1026
1027
1028 /*}}}*/