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