4 * Copyright (c) Tuomo Valkonen 1999-2008.
6 * See the included file LICENSE for details.
12 #include <libtu/objp.h>
13 #include <libtu/minmax.h>
14 #include <libextl/extl.h>
15 #include <libmainloop/defer.h>
30 #define XOR_RESIZE (!ioncore_g.opaque_resize)
33 extern int ioncore_edge_resistance;
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.base_set){
146 w=maxof(0, w-mode->hints.base_width);
147 h=maxof(0, h-mode->hints.base_height);
150 if(mode->hints.inc_set){
151 w/=maxof(1, mode->hints.width_inc);
152 h/=maxof(1, 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;*/
257 mode->snap_enabled=TRUE;
260 if(!mode->hints.min_set || mode->hints.min_width<1)
261 mode->hints.min_width=1;
262 if(!mode->hints.min_set || mode->hints.min_height<1)
263 mode->hints.min_height=1;
265 /* Set up info window */
267 int x=mode->geom.x+mode->geom.w/2;
268 int y=mode->geom.y+mode->geom.h/2;
269 mode->infowin=setup_moveres_display(parent, x, y);
272 moveres_draw_infowin(mode);
275 XGrabServer(ioncore_g.dpy);
276 moveres_draw_rubberband(mode);
283 static WMoveresMode *create_moveresmode(WRegion *reg,
284 WDrawRubberbandFn *rubfn,
287 CREATEOBJ_IMPL(WMoveresMode, moveresmode, (p, reg, rubfn, cumulative));
291 WMoveresMode *region_begin_resize(WRegion *reg, WDrawRubberbandFn *rubfn,
294 WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
297 mode->mode=MOVERES_SIZE;
298 ioncore_change_grab_cursor(IONCORE_CURSOR_RESIZE);
305 WMoveresMode *region_begin_move(WRegion *reg, WDrawRubberbandFn *rubfn,
308 WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
311 mode->mode=MOVERES_POS;
312 ioncore_change_grab_cursor(IONCORE_CURSOR_MOVE);
319 static void moveresmode_setorig(WMoveresMode *mode)
325 mode->origgeom=mode->geom;
329 static void moveresmode_do_newgeom(WMoveresMode *mode, WRQGeomParams *rq)
332 moveres_draw_rubberband(mode);
335 rq->flags|=mode->rqflags;
336 region_rqgeom(mode->reg, rq, &mode->geom);
339 moveres_draw_infowin(mode);
342 moveres_draw_rubberband(mode);
346 static int clamp_up(int t, int low, int high)
348 return (t < high && t > low ? high : t);
352 static void moveresmode_delta(WMoveresMode *mode,
353 int dx1, int dx2, int dy1, int dy2,
356 int realdx1, realdx2, realdy1, realdy2;
357 WRQGeomParams rq=RQGEOMPARAMS_INIT;
358 int er=ioncore_edge_resistance;
361 realdx1=(mode->dx1+=dx1);
362 realdx2=(mode->dx2+=dx2);
363 realdy1=(mode->dy1+=dy1);
364 realdy2=(mode->dy2+=dy2);
365 rq.geom=mode->origgeom;
368 if(mode->snap_enabled){
369 WRectangle *sg=&mode->snapgeom;
371 if(mode->dx1!=0 && rq.geom.x+mode->dx1<sg->x && rq.geom.x+mode->dx1>sg->x-er)
372 realdx1=sg->x-rq.geom.x;
373 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)
374 realdx2=sg->x+sg->w-rq.geom.x-rq.geom.w;
375 if(mode->dy1!=0 && rq.geom.y+mode->dy1<sg->y && rq.geom.y+mode->dy1>sg->y-er)
376 realdy1=sg->y-rq.geom.y;
377 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)
378 realdy2=sg->y+sg->h-rq.geom.y-rq.geom.h;
381 w=maxof(1, mode->origgeom.w-realdx1+realdx2);
382 h=maxof(1, mode->origgeom.h-realdy1+realdy2);
384 if(mode->snap_enabled && mode->hints.base_set){
385 w=clamp_up(w, mode->hints.base_width-er, mode->hints.base_width);
386 h=clamp_up(h, mode->hints.base_height-er, mode->hints.base_height);
390 sizehints_correct(&mode->hints, &w, &h, TRUE, TRUE);
392 /* Do not modify coordinates and sizes that were not requested to be
396 if(mode->dx1==mode->dx2){
397 if(mode->dx1==0 || realdx1!=mode->dx1)
403 if(mode->dx1==0 || realdx1!=mode->dx1)
406 rq.geom.x+=mode->origgeom.w-rq.geom.w;
410 if(mode->dy1==mode->dy2){
411 if(mode->dy1==0 || realdy1!=mode->dy1)
417 if(mode->dy1==0 || realdy1!=mode->dy1)
420 rq.geom.y+=mode->origgeom.h-rq.geom.h;
423 moveresmode_do_newgeom(mode, &rq);
425 if(!mode->resize_cumulative)
426 moveresmode_setorig(mode);
433 void moveresmode_delta_resize(WMoveresMode *mode,
434 int dx1, int dx2, int dy1, int dy2,
437 mode->mode=MOVERES_SIZE;
438 moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret);
442 void moveresmode_delta_move(WMoveresMode *mode,
443 int dx, int dy, WRectangle *rret)
445 mode->mode=MOVERES_POS;
446 moveresmode_delta(mode, dx, dx, dy, dy, rret);
450 void moveresmode_rqgeom(WMoveresMode *mode, WRQGeomParams *rq,
453 mode->mode=MOVERES_SIZE;
454 moveresmode_do_newgeom(mode, rq);
455 moveresmode_setorig(mode);
459 /* It is ugly to do this here, but it will have to do for now... */
460 static void set_saved(WMoveresMode *mode, WRegion *reg)
464 if(!OBJ_IS(reg, WFrame))
469 /* Restore saved sizes from the beginning of the resize action */
470 if(mode->origgeom.w!=mode->geom.w){
471 frame->saved_x=mode->origgeom.x;
472 frame->saved_w=mode->origgeom.w;
475 if(mode->origgeom.h!=mode->geom.h){
476 frame->saved_y=mode->origgeom.y;
477 frame->saved_h=mode->origgeom.h;
482 bool moveresmode_do_end(WMoveresMode *mode, bool apply)
484 WRegion *reg=mode->reg;
487 assert(tmpmode==mode);
492 moveres_draw_rubberband(mode);
494 WRQGeomParams rq=RQGEOMPARAMS_INIT;
497 rq.flags=mode->rqflags&~REGION_RQGEOM_TRYONLY;
499 region_rqgeom(reg, &rq, &mode->geom);
501 XUngrabServer(ioncore_g.dpy);
504 set_saved(mode, reg);
506 if(mode->infowin!=NULL){
507 mainloop_defer_destroy((Obj*)mode->infowin);
510 destroy_obj((Obj*)mode);
519 /*{{{ Request and other dynfuns */
522 void region_rqgeom(WRegion *reg, const WRQGeomParams *rq,
525 WRegion *mgr=REGION_MANAGER(reg);
528 if(rq->flags®ION_RQGEOM_ABSOLUTE)
529 region_managed_rqgeom_absolute(mgr, reg, rq, geomret);
531 region_managed_rqgeom(mgr, reg, rq, geomret);
535 if(rq->flags®ION_RQGEOM_ABSOLUTE)
536 region_absolute_geom_to_parent(reg, &rq->geom, &tmp);
543 if(!(rq->flags®ION_RQGEOM_TRYONLY))
544 region_fit(reg, &tmp, REGION_FIT_EXACT);
549 void rqgeomparams_from_table(WRQGeomParams *rq,
550 const WRectangle *origg, ExtlTab g)
553 rq->flags=REGION_RQGEOM_WEAK_ALL;
555 if(extl_table_gets_i(g, "x", &(rq->geom.x)))
556 rq->flags&=~REGION_RQGEOM_WEAK_X;
557 if(extl_table_gets_i(g, "y", &(rq->geom.y)))
558 rq->flags&=~REGION_RQGEOM_WEAK_Y;
559 if(extl_table_gets_i(g, "w", &(rq->geom.w)))
560 rq->flags&=~REGION_RQGEOM_WEAK_W;
561 if(extl_table_gets_i(g, "h", &(rq->geom.h)))
562 rq->flags&=~REGION_RQGEOM_WEAK_H;
564 rq->geom.w=maxof(1, rq->geom.w);
565 rq->geom.h=maxof(1, rq->geom.h);
570 * Attempt to resize and/or move \var{reg}. The table \var{g} is a usual
571 * geometry specification (fields \var{x}, \var{y}, \var{w} and \var{h}),
572 * but may contain missing fields, in which case, \var{reg}'s manager may
573 * attempt to leave that attribute unchanged.
575 EXTL_EXPORT_AS(WRegion, rqgeom)
576 ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g)
578 WRQGeomParams rq=RQGEOMPARAMS_INIT;
581 rqgeomparams_from_table(&rq, ®ION_GEOM(reg), g);
583 region_rqgeom(reg, &rq, &res);
585 return extl_table_from_rectangle(&res);
589 void region_managed_rqgeom(WRegion *mgr, WRegion *reg,
590 const WRQGeomParams *rq,
593 CALL_DYN(region_managed_rqgeom, mgr, (mgr, reg, rq, geomret));
597 void region_managed_rqgeom_absolute(WRegion *mgr, WRegion *reg,
598 const WRQGeomParams *rq,
601 CALL_DYN(region_managed_rqgeom_absolute, mgr,
602 (mgr, reg, rq, geomret));
606 void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg,
607 const WRQGeomParams *rq,
613 if(!(rq->flags®ION_RQGEOM_TRYONLY))
614 region_fit(reg, &rq->geom, REGION_FIT_EXACT);
618 void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg,
619 const WRQGeomParams *rq,
623 *geomret=REGION_GEOM(reg);
627 void region_managed_rqgeom_absolute_default(WRegion *mgr, WRegion *reg,
628 const WRQGeomParams *rq,
631 WRQGeomParams rq2=RQGEOMPARAMS_INIT;
633 rq2.flags=rq->flags&~REGION_RQGEOM_ABSOLUTE;
634 region_absolute_geom_to_parent(reg, &rq->geom, &rq2.geom);
636 region_managed_rqgeom(mgr, reg, &rq2, geomret);
640 void region_size_hints(WRegion *reg, WSizeHints *hints_ret)
642 sizehints_clear(hints_ret);
645 CALL_DYN(region_size_hints, reg, (reg, hints_ret));
648 if(!hints_ret->min_set){
649 hints_ret->min_width=1;
650 hints_ret->min_height=1;
652 if(!hints_ret->base_set){
653 hints_ret->base_width=0;
654 hints_ret->base_height=0;
656 if(!hints_ret->max_set){
657 hints_ret->max_width=INT_MAX;
658 hints_ret->max_height=INT_MAX;
663 void region_size_hints_correct(WRegion *reg,
664 int *wp, int *hp, bool min)
668 region_size_hints(reg, &hints);
670 sizehints_correct(&hints, wp, hp, min, FALSE);
674 int region_orientation(WRegion *reg)
676 int ret=REGION_ORIENTATION_NONE;
678 CALL_DYN_RET(ret, int, region_orientation, reg, (reg));
685 * Returns size hints for \var{reg}. The returned table always contains the
686 * fields \code{min_?}, \code{base_?} and sometimes the fields \code{max_?},
687 * \code{base_?} and \code{inc_?}, where \code{?}=\code{w}, \code{h}.
690 EXTL_EXPORT_AS(WRegion, size_hints)
691 ExtlTab region_size_hints_extl(WRegion *reg)
696 region_size_hints(reg, &hints);
698 tab=extl_create_table();
701 extl_table_sets_i(tab, "base_w", hints.base_width);
702 extl_table_sets_i(tab, "base_h", hints.base_height);
705 extl_table_sets_i(tab, "min_w", hints.min_width);
706 extl_table_sets_i(tab, "min_h", hints.min_height);
709 extl_table_sets_i(tab, "max_w", hints.max_width);
710 extl_table_sets_i(tab, "max_h", hints.max_height);
713 extl_table_sets_i(tab, "inc_w", hints.width_inc);
714 extl_table_sets_i(tab, "inc_h", hints.height_inc);
723 /*{{{ Restore size, maximize, shade */
726 static bool rqh(WFrame *frame, int y, int h)
728 WRQGeomParams rq=RQGEOMPARAMS_INIT;
732 rq.flags=REGION_RQGEOM_VERT_ONLY;
734 rq.geom.x=REGION_GEOM(frame).x;
735 rq.geom.w=REGION_GEOM(frame).w;
740 region_size_hints_correct((WRegion*)frame, &dummy_w, &(rq.geom.h), TRUE);
742 region_rqgeom((WRegion*)frame, &rq, &rgeom);
744 return (abs(rgeom.y-REGION_GEOM(frame).y)>1 ||
745 abs(rgeom.h-REGION_GEOM(frame).h)>1);
750 * Attempt to toggle vertical maximisation of \var{frame}.
753 void frame_maximize_vert(WFrame *frame)
755 WRegion *mp=region_manager((WRegion*)frame);
758 if(frame->flags&FRAME_SHADED || frame->flags&FRAME_MAXED_VERT){
759 if(frame->flags&FRAME_SAVED_VERT)
760 rqh(frame, frame->saved_y, frame->saved_h);
761 frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
768 oy=REGION_GEOM(frame).y;
769 oh=REGION_GEOM(frame).h;
771 rqh(frame, 0, REGION_GEOM(mp).h);
773 frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
778 static bool rqw(WFrame *frame, int x, int w)
780 WRQGeomParams rq=RQGEOMPARAMS_INIT;
784 rq.flags=REGION_RQGEOM_HORIZ_ONLY;
788 rq.geom.y=REGION_GEOM(frame).y;
789 rq.geom.h=REGION_GEOM(frame).h;
792 region_size_hints_correct((WRegion*)frame, &(rq.geom.w), &dummy_h, TRUE);
794 region_rqgeom((WRegion*)frame, &rq, &rgeom);
796 return (abs(rgeom.x-REGION_GEOM(frame).x)>1 ||
797 abs(rgeom.w-REGION_GEOM(frame).w)>1);
802 * Attempt to toggle horizontal maximisation of \var{frame}.
805 void frame_maximize_horiz(WFrame *frame)
807 WRegion *mp=region_manager((WRegion*)frame);
810 if(frame->flags&FRAME_MIN_HORIZ || frame->flags&FRAME_MAXED_HORIZ){
811 if(frame->flags&FRAME_SAVED_HORIZ)
812 rqw(frame, frame->saved_x, frame->saved_w);
813 frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
820 ox=REGION_GEOM(frame).x;
821 ow=REGION_GEOM(frame).w;
823 rqw(frame, 0, REGION_GEOM(mp).w);
825 frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
838 uint region_min_h(WRegion *reg)
841 region_size_hints(reg, &hints);
842 return hints.min_height;
846 uint region_min_w(WRegion *reg)
849 region_size_hints(reg, &hints);
850 return hints.min_width;
854 void region_convert_root_geom(WRegion *reg, WRectangle *geom)
858 region_rootpos(reg, &rx, &ry);
865 void region_absolute_geom_to_parent(WRegion *reg, const WRectangle *rgeom,
868 WRegion *parent=REGION_PARENT_REG(reg);
877 region_rootpos(reg, &pgeom->x, &pgeom->y);
878 pgeom->x=rgeom->x-pgeom->x;
879 pgeom->y=rgeom->y-pgeom->y;