4 * Copyright (c) Tuomo Valkonen 1999-2007.
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 /*{{{ Size/position display and rubberband */
36 static void draw_rubberbox(WRootWin *rw, const WRectangle *rect)
42 fpts[1].x=rect->x+rect->w;
44 fpts[2].x=rect->x+rect->w;
45 fpts[2].y=rect->y+rect->h;
47 fpts[3].y=rect->y+rect->h;
51 XDrawLines(ioncore_g.dpy, WROOTWIN_ROOT(rw), rw->xor_gc, fpts, 5,
56 static int max_width(GrBrush *brush, const char *str)
60 while(str && *str!='\0'){
61 w=grbrush_get_text_width(brush, str, 1);
71 static int chars_for_num(int d)
84 static WInfoWin *setup_moveres_display(WWindow *parent, int cx, int cy)
91 fp.mode=REGION_FIT_EXACT;
97 infowin=create_infowin(parent, &fp, "moveres_display");
102 grbrush_get_border_widths(INFOWIN_BRUSH(infowin), &bdw);
103 grbrush_get_font_extents(INFOWIN_BRUSH(infowin), &fnte);
105 /* Create move/resize position/size display window */
107 fp.g.w+=chars_for_num(REGION_GEOM(parent).w);
108 fp.g.w+=chars_for_num(REGION_GEOM(parent).h);
109 fp.g.w*=max_width(INFOWIN_BRUSH(infowin), "0123456789x+");
110 fp.g.w+=bdw.left+bdw.right;
111 fp.g.h=fnte.max_height+bdw.top+bdw.bottom;;
116 region_fitrep((WRegion*)infowin, NULL, &fp);
117 region_map((WRegion*)infowin);
123 static void moveres_draw_infowin(WMoveresMode *mode)
128 if(mode->infowin==NULL)
131 buf=INFOWIN_BUFFER(mode->infowin);
136 if(mode->mode==MOVERES_SIZE){
142 if((mode->hints.inc_set) &&
143 (mode->hints.width_inc>1 || mode->hints.height_inc>1)){
144 if(mode->hints.base_set){
145 w-=mode->hints.base_width;
146 h-=mode->hints.base_height;
148 w/=mode->hints.width_inc;
149 h/=mode->hints.height_inc;
152 snprintf(buf, INFOWIN_BUFFER_LEN, "%dx%d", w, h);
154 snprintf(buf, INFOWIN_BUFFER_LEN, "%+d %+d",
155 mode->geom.x, mode->geom.y);
158 window_draw((WWindow*)mode->infowin, TRUE);
162 static void moveres_draw_rubberband(WMoveresMode *mode)
164 WRectangle rgeom=mode->geom;
166 WRootWin *rootwin=(mode->reg==NULL
168 : region_rootwin_of(mode->reg));
173 rgeom.x+=mode->parent_rx;
174 rgeom.y+=mode->parent_ry;
176 if(mode->rubfn==NULL)
177 draw_rubberbox(rootwin, &rgeom);
179 mode->rubfn(rootwin, &rgeom);
186 /*{{{ Move/resize mode */
189 WMoveresMode *tmpmode=NULL;
193 IMPLCLASS(WMoveresMode, Obj, NULL, NULL);
196 WMoveresMode *moveres_mode(WRegion *reg)
200 return ((reg==NULL || tmpmode->reg==reg) ? tmpmode : NULL);
204 WRegion *moveresmode_target(WMoveresMode *mode)
210 static bool moveresmode_init(WMoveresMode *mode, WRegion *reg,
211 WDrawRubberbandFn *rubfn, bool cumulative)
219 parent=REGION_PARENT(reg);
226 mode->snap_enabled=FALSE;
227 region_size_hints(reg, &mode->hints);
229 region_rootpos((WRegion*)parent, &mode->parent_rx, &mode->parent_ry);
231 mode->geom=REGION_GEOM(reg);
232 mode->origgeom=REGION_GEOM(reg);
238 mode->resize_cumulative=cumulative;
239 mode->rqflags=(XOR_RESIZE ? REGION_RQGEOM_TRYONLY : 0);
241 mode->mode=MOVERES_SIZE;
243 /* Get snapping geometry */
244 mgr=REGION_MANAGER(reg);
247 mode->snapgeom=REGION_GEOM(mgr);
249 if(mgr==(WRegion*)parent){
252 mode->snap_enabled=FALSE;
253 }else if(REGION_PARENT(mgr)==parent){
254 mode->snap_enabled=TRUE;
258 if(!mode->hints.min_set || mode->hints.min_width<1)
259 mode->hints.min_width=1;
260 if(!mode->hints.min_set || mode->hints.min_height<1)
261 mode->hints.min_height=1;
263 /* Set up info window */
265 int x=mode->geom.x+mode->geom.w/2;
266 int y=mode->geom.y+mode->geom.h/2;
267 mode->infowin=setup_moveres_display(parent, x, y);
270 moveres_draw_infowin(mode);
273 XGrabServer(ioncore_g.dpy);
274 moveres_draw_rubberband(mode);
281 static WMoveresMode *create_moveresmode(WRegion *reg,
282 WDrawRubberbandFn *rubfn,
285 CREATEOBJ_IMPL(WMoveresMode, moveresmode, (p, reg, rubfn, cumulative));
289 WMoveresMode *region_begin_resize(WRegion *reg, WDrawRubberbandFn *rubfn,
292 WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
295 mode->mode=MOVERES_SIZE;
296 ioncore_change_grab_cursor(IONCORE_CURSOR_RESIZE);
303 WMoveresMode *region_begin_move(WRegion *reg, WDrawRubberbandFn *rubfn,
306 WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
309 mode->mode=MOVERES_POS;
310 ioncore_change_grab_cursor(IONCORE_CURSOR_MOVE);
317 static void moveresmode_setorig(WMoveresMode *mode)
323 mode->origgeom=mode->geom;
327 static void moveresmode_do_newgeom(WMoveresMode *mode, WRQGeomParams *rq)
330 moveres_draw_rubberband(mode);
333 rq->flags|=mode->rqflags;
334 region_rqgeom(mode->reg, rq, &mode->geom);
337 moveres_draw_infowin(mode);
340 moveres_draw_rubberband(mode);
344 static void moveresmode_delta(WMoveresMode *mode,
345 int dx1, int dx2, int dy1, int dy2,
348 int realdx1, realdx2, realdy1, realdy2;
349 WRQGeomParams rq=RQGEOMPARAMS_INIT;
352 realdx1=(mode->dx1+=dx1);
353 realdx2=(mode->dx2+=dx2);
354 realdy1=(mode->dy1+=dy1);
355 realdy2=(mode->dy2+=dy2);
356 rq.geom=mode->origgeom;
359 if(mode->snap_enabled){
360 WRectangle *sg=&mode->snapgeom;
361 int er=CF_EDGE_RESISTANCE;
363 if(mode->dx1!=0 && rq.geom.x+mode->dx1<sg->x && rq.geom.x+mode->dx1>sg->x-er)
364 realdx1=sg->x-rq.geom.x;
365 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)
366 realdx2=sg->x+sg->w-rq.geom.x-rq.geom.w;
367 if(mode->dy1!=0 && rq.geom.y+mode->dy1<sg->y && rq.geom.y+mode->dy1>sg->y-er)
368 realdy1=sg->y-rq.geom.y;
369 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)
370 realdy2=sg->y+sg->h-rq.geom.y-rq.geom.h;
373 w=mode->origgeom.w-realdx1+realdx2;
374 h=mode->origgeom.h-realdy1+realdy2;
377 w=mode->hints.min_width;
379 h=mode->hints.min_height;
381 sizehints_correct(&mode->hints, &w, &h, TRUE, TRUE);
383 /* Do not modify coordinates and sizes that were not requested to be
387 if(mode->dx1==mode->dx2){
388 if(mode->dx1==0 || realdx1!=mode->dx1)
394 if(mode->dx1==0 || realdx1!=mode->dx1)
397 rq.geom.x+=mode->origgeom.w-rq.geom.w;
401 if(mode->dy1==mode->dy2){
402 if(mode->dy1==0 || realdy1!=mode->dy1)
408 if(mode->dy1==0 || realdy1!=mode->dy1)
411 rq.geom.y+=mode->origgeom.h-rq.geom.h;
414 moveresmode_do_newgeom(mode, &rq);
416 if(!mode->resize_cumulative)
417 moveresmode_setorig(mode);
424 void moveresmode_delta_resize(WMoveresMode *mode,
425 int dx1, int dx2, int dy1, int dy2,
428 mode->mode=MOVERES_SIZE;
429 moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret);
433 void moveresmode_delta_move(WMoveresMode *mode,
434 int dx, int dy, WRectangle *rret)
436 mode->mode=MOVERES_POS;
437 moveresmode_delta(mode, dx, dx, dy, dy, rret);
441 void moveresmode_rqgeom(WMoveresMode *mode, WRQGeomParams *rq,
444 mode->mode=MOVERES_SIZE;
445 moveresmode_do_newgeom(mode, rq);
446 moveresmode_setorig(mode);
450 /* It is ugly to do this here, but it will have to do for now... */
451 static void set_saved(WMoveresMode *mode, WRegion *reg)
455 if(!OBJ_IS(reg, WFrame))
460 /* Restore saved sizes from the beginning of the resize action */
461 if(mode->origgeom.w!=mode->geom.w){
462 frame->saved_x=mode->origgeom.x;
463 frame->saved_w=mode->origgeom.w;
466 if(mode->origgeom.h!=mode->geom.h){
467 frame->saved_y=mode->origgeom.y;
468 frame->saved_h=mode->origgeom.h;
473 bool moveresmode_do_end(WMoveresMode *mode, bool apply)
475 WRegion *reg=mode->reg;
478 assert(tmpmode==mode);
483 moveres_draw_rubberband(mode);
485 WRQGeomParams rq=RQGEOMPARAMS_INIT;
488 rq.flags=mode->rqflags&~REGION_RQGEOM_TRYONLY;
490 region_rqgeom(reg, &rq, &mode->geom);
492 XUngrabServer(ioncore_g.dpy);
495 set_saved(mode, reg);
497 if(mode->infowin!=NULL){
498 mainloop_defer_destroy((Obj*)mode->infowin);
501 destroy_obj((Obj*)mode);
510 /*{{{ Request and other dynfuns */
513 void region_rqgeom(WRegion *reg, const WRQGeomParams *rq,
516 WRegion *mgr=REGION_MANAGER(reg);
519 if(rq->flags®ION_RQGEOM_ABSOLUTE)
520 region_managed_rqgeom_absolute(mgr, reg, rq, geomret);
522 region_managed_rqgeom(mgr, reg, rq, geomret);
526 if(rq->flags®ION_RQGEOM_ABSOLUTE)
527 region_absolute_geom_to_parent(reg, &rq->geom, &tmp);
534 if(!(rq->flags®ION_RQGEOM_TRYONLY))
535 region_fit(reg, &tmp, REGION_FIT_EXACT);
540 void rqgeomparams_from_table(WRQGeomParams *rq,
541 const WRectangle *origg, ExtlTab g)
544 rq->flags=REGION_RQGEOM_WEAK_ALL;
546 if(extl_table_gets_i(g, "x", &(rq->geom.x)))
547 rq->flags&=~REGION_RQGEOM_WEAK_X;
548 if(extl_table_gets_i(g, "y", &(rq->geom.y)))
549 rq->flags&=~REGION_RQGEOM_WEAK_Y;
550 if(extl_table_gets_i(g, "w", &(rq->geom.w)))
551 rq->flags&=~REGION_RQGEOM_WEAK_W;
552 if(extl_table_gets_i(g, "h", &(rq->geom.h)))
553 rq->flags&=~REGION_RQGEOM_WEAK_H;
555 rq->geom.w=maxof(1, rq->geom.w);
556 rq->geom.h=maxof(1, rq->geom.h);
561 * Attempt to resize and/or move \var{reg}. The table \var{g} is a usual
562 * geometry specification (fields \var{x}, \var{y}, \var{w} and \var{h}),
563 * but may contain missing fields, in which case, \var{reg}'s manager may
564 * attempt to leave that attribute unchanged.
566 EXTL_EXPORT_AS(WRegion, rqgeom)
567 ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g)
569 WRQGeomParams rq=RQGEOMPARAMS_INIT;
572 rqgeomparams_from_table(&rq, ®ION_GEOM(reg), g);
574 region_rqgeom(reg, &rq, &res);
576 return extl_table_from_rectangle(&res);
580 void region_managed_rqgeom(WRegion *mgr, WRegion *reg,
581 const WRQGeomParams *rq,
584 CALL_DYN(region_managed_rqgeom, mgr, (mgr, reg, rq, geomret));
588 void region_managed_rqgeom_absolute(WRegion *mgr, WRegion *reg,
589 const WRQGeomParams *rq,
592 CALL_DYN(region_managed_rqgeom_absolute, mgr,
593 (mgr, reg, rq, geomret));
597 void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg,
598 const WRQGeomParams *rq,
604 if(!(rq->flags®ION_RQGEOM_TRYONLY))
605 region_fit(reg, &rq->geom, REGION_FIT_EXACT);
609 void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg,
610 const WRQGeomParams *rq,
614 *geomret=REGION_GEOM(reg);
618 void region_managed_rqgeom_absolute_default(WRegion *mgr, WRegion *reg,
619 const WRQGeomParams *rq,
622 WRQGeomParams rq2=RQGEOMPARAMS_INIT;
624 rq2.flags=rq->flags&~REGION_RQGEOM_ABSOLUTE;
625 region_absolute_geom_to_parent(reg, &rq->geom, &rq2.geom);
627 region_managed_rqgeom(mgr, reg, &rq2, geomret);
631 void region_size_hints(WRegion *reg, WSizeHints *hints_ret)
633 sizehints_clear(hints_ret);
636 CALL_DYN(region_size_hints, reg, (reg, hints_ret));
639 if(!hints_ret->min_set){
640 hints_ret->min_width=1;
641 hints_ret->min_height=1;
643 if(!hints_ret->base_set){
644 hints_ret->base_width=0;
645 hints_ret->base_height=0;
647 if(!hints_ret->max_set){
648 hints_ret->max_width=INT_MAX;
649 hints_ret->max_height=INT_MAX;
654 void region_size_hints_correct(WRegion *reg,
655 int *wp, int *hp, bool min)
659 region_size_hints(reg, &hints);
661 sizehints_correct(&hints, wp, hp, min, FALSE);
665 int region_orientation(WRegion *reg)
667 int ret=REGION_ORIENTATION_NONE;
669 CALL_DYN_RET(ret, int, region_orientation, reg, (reg));
676 * Returns size hints for \var{reg}. The returned table always contains the
677 * fields \code{min_?}, \code{base_?} and sometimes the fields \code{max_?},
678 * \code{base_?} and \code{inc_?}, where \code{?}=\code{w}, \code{h}.
681 EXTL_EXPORT_AS(WRegion, size_hints)
682 ExtlTab region_size_hints_extl(WRegion *reg)
687 region_size_hints(reg, &hints);
689 tab=extl_create_table();
692 extl_table_sets_i(tab, "base_w", hints.base_width);
693 extl_table_sets_i(tab, "base_h", hints.base_height);
696 extl_table_sets_i(tab, "min_w", hints.min_width);
697 extl_table_sets_i(tab, "min_h", hints.min_height);
700 extl_table_sets_i(tab, "max_w", hints.max_width);
701 extl_table_sets_i(tab, "max_h", hints.max_height);
704 extl_table_sets_i(tab, "inc_w", hints.width_inc);
705 extl_table_sets_i(tab, "inc_h", hints.height_inc);
714 /*{{{ Restore size, maximize, shade */
717 static bool rqh(WFrame *frame, int y, int h)
719 WRQGeomParams rq=RQGEOMPARAMS_INIT;
723 rq.flags=REGION_RQGEOM_VERT_ONLY;
725 rq.geom.x=REGION_GEOM(frame).x;
726 rq.geom.w=REGION_GEOM(frame).w;
731 region_size_hints_correct((WRegion*)frame, &dummy_w, &(rq.geom.h), TRUE);
733 region_rqgeom((WRegion*)frame, &rq, &rgeom);
735 return (abs(rgeom.y-REGION_GEOM(frame).y)>1 ||
736 abs(rgeom.h-REGION_GEOM(frame).h)>1);
741 * Attempt to toggle vertical maximisation of \var{frame}.
744 void frame_maximize_vert(WFrame *frame)
746 WRegion *mp=region_manager((WRegion*)frame);
749 if(frame->flags&FRAME_SHADED || frame->flags&FRAME_MAXED_VERT){
750 if(frame->flags&FRAME_SAVED_VERT)
751 rqh(frame, frame->saved_y, frame->saved_h);
752 frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
759 oy=REGION_GEOM(frame).y;
760 oh=REGION_GEOM(frame).h;
762 rqh(frame, 0, REGION_GEOM(mp).h);
764 frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
769 static bool rqw(WFrame *frame, int x, int w)
771 WRQGeomParams rq=RQGEOMPARAMS_INIT;
775 rq.flags=REGION_RQGEOM_HORIZ_ONLY;
779 rq.geom.y=REGION_GEOM(frame).y;
780 rq.geom.h=REGION_GEOM(frame).h;
783 region_size_hints_correct((WRegion*)frame, &(rq.geom.w), &dummy_h, TRUE);
785 region_rqgeom((WRegion*)frame, &rq, &rgeom);
787 return (abs(rgeom.x-REGION_GEOM(frame).x)>1 ||
788 abs(rgeom.w-REGION_GEOM(frame).w)>1);
793 * Attempt to toggle horizontal maximisation of \var{frame}.
796 void frame_maximize_horiz(WFrame *frame)
798 WRegion *mp=region_manager((WRegion*)frame);
801 if(frame->flags&FRAME_MIN_HORIZ || frame->flags&FRAME_MAXED_HORIZ){
802 if(frame->flags&FRAME_SAVED_HORIZ)
803 rqw(frame, frame->saved_x, frame->saved_w);
804 frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
811 ox=REGION_GEOM(frame).x;
812 ow=REGION_GEOM(frame).w;
814 rqw(frame, 0, REGION_GEOM(mp).w);
816 frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
829 uint region_min_h(WRegion *reg)
832 region_size_hints(reg, &hints);
833 return hints.min_height;
837 uint region_min_w(WRegion *reg)
840 region_size_hints(reg, &hints);
841 return hints.min_width;
845 void region_convert_root_geom(WRegion *reg, WRectangle *geom)
849 region_rootpos(reg, &rx, &ry);
856 void region_absolute_geom_to_parent(WRegion *reg, const WRectangle *rgeom,
859 WRegion *parent=REGION_PARENT_REG(reg);
868 region_rootpos(reg, &pgeom->x, &pgeom->y);
869 pgeom->x=rgeom->x-pgeom->x;
870 pgeom->y=rgeom->y-pgeom->y;