]> git.decadent.org.uk Git - ion3.git/blob - ioncore/resize.c
0da668d5ae4f8ba37438c85527d05ca002ef0007
[ion3.git] / ioncore / resize.c
1 /* 
2  * ion/ioncore/resize.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2008. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <stdio.h>
10 #include <limits.h>
11
12 #include <libtu/objp.h>
13 #include <libtu/minmax.h>
14 #include <libextl/extl.h>
15 #include <libmainloop/defer.h>
16
17 #include "common.h"
18 #include "global.h"
19 #include "resize.h"
20 #include "gr.h"
21 #include "sizehint.h"
22 #include "event.h"
23 #include "cursor.h"
24 #include "extlconv.h"
25 #include "grab.h"
26 #include "framep.h"
27 #include "infowin.h"
28
29
30 #define XOR_RESIZE (!ioncore_g.opaque_resize)
31
32
33 extern int ioncore_edge_resistance;
34
35
36 /*{{{ Size/position display and rubberband */
37
38
39 static void draw_rubberbox(WRootWin *rw, const WRectangle *rect)
40 {
41     XPoint fpts[5];
42     
43     fpts[0].x=rect->x;
44     fpts[0].y=rect->y;
45     fpts[1].x=rect->x+rect->w;
46     fpts[1].y=rect->y;
47     fpts[2].x=rect->x+rect->w;
48     fpts[2].y=rect->y+rect->h;
49     fpts[3].x=rect->x;
50     fpts[3].y=rect->y+rect->h;
51     fpts[4].x=rect->x;
52     fpts[4].y=rect->y;
53     
54     XDrawLines(ioncore_g.dpy, WROOTWIN_ROOT(rw), rw->xor_gc, fpts, 5, 
55                CoordModeOrigin);
56 }
57
58
59 static int max_width(GrBrush *brush, const char *str)
60 {
61     int maxw=0, w;
62     
63     while(str && *str!='\0'){
64         w=grbrush_get_text_width(brush, str, 1);
65         if(w>maxw)
66             maxw=w;
67         str++;
68     }
69     
70     return maxw;
71 }
72
73
74 static int chars_for_num(int d)
75 {
76     int n=0;
77     
78     do{
79         n++;
80         d/=10;
81     }while(d);
82     
83     return n;
84 }
85
86
87 static WInfoWin *setup_moveres_display(WWindow *parent, int cx, int cy)
88 {
89     GrBorderWidths bdw;
90     GrFontExtents fnte;
91     WInfoWin *infowin;
92     WFitParams fp;
93     
94     fp.mode=REGION_FIT_EXACT;
95     fp.g.x=0;
96     fp.g.y=0;
97     fp.g.w=1;
98     fp.g.h=1;
99     
100     infowin=create_infowin(parent, &fp, "moveres_display");
101     
102     if(infowin==NULL)
103         return NULL;
104     
105     grbrush_get_border_widths(INFOWIN_BRUSH(infowin), &bdw);
106     grbrush_get_font_extents(INFOWIN_BRUSH(infowin), &fnte);
107     
108     /* Create move/resize position/size display window */
109     fp.g.w=3;
110     fp.g.w+=chars_for_num(REGION_GEOM(parent).w);
111     fp.g.w+=chars_for_num(REGION_GEOM(parent).h);
112     fp.g.w*=max_width(INFOWIN_BRUSH(infowin), "0123456789x+");     
113     fp.g.w+=bdw.left+bdw.right;
114     fp.g.h=fnte.max_height+bdw.top+bdw.bottom;;
115     
116     fp.g.x=cx-fp.g.w/2;
117     fp.g.y=cy-fp.g.h/2;
118     
119     region_fitrep((WRegion*)infowin, NULL, &fp);
120     region_map((WRegion*)infowin);
121     
122     return infowin;
123 }
124
125
126 static void moveres_draw_infowin(WMoveresMode *mode)
127 {
128     WRectangle geom;
129     char *buf;
130     
131     if(mode->infowin==NULL)
132         return;
133     
134     buf=INFOWIN_BUFFER(mode->infowin);
135     
136     if(buf==NULL)
137         return;
138     
139     if(mode->mode==MOVERES_SIZE){
140         int w, h;
141         
142         w=mode->geom.w;
143         h=mode->geom.h;
144         
145         if(mode->hints.base_set){
146             w=maxof(0, w-mode->hints.base_width);
147             h=maxof(0, h-mode->hints.base_height);
148         }
149         
150         if(mode->hints.inc_set){
151             w/=maxof(1, mode->hints.width_inc);
152             h/=maxof(1, mode->hints.height_inc);
153         }
154         
155         snprintf(buf, INFOWIN_BUFFER_LEN, "%dx%d", w, h);
156     }else{
157         snprintf(buf, INFOWIN_BUFFER_LEN, "%+d %+d", 
158                  mode->geom.x, mode->geom.y);
159     }
160     
161     window_draw((WWindow*)mode->infowin, TRUE);
162 }
163
164
165 static void moveres_draw_rubberband(WMoveresMode *mode)
166 {
167     WRectangle rgeom=mode->geom;
168     int rx, ry;
169     WRootWin *rootwin=(mode->reg==NULL 
170                        ? NULL 
171                        : region_rootwin_of(mode->reg));
172     
173     if(rootwin==NULL)
174         return;
175     
176     rgeom.x+=mode->parent_rx;
177     rgeom.y+=mode->parent_ry;
178     
179     if(mode->rubfn==NULL)
180         draw_rubberbox(rootwin, &rgeom);
181     else
182         mode->rubfn(rootwin, &rgeom);
183 }
184
185
186 /*}}}*/
187
188
189 /*{{{ Move/resize mode */
190
191
192 WMoveresMode *tmpmode=NULL;
193
194
195 EXTL_EXPORT
196 IMPLCLASS(WMoveresMode, Obj, NULL, NULL);
197
198
199 WMoveresMode *moveres_mode(WRegion *reg)
200 {
201     if(tmpmode==NULL)
202         return NULL;
203     return ((reg==NULL || tmpmode->reg==reg) ? tmpmode : NULL);
204 }
205
206
207 WRegion *moveresmode_target(WMoveresMode *mode)
208 {
209     return mode->reg;
210 }
211
212
213 static bool moveresmode_init(WMoveresMode *mode, WRegion *reg,
214                              WDrawRubberbandFn *rubfn, bool cumulative)
215 {
216     WWindow *parent;
217     WRegion *mgr;
218     
219     if(tmpmode!=NULL)
220         return FALSE;
221     
222     parent=REGION_PARENT(reg);
223     
224     if(parent==NULL)
225         return FALSE;
226     
227     tmpmode=mode;
228     
229     mode->snap_enabled=FALSE;
230     region_size_hints(reg, &mode->hints);
231     
232     region_rootpos((WRegion*)parent, &mode->parent_rx, &mode->parent_ry);
233     
234     mode->geom=REGION_GEOM(reg);
235     mode->origgeom=REGION_GEOM(reg);
236     mode->dx1=0;
237     mode->dx2=0;
238     mode->dy1=0;
239     mode->dy2=0;
240     mode->rubfn=rubfn;
241     mode->resize_cumulative=cumulative;
242     mode->rqflags=(XOR_RESIZE ? REGION_RQGEOM_TRYONLY : 0);
243     mode->reg=reg;
244     mode->mode=MOVERES_SIZE;
245     
246     /* Get snapping geometry */
247     mgr=REGION_MANAGER(reg);
248     
249     if(mgr!=NULL){
250         mode->snapgeom=REGION_GEOM(mgr);
251         
252         if(mgr==(WRegion*)parent){
253             mode->snapgeom.x=0;
254             mode->snapgeom.y=0;    
255             /*mode->snap_enabled=FALSE;*/
256         }
257         mode->snap_enabled=TRUE;
258     }
259     
260     if(!mode->hints.min_set || mode->hints.min_width<1)
261         mode->hints.min_width=1;
262     if(!mode->hints.min_set || mode->hints.min_height<1)
263         mode->hints.min_height=1;
264     
265     /* Set up info window */
266     {
267         int x=mode->geom.x+mode->geom.w/2;
268         int y=mode->geom.y+mode->geom.h/2;
269         mode->infowin=setup_moveres_display(parent, x, y);
270     }
271                                         
272     moveres_draw_infowin(mode);
273     
274     if(XOR_RESIZE){
275         XGrabServer(ioncore_g.dpy);
276         moveres_draw_rubberband(mode);
277     }
278     
279     return TRUE;
280 }
281
282
283 static WMoveresMode *create_moveresmode(WRegion *reg,
284                                         WDrawRubberbandFn *rubfn,
285                                         bool cumulative)
286 {
287     CREATEOBJ_IMPL(WMoveresMode, moveresmode, (p, reg, rubfn, cumulative));
288 }
289
290
291 WMoveresMode *region_begin_resize(WRegion *reg, WDrawRubberbandFn *rubfn, 
292                                   bool cumulative)
293 {
294     WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
295     
296     if(mode!=NULL){
297         mode->mode=MOVERES_SIZE;
298         ioncore_change_grab_cursor(IONCORE_CURSOR_RESIZE);
299     }
300
301     return mode;
302 }
303
304
305 WMoveresMode *region_begin_move(WRegion *reg, WDrawRubberbandFn *rubfn, 
306                                 bool cumulative)
307 {
308     WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
309     
310     if(mode!=NULL){
311         mode->mode=MOVERES_POS;
312         ioncore_change_grab_cursor(IONCORE_CURSOR_MOVE);
313     }
314     
315     return mode;
316 }
317
318
319 static void moveresmode_setorig(WMoveresMode *mode)
320 {
321     mode->dx1=0;
322     mode->dx2=0;
323     mode->dy1=0;
324     mode->dy2=0;
325     mode->origgeom=mode->geom;
326 }
327
328
329 static void moveresmode_do_newgeom(WMoveresMode *mode, WRQGeomParams *rq)
330 {    
331     if(XOR_RESIZE)
332         moveres_draw_rubberband(mode);
333     
334     if(mode->reg!=NULL){
335         rq->flags|=mode->rqflags;
336         region_rqgeom(mode->reg, rq, &mode->geom);
337     }
338     
339     moveres_draw_infowin(mode);
340     
341     if(XOR_RESIZE)
342         moveres_draw_rubberband(mode);
343 }
344
345
346 static int clamp_up(int t, int low, int high)
347 {
348     return (t < high && t > low ? high : t);
349 }
350
351     
352 static void moveresmode_delta(WMoveresMode *mode, 
353                               int dx1, int dx2, int dy1, int dy2,
354                               WRectangle *rret)
355 {
356     int realdx1, realdx2, realdy1, realdy2;
357     WRQGeomParams rq=RQGEOMPARAMS_INIT;
358     int er=ioncore_edge_resistance;
359     int w=0, h=0;
360     
361     realdx1=(mode->dx1+=dx1);
362     realdx2=(mode->dx2+=dx2);
363     realdy1=(mode->dy1+=dy1);
364     realdy2=(mode->dy2+=dy2);
365     rq.geom=mode->origgeom;
366     
367     /* snap */
368     if(mode->snap_enabled){
369         WRectangle *sg=&mode->snapgeom;
370         
371         if(mode->dx1!=0 && rq.geom.x+mode->dx1<sg->x && rq.geom.x+mode->dx1>sg->x-er)
372             realdx1=sg->x-rq.geom.x;
373         if(mode->dx2!=0 && rq.geom.x+rq.geom.w+mode->dx2>sg->x+sg->w && rq.geom.x+rq.geom.w+mode->dx2<sg->x+sg->w+er)
374             realdx2=sg->x+sg->w-rq.geom.x-rq.geom.w;
375         if(mode->dy1!=0 && rq.geom.y+mode->dy1<sg->y && rq.geom.y+mode->dy1>sg->y-er)
376             realdy1=sg->y-rq.geom.y;
377         if(mode->dy2!=0 && rq.geom.y+rq.geom.h+mode->dy2>sg->y+sg->h && rq.geom.y+rq.geom.h+mode->dy2<sg->y+sg->h+er)
378             realdy2=sg->y+sg->h-rq.geom.y-rq.geom.h;
379     }
380     
381     w=maxof(1, mode->origgeom.w-realdx1+realdx2);
382     h=maxof(1, mode->origgeom.h-realdy1+realdy2);
383         
384     if(mode->snap_enabled && mode->hints.base_set){
385         w=clamp_up(w, mode->hints.base_width-er, mode->hints.base_width);
386         h=clamp_up(h, mode->hints.base_height-er, mode->hints.base_height);
387     }
388     
389     /* Correct size */
390     sizehints_correct(&mode->hints, &w, &h, TRUE, TRUE);
391     
392     /* Do not modify coordinates and sizes that were not requested to be
393      * changed. 
394      */
395     
396     if(mode->dx1==mode->dx2){
397         if(mode->dx1==0 || realdx1!=mode->dx1)
398             rq.geom.x+=realdx1;
399         else
400             rq.geom.x+=realdx2;
401     }else{
402         rq.geom.w=w;
403         if(mode->dx1==0 || realdx1!=mode->dx1)
404             rq.geom.x+=realdx1;
405         else
406             rq.geom.x+=mode->origgeom.w-rq.geom.w;
407     }
408     
409     
410     if(mode->dy1==mode->dy2){
411         if(mode->dy1==0 || realdy1!=mode->dy1)
412             rq.geom.y+=realdy1;
413         else
414             rq.geom.y+=realdy2;
415     }else{
416         rq.geom.h=h;
417         if(mode->dy1==0 || realdy1!=mode->dy1)
418             rq.geom.y+=realdy1;
419         else
420             rq.geom.y+=mode->origgeom.h-rq.geom.h;
421     }
422     
423     moveresmode_do_newgeom(mode, &rq);
424     
425     if(!mode->resize_cumulative)
426         moveresmode_setorig(mode);
427     
428     if(rret!=NULL)
429         *rret=mode->geom;
430 }
431
432
433 void moveresmode_delta_resize(WMoveresMode *mode, 
434                               int dx1, int dx2, int dy1, int dy2,
435                               WRectangle *rret)
436 {
437     mode->mode=MOVERES_SIZE;
438     moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret);
439 }
440
441
442 void moveresmode_delta_move(WMoveresMode *mode, 
443                             int dx, int dy, WRectangle *rret)
444 {
445     mode->mode=MOVERES_POS;
446     moveresmode_delta(mode, dx, dx, dy, dy, rret);
447 }
448
449
450 void moveresmode_rqgeom(WMoveresMode *mode, WRQGeomParams *rq, 
451                         WRectangle *rret)
452 {
453     mode->mode=MOVERES_SIZE;
454     moveresmode_do_newgeom(mode, rq);
455     moveresmode_setorig(mode);
456 }
457
458
459 /* It is ugly to do this here, but it will have to do for now... */
460 static void set_saved(WMoveresMode *mode, WRegion *reg)
461 {
462     WFrame *frame;
463     
464     if(!OBJ_IS(reg, WFrame))
465         return;
466     
467     frame=(WFrame*)reg;
468     
469     /* Restore saved sizes from the beginning of the resize action */
470     if(mode->origgeom.w!=mode->geom.w){
471         frame->saved_x=mode->origgeom.x;
472         frame->saved_w=mode->origgeom.w;
473     }
474     
475     if(mode->origgeom.h!=mode->geom.h){
476         frame->saved_y=mode->origgeom.y;
477         frame->saved_h=mode->origgeom.h;
478     }
479 }
480
481
482 bool moveresmode_do_end(WMoveresMode *mode, bool apply)
483 {
484     WRegion *reg=mode->reg;
485     
486     assert(reg!=NULL);
487     assert(tmpmode==mode);
488     
489     tmpmode=NULL;
490     
491     if(XOR_RESIZE){
492         moveres_draw_rubberband(mode);
493         if(apply){
494             WRQGeomParams rq=RQGEOMPARAMS_INIT;
495             
496             rq.geom=mode->geom;
497             rq.flags=mode->rqflags&~REGION_RQGEOM_TRYONLY;
498
499             region_rqgeom(reg, &rq, &mode->geom);
500         }
501         XUngrabServer(ioncore_g.dpy);
502     }
503     if(apply)
504         set_saved(mode, reg);
505     
506     if(mode->infowin!=NULL){
507         mainloop_defer_destroy((Obj*)mode->infowin);
508         mode->infowin=NULL;
509     }
510     destroy_obj((Obj*)mode);
511     
512     return TRUE;
513 }
514
515
516 /*}}}*/
517
518
519 /*{{{ Request and other dynfuns */
520
521
522 void region_rqgeom(WRegion *reg, const WRQGeomParams *rq,
523                    WRectangle *geomret)
524 {
525     WRegion *mgr=REGION_MANAGER(reg);
526     
527     if(mgr!=NULL){
528         if(rq->flags&REGION_RQGEOM_ABSOLUTE)
529             region_managed_rqgeom_absolute(mgr, reg, rq, geomret);
530         else
531             region_managed_rqgeom(mgr, reg, rq, geomret);
532     }else{
533         WRectangle tmp;
534         
535         if(rq->flags&REGION_RQGEOM_ABSOLUTE)
536             region_absolute_geom_to_parent(reg, &rq->geom, &tmp);
537         else
538             tmp=rq->geom;
539         
540         if(geomret!=NULL)
541             *geomret=tmp;
542         
543         if(!(rq->flags&REGION_RQGEOM_TRYONLY))
544             region_fit(reg, &tmp, REGION_FIT_EXACT);
545     }
546 }
547
548
549 void rqgeomparams_from_table(WRQGeomParams *rq, 
550                              const WRectangle *origg, ExtlTab g)
551 {
552     rq->geom=*origg;
553     rq->flags=REGION_RQGEOM_WEAK_ALL;
554     
555     if(extl_table_gets_i(g, "x", &(rq->geom.x)))
556        rq->flags&=~REGION_RQGEOM_WEAK_X;
557     if(extl_table_gets_i(g, "y", &(rq->geom.y)))
558        rq->flags&=~REGION_RQGEOM_WEAK_Y;
559     if(extl_table_gets_i(g, "w", &(rq->geom.w)))
560        rq->flags&=~REGION_RQGEOM_WEAK_W;
561     if(extl_table_gets_i(g, "h", &(rq->geom.h)))
562        rq->flags&=~REGION_RQGEOM_WEAK_H;
563
564     rq->geom.w=maxof(1, rq->geom.w);
565     rq->geom.h=maxof(1, rq->geom.h);
566 }
567
568
569 /*EXTL_DOC
570  * Attempt to resize and/or move \var{reg}. The table \var{g} is a usual
571  * geometry specification (fields \var{x}, \var{y}, \var{w} and \var{h}),
572  * but may contain missing fields, in which case, \var{reg}'s manager may
573  * attempt to leave that attribute unchanged.
574  */
575 EXTL_EXPORT_AS(WRegion, rqgeom)
576 ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g)
577 {
578     WRQGeomParams rq=RQGEOMPARAMS_INIT;
579     WRectangle res;
580     
581     rqgeomparams_from_table(&rq, &REGION_GEOM(reg), g);
582     
583     region_rqgeom(reg, &rq, &res);
584     
585     return extl_table_from_rectangle(&res);
586 }
587
588
589 void region_managed_rqgeom(WRegion *mgr, WRegion *reg,
590                            const WRQGeomParams *rq,
591                            WRectangle *geomret)
592 {
593     CALL_DYN(region_managed_rqgeom, mgr, (mgr, reg, rq, geomret));
594 }
595
596
597 void region_managed_rqgeom_absolute(WRegion *mgr, WRegion *reg,
598                                     const WRQGeomParams *rq,
599                                     WRectangle *geomret)
600 {
601     CALL_DYN(region_managed_rqgeom_absolute, mgr,
602              (mgr, reg, rq, geomret));
603 }
604
605
606 void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg,
607                                  const WRQGeomParams *rq,
608                                  WRectangle *geomret)
609 {
610     if(geomret!=NULL)
611         *geomret=rq->geom;
612     
613     if(!(rq->flags&REGION_RQGEOM_TRYONLY))
614         region_fit(reg, &rq->geom, REGION_FIT_EXACT);
615 }
616
617
618 void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg,
619                                    const WRQGeomParams *rq,
620                                    WRectangle *geomret)
621 {
622     if(geomret!=NULL)
623         *geomret=REGION_GEOM(reg);
624 }
625
626
627 void region_managed_rqgeom_absolute_default(WRegion *mgr, WRegion *reg,
628                                             const WRQGeomParams *rq,
629                                             WRectangle *geomret)
630 {
631     WRQGeomParams rq2=RQGEOMPARAMS_INIT;
632     
633     rq2.flags=rq->flags&~REGION_RQGEOM_ABSOLUTE;
634     region_absolute_geom_to_parent(reg, &rq->geom, &rq2.geom);
635     
636     region_managed_rqgeom(mgr, reg, &rq2, geomret);
637 }
638
639
640 void region_size_hints(WRegion *reg, WSizeHints *hints_ret)
641 {
642     sizehints_clear(hints_ret);
643     
644     {
645         CALL_DYN(region_size_hints, reg, (reg, hints_ret));
646     }
647     
648     if(!hints_ret->min_set){
649         hints_ret->min_width=1;
650         hints_ret->min_height=1;
651     }
652     if(!hints_ret->base_set){
653         hints_ret->base_width=0;
654         hints_ret->base_height=0;
655     }
656     if(!hints_ret->max_set){
657         hints_ret->max_width=INT_MAX;
658         hints_ret->max_height=INT_MAX;
659     }
660 }
661
662
663 void region_size_hints_correct(WRegion *reg, 
664                                int *wp, int *hp, bool min)
665 {
666     WSizeHints hints;
667     
668     region_size_hints(reg, &hints);
669     
670     sizehints_correct(&hints, wp, hp, min, FALSE);
671 }
672
673
674 int region_orientation(WRegion *reg)
675 {
676     int ret=REGION_ORIENTATION_NONE;
677     
678     CALL_DYN_RET(ret, int, region_orientation, reg, (reg));
679     
680     return ret;
681 }
682
683
684 /*EXTL_DOC
685  * Returns size hints for \var{reg}. The returned table always contains the
686  * fields \code{min_?}, \code{base_?} and sometimes the fields \code{max_?},
687  * \code{base_?} and \code{inc_?}, where \code{?}=\code{w}, \code{h}.
688  */
689 EXTL_SAFE
690 EXTL_EXPORT_AS(WRegion, size_hints)
691 ExtlTab region_size_hints_extl(WRegion *reg)
692 {
693     WSizeHints hints;
694     ExtlTab tab;
695     
696     region_size_hints(reg, &hints);
697     
698     tab=extl_create_table();
699     
700     if(hints.base_set){
701         extl_table_sets_i(tab, "base_w", hints.base_width);
702         extl_table_sets_i(tab, "base_h", hints.base_height);
703     }
704     if(hints.min_set){
705         extl_table_sets_i(tab, "min_w", hints.min_width);
706         extl_table_sets_i(tab, "min_h", hints.min_height);
707     }
708     if(hints.max_set){
709         extl_table_sets_i(tab, "max_w", hints.max_width);
710         extl_table_sets_i(tab, "max_h", hints.max_height);
711     }
712     if(hints.inc_set){
713         extl_table_sets_i(tab, "inc_w", hints.width_inc);
714         extl_table_sets_i(tab, "inc_h", hints.height_inc);
715     }
716     
717     return tab;
718 }
719
720 /*}}}*/
721
722
723 /*{{{ Restore size, maximize, shade */
724
725
726 static bool rqh(WFrame *frame, int y, int h)
727 {
728     WRQGeomParams rq=RQGEOMPARAMS_INIT;
729     WRectangle rgeom;
730     int dummy_w;
731     
732     rq.flags=REGION_RQGEOM_VERT_ONLY;
733     
734     rq.geom.x=REGION_GEOM(frame).x;
735     rq.geom.w=REGION_GEOM(frame).w;
736     rq.geom.y=y;
737     rq.geom.h=h;
738     
739     dummy_w=rq.geom.w;
740     region_size_hints_correct((WRegion*)frame, &dummy_w, &(rq.geom.h), TRUE);
741     
742     region_rqgeom((WRegion*)frame, &rq, &rgeom);
743     
744     return (abs(rgeom.y-REGION_GEOM(frame).y)>1 ||
745             abs(rgeom.h-REGION_GEOM(frame).h)>1);
746 }
747
748
749 /*EXTL_DOC
750  * Attempt to toggle vertical maximisation of \var{frame}.
751  */
752 EXTL_EXPORT_MEMBER
753 void frame_maximize_vert(WFrame *frame)
754 {
755     WRegion *mp=region_manager((WRegion*)frame);
756     int oy, oh;
757     
758     if(frame->flags&FRAME_SHADED || frame->flags&FRAME_MAXED_VERT){
759         if(frame->flags&FRAME_SAVED_VERT)
760             rqh(frame, frame->saved_y, frame->saved_h);
761         frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
762         return;
763     }
764
765     if(mp==NULL)
766         return;
767     
768     oy=REGION_GEOM(frame).y;
769     oh=REGION_GEOM(frame).h;
770     
771     rqh(frame, 0, REGION_GEOM(mp).h);
772
773     frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
774     frame->saved_y=oy;
775     frame->saved_h=oh;
776 }
777
778 static bool rqw(WFrame *frame, int x, int w)
779 {
780     WRQGeomParams rq=RQGEOMPARAMS_INIT;
781     WRectangle rgeom;
782     int dummy_h;
783
784     rq.flags=REGION_RQGEOM_HORIZ_ONLY;
785     
786     rq.geom.x=x;
787     rq.geom.w=w;
788     rq.geom.y=REGION_GEOM(frame).y;
789     rq.geom.h=REGION_GEOM(frame).h;
790     
791     dummy_h=rq.geom.h;
792     region_size_hints_correct((WRegion*)frame, &(rq.geom.w), &dummy_h, TRUE);
793     
794     region_rqgeom((WRegion*)frame, &rq, &rgeom);
795     
796     return (abs(rgeom.x-REGION_GEOM(frame).x)>1 ||
797             abs(rgeom.w-REGION_GEOM(frame).w)>1);
798 }
799
800
801 /*EXTL_DOC
802  * Attempt to toggle horizontal maximisation of \var{frame}.
803  */
804 EXTL_EXPORT_MEMBER
805 void frame_maximize_horiz(WFrame *frame)
806 {
807     WRegion *mp=region_manager((WRegion*)frame);
808     int ox, ow;
809     
810     if(frame->flags&FRAME_MIN_HORIZ || frame->flags&FRAME_MAXED_HORIZ){
811         if(frame->flags&FRAME_SAVED_HORIZ)
812             rqw(frame, frame->saved_x, frame->saved_w);
813         frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
814         return;
815     }
816
817     if(mp==NULL)
818         return;
819     
820     ox=REGION_GEOM(frame).x;
821     ow=REGION_GEOM(frame).w;
822     
823     rqw(frame, 0, REGION_GEOM(mp).w);
824         
825     frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
826     frame->saved_x=ox;
827     frame->saved_w=ow;
828 }
829
830
831
832 /*}}}*/
833
834
835 /*{{{ Misc. */
836
837
838 uint region_min_h(WRegion *reg)
839 {
840     WSizeHints hints;
841     region_size_hints(reg, &hints);
842     return hints.min_height;
843 }
844
845
846 uint region_min_w(WRegion *reg)
847 {
848     WSizeHints hints;
849     region_size_hints(reg, &hints);
850     return hints.min_width;
851 }
852
853
854 void region_convert_root_geom(WRegion *reg, WRectangle *geom)
855 {
856     int rx, ry;
857     if(reg!=NULL){
858         region_rootpos(reg, &rx, &ry);
859         geom->x-=rx;
860         geom->y-=ry;
861     }
862 }
863
864
865 void region_absolute_geom_to_parent(WRegion *reg, const WRectangle *rgeom,
866                                     WRectangle *pgeom)
867 {
868     WRegion *parent=REGION_PARENT_REG(reg);
869     
870     pgeom->w=rgeom->w;
871     pgeom->h=rgeom->h;
872     
873     if(parent==NULL){
874         pgeom->x=rgeom->x;
875         pgeom->y=rgeom->y;
876     }else{
877         region_rootpos(reg, &pgeom->x, &pgeom->y);
878         pgeom->x=rgeom->x-pgeom->x;
879         pgeom->y=rgeom->y-pgeom->y;
880     }
881 }
882
883 /*}}}*/
884