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