4 * Copyright (c) Tuomo Valkonen 1999-2006.
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_delta(WMoveresMode *mode,
321 int dx1, int dx2, int dy1, int dy2,
324 int realdx1, realdx2, realdy1, realdy2;
325 WRQGeomParams rq=RQGEOMPARAMS_INIT;
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;
335 if(mode->snap_enabled){
336 WRectangle *sg=&mode->snapgeom;
337 int er=CF_EDGE_RESISTANCE;
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;
349 w=mode->origgeom.w-realdx1+realdx2;
350 h=mode->origgeom.h-realdy1+realdy2;
353 w=mode->hints.min_width;
355 h=mode->hints.min_height;
357 sizehints_correct(&mode->hints, &w, &h, TRUE, TRUE);
359 /* Do not modify coordinates and sizes that were not requested to be
363 if(mode->dx1==mode->dx2){
364 if(mode->dx1==0 || realdx1!=mode->dx1)
370 if(mode->dx1==0 || realdx1!=mode->dx1)
373 rq.geom.x+=mode->origgeom.w-rq.geom.w;
377 if(mode->dy1==mode->dy2){
378 if(mode->dy1==0 || realdy1!=mode->dy1)
384 if(mode->dy1==0 || realdy1!=mode->dy1)
387 rq.geom.y+=mode->origgeom.h-rq.geom.h;
391 moveres_draw_rubberband(mode);
394 rq.flags=mode->rqflags;
395 region_rqgeom(mode->reg, &rq, &mode->geom);
398 if(!mode->resize_cumulative){
403 mode->origgeom=mode->geom;
406 moveres_draw_infowin(mode);
409 moveres_draw_rubberband(mode);
416 void moveresmode_delta_resize(WMoveresMode *mode,
417 int dx1, int dx2, int dy1, int dy2,
420 mode->mode=MOVERES_SIZE;
421 moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret);
425 void moveresmode_delta_move(WMoveresMode *mode,
426 int dx, int dy, WRectangle *rret)
428 mode->mode=MOVERES_POS;
429 moveresmode_delta(mode, dx, dx, dy, dy, rret);
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)
438 if(!OBJ_IS(reg, WFrame))
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;
449 if(mode->origgeom.h!=mode->geom.h){
450 frame->saved_y=mode->origgeom.y;
451 frame->saved_h=mode->origgeom.h;
456 bool moveresmode_do_end(WMoveresMode *mode, bool apply)
458 WRegion *reg=mode->reg;
461 assert(tmpmode==mode);
466 moveres_draw_rubberband(mode);
468 WRQGeomParams rq=RQGEOMPARAMS_INIT;
471 rq.flags=mode->rqflags&~REGION_RQGEOM_TRYONLY;
473 region_rqgeom(reg, &rq, &mode->geom);
475 XUngrabServer(ioncore_g.dpy);
478 set_saved(mode, reg);
480 if(mode->infowin!=NULL){
481 mainloop_defer_destroy((Obj*)mode->infowin);
484 destroy_obj((Obj*)mode);
493 /*{{{ Request and other dynfuns */
496 void region_rqgeom(WRegion *reg, const WRQGeomParams *rq,
499 WRegion *mgr=REGION_MANAGER(reg);
502 if(rq->flags®ION_RQGEOM_ABSOLUTE)
503 region_managed_rqgeom_absolute(mgr, reg, rq, geomret);
505 region_managed_rqgeom(mgr, reg, rq, geomret);
509 if(rq->flags®ION_RQGEOM_ABSOLUTE)
510 region_absolute_geom_to_parent(reg, &rq->geom, &tmp);
517 if(!(rq->flags®ION_RQGEOM_TRYONLY))
518 region_fit(reg, &tmp, REGION_FIT_EXACT);
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.
529 EXTL_EXPORT_AS(WRegion, rqgeom)
530 ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g)
532 WRectangle ogeom=REGION_GEOM(reg);
533 WRQGeomParams rq=RQGEOMPARAMS_INIT;
537 rq.flags=REGION_RQGEOM_WEAK_ALL;
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;
549 rq.geom.w=maxof(1, rq.geom.w);
550 rq.geom.h=maxof(1, rq.geom.h);
552 region_rqgeom(reg, &rq, &ogeom);
554 return extl_table_from_rectangle(&ogeom);
558 void region_managed_rqgeom(WRegion *mgr, WRegion *reg,
559 const WRQGeomParams *rq,
562 CALL_DYN(region_managed_rqgeom, mgr, (mgr, reg, rq, geomret));
566 void region_managed_rqgeom_absolute(WRegion *mgr, WRegion *reg,
567 const WRQGeomParams *rq,
570 CALL_DYN(region_managed_rqgeom_absolute, mgr,
571 (mgr, reg, rq, geomret));
575 void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg,
576 const WRQGeomParams *rq,
582 if(!(rq->flags®ION_RQGEOM_TRYONLY))
583 region_fit(reg, &rq->geom, REGION_FIT_EXACT);
587 void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg,
588 const WRQGeomParams *rq,
592 *geomret=REGION_GEOM(reg);
596 void region_managed_rqgeom_absolute_default(WRegion *mgr, WRegion *reg,
597 const WRQGeomParams *rq,
600 WRQGeomParams rq2=RQGEOMPARAMS_INIT;
602 rq2.flags=rq->flags&~REGION_RQGEOM_ABSOLUTE;
603 region_absolute_geom_to_parent(reg, &rq->geom, &rq2.geom);
605 region_managed_rqgeom(mgr, reg, &rq2, geomret);
609 void region_size_hints(WRegion *reg, WSizeHints *hints_ret)
611 sizehints_clear(hints_ret);
614 CALL_DYN(region_size_hints, reg, (reg, hints_ret));
617 if(!hints_ret->min_set){
618 hints_ret->min_width=1;
619 hints_ret->min_height=1;
621 if(!hints_ret->base_set){
622 hints_ret->base_width=0;
623 hints_ret->base_height=0;
625 if(!hints_ret->max_set){
626 hints_ret->max_width=INT_MAX;
627 hints_ret->max_height=INT_MAX;
632 void region_size_hints_correct(WRegion *reg,
633 int *wp, int *hp, bool min)
637 region_size_hints(reg, &hints);
639 sizehints_correct(&hints, wp, hp, min, FALSE);
643 int region_orientation(WRegion *reg)
645 int ret=REGION_ORIENTATION_NONE;
647 CALL_DYN_RET(ret, int, region_orientation, reg, (reg));
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}.
659 EXTL_EXPORT_AS(WRegion, size_hints)
660 ExtlTab region_size_hints_extl(WRegion *reg)
665 region_size_hints(reg, &hints);
667 tab=extl_create_table();
670 extl_table_sets_i(tab, "base_w", hints.base_width);
671 extl_table_sets_i(tab, "base_h", hints.base_height);
674 extl_table_sets_i(tab, "min_w", hints.min_width);
675 extl_table_sets_i(tab, "min_h", hints.min_height);
678 extl_table_sets_i(tab, "max_w", hints.max_width);
679 extl_table_sets_i(tab, "max_h", hints.max_height);
682 extl_table_sets_i(tab, "inc_w", hints.width_inc);
683 extl_table_sets_i(tab, "inc_h", hints.height_inc);
692 /*{{{ Restore size, maximize, shade */
695 static bool rqh(WFrame *frame, int y, int h)
697 WRQGeomParams rq=RQGEOMPARAMS_INIT;
701 rq.flags=REGION_RQGEOM_VERT_ONLY;
703 rq.geom.x=REGION_GEOM(frame).x;
704 rq.geom.w=REGION_GEOM(frame).w;
709 region_size_hints_correct((WRegion*)frame, &dummy_w, &(rq.geom.h), TRUE);
711 region_rqgeom((WRegion*)frame, &rq, &rgeom);
713 return (abs(rgeom.y-REGION_GEOM(frame).y)>1 ||
714 abs(rgeom.h-REGION_GEOM(frame).h)>1);
719 * Attempt to toggle vertical maximisation of \var{frame}.
722 void frame_maximize_vert(WFrame *frame)
724 WRegion *mp=region_manager((WRegion*)frame);
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);
737 oy=REGION_GEOM(frame).y;
738 oh=REGION_GEOM(frame).h;
740 rqh(frame, 0, REGION_GEOM(mp).h);
742 frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
747 static bool rqw(WFrame *frame, int x, int w)
749 WRQGeomParams rq=RQGEOMPARAMS_INIT;
753 rq.flags=REGION_RQGEOM_HORIZ_ONLY;
757 rq.geom.y=REGION_GEOM(frame).y;
758 rq.geom.h=REGION_GEOM(frame).h;
761 region_size_hints_correct((WRegion*)frame, &(rq.geom.w), &dummy_h, TRUE);
763 region_rqgeom((WRegion*)frame, &rq, &rgeom);
765 return (abs(rgeom.x-REGION_GEOM(frame).x)>1 ||
766 abs(rgeom.w-REGION_GEOM(frame).w)>1);
771 * Attempt to toggle horizontal maximisation of \var{frame}.
774 void frame_maximize_horiz(WFrame *frame)
776 WRegion *mp=region_manager((WRegion*)frame);
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);
789 ox=REGION_GEOM(frame).x;
790 ow=REGION_GEOM(frame).w;
792 rqw(frame, 0, REGION_GEOM(mp).w);
794 frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
807 uint region_min_h(WRegion *reg)
810 region_size_hints(reg, &hints);
811 return hints.min_height;
815 uint region_min_w(WRegion *reg)
818 region_size_hints(reg, &hints);
819 return hints.min_width;
823 void region_convert_root_geom(WRegion *reg, WRectangle *geom)
827 region_rootpos(reg, &rx, &ry);
834 void region_absolute_geom_to_parent(WRegion *reg, const WRectangle *rgeom,
837 WRegion *parent=REGION_PARENT_REG(reg);
846 region_rootpos(reg, &pgeom->x, &pgeom->y);
847 pgeom->x=rgeom->x-pgeom->x;
848 pgeom->y=rgeom->y-pgeom->y;