4 * Copyright (c) Tuomo Valkonen 1999-2007.
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.
15 #include <libtu/objp.h>
16 #include <libtu/minmax.h>
17 #include <libextl/extl.h>
18 #include <libmainloop/defer.h>
33 #define XOR_RESIZE (!ioncore_g.opaque_resize)
36 /*{{{ Size/position display and rubberband */
39 static void draw_rubberbox(WRootWin *rw, const WRectangle *rect)
45 fpts[1].x=rect->x+rect->w;
47 fpts[2].x=rect->x+rect->w;
48 fpts[2].y=rect->y+rect->h;
50 fpts[3].y=rect->y+rect->h;
54 XDrawLines(ioncore_g.dpy, WROOTWIN_ROOT(rw), rw->xor_gc, fpts, 5,
59 static int max_width(GrBrush *brush, const char *str)
63 while(str && *str!='\0'){
64 w=grbrush_get_text_width(brush, str, 1);
74 static int chars_for_num(int d)
87 static WInfoWin *setup_moveres_display(WWindow *parent, int cx, int cy)
94 fp.mode=REGION_FIT_EXACT;
100 infowin=create_infowin(parent, &fp, "moveres_display");
105 grbrush_get_border_widths(INFOWIN_BRUSH(infowin), &bdw);
106 grbrush_get_font_extents(INFOWIN_BRUSH(infowin), &fnte);
108 /* Create move/resize position/size display window */
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;;
119 region_fitrep((WRegion*)infowin, NULL, &fp);
120 region_map((WRegion*)infowin);
126 static void moveres_draw_infowin(WMoveresMode *mode)
131 if(mode->infowin==NULL)
134 buf=INFOWIN_BUFFER(mode->infowin);
139 if(mode->mode==MOVERES_SIZE){
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;
151 w/=mode->hints.width_inc;
152 h/=mode->hints.height_inc;
155 snprintf(buf, INFOWIN_BUFFER_LEN, "%dx%d", w, h);
157 snprintf(buf, INFOWIN_BUFFER_LEN, "%+d %+d",
158 mode->geom.x, mode->geom.y);
161 window_draw((WWindow*)mode->infowin, TRUE);
165 static void moveres_draw_rubberband(WMoveresMode *mode)
167 WRectangle rgeom=mode->geom;
169 WRootWin *rootwin=(mode->reg==NULL
171 : region_rootwin_of(mode->reg));
176 rgeom.x+=mode->parent_rx;
177 rgeom.y+=mode->parent_ry;
179 if(mode->rubfn==NULL)
180 draw_rubberbox(rootwin, &rgeom);
182 mode->rubfn(rootwin, &rgeom);
189 /*{{{ Move/resize mode */
192 WMoveresMode *tmpmode=NULL;
196 IMPLCLASS(WMoveresMode, Obj, NULL, NULL);
199 WMoveresMode *moveres_mode(WRegion *reg)
203 return ((reg==NULL || tmpmode->reg==reg) ? tmpmode : NULL);
207 WRegion *moveresmode_target(WMoveresMode *mode)
213 static bool moveresmode_init(WMoveresMode *mode, WRegion *reg,
214 WDrawRubberbandFn *rubfn, bool cumulative)
222 parent=REGION_PARENT(reg);
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);
241 mode->resize_cumulative=cumulative;
242 mode->rqflags=(XOR_RESIZE ? REGION_RQGEOM_TRYONLY : 0);
244 mode->mode=MOVERES_SIZE;
246 /* Get snapping geometry */
247 mgr=REGION_MANAGER(reg);
250 mode->snapgeom=REGION_GEOM(mgr);
252 if(mgr==(WRegion*)parent){
255 mode->snap_enabled=FALSE;
256 }else if(REGION_PARENT(mgr)==parent){
257 mode->snap_enabled=TRUE;
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;
266 /* Set up info window */
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);
273 moveres_draw_infowin(mode);
276 XGrabServer(ioncore_g.dpy);
277 moveres_draw_rubberband(mode);
284 static WMoveresMode *create_moveresmode(WRegion *reg,
285 WDrawRubberbandFn *rubfn,
288 CREATEOBJ_IMPL(WMoveresMode, moveresmode, (p, reg, rubfn, cumulative));
292 WMoveresMode *region_begin_resize(WRegion *reg, WDrawRubberbandFn *rubfn,
295 WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
298 mode->mode=MOVERES_SIZE;
299 ioncore_change_grab_cursor(IONCORE_CURSOR_RESIZE);
306 WMoveresMode *region_begin_move(WRegion *reg, WDrawRubberbandFn *rubfn,
309 WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
312 mode->mode=MOVERES_POS;
313 ioncore_change_grab_cursor(IONCORE_CURSOR_MOVE);
320 static void moveresmode_setorig(WMoveresMode *mode)
326 mode->origgeom=mode->geom;
330 static void moveresmode_do_newgeom(WMoveresMode *mode, WRQGeomParams *rq)
333 moveres_draw_rubberband(mode);
336 rq->flags|=mode->rqflags;
337 region_rqgeom(mode->reg, rq, &mode->geom);
340 moveres_draw_infowin(mode);
343 moveres_draw_rubberband(mode);
347 static void moveresmode_delta(WMoveresMode *mode,
348 int dx1, int dx2, int dy1, int dy2,
351 int realdx1, realdx2, realdy1, realdy2;
352 WRQGeomParams rq=RQGEOMPARAMS_INIT;
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;
362 if(mode->snap_enabled){
363 WRectangle *sg=&mode->snapgeom;
364 int er=CF_EDGE_RESISTANCE;
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;
376 w=mode->origgeom.w-realdx1+realdx2;
377 h=mode->origgeom.h-realdy1+realdy2;
380 w=mode->hints.min_width;
382 h=mode->hints.min_height;
384 sizehints_correct(&mode->hints, &w, &h, TRUE, TRUE);
386 /* Do not modify coordinates and sizes that were not requested to be
390 if(mode->dx1==mode->dx2){
391 if(mode->dx1==0 || realdx1!=mode->dx1)
397 if(mode->dx1==0 || realdx1!=mode->dx1)
400 rq.geom.x+=mode->origgeom.w-rq.geom.w;
404 if(mode->dy1==mode->dy2){
405 if(mode->dy1==0 || realdy1!=mode->dy1)
411 if(mode->dy1==0 || realdy1!=mode->dy1)
414 rq.geom.y+=mode->origgeom.h-rq.geom.h;
417 moveresmode_do_newgeom(mode, &rq);
419 if(!mode->resize_cumulative)
420 moveresmode_setorig(mode);
427 void moveresmode_delta_resize(WMoveresMode *mode,
428 int dx1, int dx2, int dy1, int dy2,
431 mode->mode=MOVERES_SIZE;
432 moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret);
436 void moveresmode_delta_move(WMoveresMode *mode,
437 int dx, int dy, WRectangle *rret)
439 mode->mode=MOVERES_POS;
440 moveresmode_delta(mode, dx, dx, dy, dy, rret);
444 void moveresmode_rqgeom(WMoveresMode *mode, WRQGeomParams *rq,
447 mode->mode=MOVERES_SIZE;
448 moveresmode_do_newgeom(mode, rq);
449 moveresmode_setorig(mode);
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)
458 if(!OBJ_IS(reg, WFrame))
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;
469 if(mode->origgeom.h!=mode->geom.h){
470 frame->saved_y=mode->origgeom.y;
471 frame->saved_h=mode->origgeom.h;
476 bool moveresmode_do_end(WMoveresMode *mode, bool apply)
478 WRegion *reg=mode->reg;
481 assert(tmpmode==mode);
486 moveres_draw_rubberband(mode);
488 WRQGeomParams rq=RQGEOMPARAMS_INIT;
491 rq.flags=mode->rqflags&~REGION_RQGEOM_TRYONLY;
493 region_rqgeom(reg, &rq, &mode->geom);
495 XUngrabServer(ioncore_g.dpy);
498 set_saved(mode, reg);
500 if(mode->infowin!=NULL){
501 mainloop_defer_destroy((Obj*)mode->infowin);
504 destroy_obj((Obj*)mode);
513 /*{{{ Request and other dynfuns */
516 void region_rqgeom(WRegion *reg, const WRQGeomParams *rq,
519 WRegion *mgr=REGION_MANAGER(reg);
522 if(rq->flags®ION_RQGEOM_ABSOLUTE)
523 region_managed_rqgeom_absolute(mgr, reg, rq, geomret);
525 region_managed_rqgeom(mgr, reg, rq, geomret);
529 if(rq->flags®ION_RQGEOM_ABSOLUTE)
530 region_absolute_geom_to_parent(reg, &rq->geom, &tmp);
537 if(!(rq->flags®ION_RQGEOM_TRYONLY))
538 region_fit(reg, &tmp, REGION_FIT_EXACT);
543 void rqgeomparams_from_table(WRQGeomParams *rq,
544 const WRectangle *origg, ExtlTab g)
547 rq->flags=REGION_RQGEOM_WEAK_ALL;
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;
558 rq->geom.w=maxof(1, rq->geom.w);
559 rq->geom.h=maxof(1, rq->geom.h);
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.
569 EXTL_EXPORT_AS(WRegion, rqgeom)
570 ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g)
572 WRQGeomParams rq=RQGEOMPARAMS_INIT;
575 rqgeomparams_from_table(&rq, ®ION_GEOM(reg), g);
577 region_rqgeom(reg, &rq, &res);
579 return extl_table_from_rectangle(&res);
583 void region_managed_rqgeom(WRegion *mgr, WRegion *reg,
584 const WRQGeomParams *rq,
587 CALL_DYN(region_managed_rqgeom, mgr, (mgr, reg, rq, geomret));
591 void region_managed_rqgeom_absolute(WRegion *mgr, WRegion *reg,
592 const WRQGeomParams *rq,
595 CALL_DYN(region_managed_rqgeom_absolute, mgr,
596 (mgr, reg, rq, geomret));
600 void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg,
601 const WRQGeomParams *rq,
607 if(!(rq->flags®ION_RQGEOM_TRYONLY))
608 region_fit(reg, &rq->geom, REGION_FIT_EXACT);
612 void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg,
613 const WRQGeomParams *rq,
617 *geomret=REGION_GEOM(reg);
621 void region_managed_rqgeom_absolute_default(WRegion *mgr, WRegion *reg,
622 const WRQGeomParams *rq,
625 WRQGeomParams rq2=RQGEOMPARAMS_INIT;
627 rq2.flags=rq->flags&~REGION_RQGEOM_ABSOLUTE;
628 region_absolute_geom_to_parent(reg, &rq->geom, &rq2.geom);
630 region_managed_rqgeom(mgr, reg, &rq2, geomret);
634 void region_size_hints(WRegion *reg, WSizeHints *hints_ret)
636 sizehints_clear(hints_ret);
639 CALL_DYN(region_size_hints, reg, (reg, hints_ret));
642 if(!hints_ret->min_set){
643 hints_ret->min_width=1;
644 hints_ret->min_height=1;
646 if(!hints_ret->base_set){
647 hints_ret->base_width=0;
648 hints_ret->base_height=0;
650 if(!hints_ret->max_set){
651 hints_ret->max_width=INT_MAX;
652 hints_ret->max_height=INT_MAX;
657 void region_size_hints_correct(WRegion *reg,
658 int *wp, int *hp, bool min)
662 region_size_hints(reg, &hints);
664 sizehints_correct(&hints, wp, hp, min, FALSE);
668 int region_orientation(WRegion *reg)
670 int ret=REGION_ORIENTATION_NONE;
672 CALL_DYN_RET(ret, int, region_orientation, reg, (reg));
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}.
684 EXTL_EXPORT_AS(WRegion, size_hints)
685 ExtlTab region_size_hints_extl(WRegion *reg)
690 region_size_hints(reg, &hints);
692 tab=extl_create_table();
695 extl_table_sets_i(tab, "base_w", hints.base_width);
696 extl_table_sets_i(tab, "base_h", hints.base_height);
699 extl_table_sets_i(tab, "min_w", hints.min_width);
700 extl_table_sets_i(tab, "min_h", hints.min_height);
703 extl_table_sets_i(tab, "max_w", hints.max_width);
704 extl_table_sets_i(tab, "max_h", hints.max_height);
707 extl_table_sets_i(tab, "inc_w", hints.width_inc);
708 extl_table_sets_i(tab, "inc_h", hints.height_inc);
717 /*{{{ Restore size, maximize, shade */
720 static bool rqh(WFrame *frame, int y, int h)
722 WRQGeomParams rq=RQGEOMPARAMS_INIT;
726 rq.flags=REGION_RQGEOM_VERT_ONLY;
728 rq.geom.x=REGION_GEOM(frame).x;
729 rq.geom.w=REGION_GEOM(frame).w;
734 region_size_hints_correct((WRegion*)frame, &dummy_w, &(rq.geom.h), TRUE);
736 region_rqgeom((WRegion*)frame, &rq, &rgeom);
738 return (abs(rgeom.y-REGION_GEOM(frame).y)>1 ||
739 abs(rgeom.h-REGION_GEOM(frame).h)>1);
744 * Attempt to toggle vertical maximisation of \var{frame}.
747 void frame_maximize_vert(WFrame *frame)
749 WRegion *mp=region_manager((WRegion*)frame);
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);
762 oy=REGION_GEOM(frame).y;
763 oh=REGION_GEOM(frame).h;
765 rqh(frame, 0, REGION_GEOM(mp).h);
767 frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
772 static bool rqw(WFrame *frame, int x, int w)
774 WRQGeomParams rq=RQGEOMPARAMS_INIT;
778 rq.flags=REGION_RQGEOM_HORIZ_ONLY;
782 rq.geom.y=REGION_GEOM(frame).y;
783 rq.geom.h=REGION_GEOM(frame).h;
786 region_size_hints_correct((WRegion*)frame, &(rq.geom.w), &dummy_h, TRUE);
788 region_rqgeom((WRegion*)frame, &rq, &rgeom);
790 return (abs(rgeom.x-REGION_GEOM(frame).x)>1 ||
791 abs(rgeom.w-REGION_GEOM(frame).w)>1);
796 * Attempt to toggle horizontal maximisation of \var{frame}.
799 void frame_maximize_horiz(WFrame *frame)
801 WRegion *mp=region_manager((WRegion*)frame);
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);
814 ox=REGION_GEOM(frame).x;
815 ow=REGION_GEOM(frame).w;
817 rqw(frame, 0, REGION_GEOM(mp).w);
819 frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
832 uint region_min_h(WRegion *reg)
835 region_size_hints(reg, &hints);
836 return hints.min_height;
840 uint region_min_w(WRegion *reg)
843 region_size_hints(reg, &hints);
844 return hints.min_width;
848 void region_convert_root_geom(WRegion *reg, WRectangle *geom)
852 region_rootpos(reg, &rx, &ry);
859 void region_absolute_geom_to_parent(WRegion *reg, const WRectangle *rgeom,
862 WRegion *parent=REGION_PARENT_REG(reg);
871 region_rootpos(reg, &pgeom->x, &pgeom->y);
872 pgeom->x=rgeom->x-pgeom->x;
873 pgeom->y=rgeom->y-pgeom->y;