trunk: changeset 1886
[notion/jeffpc.git] / ioncore / resize.c
blob0e47c6089eccb977822208c102dc6cb3b497e95c
1 /*
2 * ion/ioncore/resize.c
4 * Copyright (c) Tuomo Valkonen 1999-2004.
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.
12 #include <stdio.h>
13 #include <limits.h>
15 #include <libtu/objp.h>
16 #include <libtu/minmax.h>
17 #include <libextl/extl.h>
18 #include <libmainloop/defer.h>
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 #include "region-iter.h"
34 #define XOR_RESIZE (!ioncore_g.opaque_resize)
37 /*{{{ Size/position display and rubberband */
40 static void draw_rubberbox(WRootWin *rw, const WRectangle *rect)
42 XPoint fpts[5];
44 fpts[0].x=rect->x;
45 fpts[0].y=rect->y;
46 fpts[1].x=rect->x+rect->w;
47 fpts[1].y=rect->y;
48 fpts[2].x=rect->x+rect->w;
49 fpts[2].y=rect->y+rect->h;
50 fpts[3].x=rect->x;
51 fpts[3].y=rect->y+rect->h;
52 fpts[4].x=rect->x;
53 fpts[4].y=rect->y;
55 XDrawLines(ioncore_g.dpy, WROOTWIN_ROOT(rw), rw->xor_gc, fpts, 5,
56 CoordModeOrigin);
60 static int max_width(GrBrush *brush, const char *str)
62 int maxw=0, w;
64 while(str && *str!='\0'){
65 w=grbrush_get_text_width(brush, str, 1);
66 if(w>maxw)
67 maxw=w;
68 str++;
71 return maxw;
75 static int chars_for_num(int d)
77 int n=0;
79 do{
80 n++;
81 d/=10;
82 }while(d);
84 return n;
88 static WInfoWin *setup_moveres_display(WWindow *parent, int cx, int cy)
90 GrBorderWidths bdw;
91 GrFontExtents fnte;
92 WInfoWin *infowin;
93 WFitParams fp;
95 fp.mode=REGION_FIT_EXACT;
96 fp.g.x=0;
97 fp.g.y=0;
98 fp.g.w=1;
99 fp.g.h=1;
101 infowin=create_infowin(parent, &fp, "moveres_display");
103 if(infowin==NULL)
104 return NULL;
106 grbrush_get_border_widths(INFOWIN_BRUSH(infowin), &bdw);
107 grbrush_get_font_extents(INFOWIN_BRUSH(infowin), &fnte);
109 /* Create move/resize position/size display window */
110 fp.g.w=3;
111 fp.g.w+=chars_for_num(REGION_GEOM(parent).w);
112 fp.g.w+=chars_for_num(REGION_GEOM(parent).h);
113 fp.g.w*=max_width(INFOWIN_BRUSH(infowin), "0123456789x+");
114 fp.g.w+=bdw.left+bdw.right;
115 fp.g.h=fnte.max_height+bdw.top+bdw.bottom;;
117 fp.g.x=cx-fp.g.w/2;
118 fp.g.y=cy-fp.g.h/2;
120 region_fitrep((WRegion*)infowin, NULL, &fp);
121 region_map((WRegion*)infowin);
123 return infowin;
127 static void moveres_draw_infowin(WMoveresMode *mode)
129 WRectangle geom;
130 char *buf;
132 if(mode->infowin==NULL)
133 return;
135 buf=INFOWIN_BUFFER(mode->infowin);
137 if(buf==NULL)
138 return;
140 if(mode->mode==MOVERES_SIZE){
141 int w, h;
143 w=mode->geom.w;
144 h=mode->geom.h;
146 if((mode->hints.flags&PResizeInc) &&
147 (mode->hints.width_inc>1 || mode->hints.height_inc>1)){
148 if(mode->hints.flags&PBaseSize){
149 w-=mode->hints.base_width;
150 h-=mode->hints.base_height;
152 w/=mode->hints.width_inc;
153 h/=mode->hints.height_inc;
156 snprintf(buf, INFOWIN_BUFFER_LEN, "%dx%d", w, h);
157 }else{
158 snprintf(buf, INFOWIN_BUFFER_LEN, "%+d %+d",
159 mode->geom.x, mode->geom.y);
162 window_draw((WWindow*)mode->infowin, TRUE);
166 static void moveres_draw_rubberband(WMoveresMode *mode)
168 WRectangle rgeom=mode->geom;
169 int rx, ry;
170 WRootWin *rootwin=(mode->reg==NULL
171 ? NULL
172 : region_rootwin_of(mode->reg));
174 if(rootwin==NULL)
175 return;
177 rgeom.x+=mode->parent_rx;
178 rgeom.y+=mode->parent_ry;
180 if(mode->rubfn==NULL)
181 draw_rubberbox(rootwin, &rgeom);
182 else
183 mode->rubfn(rootwin, &rgeom);
187 /*}}}*/
190 /*{{{ Move/resize mode */
193 WMoveresMode *tmpmode=NULL;
196 IMPLCLASS(WMoveresMode, Obj, NULL, NULL);
199 WMoveresMode *moveres_mode(WRegion *reg)
201 if(tmpmode==NULL)
202 return NULL;
203 return ((reg==NULL || tmpmode->reg==reg) ? tmpmode : NULL);
207 WRegion *moveresmode_target(WMoveresMode *mode)
209 return mode->reg;
213 static bool moveresmode_init(WMoveresMode *mode, WRegion *reg,
214 WDrawRubberbandFn *rubfn, bool cumulative)
216 WWindow *parent;
217 WRegion *mgr;
219 if(tmpmode!=NULL)
220 return FALSE;
222 parent=REGION_PARENT(reg);
224 if(parent==NULL)
225 return FALSE;
227 tmpmode=mode;
229 mode->snap_enabled=FALSE;
230 region_size_hints(reg, &mode->hints);
232 region_rootpos((WRegion*)parent, &mode->parent_rx, &mode->parent_ry);
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;
246 /* Get snapping geometry */
247 mgr=REGION_MANAGER(reg);
249 if(mgr!=NULL){
250 mode->snapgeom=REGION_GEOM(mgr);
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;
261 if(!mode->hints.flags&PMinSize || mode->hints.min_width<1)
262 mode->hints.min_width=1;
263 if(!mode->hints.flags&PMinSize || mode->hints.min_height<1)
264 mode->hints.min_height=1;
266 /* Set up info window */
268 int x=mode->parent_rx+mode->geom.x+mode->geom.w/2;
269 int y=mode->parent_ry+mode->geom.y+mode->geom.h/2;
270 mode->infowin=setup_moveres_display(parent, x, y);
273 moveres_draw_infowin(mode);
275 if(XOR_RESIZE){
276 XGrabServer(ioncore_g.dpy);
277 moveres_draw_rubberband(mode);
280 return TRUE;
284 static WMoveresMode *create_moveresmode(WRegion *reg,
285 WDrawRubberbandFn *rubfn,
286 bool cumulative)
288 CREATEOBJ_IMPL(WMoveresMode, moveresmode, (p, reg, rubfn, cumulative));
292 WMoveresMode *region_begin_resize(WRegion *reg, WDrawRubberbandFn *rubfn,
293 bool cumulative)
295 WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
297 if(mode!=NULL){
298 mode->mode=MOVERES_SIZE;
299 ioncore_change_grab_cursor(IONCORE_CURSOR_RESIZE);
302 return mode;
306 WMoveresMode *region_begin_move(WRegion *reg, WDrawRubberbandFn *rubfn,
307 bool cumulative)
309 WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
311 if(mode!=NULL){
312 mode->mode=MOVERES_POS;
313 ioncore_change_grab_cursor(IONCORE_CURSOR_MOVE);
316 return mode;
320 static void moveresmode_delta(WMoveresMode *mode,
321 int dx1, int dx2, int dy1, int dy2,
322 WRectangle *rret)
324 WRectangle geom;
325 int w, h;
326 int realdx1, realdx2, realdy1, realdy2;
328 realdx1=(mode->dx1+=dx1);
329 realdx2=(mode->dx2+=dx2);
330 realdy1=(mode->dy1+=dy1);
331 realdy2=(mode->dy2+=dy2);
332 geom=mode->origgeom;
334 /* snap */
335 if(mode->snap_enabled){
336 WRectangle *sg=&mode->snapgeom;
337 int er=CF_EDGE_RESISTANCE;
339 if(mode->dx1!=0 && geom.x+mode->dx1<sg->x && geom.x+mode->dx1>sg->x-er)
340 realdx1=sg->x-geom.x;
341 if(mode->dx2!=0 && geom.x+geom.w+mode->dx2>sg->x+sg->w && geom.x+geom.w+mode->dx2<sg->x+sg->w+er)
342 realdx2=sg->x+sg->w-geom.x-geom.w;
343 if(mode->dy1!=0 && geom.y+mode->dy1<sg->y && geom.y+mode->dy1>sg->y-er)
344 realdy1=sg->y-geom.y;
345 if(mode->dy2!=0 && geom.y+geom.h+mode->dy2>sg->y+sg->h && geom.y+geom.h+mode->dy2<sg->y+sg->h+er)
346 realdy2=sg->y+sg->h-geom.y-geom.h;
349 w=mode->origgeom.w-realdx1+realdx2;
350 h=mode->origgeom.h-realdy1+realdy2;
352 if(w<=0)
353 w=mode->hints.min_width;
354 if(h<=0)
355 h=mode->hints.min_height;
357 xsizehints_correct(&mode->hints, &w, &h, TRUE);
359 /* Do not modify coordinates and sizes that were not requested to be
360 * changed.
363 if(mode->dx1==mode->dx2){
364 if(mode->dx1==0 || realdx1!=mode->dx1)
365 geom.x+=realdx1;
366 else
367 geom.x+=realdx2;
368 }else{
369 geom.w=w;
370 if(mode->dx1==0 || realdx1!=mode->dx1)
371 geom.x+=realdx1;
372 else
373 geom.x+=mode->origgeom.w-geom.w;
377 if(mode->dy1==mode->dy2){
378 if(mode->dy1==0 || realdy1!=mode->dy1)
379 geom.y+=realdy1;
380 else
381 geom.y+=realdy2;
382 }else{
383 geom.h=h;
384 if(mode->dy1==0 || realdy1!=mode->dy1)
385 geom.y+=realdy1;
386 else
387 geom.y+=mode->origgeom.h-geom.h;
390 if(XOR_RESIZE)
391 moveres_draw_rubberband(mode);
393 if(mode->reg!=NULL)
394 region_rqgeom(mode->reg, mode->rqflags, &geom, &mode->geom);
396 if(!mode->resize_cumulative){
397 mode->dx1=0;
398 mode->dx2=0;
399 mode->dy1=0;
400 mode->dy2=0;
401 mode->origgeom=mode->geom;
404 moveres_draw_infowin(mode);
406 if(XOR_RESIZE)
407 moveres_draw_rubberband(mode);
409 if(rret!=NULL)
410 *rret=mode->geom;
414 void moveresmode_delta_resize(WMoveresMode *mode,
415 int dx1, int dx2, int dy1, int dy2,
416 WRectangle *rret)
418 mode->mode=MOVERES_SIZE;
419 moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret);
423 void moveresmode_delta_move(WMoveresMode *mode,
424 int dx, int dy, WRectangle *rret)
426 mode->mode=MOVERES_POS;
427 moveresmode_delta(mode, dx, dx, dy, dy, rret);
431 /* It is ugly to do this here, but it will have to do for now... */
432 static void set_saved(WMoveresMode *mode, WRegion *reg)
434 WFrame *frame;
436 if(!OBJ_IS(reg, WFrame))
437 return;
439 frame=(WFrame*)reg;
441 /* Restore saved sizes from the beginning of the resize action */
442 if(mode->origgeom.w!=mode->geom.w){
443 frame->saved_x=mode->origgeom.x;
444 frame->saved_w=mode->origgeom.w;
447 if(mode->origgeom.h!=mode->geom.h){
448 frame->saved_y=mode->origgeom.y;
449 frame->saved_h=mode->origgeom.h;
454 bool moveresmode_do_end(WMoveresMode *mode, bool apply)
456 WRegion *reg=mode->reg;
458 assert(reg!=NULL);
459 assert(tmpmode==mode);
461 tmpmode=NULL;
463 if(XOR_RESIZE){
464 moveres_draw_rubberband(mode);
465 if(apply){
466 WRectangle g2=mode->geom;
467 region_rqgeom(reg, mode->rqflags&~REGION_RQGEOM_TRYONLY,
468 &g2, &mode->geom);
470 XUngrabServer(ioncore_g.dpy);
472 if(apply)
473 set_saved(mode, reg);
475 if(mode->infowin!=NULL){
476 mainloop_defer_destroy((Obj*)mode->infowin);
477 mode->infowin=NULL;
479 destroy_obj((Obj*)mode);
481 return TRUE;
485 /*}}}*/
488 /*{{{ Request and other dynfuns */
491 void region_rqgeom(WRegion *reg, int flags, const WRectangle *geom,
492 WRectangle *geomret)
494 bool tryonly=(flags&REGION_RQGEOM_TRYONLY);
496 if(REGION_MANAGER(reg)!=NULL){
497 region_managed_rqgeom(REGION_MANAGER(reg), reg, flags, geom,
498 geomret);
499 }else{
500 if(geomret!=NULL)
501 *geomret=REGION_GEOM(reg);
502 if(!tryonly)
503 region_fit(reg, geom, REGION_FIT_EXACT);
506 /*if(!tryonly && geomret!=NULL)
507 *geomret=REGION_GEOM(reg);*/
511 /*EXTL_DOC
512 * Attempt to resize and/or move \var{reg}. The table \var{g} is a usual
513 * geometry specification (fields \var{x}, \var{y}, \var{w} and \var{h}),
514 * but may contain missing fields, in which case, \var{reg}'s manager may
515 * attempt to leave that attribute unchanged.
517 EXTL_EXPORT_AS(WRegion, rqgeom)
518 ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g)
520 WRectangle geom=REGION_GEOM(reg);
521 WRectangle ogeom=REGION_GEOM(reg);
522 int flags=REGION_RQGEOM_WEAK_ALL;
524 if(extl_table_gets_i(g, "x", &(geom.x)))
525 flags&=~REGION_RQGEOM_WEAK_X;
526 if(extl_table_gets_i(g, "y", &(geom.y)))
527 flags&=~REGION_RQGEOM_WEAK_Y;
528 if(extl_table_gets_i(g, "w", &(geom.w)))
529 flags&=~REGION_RQGEOM_WEAK_W;
530 if(extl_table_gets_i(g, "h", &(geom.h)))
531 flags&=~REGION_RQGEOM_WEAK_H;
533 geom.w=maxof(1, geom.w);
534 geom.h=maxof(1, geom.h);
536 region_rqgeom(reg, flags, &geom, &ogeom);
538 return extl_table_from_rectangle(&ogeom);
542 void region_managed_rqgeom(WRegion *mgr, WRegion *reg,
543 int flags, const WRectangle *geom,
544 WRectangle *geomret)
546 CALL_DYN(region_managed_rqgeom, mgr,
547 (mgr, reg, flags, geom, geomret));
551 void region_rqgeom_clientwin(WRegion *mgr, WClientWin *cwin,
552 int flags, const WRectangle *geom)
554 CALL_DYN(region_rqgeom_clientwin, mgr, (mgr, cwin, flags, geom));
558 void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg,
559 int flags, const WRectangle *geom,
560 WRectangle *geomret)
562 if(geomret!=NULL)
563 *geomret=*geom;
565 if(!(flags&REGION_RQGEOM_TRYONLY))
566 region_fit(reg, geom, REGION_FIT_EXACT);
570 void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg,
571 int flags, const WRectangle *geom,
572 WRectangle *geomret)
574 if(geomret!=NULL)
575 *geomret=REGION_GEOM(reg);
579 void region_size_hints(WRegion *reg, XSizeHints *hints_ret)
581 hints_ret->flags=0;
583 CALL_DYN(region_size_hints, reg, (reg, hints_ret));
585 if(!(hints_ret->flags&PMinSize)){
586 hints_ret->min_width=1;
587 hints_ret->min_height=1;
589 if(!(hints_ret->flags&PBaseSize)){
590 hints_ret->base_width=0;
591 hints_ret->base_height=0;
593 if(!(hints_ret->flags&PMaxSize)){
594 hints_ret->max_width=INT_MAX;
595 hints_ret->max_height=INT_MAX;
597 hints_ret->flags|=(PMinSize|PBaseSize|PMaxSize);
601 int region_orientation(WRegion *reg)
603 int ret=REGION_ORIENTATION_NONE;
605 CALL_DYN_RET(ret, int, region_orientation, reg, (reg));
607 return ret;
611 /*EXTL_DOC
612 * Returns size hints for \var{reg}. The returned table always contains the
613 * fields \code{min_?}, \code{base_?} and sometimes the fields \code{max_?},
614 * \code{base_?} and \code{inc_?}, where \code{?}=\code{w}, \code{h}.
616 EXTL_EXPORT_AS(WRegion, size_hints)
617 ExtlTab region_size_hints_extl(WRegion *reg)
619 XSizeHints hints;
620 ExtlTab tab;
622 region_size_hints(reg, &hints);
624 tab=extl_create_table();
626 /* Base size is always guaranteed to be set. */
627 extl_table_sets_i(tab, "base_w", hints.base_width);
628 extl_table_sets_i(tab, "base_h", hints.base_height);
629 /* Minimum size is always guaranteed to be set. */
630 extl_table_sets_i(tab, "min_w", hints.min_width);
631 extl_table_sets_i(tab, "min_h", hints.min_height);
632 if(hints.flags&PMaxSize){
633 extl_table_sets_i(tab, "max_w", hints.max_width);
634 extl_table_sets_i(tab, "max_h", hints.max_height);
636 if(hints.flags&PBaseSize){
637 extl_table_sets_i(tab, "base_w", hints.base_width);
638 extl_table_sets_i(tab, "base_h", hints.base_height);
640 if(hints.flags&PResizeInc){
641 extl_table_sets_i(tab, "inc_w", hints.width_inc);
642 extl_table_sets_i(tab, "inc_h", hints.height_inc);
645 return tab;
648 /*}}}*/
651 /*{{{ Restore size, maximize, shade */
654 void frame_restore_size(WFrame *frame, bool horiz, bool vert)
656 WRectangle geom;
657 int rqf=REGION_RQGEOM_WEAK_ALL;
659 geom=REGION_GEOM(frame);
661 if(vert && frame->flags&FRAME_SAVED_VERT){
662 geom.y=frame->saved_y;
663 geom.h=frame->saved_h;
664 rqf&=~(REGION_RQGEOM_WEAK_Y|REGION_RQGEOM_WEAK_H);
667 if(horiz && frame->flags&FRAME_SAVED_HORIZ){
668 geom.x=frame->saved_x;
669 geom.w=frame->saved_w;
670 rqf&=~(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_W);
673 if((rqf&REGION_RQGEOM_WEAK_ALL)!=REGION_RQGEOM_WEAK_ALL)
674 region_rqgeom((WRegion*)frame, rqf, &geom, NULL);
678 static void correct_frame_size(WFrame *frame, int *w, int *h)
680 XSizeHints hints;
681 int wdiff, hdiff;
683 region_size_hints((WRegion*)frame, &hints);
684 xsizehints_correct(&hints, w, h, TRUE);
688 static bool trymaxv(WFrame *frame, WRegion *mgr, int tryonlyflag)
690 WRectangle geom=REGION_GEOM(frame), rgeom;
691 geom.y=0;
692 geom.h=REGION_GEOM(mgr).h;
695 int dummy_w=geom.w;
696 correct_frame_size(frame, &dummy_w, &(geom.h));
699 region_rqgeom((WRegion*)frame,
700 tryonlyflag|REGION_RQGEOM_VERT_ONLY,
701 &geom, &rgeom);
702 return (abs(rgeom.y-REGION_GEOM(frame).y)>1 ||
703 abs(rgeom.h-REGION_GEOM(frame).h)>1);
707 /*EXTL_DOC
708 * Attempt to maximize \var{frame} vertically.
710 EXTL_EXPORT_MEMBER
711 void frame_maximize_vert(WFrame *frame)
713 WRegion *mgr=REGION_MANAGER(frame);
715 if(frame->flags&FRAME_SHADED){
716 frame_toggle_shade(frame);
717 return;
720 if(mgr==NULL)
721 return;
723 if(!trymaxv(frame, mgr, REGION_RQGEOM_TRYONLY)){
724 /* Could not maximize further, restore */
725 frame_restore_size(frame, FALSE, TRUE);
726 return;
729 trymaxv(frame, mgr, 0);
733 static bool trymaxh(WFrame *frame, WRegion *mgr, int tryonlyflag)
735 WRectangle geom=REGION_GEOM(frame), rgeom;
736 geom.x=0;
737 geom.w=REGION_GEOM(mgr).w;
740 int dummy_h=geom.h;
741 correct_frame_size(frame, &(geom.w), &dummy_h);
744 region_rqgeom((WRegion*)frame,
745 tryonlyflag|REGION_RQGEOM_HORIZ_ONLY,
746 &geom, &rgeom);
747 return (abs(rgeom.x-REGION_GEOM(frame).x)>1 ||
748 abs(rgeom.w-REGION_GEOM(frame).w)>1);
751 /*EXTL_DOC
752 * Attempt to maximize \var{frame} horizontally.
754 EXTL_EXPORT_MEMBER
755 void frame_maximize_horiz(WFrame *frame)
757 WRegion *mgr=REGION_MANAGER(frame);
759 if(mgr==NULL)
760 return;
762 if(!trymaxh(frame, mgr, REGION_RQGEOM_TRYONLY)){
763 /* Could not maximize further, restore */
764 frame_restore_size(frame, TRUE, FALSE);
765 return;
768 trymaxh(frame, mgr, 0);
772 /*}}}*/
775 /*{{{ Misc. */
778 uint region_min_h(WRegion *reg)
780 XSizeHints hints;
781 region_size_hints(reg, &hints);
782 return hints.min_height;
786 uint region_min_w(WRegion *reg)
788 XSizeHints hints;
789 region_size_hints(reg, &hints);
790 return hints.min_width;
794 void region_convert_root_geom(WRegion *reg, WRectangle *geom)
796 int rx, ry;
797 if(reg!=NULL){
798 region_rootpos(reg, &rx, &ry);
799 geom->x-=rx;
800 geom->y-=ry;
804 /*}}}*/