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