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