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