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