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.
15 #include <libtu/objp.h>
16 #include <libtu/minmax.h>
17 #include <libextl/extl.h>
18 #include <libmainloop/defer.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
)
46 fpts
[1].x
=rect
->x
+rect
->w
;
48 fpts
[2].x
=rect
->x
+rect
->w
;
49 fpts
[2].y
=rect
->y
+rect
->h
;
51 fpts
[3].y
=rect
->y
+rect
->h
;
55 XDrawLines(ioncore_g
.dpy
, WROOTWIN_ROOT(rw
), rw
->xor_gc
, fpts
, 5,
60 static int max_width(GrBrush
*brush
, const char *str
)
64 while(str
&& *str
!='\0'){
65 w
=grbrush_get_text_width(brush
, str
, 1);
75 static int chars_for_num(int d
)
88 static WInfoWin
*setup_moveres_display(WWindow
*parent
, int cx
, int cy
)
95 fp
.mode
=REGION_FIT_EXACT
;
101 infowin
=create_infowin(parent
, &fp
, "moveres_display");
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 */
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
;;
120 region_fitrep((WRegion
*)infowin
, NULL
, &fp
);
121 region_map((WRegion
*)infowin
);
127 static void moveres_draw_infowin(WMoveresMode
*mode
)
132 if(mode
->infowin
==NULL
)
135 buf
=INFOWIN_BUFFER(mode
->infowin
);
140 if(mode
->mode
==MOVERES_SIZE
){
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
);
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
;
170 WRootWin
*rootwin
=(mode
->reg
==NULL
172 : region_rootwin_of(mode
->reg
));
177 rgeom
.x
+=mode
->parent_rx
;
178 rgeom
.y
+=mode
->parent_ry
;
180 if(mode
->rubfn
==NULL
)
181 draw_rubberbox(rootwin
, &rgeom
);
183 mode
->rubfn(rootwin
, &rgeom
);
190 /*{{{ Move/resize mode */
193 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
.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
);
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
,
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
);
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
;
353 w
=mode
->hints
.min_width
;
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
363 if(mode
->dx1
==mode
->dx2
){
364 if(mode
->dx1
==0 || realdx1
!=mode
->dx1
)
370 if(mode
->dx1
==0 || realdx1
!=mode
->dx1
)
373 geom
.x
+=mode
->origgeom
.w
-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 geom
.y
+=mode
->origgeom
.h
-geom
.h
;
391 moveres_draw_rubberband(mode
);
394 region_rqgeom(mode
->reg
, mode
->rqflags
, &geom
, &mode
->geom
);
396 if(!mode
->resize_cumulative
){
401 mode
->origgeom
=mode
->geom
;
404 moveres_draw_infowin(mode
);
407 moveres_draw_rubberband(mode
);
414 void moveresmode_delta_resize(WMoveresMode
*mode
,
415 int dx1
, int dx2
, int dy1
, int dy2
,
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
)
436 if(!OBJ_IS(reg
, WFrame
))
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
;
459 assert(tmpmode
==mode
);
464 moveres_draw_rubberband(mode
);
466 WRectangle g2
=mode
->geom
;
467 region_rqgeom(reg
, mode
->rqflags
&~REGION_RQGEOM_TRYONLY
,
470 XUngrabServer(ioncore_g
.dpy
);
473 set_saved(mode
, reg
);
475 if(mode
->infowin
!=NULL
){
476 mainloop_defer_destroy((Obj
*)mode
->infowin
);
479 destroy_obj((Obj
*)mode
);
488 /*{{{ Request and other dynfuns */
491 void region_rqgeom(WRegion
*reg
, int flags
, const WRectangle
*geom
,
494 bool tryonly
=(flags
®ION_RQGEOM_TRYONLY
);
496 if(REGION_MANAGER(reg
)!=NULL
){
497 region_managed_rqgeom(REGION_MANAGER(reg
), reg
, flags
, geom
,
501 *geomret
=REGION_GEOM(reg
);
503 region_fit(reg
, geom
, REGION_FIT_EXACT
);
506 /*if(!tryonly && geomret!=NULL)
507 *geomret=REGION_GEOM(reg);*/
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
,
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
,
565 if(!(flags
®ION_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
,
575 *geomret
=REGION_GEOM(reg
);
579 void region_size_hints(WRegion
*reg
, XSizeHints
*hints_ret
)
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
));
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
)
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
);
651 /*{{{ Restore size, maximize, shade */
654 void frame_restore_size(WFrame
*frame
, bool horiz
, bool vert
)
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
®ION_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
)
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
;
692 geom
.h
=REGION_GEOM(mgr
).h
;
696 correct_frame_size(frame
, &dummy_w
, &(geom
.h
));
699 region_rqgeom((WRegion
*)frame
,
700 tryonlyflag
|REGION_RQGEOM_VERT_ONLY
,
702 return (abs(rgeom
.y
-REGION_GEOM(frame
).y
)>1 ||
703 abs(rgeom
.h
-REGION_GEOM(frame
).h
)>1);
708 * Attempt to maximize \var{frame} vertically.
711 void frame_maximize_vert(WFrame
*frame
)
713 WRegion
*mgr
=REGION_MANAGER(frame
);
715 if(frame
->flags
&FRAME_SHADED
){
716 frame_toggle_shade(frame
);
723 if(!trymaxv(frame
, mgr
, REGION_RQGEOM_TRYONLY
)){
724 /* Could not maximize further, restore */
725 frame_restore_size(frame
, FALSE
, TRUE
);
729 trymaxv(frame
, mgr
, 0);
733 static bool trymaxh(WFrame
*frame
, WRegion
*mgr
, int tryonlyflag
)
735 WRectangle geom
=REGION_GEOM(frame
), rgeom
;
737 geom
.w
=REGION_GEOM(mgr
).w
;
741 correct_frame_size(frame
, &(geom
.w
), &dummy_h
);
744 region_rqgeom((WRegion
*)frame
,
745 tryonlyflag
|REGION_RQGEOM_HORIZ_ONLY
,
747 return (abs(rgeom
.x
-REGION_GEOM(frame
).x
)>1 ||
748 abs(rgeom
.w
-REGION_GEOM(frame
).w
)>1);
752 * Attempt to maximize \var{frame} horizontally.
755 void frame_maximize_horiz(WFrame
*frame
)
757 WRegion
*mgr
=REGION_MANAGER(frame
);
762 if(!trymaxh(frame
, mgr
, REGION_RQGEOM_TRYONLY
)){
763 /* Could not maximize further, restore */
764 frame_restore_size(frame
, TRUE
, FALSE
);
768 trymaxh(frame
, mgr
, 0);
778 uint
region_min_h(WRegion
*reg
)
781 region_size_hints(reg
, &hints
);
782 return hints
.min_height
;
786 uint
region_min_w(WRegion
*reg
)
789 region_size_hints(reg
, &hints
);
790 return hints
.min_width
;
794 void region_convert_root_geom(WRegion
*reg
, WRectangle
*geom
)
798 region_rootpos(reg
, &rx
, &ry
);