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