]> git.decadent.org.uk Git - ion3.git/blob - ioncore/resize.c
[svn-inject] Installing original source of ion3
[ion3.git] / ioncore / resize.c
1 /* 
2  * ion/ioncore/resize.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 <stdio.h>
13 #include <limits.h>
14
15 #include <libtu/objp.h>
16 #include <libtu/minmax.h>
17 #include <libextl/extl.h>
18 #include <libmainloop/defer.h>
19
20 #include "common.h"
21 #include "global.h"
22 #include "resize.h"
23 #include "gr.h"
24 #include "sizehint.h"
25 #include "event.h"
26 #include "cursor.h"
27 #include "extlconv.h"
28 #include "grab.h"
29 #include "framep.h"
30 #include "infowin.h"
31
32
33 #define XOR_RESIZE (!ioncore_g.opaque_resize)
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.inc_set) &&
146            (mode->hints.width_inc>1 || mode->hints.height_inc>1)){
147             if(mode->hints.base_set){
148                 w-=mode->hints.base_width;
149                 h-=mode->hints.base_height;
150             }
151             w/=mode->hints.width_inc;
152             h/=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         }else if(REGION_PARENT(mgr)==parent){
257             mode->snap_enabled=TRUE;
258         }
259     }
260     
261     if(!mode->hints.min_set || mode->hints.min_width<1)
262         mode->hints.min_width=1;
263     if(!mode->hints.min_set || mode->hints.min_height<1)
264         mode->hints.min_height=1;
265     
266     /* Set up info window */
267     {
268         int x=mode->geom.x+mode->geom.w/2;
269         int y=mode->geom.y+mode->geom.h/2;
270         mode->infowin=setup_moveres_display(parent, x, y);
271     }
272                                         
273     moveres_draw_infowin(mode);
274     
275     if(XOR_RESIZE){
276         XGrabServer(ioncore_g.dpy);
277         moveres_draw_rubberband(mode);
278     }
279     
280     return TRUE;
281 }
282
283
284 static WMoveresMode *create_moveresmode(WRegion *reg,
285                                         WDrawRubberbandFn *rubfn,
286                                         bool cumulative)
287 {
288     CREATEOBJ_IMPL(WMoveresMode, moveresmode, (p, reg, rubfn, cumulative));
289 }
290
291
292 WMoveresMode *region_begin_resize(WRegion *reg, WDrawRubberbandFn *rubfn, 
293                                   bool cumulative)
294 {
295     WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
296     
297     if(mode!=NULL){
298         mode->mode=MOVERES_SIZE;
299         ioncore_change_grab_cursor(IONCORE_CURSOR_RESIZE);
300     }
301
302     return mode;
303 }
304
305
306 WMoveresMode *region_begin_move(WRegion *reg, WDrawRubberbandFn *rubfn, 
307                                 bool cumulative)
308 {
309     WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
310     
311     if(mode!=NULL){
312         mode->mode=MOVERES_POS;
313         ioncore_change_grab_cursor(IONCORE_CURSOR_MOVE);
314     }
315     
316     return mode;
317 }
318
319
320 static void moveresmode_delta(WMoveresMode *mode, 
321                               int dx1, int dx2, int dy1, int dy2,
322                               WRectangle *rret)
323 {
324     int realdx1, realdx2, realdy1, realdy2;
325     WRQGeomParams rq=RQGEOMPARAMS_INIT;
326     int w=0, h=0;
327     
328     realdx1=(mode->dx1+=dx1);
329     realdx2=(mode->dx2+=dx2);
330     realdy1=(mode->dy1+=dy1);
331     realdy2=(mode->dy2+=dy2);
332     rq.geom=mode->origgeom;
333     
334     /* snap */
335     if(mode->snap_enabled){
336         WRectangle *sg=&mode->snapgeom;
337         int er=CF_EDGE_RESISTANCE;
338         
339         if(mode->dx1!=0 && rq.geom.x+mode->dx1<sg->x && rq.geom.x+mode->dx1>sg->x-er)
340             realdx1=sg->x-rq.geom.x;
341         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)
342             realdx2=sg->x+sg->w-rq.geom.x-rq.geom.w;
343         if(mode->dy1!=0 && rq.geom.y+mode->dy1<sg->y && rq.geom.y+mode->dy1>sg->y-er)
344             realdy1=sg->y-rq.geom.y;
345         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)
346             realdy2=sg->y+sg->h-rq.geom.y-rq.geom.h;
347     }
348     
349     w=mode->origgeom.w-realdx1+realdx2;
350     h=mode->origgeom.h-realdy1+realdy2;
351     
352     if(w<=0)
353         w=mode->hints.min_width;
354     if(h<=0)
355         h=mode->hints.min_height;
356     
357     sizehints_correct(&mode->hints, &w, &h, TRUE, TRUE);
358     
359     /* Do not modify coordinates and sizes that were not requested to be
360      * changed. 
361      */
362     
363     if(mode->dx1==mode->dx2){
364         if(mode->dx1==0 || realdx1!=mode->dx1)
365             rq.geom.x+=realdx1;
366         else
367             rq.geom.x+=realdx2;
368     }else{
369         rq.geom.w=w;
370         if(mode->dx1==0 || realdx1!=mode->dx1)
371             rq.geom.x+=realdx1;
372         else
373             rq.geom.x+=mode->origgeom.w-rq.geom.w;
374     }
375     
376     
377     if(mode->dy1==mode->dy2){
378         if(mode->dy1==0 || realdy1!=mode->dy1)
379             rq.geom.y+=realdy1;
380         else
381             rq.geom.y+=realdy2;
382     }else{
383         rq.geom.h=h;
384         if(mode->dy1==0 || realdy1!=mode->dy1)
385             rq.geom.y+=realdy1;
386         else
387             rq.geom.y+=mode->origgeom.h-rq.geom.h;
388     }
389     
390     if(XOR_RESIZE)
391         moveres_draw_rubberband(mode);
392     
393     if(mode->reg!=NULL){
394         rq.flags=mode->rqflags;
395         region_rqgeom(mode->reg, &rq, &mode->geom);
396     }
397     
398     if(!mode->resize_cumulative){
399         mode->dx1=0;
400         mode->dx2=0;
401         mode->dy1=0;
402         mode->dy2=0;
403         mode->origgeom=mode->geom;
404     }
405     
406     moveres_draw_infowin(mode);
407     
408     if(XOR_RESIZE)
409         moveres_draw_rubberband(mode);
410     
411     if(rret!=NULL)
412         *rret=mode->geom;
413 }
414
415
416 void moveresmode_delta_resize(WMoveresMode *mode, 
417                               int dx1, int dx2, int dy1, int dy2,
418                               WRectangle *rret)
419 {
420     mode->mode=MOVERES_SIZE;
421     moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret);
422 }
423
424
425 void moveresmode_delta_move(WMoveresMode *mode, 
426                             int dx, int dy, WRectangle *rret)
427 {
428     mode->mode=MOVERES_POS;
429     moveresmode_delta(mode, dx, dx, dy, dy, rret);
430 }
431
432
433 /* It is ugly to do this here, but it will have to do for now... */
434 static void set_saved(WMoveresMode *mode, WRegion *reg)
435 {
436     WFrame *frame;
437     
438     if(!OBJ_IS(reg, WFrame))
439         return;
440     
441     frame=(WFrame*)reg;
442     
443     /* Restore saved sizes from the beginning of the resize action */
444     if(mode->origgeom.w!=mode->geom.w){
445         frame->saved_x=mode->origgeom.x;
446         frame->saved_w=mode->origgeom.w;
447     }
448     
449     if(mode->origgeom.h!=mode->geom.h){
450         frame->saved_y=mode->origgeom.y;
451         frame->saved_h=mode->origgeom.h;
452     }
453 }
454
455
456 bool moveresmode_do_end(WMoveresMode *mode, bool apply)
457 {
458     WRegion *reg=mode->reg;
459     
460     assert(reg!=NULL);
461     assert(tmpmode==mode);
462     
463     tmpmode=NULL;
464     
465     if(XOR_RESIZE){
466         moveres_draw_rubberband(mode);
467         if(apply){
468             WRQGeomParams rq=RQGEOMPARAMS_INIT;
469             
470             rq.geom=mode->geom;
471             rq.flags=mode->rqflags&~REGION_RQGEOM_TRYONLY;
472
473             region_rqgeom(reg, &rq, &mode->geom);
474         }
475         XUngrabServer(ioncore_g.dpy);
476     }
477     if(apply)
478         set_saved(mode, reg);
479     
480     if(mode->infowin!=NULL){
481         mainloop_defer_destroy((Obj*)mode->infowin);
482         mode->infowin=NULL;
483     }
484     destroy_obj((Obj*)mode);
485     
486     return TRUE;
487 }
488
489
490 /*}}}*/
491
492
493 /*{{{ Request and other dynfuns */
494
495
496 void region_rqgeom(WRegion *reg, const WRQGeomParams *rq,
497                    WRectangle *geomret)
498 {
499     WRegion *mgr=REGION_MANAGER(reg);
500     
501     if(mgr!=NULL){
502         if(rq->flags&REGION_RQGEOM_ABSOLUTE)
503             region_managed_rqgeom_absolute(mgr, reg, rq, geomret);
504         else
505             region_managed_rqgeom(mgr, reg, rq, geomret);
506     }else{
507         WRectangle tmp;
508         
509         if(rq->flags&REGION_RQGEOM_ABSOLUTE)
510             region_absolute_geom_to_parent(reg, &rq->geom, &tmp);
511         else
512             tmp=rq->geom;
513         
514         if(geomret!=NULL)
515             *geomret=tmp;
516         
517         if(!(rq->flags&REGION_RQGEOM_TRYONLY))
518             region_fit(reg, &tmp, REGION_FIT_EXACT);
519     }
520 }
521
522
523 /*EXTL_DOC
524  * Attempt to resize and/or move \var{reg}. The table \var{g} is a usual
525  * geometry specification (fields \var{x}, \var{y}, \var{w} and \var{h}),
526  * but may contain missing fields, in which case, \var{reg}'s manager may
527  * attempt to leave that attribute unchanged.
528  */
529 EXTL_EXPORT_AS(WRegion, rqgeom)
530 ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g)
531 {
532     WRectangle ogeom=REGION_GEOM(reg);
533     WRQGeomParams rq=RQGEOMPARAMS_INIT;
534     
535     
536     rq.geom=ogeom;
537     rq.flags=REGION_RQGEOM_WEAK_ALL;
538     
539     
540     if(extl_table_gets_i(g, "x", &(rq.geom.x)))
541        rq.flags&=~REGION_RQGEOM_WEAK_X;
542     if(extl_table_gets_i(g, "y", &(rq.geom.y)))
543        rq.flags&=~REGION_RQGEOM_WEAK_Y;
544     if(extl_table_gets_i(g, "w", &(rq.geom.w)))
545        rq.flags&=~REGION_RQGEOM_WEAK_W;
546     if(extl_table_gets_i(g, "h", &(rq.geom.h)))
547        rq.flags&=~REGION_RQGEOM_WEAK_H;
548
549     rq.geom.w=maxof(1, rq.geom.w);
550     rq.geom.h=maxof(1, rq.geom.h);
551     
552     region_rqgeom(reg, &rq, &ogeom);
553     
554     return extl_table_from_rectangle(&ogeom);
555 }
556
557
558 void region_managed_rqgeom(WRegion *mgr, WRegion *reg,
559                            const WRQGeomParams *rq,
560                            WRectangle *geomret)
561 {
562     CALL_DYN(region_managed_rqgeom, mgr, (mgr, reg, rq, geomret));
563 }
564
565
566 void region_managed_rqgeom_absolute(WRegion *mgr, WRegion *reg,
567                                     const WRQGeomParams *rq,
568                                     WRectangle *geomret)
569 {
570     CALL_DYN(region_managed_rqgeom_absolute, mgr,
571              (mgr, reg, rq, geomret));
572 }
573
574
575 void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg,
576                                  const WRQGeomParams *rq,
577                                  WRectangle *geomret)
578 {
579     if(geomret!=NULL)
580         *geomret=rq->geom;
581     
582     if(!(rq->flags&REGION_RQGEOM_TRYONLY))
583         region_fit(reg, &rq->geom, REGION_FIT_EXACT);
584 }
585
586
587 void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg,
588                                    const WRQGeomParams *rq,
589                                    WRectangle *geomret)
590 {
591     if(geomret!=NULL)
592         *geomret=REGION_GEOM(reg);
593 }
594
595
596 void region_managed_rqgeom_absolute_default(WRegion *mgr, WRegion *reg,
597                                             const WRQGeomParams *rq,
598                                             WRectangle *geomret)
599 {
600     WRQGeomParams rq2=RQGEOMPARAMS_INIT;
601     
602     rq2.flags=rq->flags&~REGION_RQGEOM_ABSOLUTE;
603     region_absolute_geom_to_parent(reg, &rq->geom, &rq2.geom);
604     
605     region_managed_rqgeom(mgr, reg, &rq2, geomret);
606 }
607
608
609 void region_size_hints(WRegion *reg, WSizeHints *hints_ret)
610 {
611     sizehints_clear(hints_ret);
612     
613     {
614         CALL_DYN(region_size_hints, reg, (reg, hints_ret));
615     }
616     
617     if(!hints_ret->min_set){
618         hints_ret->min_width=1;
619         hints_ret->min_height=1;
620     }
621     if(!hints_ret->base_set){
622         hints_ret->base_width=0;
623         hints_ret->base_height=0;
624     }
625     if(!hints_ret->max_set){
626         hints_ret->max_width=INT_MAX;
627         hints_ret->max_height=INT_MAX;
628     }
629 }
630
631
632 void region_size_hints_correct(WRegion *reg, 
633                                int *wp, int *hp, bool min)
634 {
635     WSizeHints hints;
636     
637     region_size_hints(reg, &hints);
638     
639     sizehints_correct(&hints, wp, hp, min, FALSE);
640 }
641
642
643 int region_orientation(WRegion *reg)
644 {
645     int ret=REGION_ORIENTATION_NONE;
646     
647     CALL_DYN_RET(ret, int, region_orientation, reg, (reg));
648     
649     return ret;
650 }
651
652
653 /*EXTL_DOC
654  * Returns size hints for \var{reg}. The returned table always contains the
655  * fields \code{min_?}, \code{base_?} and sometimes the fields \code{max_?},
656  * \code{base_?} and \code{inc_?}, where \code{?}=\code{w}, \code{h}.
657  */
658 EXTL_SAFE
659 EXTL_EXPORT_AS(WRegion, size_hints)
660 ExtlTab region_size_hints_extl(WRegion *reg)
661 {
662     WSizeHints hints;
663     ExtlTab tab;
664     
665     region_size_hints(reg, &hints);
666     
667     tab=extl_create_table();
668     
669     if(hints.base_set){
670         extl_table_sets_i(tab, "base_w", hints.base_width);
671         extl_table_sets_i(tab, "base_h", hints.base_height);
672     }
673     if(hints.min_set){
674         extl_table_sets_i(tab, "min_w", hints.min_width);
675         extl_table_sets_i(tab, "min_h", hints.min_height);
676     }
677     if(hints.max_set){
678         extl_table_sets_i(tab, "max_w", hints.max_width);
679         extl_table_sets_i(tab, "max_h", hints.max_height);
680     }
681     if(hints.inc_set){
682         extl_table_sets_i(tab, "inc_w", hints.width_inc);
683         extl_table_sets_i(tab, "inc_h", hints.height_inc);
684     }
685     
686     return tab;
687 }
688
689 /*}}}*/
690
691
692 /*{{{ Restore size, maximize, shade */
693
694
695 static bool rqh(WFrame *frame, int y, int h)
696 {
697     WRQGeomParams rq=RQGEOMPARAMS_INIT;
698     WRectangle rgeom;
699     int dummy_w;
700     
701     rq.flags=REGION_RQGEOM_VERT_ONLY;
702     
703     rq.geom.x=REGION_GEOM(frame).x;
704     rq.geom.w=REGION_GEOM(frame).w;
705     rq.geom.y=y;
706     rq.geom.h=h;
707     
708     dummy_w=rq.geom.w;
709     region_size_hints_correct((WRegion*)frame, &dummy_w, &(rq.geom.h), TRUE);
710     
711     region_rqgeom((WRegion*)frame, &rq, &rgeom);
712     
713     return (abs(rgeom.y-REGION_GEOM(frame).y)>1 ||
714             abs(rgeom.h-REGION_GEOM(frame).h)>1);
715 }
716
717
718 /*EXTL_DOC
719  * Attempt to toggle vertical maximisation of \var{frame}.
720  */
721 EXTL_EXPORT_MEMBER
722 void frame_maximize_vert(WFrame *frame)
723 {
724     WRegion *mp=region_manager((WRegion*)frame);
725     int oy, oh;
726     
727     if(frame->flags&FRAME_SHADED || frame->flags&FRAME_MAXED_VERT){
728         if(frame->flags&FRAME_SAVED_VERT)
729             rqh(frame, frame->saved_y, frame->saved_h);
730         frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
731         return;
732     }
733
734     if(mp==NULL)
735         return;
736     
737     oy=REGION_GEOM(frame).y;
738     oh=REGION_GEOM(frame).h;
739     
740     rqh(frame, 0, REGION_GEOM(mp).h);
741
742     frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
743     frame->saved_y=oy;
744     frame->saved_h=oh;
745 }
746
747 static bool rqw(WFrame *frame, int x, int w)
748 {
749     WRQGeomParams rq=RQGEOMPARAMS_INIT;
750     WRectangle rgeom;
751     int dummy_h;
752
753     rq.flags=REGION_RQGEOM_HORIZ_ONLY;
754     
755     rq.geom.x=x;
756     rq.geom.w=w;
757     rq.geom.y=REGION_GEOM(frame).y;
758     rq.geom.h=REGION_GEOM(frame).h;
759     
760     dummy_h=rq.geom.h;
761     region_size_hints_correct((WRegion*)frame, &(rq.geom.w), &dummy_h, TRUE);
762     
763     region_rqgeom((WRegion*)frame, &rq, &rgeom);
764     
765     return (abs(rgeom.x-REGION_GEOM(frame).x)>1 ||
766             abs(rgeom.w-REGION_GEOM(frame).w)>1);
767 }
768
769
770 /*EXTL_DOC
771  * Attempt to toggle horizontal maximisation of \var{frame}.
772  */
773 EXTL_EXPORT_MEMBER
774 void frame_maximize_horiz(WFrame *frame)
775 {
776     WRegion *mp=region_manager((WRegion*)frame);
777     int ox, ow;
778     
779     if(frame->flags&FRAME_MIN_HORIZ || frame->flags&FRAME_MAXED_HORIZ){
780         if(frame->flags&FRAME_SAVED_HORIZ)
781             rqw(frame, frame->saved_x, frame->saved_w);
782         frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
783         return;
784     }
785
786     if(mp==NULL)
787         return;
788     
789     ox=REGION_GEOM(frame).x;
790     ow=REGION_GEOM(frame).w;
791     
792     rqw(frame, 0, REGION_GEOM(mp).w);
793         
794     frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
795     frame->saved_x=ox;
796     frame->saved_w=ow;
797 }
798
799
800
801 /*}}}*/
802
803
804 /*{{{ Misc. */
805
806
807 uint region_min_h(WRegion *reg)
808 {
809     WSizeHints hints;
810     region_size_hints(reg, &hints);
811     return hints.min_height;
812 }
813
814
815 uint region_min_w(WRegion *reg)
816 {
817     WSizeHints hints;
818     region_size_hints(reg, &hints);
819     return hints.min_width;
820 }
821
822
823 void region_convert_root_geom(WRegion *reg, WRectangle *geom)
824 {
825     int rx, ry;
826     if(reg!=NULL){
827         region_rootpos(reg, &rx, &ry);
828         geom->x-=rx;
829         geom->y-=ry;
830     }
831 }
832
833
834 void region_absolute_geom_to_parent(WRegion *reg, const WRectangle *rgeom,
835                                     WRectangle *pgeom)
836 {
837     WRegion *parent=REGION_PARENT_REG(reg);
838     
839     pgeom->w=rgeom->w;
840     pgeom->h=rgeom->h;
841     
842     if(parent==NULL){
843         pgeom->x=rgeom->x;
844         pgeom->y=rgeom->y;
845     }else{
846         region_rootpos(reg, &pgeom->x, &pgeom->y);
847         pgeom->x=rgeom->x-pgeom->x;
848         pgeom->y=rgeom->y-pgeom->y;
849     }
850 }
851
852 /*}}}*/
853