add tests for menu and menustyle commands.
[fvwm.git] / fvwm / move_resize.c
blob3ab5d828ece445e3e0397f96abef541a42a00ee6
1 /* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License as published by
3 * the Free Software Foundation; either version 2 of the License, or
4 * (at your option) any later version.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 /*****************************************************************************
17 * This module is all original code
18 * by Rob Nation
19 * Copyright 1993, Robert Nation
20 * You may use this code for any purpose, as long as the original
21 * copyright remains in the source code and all documentation
22 ****************************************************************************/
24 /************************************************************************
26 * code for moving and resizing windows
28 ***********************************************************************/
30 #include "config.h"
32 #include <stdio.h>
34 #include "libs/fvwmlib.h"
35 #include "fvwm.h"
36 #include "externs.h"
37 #include "cursor.h"
38 #include "functions.h"
39 #include "bindings.h"
40 #include "misc.h"
41 #include "screen.h"
42 #include "defaults.h"
43 #include "move_resize.h"
44 #include "module_interface.h"
45 #include "borders.h"
46 #include "geometry.h"
47 #include "gnome.h"
48 #include "colormaps.h"
49 #include "virtual.h"
50 #include "decorations.h"
51 #include "events.h"
52 #include <X11/keysym.h>
54 /* ----- move globals ----- */
55 extern XEvent Event;
57 /* Animated move stuff added by Greg J. Badros, gjb@cs.washington.edu */
59 float rgpctMovementDefault[32] = {
60 -.01, 0, .01, .03,.08,.18,.3,.45,.60,.75,.85,.90,.94,.97,.99,1.0
61 /* must end in 1.0 */
64 int cmsDelayDefault = 10; /* milliseconds */
65 static void DisplayPosition(FvwmWindow *, int, int,Bool);
66 /* ----- end of move globals ----- */
68 /* ----- resize globals ----- */
70 /* DO NOT USE (STATIC) GLOBALS IN THIS MODULE!
71 * Since some functions are called from other modules unwanted side effects
72 * (i.e. bugs.) would be created */
74 extern Window PressedW;
76 static void DoResize(
77 int x_root, int y_root, FvwmWindow *tmp_win, rectangle *drag,
78 rectangle *orig, int *xmotionp, int *ymotionp, Bool do_resize_opaque);
79 static void DisplaySize(FvwmWindow *, int, int, Bool, Bool);
80 /* ----- end of resize globals ----- */
82 /* The vars are named for the x-direction, but this is used for both x and y */
83 static int GetOnePositionArgument(
84 char *s1,int x,int w,int *pFinalX,float factor, int max, Bool is_x)
86 int val;
87 int cch = strlen(s1);
88 Bool add_pointer_position = False;
90 if (cch == 0)
91 return 0;
92 if (s1[cch-1] == 'p')
94 factor = 1; /* Use pixels, so don't multiply by factor */
95 s1[cch-1] = '\0';
97 if (strcmp(s1,"w") == 0)
99 *pFinalX = x;
101 else if (sscanf(s1,"w-%d",&val) == 1)
103 *pFinalX = x - (int)(val*factor);
105 else if (sscanf(s1,"w+%d",&val) == 1 || sscanf(s1,"w%d",&val) == 1 )
107 *pFinalX = x + (int)(val*factor);
109 else if (sscanf(s1,"m-%d",&val) == 1)
111 add_pointer_position = True;
112 *pFinalX = -(int)(val*factor);
114 else if (sscanf(s1,"m+%d",&val) == 1 || sscanf(s1,"m%d",&val) == 1 )
116 add_pointer_position = True;
117 *pFinalX = (int)(val*factor);
119 else if (sscanf(s1,"-%d",&val) == 1)
121 *pFinalX = max-w - (int)(val*factor);
123 else if (sscanf(s1,"%d",&val) == 1)
125 *pFinalX = (int)(val*factor);
127 else
129 return 0;
131 if (add_pointer_position)
133 int x = 0;
134 int y = 0;
136 XQueryPointer(
137 dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY, &x, &y, &JunkMask);
138 *pFinalX += (is_x) ? x : y;
141 return 1;
144 /* GetMoveArguments is used for Move & AnimatedMove
145 * It lets you specify in all the following ways
146 * 20 30 Absolute percent position, from left edge and top
147 * -50 50 Absolute percent position, from right edge and top
148 * 10p 5p Absolute pixel position
149 * 10p -0p Absolute pixel position, from bottom
150 * w+5 w-10p Relative position, right 5%, up ten pixels
151 * w+5 w-10p Pointer relative position, right 5%, up ten pixels
152 * Returns 2 when x & y have parsed without error, 0 otherwise
154 static int GetMoveArguments(
155 char **paction, int w, int h, int *pFinalX, int *pFinalY,
156 Bool *fWarp, Bool *fPointer)
158 char *s1 = NULL;
159 char *s2 = NULL;
160 char *warp = NULL;
161 char *action;
162 char *naction;
163 int scrWidth = Scr.MyDisplayWidth;
164 int scrHeight = Scr.MyDisplayHeight;
165 int retval = 0;
167 if (!paction)
168 return 0;
169 action = *paction;
170 action = GetNextToken(action, &s1);
171 if (s1 && fPointer && StrEquals(s1, "pointer"))
173 *fPointer = True;
174 free(s1);
175 return 0;
177 else
179 action = GetNextToken(action, &s2);
180 if (fWarp)
182 warp = PeekToken(action, &naction);
183 if (StrEquals(warp, "Warp"))
185 *fWarp = True;
186 action = naction;
191 if (s1 != NULL && s2 != NULL)
193 retval = 0;
194 if (StrEquals(s1, "keep"))
196 retval++;
198 else if (GetOnePositionArgument(
199 s1, *pFinalX, w, pFinalX, (float)scrWidth/100, scrWidth, True))
201 retval++;
203 if (StrEquals(s2, "keep"))
205 retval++;
207 else if (GetOnePositionArgument(
208 s2, *pFinalY, h, pFinalY, (float)scrHeight/100, scrHeight, False))
210 retval++;
212 if (retval == 0)
213 *fWarp = False; /* make sure warping is off for interactive moves */
215 else
217 /* not enough arguments, switch to current page. */
218 while (*pFinalX < 0)
220 *pFinalX = Scr.MyDisplayWidth + *pFinalX;
222 while (*pFinalY < 0)
224 *pFinalY = Scr.MyDisplayHeight + *pFinalY;
228 if (s1)
229 free(s1);
230 if (s2)
231 free(s2);
233 *paction = action;
234 return retval;
237 static int ParseOneResizeArgument(
238 char *arg, int scr_size, int base_size, int size_inc, int bw, int title_size,
239 int *ret_size)
241 int unit_table[3];
242 int value;
243 int suffix;
245 if (StrEquals(arg, "keep"))
247 /* do not change width */
249 else
251 if (GetSuffixedIntegerArguments(arg, NULL, &value, 1, "pc", &suffix) < 1)
253 return 0;
255 else
257 /* convert the value/suffix pairs to pixels */
258 unit_table[0] = scr_size;
259 unit_table[1] = 100;
260 unit_table[2] = 100 * size_inc;
261 *ret_size = SuffixToPercentValue(value, suffix, unit_table);
262 if (*ret_size < 0)
263 *ret_size += scr_size;
264 else
266 if (suffix == 2)
268 /* account for base width */
269 *ret_size += base_size;
271 *ret_size += title_size + 2 * bw;
276 return 1;
279 static int GetResizeArguments(
280 char **paction, int x, int y, int w_base, int h_base, int w_inc, int h_inc,
281 int bw, int h_title, int *pFinalW, int *pFinalH)
283 int n;
284 char *naction;
285 char *token;
286 char *s1;
287 char *s2;
289 if (!paction)
290 return 0;
292 token = PeekToken(*paction, &naction);
293 if (!token)
294 return 0;
295 if (StrEquals(token, "bottomright") || StrEquals(token, "br"))
297 int nx = x + *pFinalW - 1;
298 int ny = y + *pFinalH - 1;
300 n = GetMoveArguments(&naction, 0, 0, &nx, &ny, NULL, NULL);
301 if (n < 2)
302 return 0;
303 *pFinalW = nx - x + 1;
304 *pFinalH = ny - y + 1;
305 *paction = naction;
307 else
309 s1 = strdup(token);
310 naction = GetNextToken(naction, &s2);
311 if (!s2)
313 free(s1);
314 return 0;
316 *paction = naction;
318 n = 0;
319 n += ParseOneResizeArgument(
320 s1, Scr.MyDisplayWidth, w_base, w_inc, bw, 0, pFinalW);
321 n += ParseOneResizeArgument(
322 s2, Scr.MyDisplayHeight, h_base, h_inc, bw, h_title, pFinalH);
323 free(s1);
324 free(s2);
325 if (n < 2)
326 n = 0;
329 return n;
332 static int GetResizeMoveArguments(
333 char **paction, int w_base, int h_base, int w_inc, int h_inc, int bw,
334 int h_title, int *pFinalX, int *pFinalY, int *pFinalW, int *pFinalH,
335 Bool *fWarp, Bool *fPointer)
337 char *action = *paction;
339 if (!paction)
340 return 0;
341 if (GetResizeArguments(
342 &action, *pFinalX, *pFinalY, w_base, h_base, w_inc, h_inc, bw, h_title,
343 pFinalW, pFinalH) < 2)
345 return 0;
347 if (GetMoveArguments(
348 &action, *pFinalW, *pFinalH, pFinalX, pFinalY, fWarp, NULL) < 2)
350 return 0;
352 *paction = action;
354 return 4;
357 void resize_move_window(F_CMD_ARGS)
359 int FinalX = 0;
360 int FinalY = 0;
361 int FinalW = 0;
362 int FinalH = 0;
363 int n;
364 int x,y;
365 Bool fWarp = False;
366 Bool fPointer = False;
367 int dx;
368 int dy;
370 if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_RESIZE, ButtonPress))
371 return;
372 if (tmp_win == NULL || IS_ICONIFIED(tmp_win))
373 return;
374 if (IS_FIXED(tmp_win))
375 return;
376 if(check_if_function_allowed(F_RESIZE,tmp_win,True,NULL) == 0)
378 XBell(dpy, 0);
379 return;
382 /* gotta have a window */
383 w = tmp_win->frame;
384 if (!XGetGeometry(dpy, w, &JunkRoot, &x, &y, (unsigned int *)&FinalW,
385 (unsigned int *)&FinalH, &JunkBW, &JunkDepth))
387 XBell(dpy, 0);
388 return;
391 FinalX = x;
392 FinalY = y;
394 n = GetResizeMoveArguments(
395 &action,
396 tmp_win->hints.base_width, tmp_win->hints.base_height,
397 tmp_win->hints.width_inc, tmp_win->hints.height_inc,
398 tmp_win->boundary_width, tmp_win->title_g.height,
399 &FinalX, &FinalY, &FinalW, &FinalH, &fWarp, &fPointer);
400 if (n < 4)
401 return;
403 if (IS_MAXIMIZED(tmp_win))
405 /* must redraw the buttons now so that the 'maximize' button does not stay
406 * depressed. */
407 SET_MAXIMIZED(tmp_win, 0);
408 DrawDecorations(
409 tmp_win, DRAW_BUTTONS, (tmp_win == Scr.Hilite), True, None);
411 dx = FinalX - tmp_win->frame_g.x;
412 dy = FinalY - tmp_win->frame_g.y;
413 /* size will be less or equal to requested */
414 constrain_size(tmp_win, (unsigned int *)&FinalW, (unsigned int *)&FinalH,
415 0, 0, False);
416 if (IS_SHADED(tmp_win))
418 SetupFrame(tmp_win, FinalX, FinalY, FinalW, tmp_win->frame_g.height, False);
420 else
422 SetupFrame(tmp_win, FinalX, FinalY, FinalW, FinalH, True);
424 if (fWarp)
425 XWarpPointer(dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y);
426 if (IS_MAXIMIZED(tmp_win))
428 tmp_win->max_g.x += dx;
429 tmp_win->max_g.y += dy;
431 else
433 tmp_win->normal_g.x += dx;
434 tmp_win->normal_g.y += dy;
436 DrawDecorations(tmp_win, DRAW_ALL, True, True, None);
437 update_absolute_geometry(tmp_win);
438 maximize_adjust_offset(tmp_win);
439 XSync(dpy, 0);
440 GNOME_SetWinArea(tmp_win);
442 return;
446 static void InteractiveMove(
447 Window *win, FvwmWindow *tmp_win, int *FinalX, int *FinalY, XEvent *eventp,
448 Bool do_start_at_pointer)
450 int origDragX,origDragY,DragX, DragY, DragWidth, DragHeight;
451 int XOffset, YOffset;
452 Window w;
453 Bool do_move_opaque = False;
455 w = *win;
457 if (Scr.bo.InstallRootCmap)
458 InstallRootColormap();
459 else
460 InstallFvwmColormap();
461 /* warp the pointer to the cursor position from before menu appeared*/
462 /* domivogt (17-May-1999): an XFlush should not hurt anyway, so do it
463 * unconditionally to remove the external */
464 XFlush(dpy);
466 if (do_start_at_pointer)
468 XQueryPointer(
469 dpy, Scr.Root, &JunkRoot, &JunkChild, &DragX, &DragY, &JunkX, &JunkY,
470 &JunkMask);
472 else
474 /* Although a move is usually done with a button depressed we have to check
475 * for ButtonRelease too since the event may be faked. */
476 GetLocationFromEventOrQuery(dpy, Scr.Root, &Event, &DragX, &DragY);
479 if(!GrabEm(CRS_MOVE, GRAB_NORMAL))
481 XBell(dpy, 0);
482 return;
485 if (!XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY,
486 (unsigned int *)&DragWidth, (unsigned int *)&DragHeight,
487 &JunkBW, &JunkDepth))
489 UngrabEm(GRAB_NORMAL);
490 return;
492 if (do_start_at_pointer)
494 origDragX = DragX;
495 origDragY = DragY;
498 if(IS_MAPPED(tmp_win) && DragWidth*DragHeight <
499 (Scr.OpaqueSize*Scr.MyDisplayWidth*Scr.MyDisplayHeight)/100)
500 do_move_opaque = True;
501 else if (IS_ICONIFIED(tmp_win))
502 do_move_opaque = True;
503 else
505 Scr.flags.is_wire_frame_displayed = True;
506 MyXGrabServer(dpy);
509 if((!do_move_opaque)&&(IS_ICONIFIED(tmp_win)))
510 XUnmapWindow(dpy,w);
512 XOffset = origDragX - DragX;
513 YOffset = origDragY - DragY;
514 if (!Scr.gs.do_hide_position_window)
515 XMapRaised(dpy,Scr.SizeWindow);
516 moveLoop(tmp_win, XOffset,YOffset,DragWidth,DragHeight, FinalX,FinalY,
517 do_move_opaque);
518 if (Scr.bo.InstallRootCmap)
519 UninstallRootColormap();
520 else
521 UninstallFvwmColormap();
523 if(!do_move_opaque)
525 XEvent event;
526 /* Throw away some events that dont interest us right now. */
527 while (XCheckMaskEvent(dpy, EnterWindowMask|LeaveWindowMask,
528 &event) != False)
530 Scr.flags.is_wire_frame_displayed = False;
531 MyXUngrabServer(dpy);
533 UngrabEm(GRAB_NORMAL);
537 /* Perform the movement of the window. ppctMovement *must* have a 1.0 entry
538 * somewhere in ins list of floats, and movement will stop when it hits a 1.0
539 * entry */
540 static void AnimatedMoveAnyWindow(FvwmWindow *tmp_win, Window w, int startX,
541 int startY, int endX, int endY,
542 Bool fWarpPointerToo, int cmsDelay,
543 float *ppctMovement )
545 int pointerX, pointerY;
546 int currentX, currentY;
547 int lastX, lastY;
548 int deltaX, deltaY;
550 if (tmp_win && IS_FIXED(tmp_win))
551 return;
553 /* set our defaults */
554 if (ppctMovement == NULL)
555 ppctMovement = rgpctMovementDefault;
556 if (cmsDelay < 0)
557 cmsDelay = cmsDelayDefault;
559 if (startX < 0 || startY < 0)
561 if (!XGetGeometry(dpy, w, &JunkRoot, &currentX, &currentY,
562 &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth))
564 XBell(dpy, 0);
565 return;
567 if (startX < 0)
568 startX = currentX;
569 if (startY < 0)
570 startY = currentY;
573 deltaX = endX - startX;
574 deltaY = endY - startY;
575 lastX = startX;
576 lastY = startY;
578 if (deltaX == 0 && deltaY == 0)
579 /* go nowhere fast */
580 return;
582 /* Needed for aborting */
583 XGrabKeyboard(dpy, Scr.Root, False, GrabModeAsync, GrabModeAsync,
584 CurrentTime);
587 currentX = startX + deltaX * (*ppctMovement);
588 currentY = startY + deltaY * (*ppctMovement);
589 if (lastX == currentX && lastY == currentY)
590 /* don't waste time in the same spot */
591 continue;
592 XMoveWindow(dpy,w,currentX,currentY);
593 if (fWarpPointerToo == True)
595 XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild,
596 &JunkX,&JunkY,&pointerX,&pointerY,&JunkMask);
597 pointerX += currentX - lastX;
598 pointerY += currentY - lastY;
599 XWarpPointer(dpy,None,Scr.Root,0,0,0,0,
600 pointerX,pointerY);
602 if (tmp_win && !IS_SHADED(tmp_win))
604 /* send configure notify event for windows that care about their
605 * location */
606 SendConfigureNotify(
607 tmp_win, currentX, currentY, tmp_win->frame_g.width,
608 tmp_win->frame_g.height, 0, False);
609 #ifdef FVWM_DEBUG_MSGS
610 fvwm_msg(DBG,"AnimatedMoveAnyWindow",
611 "Sent ConfigureNotify (w == %d, h == %d)",
612 tmp_win->frame_g.width, tmp_win->frame_g.height);
613 #endif
615 XFlush(dpy);
616 if (tmp_win)
618 tmp_win->frame_g.x = currentX;
619 tmp_win->frame_g.y = currentY;
620 update_absolute_geometry(tmp_win);
621 maximize_adjust_offset(tmp_win);
622 BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win);
623 FlushAllMessageQueues();
626 usleep(cmsDelay * 1000); /* usleep takes microseconds */
627 /* this didn't work for me -- maybe no longer necessary since
628 * we warn the user when they use > .5 seconds as a between-frame delay
629 * time.
631 * domivogt (28-apr-1999): That is because the keyboard was not grabbed.
632 * works nicely now.
634 if (XCheckMaskEvent(dpy, ButtonPressMask|ButtonReleaseMask|KeyPressMask,
635 &Event))
637 StashEventTime(&Event);
638 /* finish the move immediately */
639 XMoveWindow(dpy,w,endX,endY);
640 break;
642 lastX = currentX;
643 lastY = currentY;
645 while
646 (*ppctMovement != 1.0 && ppctMovement++);
647 XUngrabKeyboard(dpy, CurrentTime);
648 XFlush(dpy);
649 if (tmp_win)
650 GNOME_SetWinArea(tmp_win);
651 return;
654 /* used for moving menus, not a client window */
655 void AnimatedMoveOfWindow(Window w, int startX, int startY,
656 int endX, int endY, Bool fWarpPointerToo,
657 int cmsDelay, float *ppctMovement)
659 AnimatedMoveAnyWindow(NULL, w, startX, startY, endX, endY, fWarpPointerToo,
660 cmsDelay, ppctMovement);
663 /* used for moving client windows */
664 void AnimatedMoveFvwmWindow(FvwmWindow *tmp_win, Window w, int startX,
665 int startY, int endX, int endY,
666 Bool fWarpPointerToo, int cmsDelay,
667 float *ppctMovement)
669 AnimatedMoveAnyWindow(tmp_win, w, startX, startY, endX, endY,
670 fWarpPointerToo, cmsDelay, ppctMovement);
674 /****************************************************************************
676 * Start a window move operation
678 ****************************************************************************/
679 void move_window_doit(F_CMD_ARGS, Bool do_animate, Bool do_move_to_page)
681 int FinalX = 0;
682 int FinalY = 0;
683 int n;
684 int x,y;
685 unsigned int width, height;
686 int page_x, page_y;
687 Bool fWarp = False;
688 Bool fPointer = False;
689 int dx;
690 int dy;
692 if (DeferExecution(eventp,&w,&tmp_win,&context,
693 (do_move_to_page) ? CRS_SELECT : CRS_MOVE, ButtonPress))
694 return;
696 if (tmp_win == NULL)
697 return;
699 if (IS_FIXED(tmp_win))
700 return;
702 /* gotta have a window */
703 w = tmp_win->frame;
704 if(IS_ICONIFIED(tmp_win))
706 if(tmp_win->icon_pixmap_w != None)
708 w = tmp_win->icon_pixmap_w;
709 XUnmapWindow(dpy,tmp_win->icon_w);
711 else
713 w = tmp_win->icon_w;
716 if (!XGetGeometry(dpy, w, &JunkRoot, &x, &y, &width, &height,
717 &JunkBW, &JunkDepth))
719 return;
722 if (do_move_to_page)
724 rectangle r;
725 rectangle s;
727 do_animate = False;
728 SET_STICKY(tmp_win, 0);
730 if (!get_page_arguments(action, &page_x, &page_y))
732 page_x = Scr.Vx;
733 page_y = Scr.Vy;
735 #if 1
736 r.x = x;
737 r.y = y;
738 r.width = width;
739 r.height = height;
740 s.x = page_x - Scr.Vx;
741 s.y = page_y - Scr.Vy;
742 s.width = Scr.MyDisplayWidth;
743 s.height = Scr.MyDisplayHeight;
744 move_into_rectangle(&r, &s);
745 FinalX = r.x;
746 FinalY = r.y;
747 #else
748 if (!IntersectsInterval(x, width, page_x - Scr.Vx, Scr.MyDisplayWidth))
750 FinalX = x % Scr.MyDisplayWidth;
751 if(FinalX < 0)
752 FinalX += Scr.MyDisplayWidth;
753 FinalX += page_x - Scr.Vx;
755 else
757 FinalX = x;
759 if (!IntersectsInterval(y, height, page_y - Scr.Vy, Scr.MyDisplayHeight))
761 FinalY = y % Scr.MyDisplayHeight;
762 if(FinalY < 0)
763 FinalY += Scr.MyDisplayHeight;
764 FinalY += page_y - Scr.Vy;
766 else
768 FinalY = y;
770 #endif
772 else
774 FinalX = x;
775 FinalY = y;
776 n = GetMoveArguments(
777 &action, width, height, &FinalX, &FinalY, &fWarp, &fPointer);
779 if (n != 2 || fPointer)
780 InteractiveMove(&w, tmp_win, &FinalX, &FinalY, eventp, fPointer);
783 if (w == tmp_win->frame)
785 dx = FinalX - tmp_win->frame_g.x;
786 dy = FinalY - tmp_win->frame_g.y;
787 if (do_animate)
789 AnimatedMoveFvwmWindow(tmp_win,w,-1,-1,FinalX,FinalY,fWarp,-1,NULL);
791 SetupFrame(tmp_win, FinalX, FinalY,
792 tmp_win->frame_g.width, tmp_win->frame_g.height, True);
793 if (fWarp & !do_animate)
794 XWarpPointer(dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y);
795 if (IS_MAXIMIZED(tmp_win))
797 tmp_win->max_g.x += dx;
798 tmp_win->max_g.y += dy;
800 else
802 tmp_win->normal_g.x += dx;
803 tmp_win->normal_g.y += dy;
805 update_absolute_geometry(tmp_win);
806 maximize_adjust_offset(tmp_win);
807 XSync(dpy, 0);
808 GNOME_SetWinArea(tmp_win);
810 else /* icon window */
812 tmp_win->icon_g.x = FinalX;
813 tmp_win->icon_xl_loc = FinalX -
814 (tmp_win->icon_g.width - tmp_win->icon_p_width)/2;
815 tmp_win->icon_g.y = FinalY;
816 BroadcastPacket(M_ICON_LOCATION, 7,
817 tmp_win->w, tmp_win->frame,
818 (unsigned long)tmp_win,
819 tmp_win->icon_g.x, tmp_win->icon_g.y,
820 tmp_win->icon_p_width,
821 tmp_win->icon_g.height + tmp_win->icon_p_height);
822 if (do_animate)
824 AnimatedMoveOfWindow(tmp_win->icon_w,-1,-1,tmp_win->icon_xl_loc,
825 FinalY+tmp_win->icon_p_height, fWarp,-1,NULL);
827 else
829 XMoveWindow(dpy,tmp_win->icon_w, tmp_win->icon_xl_loc,
830 FinalY+tmp_win->icon_p_height);
831 if (fWarp)
832 XWarpPointer(dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y);
834 if(tmp_win->icon_pixmap_w != None)
836 XMapWindow(dpy,tmp_win->icon_w);
837 if (do_animate)
839 AnimatedMoveOfWindow(tmp_win->icon_pixmap_w, -1,-1,
840 tmp_win->icon_g.x,FinalY,fWarp,-1,NULL);
842 else
844 XMoveWindow(dpy, tmp_win->icon_pixmap_w, tmp_win->icon_g.x, FinalY);
845 if (fWarp)
846 XWarpPointer(dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y);
848 XMapWindow(dpy,w);
850 XSync(dpy, 0);
852 return;
855 void move_window(F_CMD_ARGS)
857 move_window_doit(F_PASS_ARGS, False, False);
860 void animated_move_window(F_CMD_ARGS)
862 move_window_doit(F_PASS_ARGS, True, False);
865 void move_window_to_page(F_CMD_ARGS)
867 move_window_doit(F_PASS_ARGS, False, True);
870 /* This function does the SnapAttraction stuff. If takes x and y coordinates
871 * (*px and *py) and returns the snapped values. */
872 static void DoSnapAttract(
873 FvwmWindow *tmp_win, unsigned int Width, unsigned int Height,
874 int *px, int *py)
876 int nyt,nxl,dist,closestLeft,closestRight,closestBottom,closestTop;
877 rectangle self, other;
878 FvwmWindow *tmp;
880 /* resist based on window edges */
881 tmp = Scr.FvwmRoot.next;
882 closestTop = Scr.SnapAttraction;
883 closestBottom = Scr.SnapAttraction;
884 closestRight = Scr.SnapAttraction;
885 closestLeft = Scr.SnapAttraction;
886 nxl = -1;
887 nyt = -1;
888 self.x = *px;
889 self.y = *py;
890 self.width = Width;
891 self.height = Height;
892 if (IS_ICONIFIED(tmp_win) && !HAS_NO_ICON_TITLE(tmp_win))
894 self.height += tmp_win->icon_g.height;
896 while(Scr.SnapAttraction >= 0 && tmp)
898 switch (Scr.SnapMode)
900 case 1: /* SameType */
901 if( IS_ICONIFIED(tmp) != IS_ICONIFIED(tmp_win))
903 tmp = tmp->next;
904 continue;
906 break;
907 case 2: /* Icons */
908 if( !IS_ICONIFIED(tmp) || !IS_ICONIFIED(tmp_win))
910 tmp = tmp->next;
911 continue;
913 break;
914 case 3: /* Windows */
915 if( IS_ICONIFIED(tmp) || IS_ICONIFIED(tmp_win))
917 tmp = tmp->next;
918 continue;
920 break;
921 case 0: /* All */
922 default:
923 /* NOOP */
924 break;
926 if (tmp_win != tmp && (tmp_win->Desk == tmp->Desk))
928 if(IS_ICONIFIED(tmp))
930 if(tmp->icon_p_height > 0)
932 other.width = tmp->icon_p_width;
933 other.height = tmp->icon_p_height;
935 else
937 other.width = tmp->icon_g.width;
938 other.height = tmp->icon_g.height;
940 other.x = tmp->icon_g.x;
941 other.y = tmp->icon_g.y;
942 if (!HAS_NO_ICON_TITLE(tmp))
944 other.height += tmp->icon_g.height;
947 else
949 other.width = tmp->frame_g.width;
950 other.height = tmp->frame_g.height;
951 other.x = tmp->frame_g.x;
952 other.y = tmp->frame_g.y;
954 if(!((other.y + (int)other.height) < (*py) ||
955 (other.y) > (*py + (int)self.height) ))
957 dist = abs(other.x - (*px + (int)self.width));
958 if(dist < closestRight)
960 closestRight = dist;
961 if(((*px + (int)self.width) >= other.x)&&
962 ((*px + (int)self.width) < other.x+Scr.SnapAttraction))
964 nxl = other.x - (int)self.width;
966 if(((*px + (int)self.width) >= other.x - Scr.SnapAttraction)&&
967 ((*px + (int)self.width) < other.x))
969 nxl = other.x - (int)self.width;
972 dist = abs(other.x + (int)other.width - *px);
973 if(dist < closestLeft)
975 closestLeft = dist;
976 if((*px <= other.x + (int)other.width)&&
977 (*px > other.x + (int)other.width - Scr.SnapAttraction))
979 nxl = other.x + (int)other.width;
981 if((*px <= other.x + (int)other.width + Scr.SnapAttraction)&&
982 (*px > other.x + (int)other.width))
984 nxl = other.x + (int)other.width;
988 /* ScreenEdges - SJL */
989 if(!(Scr.MyDisplayHeight < (*py) ||
990 (*py + (int)self.height) < 0) && (Scr.SnapMode & 8))
992 dist = abs(Scr.MyDisplayWidth - (*px + (int)self.width));
994 if(dist < closestRight)
996 closestRight = dist;
998 if(((*px + (int)self.width) >= Scr.MyDisplayWidth)&&
999 ((*px + (int)self.width) < Scr.MyDisplayWidth +
1000 Scr.SnapAttraction))
1002 nxl = Scr.MyDisplayWidth - (int)self.width;
1005 if(((*px + (int)self.width) >= Scr.MyDisplayWidth -
1006 Scr.SnapAttraction)&&
1007 ((*px + (int)self.width) < Scr.MyDisplayWidth))
1009 nxl = Scr.MyDisplayWidth - (int)self.width;
1013 dist = abs(*px);
1015 if(dist < closestLeft)
1017 closestLeft = dist;
1019 if((*px <= 0)&&
1020 (*px > - Scr.SnapAttraction))
1022 nxl = 0;
1024 if((*px <= Scr.SnapAttraction)&&
1025 (*px > 0))
1027 nxl = 0;
1032 if(!((other.x + (int)other.width) < (*px) ||
1033 (other.x) > (*px + (int)self.width)))
1035 dist = abs(other.y - (*py + (int)self.height));
1036 if(dist < closestBottom)
1038 closestBottom = dist;
1039 if(((*py + (int)self.height) >= other.y)&&
1040 ((*py + (int)self.height) < other.y+Scr.SnapAttraction))
1042 nyt = other.y - (int)self.height;
1044 if(((*py + (int)self.height) >= other.y - Scr.SnapAttraction)&&
1045 ((*py + (int)self.height) < other.y))
1047 nyt = other.y - (int)self.height;
1050 dist = abs(other.y + (int)other.height - *py);
1051 if(dist < closestTop)
1053 closestTop = dist;
1054 if((*py <= other.y + (int)other.height)&&
1055 (*py > other.y + (int)other.height - Scr.SnapAttraction))
1057 nyt = other.y + (int)other.height;
1059 if((*py <= other.y + (int)other.height + Scr.SnapAttraction)&&
1060 (*py > other.y + (int)other.height))
1062 nyt = other.y + (int)other.height;
1066 /* ScreenEdges - SJL */
1067 if (!(Scr.MyDisplayWidth < (*px) || (*px + (int)self.width) < 0 )
1068 && (Scr.SnapMode & 8))
1070 dist = abs(Scr.MyDisplayHeight - (*py + (int)self.height));
1072 if(dist < closestBottom)
1074 closestBottom = dist;
1075 if(((*py + (int)self.height) >= Scr.MyDisplayHeight)&&
1076 ((*py + (int)self.height) < Scr.MyDisplayHeight +
1077 Scr.SnapAttraction))
1079 nyt = Scr.MyDisplayHeight - (int)self.height;
1081 if(((*py + (int)self.height) >= Scr.MyDisplayHeight -
1082 Scr.SnapAttraction)&&
1083 ((*py + (int)self.height) < Scr.MyDisplayHeight))
1085 nyt = Scr.MyDisplayHeight - (int)self.height;
1089 dist = abs(- *py);
1091 if(dist < closestTop)
1093 closestTop = dist;
1094 if((*py <= 0)&&(*py > - Scr.SnapAttraction))
1096 nyt = 0;
1098 if((*py <= Scr.SnapAttraction)&&(*py > 0))
1100 nyt = 0;
1106 tmp = tmp->next;
1108 /* Snap grid handling */
1109 if(nxl == -1)
1111 if(*px != *px / Scr.SnapGridX * Scr.SnapGridX)
1113 *px = (*px + ((*px >= 0) ? Scr.SnapGridX : -Scr.SnapGridX) / 2) /
1114 Scr.SnapGridX * Scr.SnapGridX;
1117 else
1119 *px = nxl;
1121 if(nyt == -1)
1123 if(*py != *py / Scr.SnapGridY * Scr.SnapGridY)
1125 *py = (*py + ((*py >= 0) ? Scr.SnapGridY : -Scr.SnapGridY) / 2) /
1126 Scr.SnapGridY * Scr.SnapGridY;
1129 else
1131 *py = nyt;
1133 /* Resist moving windows beyond the edge of the screen */
1134 if (((*px + Width) >= Scr.MyDisplayWidth)
1135 && ((*px + Width) < Scr.MyDisplayWidth + Scr.MoveResistance))
1136 *px = Scr.MyDisplayWidth - Width;
1137 if ((*px <= 0) && (*px > -Scr.MoveResistance))
1138 *px = 0;
1139 if (((*py + Height) >= Scr.MyDisplayHeight)
1140 && ((*py + Height) < Scr.MyDisplayHeight + Scr.MoveResistance))
1141 *py = Scr.MyDisplayHeight - Height;
1142 if ((*py <= 0) && (*py > -Scr.MoveResistance))
1143 *py = 0;
1146 /****************************************************************************
1148 * Move the rubberband around, return with the new window location
1150 * Returns True if the window has to be resized after the move.
1152 ****************************************************************************/
1153 Bool moveLoop(FvwmWindow *tmp_win, int XOffset, int YOffset, int Width,
1154 int Height, int *FinalX, int *FinalY,Bool do_move_opaque)
1156 extern Window bad_window;
1157 Bool finished = False;
1158 Bool done;
1159 Bool aborted = False;
1160 int xl,xl2,yt,yt2,delta_x,delta_y,paged;
1161 unsigned int button_mask = 0;
1162 FvwmWindow tmp_win_copy;
1163 int dx = Scr.EdgeScrollX ? Scr.EdgeScrollX : Scr.MyDisplayWidth;
1164 int dy = Scr.EdgeScrollY ? Scr.EdgeScrollY : Scr.MyDisplayHeight;
1165 int vx = Scr.Vx;
1166 int vy = Scr.Vy;
1167 int xl_orig = 0;
1168 int yt_orig = 0;
1169 int cnx = 0;
1170 int cny = 0;
1171 Bool sent_cn = False;
1172 Bool do_resize_too = False;
1173 Bool do_exec_placement_func = False;
1174 int x_bak;
1175 int y_bak;
1176 Window move_w = None;
1177 int orig_icon_x = 0;
1178 int orig_icon_y = 0;
1180 if (!IS_MAPPED(tmp_win) && !IS_ICONIFIED(tmp_win))
1181 do_move_opaque = False;
1183 bad_window = None;
1184 if (IS_ICONIFIED(tmp_win))
1186 if(tmp_win->icon_pixmap_w != None)
1187 move_w = tmp_win->icon_pixmap_w;
1188 else if (tmp_win->icon_w != None)
1189 move_w = tmp_win->icon_w;
1191 else
1193 move_w = tmp_win->frame;
1195 if (!XGetGeometry(dpy, move_w, &JunkRoot, &x_bak, &y_bak,
1196 &JunkWidth, &JunkHeight, &JunkBW,&JunkDepth))
1198 /* This is allright here since the window may not be mapped yet. */
1201 if (IS_ICONIFIED(tmp_win))
1203 orig_icon_x = tmp_win->icon_g.x;
1204 orig_icon_y = tmp_win->icon_g.y;
1207 /* make a copy of the tmp_win structure for sending to the pager */
1208 memcpy(&tmp_win_copy, tmp_win, sizeof(FvwmWindow));
1209 /* prevent flicker when paging */
1210 SET_WINDOW_BEING_MOVED_OPAQUE(tmp_win, do_move_opaque);
1212 XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild,&xl, &yt,
1213 &JunkX, &JunkY, &button_mask);
1214 button_mask &= DEFAULT_ALL_BUTTONS_MASK;
1215 xl += XOffset;
1216 yt += YOffset;
1217 xl_orig = xl;
1218 yt_orig = yt;
1220 /* draw initial outline */
1221 if (!IS_ICONIFIED(tmp_win) &&
1222 ((!do_move_opaque && !Scr.gs.EmulateMWM) || !IS_MAPPED(tmp_win)))
1223 MoveOutline(xl, yt, Width - 1, Height - 1);
1225 DisplayPosition(tmp_win,xl,yt,True);
1227 while (!finished && bad_window != tmp_win->w)
1229 /* wait until there is an interesting event */
1230 while (!XPending(dpy) ||
1231 !XCheckMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
1232 KeyPressMask | PointerMotionMask |
1233 ButtonMotionMask | ExposureMask, &Event))
1235 if (HandlePaging(dx, dy, &xl,&yt, &delta_x,&delta_y, False, False, True))
1237 /* Fake an event to force window reposition */
1238 xl += XOffset;
1239 yt += YOffset;
1240 DoSnapAttract(tmp_win, Width, Height, &xl, &yt);
1241 Event.type = MotionNotify;
1242 Event.xmotion.time = lastTimestamp;
1243 Event.xmotion.x_root = xl - XOffset;
1244 Event.xmotion.y_root = yt - YOffset;
1245 break;
1248 StashEventTime(&Event);
1250 /* discard any extra motion events before a logical release */
1251 if (Event.type == MotionNotify)
1253 XEvent new_event;
1255 /*** logic borrowed from icewm ***/
1256 while (XPending(dpy) > 0 &&
1257 XCheckMaskEvent(dpy, ButtonMotionMask | PointerMotionMask |
1258 ButtonPressMask | ButtonRelease | KeyPressMask,
1259 &new_event))
1261 if (Event.type != new_event.type)
1263 XPutBackEvent(dpy, &new_event);
1264 break;
1266 else
1268 Event = new_event;
1271 /*** end of code borrowed from icewm ***/
1272 StashEventTime(&Event);
1274 } /* if (Event.type == MotionNotify) */
1276 done = False;
1277 /* Handle a limited number of key press events to allow mouseless
1278 * operation */
1279 if (Event.type == KeyPress)
1280 Keyboard_shortcuts(&Event, tmp_win, ButtonRelease);
1281 switch(Event.type)
1283 case KeyPress:
1284 /* simple code to bag out of move - CKH */
1285 if (XLookupKeysym(&(Event.xkey),0) == XK_Escape)
1287 if(!do_move_opaque)
1288 MoveOutline(0, 0, 0, 0);
1289 if (!IS_ICONIFIED(tmp_win))
1291 *FinalX = tmp_win->frame_g.x;
1292 *FinalY = tmp_win->frame_g.y;
1294 else
1296 *FinalX = orig_icon_x;
1297 *FinalY = orig_icon_y;
1299 aborted = True;
1300 finished = True;
1302 done = True;
1303 break;
1304 case ButtonPress:
1305 XAllowEvents(dpy,ReplayPointer,CurrentTime);
1306 if (Event.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
1307 ((Button1Mask << (Event.xbutton.button - 1)) & button_mask))
1309 /* No new button was pressed, just a delayed event */
1310 done = True;
1311 break;
1313 if(((Event.xbutton.button == 2)&&(!Scr.gs.EmulateMWM))||
1314 ((Event.xbutton.button == 1)&&(Scr.gs.EmulateMWM)&&
1315 (Event.xbutton.state & ShiftMask)))
1317 do_resize_too = True;
1318 do_exec_placement_func = False;
1319 /* Fallthrough to button-release */
1321 else if(Event.xbutton.button == 3)
1323 do_exec_placement_func = True;
1324 do_resize_too = False;
1325 /* Fallthrough to button-release */
1327 else
1329 /* Abort the move if
1330 * - the move started with a pressed button and another button
1331 * was pressed during the operation
1332 * - no button was pressed at the beginning and any button
1333 * except button 1 was pressed. */
1334 if (button_mask || (Event.xbutton.button != 1))
1336 if(!do_move_opaque)
1337 MoveOutline(0, 0, 0, 0);
1338 if (!IS_ICONIFIED(tmp_win))
1340 *FinalX = tmp_win->frame_g.x;
1341 *FinalY = tmp_win->frame_g.y;
1343 else
1345 *FinalX = orig_icon_x;
1346 *FinalY = orig_icon_y;
1348 aborted = True;
1349 finished = True;
1351 done = True;
1352 break;
1354 case ButtonRelease:
1355 if(!do_move_opaque)
1356 MoveOutline(0, 0, 0, 0);
1357 xl2 = Event.xbutton.x_root + XOffset;
1358 yt2 = Event.xbutton.y_root + YOffset;
1359 /* ignore the position of the button release if it was on a
1360 * different page. */
1361 if (!(((xl < 0 && xl2 >= 0) || (xl >= 0 && xl2 < 0) ||
1362 (yt < 0 && yt2 >= 0) || (yt >= 0 && yt2 < 0)) &&
1363 (abs(xl - xl2) > Scr.MyDisplayWidth / 2 ||
1364 abs(yt - yt2) > Scr.MyDisplayHeight / 2)))
1366 xl = xl2;
1367 yt = yt2;
1369 if (xl != xl_orig || yt != yt_orig || vx != Scr.Vx || vy != Scr.Vy)
1371 /* only snap if the window actually moved! */
1372 DoSnapAttract(tmp_win, Width, Height, &xl, &yt);
1375 *FinalX = xl;
1376 *FinalY = yt;
1378 done = True;
1379 finished = True;
1380 break;
1382 case MotionNotify:
1383 xl = Event.xmotion.x_root + XOffset;
1384 yt = Event.xmotion.y_root + YOffset;
1386 DoSnapAttract(tmp_win, Width, Height, &xl, &yt);
1388 /* check Paging request once and only once after outline redrawn */
1389 /* redraw after paging if needed - mab */
1390 paged=0;
1391 while(paged<=1)
1393 if(!do_move_opaque)
1394 MoveOutline(xl, yt, Width - 1, Height - 1);
1395 else
1397 if (IS_ICONIFIED(tmp_win))
1399 tmp_win->icon_g.x = xl ;
1400 tmp_win->icon_xl_loc = xl -
1401 (tmp_win->icon_g.width - tmp_win->icon_p_width)/2;
1402 tmp_win->icon_g.y = yt;
1403 if(tmp_win->icon_pixmap_w != None)
1404 XMoveWindow(dpy, tmp_win->icon_pixmap_w, tmp_win->icon_g.x,yt);
1405 else if (tmp_win->icon_w != None)
1406 XMoveWindow(dpy, tmp_win->icon_w,tmp_win->icon_xl_loc,
1407 yt+tmp_win->icon_p_height);
1409 else
1411 XMoveWindow(dpy,tmp_win->frame,xl,yt);
1414 DisplayPosition(tmp_win,xl,yt,False);
1416 /* prevent window from lagging behind mouse when paging - mab */
1417 if(paged==0)
1419 xl = Event.xmotion.x_root;
1420 yt = Event.xmotion.y_root;
1421 HandlePaging(
1422 dx, dy, &xl, &yt, &delta_x, &delta_y, False, False, False);
1423 xl += XOffset;
1424 yt += YOffset;
1425 DoSnapAttract(tmp_win, Width, Height, &xl, &yt);
1426 if (!delta_x && !delta_y)
1427 /* break from while (paged)*/
1428 break;
1430 paged++;
1431 } /* end while (paged) */
1433 done = True;
1434 break;
1436 default:
1437 break;
1438 } /* switch */
1439 if(!done)
1441 if (!do_move_opaque)
1442 /* must undraw the rubber band in case the event causes some drawing */
1443 MoveOutline(0,0,0,0);
1444 DispatchEvent(False);
1445 if(!do_move_opaque)
1446 MoveOutline(xl, yt, Width - 1, Height - 1);
1448 if (do_move_opaque && !IS_ICONIFIED(tmp_win) && !IS_SHADED(tmp_win))
1450 /* send configure notify event for windows that care about their
1451 * location; don't send anything if position didn't change */
1452 if (!sent_cn || cnx != xl || cny != yt)
1454 cnx = xl;
1455 cny = yt;
1456 sent_cn = True;
1457 SendConfigureNotify(tmp_win, xl, yt, Width, Height, 0, False);
1458 #ifdef FVWM_DEBUG_MSGS
1459 fvwm_msg(DBG,"SetupFrame","Sent ConfigureNotify (w == %d, h == %d)",
1460 Width,
1461 Height);
1462 #endif
1465 if (do_move_opaque)
1467 if (!IS_ICONIFIED(tmp_win))
1469 tmp_win_copy.frame_g.x = xl;
1470 tmp_win_copy.frame_g.y = yt;
1472 /* only do this with opaque moves, (i.e. the server is not grabbed) */
1473 BroadcastConfig(M_CONFIGURE_WINDOW, &tmp_win_copy);
1474 FlushAllMessageQueues();
1476 } /* while (!finished) */
1478 if (!Scr.gs.do_hide_position_window)
1479 XUnmapWindow(dpy,Scr.SizeWindow);
1480 if (aborted || bad_window == tmp_win->w)
1482 if (vx != Scr.Vx || vy != Scr.Vy)
1484 MoveViewport(vx, vy, False);
1486 if (aborted && do_move_opaque)
1488 XMoveWindow(dpy, move_w, x_bak, y_bak);
1490 if (bad_window == tmp_win->w)
1492 XUnmapWindow(dpy, move_w);
1493 XBell(dpy, 0);
1496 if (!aborted && bad_window != tmp_win->w && IS_ICONIFIED(tmp_win))
1498 SET_ICON_MOVED(tmp_win, 1);
1500 if (!do_resize_too)
1502 /* Don't wait for buttons to come up when user is placing a new window
1503 * and wants to resize it. */
1504 WaitForButtonsUp(True);
1506 SET_WINDOW_BEING_MOVED_OPAQUE(tmp_win, 0);
1507 bad_window = None;
1509 return do_resize_too;
1512 /***********************************************************************
1514 * Procedure:
1515 * DisplayPosition - display the position in the dimensions window
1517 * Inputs:
1518 * tmp_win - the current fvwm window
1519 * x, y - position of the window
1521 ************************************************************************/
1523 static void DisplayPosition(FvwmWindow *tmp_win, int x, int y,int Init)
1525 char str [100];
1526 int offset;
1528 if (Scr.gs.do_hide_position_window)
1529 return;
1530 (void) sprintf (str, " %+-4d %+-4d ", x, y);
1531 if(Init)
1533 XClearWindow(dpy,Scr.SizeWindow);
1535 else
1537 /* just clear indside the relief lines to reduce flicker */
1538 XClearArea(dpy,Scr.SizeWindow,2,2,
1539 Scr.SizeStringWidth + SIZE_HINDENT*2 - 3,
1540 Scr.DefaultFont.height + SIZE_VINDENT*2 - 3,False);
1543 if(Pdepth >= 2)
1544 RelieveRectangle(dpy,Scr.SizeWindow,0,0,
1545 Scr.SizeStringWidth+ SIZE_HINDENT*2 - 1,
1546 Scr.DefaultFont.height + SIZE_VINDENT*2 - 1,
1547 Scr.StdReliefGC,
1548 Scr.StdShadowGC, 2);
1549 offset = (Scr.SizeStringWidth + SIZE_HINDENT*2
1550 - XTextWidth(Scr.DefaultFont.font,str,strlen(str)))/2;
1551 #ifdef I18N_MB
1552 XmbDrawString (dpy, Scr.SizeWindow, Scr.DefaultFont.fontset, Scr.StdGC,
1553 #else
1554 XDrawString (dpy, Scr.SizeWindow, Scr.StdGC,
1555 #endif
1556 offset,
1557 Scr.DefaultFont.font->ascent + SIZE_VINDENT,
1558 str, strlen(str));
1562 void SetMoveThreshold(F_CMD_ARGS)
1564 int val = 0;
1566 if (GetIntegerArguments(action, NULL, &val, 1) < 1 || val < 0)
1567 Scr.MoveThreshold = DEFAULT_MOVE_THRESHOLD;
1568 else
1569 Scr.MoveThreshold = val;
1573 void SetOpaque(F_CMD_ARGS)
1575 int val;
1577 if(GetIntegerArguments(action, NULL, &val, 1) < 1 || val < 0)
1578 Scr.OpaqueSize = DEFAULT_OPAQUE_MOVE_SIZE;
1579 else
1580 Scr.OpaqueSize = val;
1584 static char *hide_options[] =
1586 "never",
1587 "move",
1588 "resize",
1589 NULL
1592 void HideGeometryWindow(F_CMD_ARGS)
1594 char *token = PeekToken(action, NULL);
1596 Scr.gs.do_hide_position_window = 0;
1597 Scr.gs.do_hide_resize_window = 0;
1598 switch(GetTokenIndex(token, hide_options, 0, NULL))
1600 case 0:
1601 break;
1602 case 1:
1603 Scr.gs.do_hide_position_window = 1;
1604 break;
1605 case 2:
1606 Scr.gs.do_hide_resize_window = 1;
1607 break;
1608 default:
1609 Scr.gs.do_hide_position_window = 1;
1610 Scr.gs.do_hide_resize_window = 1;
1611 break;
1613 return;
1617 void SetSnapAttraction(F_CMD_ARGS)
1619 int val;
1620 char *token;
1622 if(GetIntegerArguments(action, &action, &val, 1) != 1)
1624 Scr.SnapAttraction = DEFAULT_SNAP_ATTRACTION;
1625 Scr.SnapMode = DEFAULT_SNAP_ATTRACTION_MODE;
1626 return;
1628 Scr.SnapAttraction = val;
1629 if (val < 0)
1631 Scr.SnapAttraction = DEFAULT_SNAP_ATTRACTION;
1634 action = GetNextToken(action, &token);
1635 if (token == NULL)
1637 return;
1640 Scr.SnapMode = -1;
1641 if (StrEquals(token,"All"))
1643 Scr.SnapMode = 0;
1645 else if (StrEquals(token,"SameType"))
1647 Scr.SnapMode = 1;
1649 else if (StrEquals(token,"Icons"))
1651 Scr.SnapMode = 2;
1653 else if (StrEquals(token,"Windows"))
1655 Scr.SnapMode = 3;
1658 if (Scr.SnapMode != -1)
1660 free(token);
1661 action = GetNextToken(action, &token);
1662 if (token == NULL)
1664 return;
1667 else
1669 Scr.SnapMode = DEFAULT_SNAP_ATTRACTION_MODE;
1672 if (StrEquals(token,"Screen"))
1674 Scr.SnapMode += 8;
1676 else
1678 fvwm_msg(ERR,"SetSnapAttraction", "Invalid argument: %s", token);
1681 free(token);
1684 void SetSnapGrid(F_CMD_ARGS)
1686 int val[2];
1688 if(GetIntegerArguments(action, NULL, &val[0], 2) != 2)
1690 Scr.SnapGridX = DEFAULT_SNAP_GRID_X;
1691 Scr.SnapGridY = DEFAULT_SNAP_GRID_Y;
1692 return;
1695 Scr.SnapGridX = val[0];
1696 if(Scr.SnapGridX < 1)
1698 Scr.SnapGridX = DEFAULT_SNAP_GRID_X;
1700 Scr.SnapGridY = val[1];
1701 if(Scr.SnapGridY < 1)
1703 Scr.SnapGridY = DEFAULT_SNAP_GRID_Y;
1707 static Pixmap XorPixmap = None;
1709 void SetXOR(F_CMD_ARGS)
1711 int val;
1712 XGCValues gcv;
1713 unsigned long gcm;
1715 if(GetIntegerArguments(action, NULL, &val, 1) != 1)
1717 val = 0;
1720 gcm = GCFunction|GCLineWidth|GCForeground|GCFillStyle|GCSubwindowMode;
1721 gcv.subwindow_mode = IncludeInferiors;
1722 gcv.function = GXxor;
1723 gcv.line_width = 0;
1724 /* use passed in value, or try to calculate appropriate value if 0 */
1725 /* ctwm method: */
1727 gcv.foreground = (val1)?(val1):((((unsigned long) 1) << Scr.d_depth) - 1);
1729 /* Xlib programming manual suggestion: */
1730 gcv.foreground = (val)?
1731 (val):(BlackPixel(dpy,Scr.screen) ^ WhitePixel(dpy,Scr.screen));
1732 gcv.fill_style = FillSolid;
1733 gcv.subwindow_mode = IncludeInferiors;
1735 /* modify XorGC, only create once */
1736 if (Scr.XorGC)
1737 XChangeGC(dpy, Scr.XorGC, gcm, &gcv);
1738 else
1739 Scr.XorGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);
1741 /* free up XorPixmap if neccesary */
1742 if (XorPixmap != None) {
1743 XFreePixmap(dpy, XorPixmap);
1744 XorPixmap = None;
1749 void SetXORPixmap(F_CMD_ARGS)
1751 char *PixmapName;
1752 Picture *xp;
1753 XGCValues gcv;
1754 unsigned long gcm;
1756 action = GetNextToken(action, &PixmapName);
1757 if(PixmapName == NULL)
1759 /* return to default value. */
1760 action = "0";
1761 SetXOR(F_PASS_ARGS);
1762 return;
1764 /* get the picture in the root visual, colorlimit is ignored because the
1765 * pixels will be freed */
1766 UseDefaultVisual();
1767 xp = GetPicture(dpy, Scr.Root, NULL, PixmapName, 0);
1768 if (xp == NULL) {
1769 fvwm_msg(ERR,"SetXORPixmap","Can't find pixmap %s", PixmapName);
1770 free(PixmapName);
1771 UseFvwmVisual();
1772 return;
1774 free(PixmapName);
1775 /* free up old pixmap */
1776 if (XorPixmap != None)
1777 XFreePixmap(dpy, XorPixmap);
1779 /* make a copy of the picture pixmap */
1780 XorPixmap = XCreatePixmap(dpy, Scr.Root, xp->width, xp->height, Pdepth);
1781 XCopyArea(dpy, xp->picture, XorPixmap, DefaultGC(dpy, Scr.screen), 0, 0,
1782 xp->width, xp->height, 0, 0);
1783 /* destroy picture and free colors */
1784 DestroyPicture(dpy, xp);
1785 UseFvwmVisual();
1787 /* create Graphics context */
1788 gcm = GCFunction|GCLineWidth|GCTile|GCFillStyle|GCSubwindowMode;
1789 gcv.subwindow_mode = IncludeInferiors;
1790 gcv.function = GXxor;
1791 /* line width of 1 is necessary for Exceed servers */
1792 gcv.line_width = 1;
1793 gcv.tile = XorPixmap;
1794 gcv.fill_style = FillTiled;
1795 gcv.subwindow_mode = IncludeInferiors;
1796 /* modify XorGC, only create once */
1797 if (Scr.XorGC)
1798 XChangeGC(dpy, Scr.XorGC, gcm, &gcv);
1799 else
1800 Scr.XorGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);
1804 /* ----------------------------- resizing code ----------------------------- */
1806 /***********************************************************************
1808 * window resizing borrowed from the "wm" window manager
1810 ***********************************************************************/
1812 /****************************************************************************
1814 * Starts a window resize operation
1816 ****************************************************************************/
1817 void resize_window(F_CMD_ARGS)
1819 extern Window bad_window;
1820 Bool finished = False, done = False, abort = False;
1821 Bool do_resize_opaque;
1822 int x,y,delta_x,delta_y,stashed_x,stashed_y;
1823 Window ResizeWindow;
1824 Bool fButtonAbort = False;
1825 Bool fForceRedraw = False;
1826 int n;
1827 unsigned int button_mask = 0;
1828 rectangle sdrag;
1829 rectangle sorig;
1830 rectangle *drag = &sdrag;
1831 rectangle *orig = &sorig;
1832 rectangle start_g;
1833 int ymotion = 0;
1834 int xmotion = 0;
1835 int was_maximized;
1836 unsigned edge_wrap_x;
1837 unsigned edge_wrap_y;
1838 int px;
1839 int py;
1840 int i;
1841 Bool called_from_title = False;
1843 bad_window = False;
1844 if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_RESIZE, ButtonPress))
1845 return;
1846 if (tmp_win == NULL || IS_ICONIFIED(tmp_win))
1847 return;
1849 ResizeWindow = tmp_win->frame;
1850 XQueryPointer( dpy, ResizeWindow, &JunkRoot, &JunkChild,
1851 &JunkX, &JunkY, &px, &py, &button_mask);
1852 button_mask &= DEFAULT_ALL_BUTTONS_MASK;
1854 if(check_if_function_allowed(F_RESIZE,tmp_win,True,NULL) == 0)
1856 XBell(dpy, 0);
1857 return;
1860 was_maximized = IS_MAXIMIZED(tmp_win);
1861 SET_MAXIMIZED(tmp_win, 0);
1862 if (was_maximized)
1864 /* must redraw the buttons now so that the 'maximize' button does not stay
1865 * depressed. */
1866 DrawDecorations(
1867 tmp_win, DRAW_BUTTONS, (tmp_win == Scr.Hilite), True, None);
1870 if (IS_SHADED(tmp_win) || !IS_MAPPED(tmp_win))
1871 do_resize_opaque = False;
1872 else
1873 do_resize_opaque = DO_RESIZE_OPAQUE(tmp_win);
1875 /* no suffix = % of screen, 'p' = pixels, 'c' = increment units */
1876 drag->width = tmp_win->frame_g.width;
1877 drag->height = tmp_win->frame_g.height;
1878 n = GetResizeArguments(
1879 &action, tmp_win->frame_g.x, tmp_win->frame_g.y,
1880 tmp_win->hints.base_width, tmp_win->hints.base_height,
1881 tmp_win->hints.width_inc, tmp_win->hints.height_inc,
1882 tmp_win->boundary_width, tmp_win->title_g.height,
1883 &(drag->width), &(drag->height));
1885 if (n == 2)
1887 /* size will be less or equal to requested */
1888 constrain_size(
1889 tmp_win, (unsigned int *)&drag->width, (unsigned int *)&drag->height,
1890 xmotion, ymotion, False);
1891 if (IS_SHADED(tmp_win))
1893 SetupFrame(tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y,
1894 drag->width, tmp_win->frame_g.height, False);
1896 else
1898 SetupFrame(tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y,
1899 drag->width, drag->height, False);
1901 DrawDecorations(tmp_win, DRAW_ALL, True, True, None);
1902 update_absolute_geometry(tmp_win);
1903 maximize_adjust_offset(tmp_win);
1904 GNOME_SetWinArea(tmp_win);
1905 ResizeWindow = None;
1906 return;
1909 if (Scr.bo.InstallRootCmap)
1910 InstallRootColormap();
1911 else
1912 InstallFvwmColormap();
1914 if(!GrabEm(CRS_RESIZE, GRAB_NORMAL))
1916 XBell(dpy, 0);
1917 return;
1920 if (!do_resize_opaque)
1922 Scr.flags.is_wire_frame_displayed = True;
1923 MyXGrabServer(dpy);
1926 /* handle problems with edge-wrapping while resizing */
1927 edge_wrap_x = Scr.flags.edge_wrap_x;
1928 edge_wrap_y = Scr.flags.edge_wrap_y;
1929 Scr.flags.edge_wrap_x = 0;
1930 Scr.flags.edge_wrap_y = 0;
1932 if (IS_SHADED(tmp_win))
1934 if (HAS_BOTTOM_TITLE(tmp_win))
1935 drag->height = tmp_win->frame_g.height;
1936 else
1938 if (was_maximized)
1939 drag->height = tmp_win->max_g.height;
1940 else
1941 drag->height = tmp_win->normal_g.height;
1943 drag->x = tmp_win->frame_g.x;
1944 drag->y = tmp_win->frame_g.y;
1945 drag->width = tmp_win->frame_g.width;
1947 else
1949 if (!XGetGeometry(dpy, (Drawable) ResizeWindow, &JunkRoot,
1950 &drag->x, &drag->y, (unsigned int *)&drag->width,
1951 (unsigned int *)&drag->height, &JunkBW,&JunkDepth))
1953 UngrabEm(GRAB_NORMAL);
1954 return;
1958 orig->x = drag->x;
1959 orig->y = drag->y;
1960 orig->width = drag->width;
1961 orig->height = drag->height;
1962 start_g.x = drag->x;
1963 start_g.y = drag->y;
1964 start_g.width = drag->width;
1965 start_g.height = drag->height;
1966 ymotion=xmotion=0;
1968 /* pop up a resize dimensions window */
1969 if (!Scr.gs.do_hide_resize_window)
1970 XMapRaised(dpy, Scr.SizeWindow);
1971 DisplaySize(tmp_win, orig->width, orig->height,True,True);
1973 if((PressedW != Scr.Root)&&(PressedW != None))
1975 /* Get the current position to determine which border to resize */
1976 if(PressedW == tmp_win->sides[0]) /* top */
1977 ymotion = 1;
1978 else if(PressedW == tmp_win->sides[1]) /* right */
1979 xmotion = -1;
1980 else if(PressedW == tmp_win->sides[2]) /* bottom */
1981 ymotion = -1;
1982 else if(PressedW == tmp_win->sides[3]) /* left */
1983 xmotion = 1;
1984 else if(PressedW == tmp_win->corners[0]) /* upper-left */
1986 ymotion = 1;
1987 xmotion = 1;
1989 else if(PressedW == tmp_win->corners[1]) /* upper-right */
1991 xmotion = -1;
1992 ymotion = 1;
1994 else if(PressedW == tmp_win->corners[2]) /* lower left */
1996 ymotion = -1;
1997 xmotion = 1;
1999 else if(PressedW == tmp_win->corners[3]) /* lower right */
2001 ymotion = -1;
2002 xmotion = -1;
2006 /* begin of code responsible for warping the pointer to the border when
2007 * starting a resize. */
2008 if (tmp_win->title_w != None && PressedW == tmp_win->title_w)
2010 /* title was pressed to thart the resize */
2011 called_from_title = True;
2013 else
2015 for (i = NUMBER_OF_BUTTONS; i--; )
2017 /* see if the title button was pressed to that the resize */
2018 if (tmp_win->button_w[i] != None && PressedW == tmp_win->button_w[i])
2020 /* yes */
2021 called_from_title = True;
2025 /* don't warp if the resize was triggered by a press somwhere on the title
2026 * bar */
2027 if(PressedW != Scr.Root && xmotion == 0 && ymotion == 0 &&
2028 !called_from_title)
2030 int dx = orig->width - px;
2031 int dy = orig->height - py;
2032 int wx = -1;
2033 int wy = -1;
2034 int tx;
2035 int ty;
2037 /* Now find the place to warp to. We simply use the sectors drawn when we
2038 * start resizing the window. */
2039 #if 0
2040 tx = orig->width / 10 - 1;
2041 ty = orig->height / 10 - 1;
2042 #else
2043 tx = 0;
2044 ty = 0;
2045 #endif
2046 tx = max(tmp_win->boundary_width, tx);
2047 ty = max(tmp_win->boundary_width, ty);
2048 if (px >= 0 && dx >= 0 && py >= 0 && dy >= 0)
2050 if (px < tx)
2052 if (py < ty)
2054 xmotion = 1;
2055 ymotion = 1;
2056 wx = 0;
2057 wy = 0;
2059 else if (dy < ty)
2061 xmotion = 1;
2062 ymotion = -1;
2063 wx = 0;
2064 wy = orig->height -1;
2066 else
2068 xmotion = 1;
2069 wx = 0;
2070 wy = orig->height/2;
2071 wy = py;
2074 else if (dx < tx)
2076 if (py < ty)
2078 xmotion = -1;
2079 ymotion = 1;
2080 wx = orig->width - 1;
2081 wy = 0;
2083 else if (dy < ty)
2085 xmotion = -1;
2086 ymotion = -1;
2087 wx = orig->width - 1;
2088 wy = orig->height -1;
2090 else
2092 xmotion = -1;
2093 wx = orig->width - 1;
2094 wy = orig->height/2;
2095 wy = py;
2098 else
2100 if (py < ty)
2102 ymotion = 1;
2103 wx = orig->width/2;
2104 wy = 0;
2105 wx = px;
2107 else if (dy < ty)
2109 ymotion = -1;
2110 wx = orig->width/2;
2111 wy = orig->height -1;
2112 wx = px;
2117 if (wx != -1)
2119 /* now warp the pointer to the border */
2120 XWarpPointer(dpy, None, ResizeWindow, 0, 0, 1, 1, wx, wy);
2121 XFlush(dpy);
2124 /* end of code responsible for warping the pointer to the border when
2125 * starting a resize. */
2127 /* draw the rubber-band window */
2128 if (!do_resize_opaque)
2129 MoveOutline(drag->x, drag->y, drag->width - 1, drag->height - 1);
2130 /* kick off resizing without requiring any motion if invoked with a key
2131 * press */
2132 if (eventp->type == KeyPress)
2134 XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild,
2135 &stashed_x,&stashed_y,&JunkX, &JunkY, &JunkMask);
2136 DoResize(stashed_x, stashed_y, tmp_win, drag, orig, &xmotion, &ymotion,
2137 do_resize_opaque);
2139 else
2140 stashed_x = stashed_y = -1;
2142 /* loop to resize */
2143 while(!finished && bad_window != tmp_win->w)
2145 /* block until there is an interesting event */
2146 while (!XCheckMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
2147 KeyPressMask | PointerMotionMask |
2148 ButtonMotionMask | ExposureMask, &Event))
2150 if (HandlePaging(Scr.EdgeScrollX, Scr.EdgeScrollY, &x, &y,
2151 &delta_x, &delta_y, False, False, True))
2153 /* Fake an event to force window reposition */
2154 Event.type = MotionNotify;
2155 Event.xmotion.time = lastTimestamp;
2156 fForceRedraw = True;
2157 break;
2160 StashEventTime(&Event);
2162 if (Event.type == MotionNotify)
2163 /* discard any extra motion events before a release */
2164 while(XCheckMaskEvent(dpy, ButtonMotionMask | PointerMotionMask |
2165 ButtonReleaseMask | ButtonPressMask, &Event))
2167 StashEventTime(&Event);
2168 if (Event.type == ButtonRelease || Event.type == ButtonPress)
2169 break;
2172 done = False;
2173 /* Handle a limited number of key press events to allow mouseless
2174 * operation */
2175 if(Event.type == KeyPress)
2176 Keyboard_shortcuts(&Event, tmp_win, ButtonRelease);
2177 switch(Event.type)
2179 case ButtonPress:
2180 XAllowEvents(dpy,ReplayPointer,CurrentTime);
2181 done = True;
2182 if (Event.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
2183 ((Button1Mask << (Event.xbutton.button - 1)) & button_mask))
2185 /* No new button was pressed, just a delayed event */
2186 break;
2188 /* Abort the resize if
2189 * - the move started with a pressed button and another button
2190 * was pressed during the operation
2191 * - no button was started at the beginning and any button
2192 * except button 1 was pressed. */
2193 if (button_mask || (Event.xbutton.button != 1))
2194 fButtonAbort = True;
2195 case KeyPress:
2196 /* simple code to bag out of move - CKH */
2197 if (XLookupKeysym(&(Event.xkey),0) == XK_Escape || fButtonAbort)
2199 abort = True;
2200 finished = True;
2201 /* return pointer if aborted resize was invoked with key */
2202 if (stashed_x >= 0)
2204 XWarpPointer(dpy, None, Scr.Root, 0, 0, 0, 0, stashed_x,
2205 stashed_y);
2207 if (do_resize_opaque)
2209 DoResize(
2210 start_g.x, start_g.y, tmp_win, &start_g, orig, &xmotion, &ymotion,
2211 do_resize_opaque);
2212 DrawDecorations(tmp_win, DRAW_ALL, True, True, None);
2215 done = True;
2216 break;
2218 case ButtonRelease:
2219 finished = True;
2220 done = True;
2221 break;
2223 case MotionNotify:
2224 if (!fForceRedraw)
2226 x = Event.xmotion.x_root;
2227 y = Event.xmotion.y_root;
2228 /* resize before paging request to prevent resize from lagging
2229 * mouse - mab */
2230 DoResize(
2231 x, y, tmp_win, drag, orig, &xmotion, &ymotion, do_resize_opaque);
2232 /* need to move the viewport */
2233 HandlePaging(Scr.EdgeScrollX, Scr.EdgeScrollY, &x, &y,
2234 &delta_x, &delta_y, False, False, False);
2236 /* redraw outline if we paged - mab */
2237 if (delta_x != 0 || delta_y != 0)
2239 orig->x -= delta_x;
2240 orig->y -= delta_y;
2241 drag->x -= delta_x;
2242 drag->y -= delta_y;
2244 DoResize(
2245 x, y, tmp_win, drag, orig, &xmotion, &ymotion, do_resize_opaque);
2247 fForceRedraw = False;
2248 done = True;
2249 default:
2250 break;
2252 if(!done)
2254 if (!do_resize_opaque)
2255 /* must undraw the rubber band in case the event causes some drawing */
2256 MoveOutline(0,0,0,0);
2257 DispatchEvent(False);
2258 if (!do_resize_opaque)
2259 MoveOutline(drag->x, drag->y, drag->width - 1, drag->height - 1);
2261 else
2263 if (do_resize_opaque)
2265 /* only do this with opaque resizes, (i.e. the server is not grabbed)
2267 BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win);
2268 FlushAllMessageQueues();
2273 /* erase the rubber-band */
2274 if (!do_resize_opaque)
2275 MoveOutline(0, 0, 0, 0);
2277 /* pop down the size window */
2278 if (!Scr.gs.do_hide_resize_window)
2279 XUnmapWindow(dpy, Scr.SizeWindow);
2281 if(!abort && bad_window != tmp_win->w)
2283 /* size will be >= to requested */
2284 constrain_size(
2285 tmp_win, (unsigned int *)&drag->width, (unsigned int *)&drag->height,
2286 xmotion, ymotion, True);
2287 if (IS_SHADED(tmp_win))
2289 if (HAS_BOTTOM_TITLE(tmp_win))
2291 SetupFrame(tmp_win, drag->x, tmp_win->frame_g.y,
2292 drag->width, tmp_win->frame_g.height, False);
2294 else
2296 SetupFrame(tmp_win, drag->x, drag->y,
2297 drag->width, tmp_win->frame_g.height, False);
2299 tmp_win->normal_g.height = drag->height;
2301 else
2303 SetupFrame(
2304 tmp_win, drag->x, drag->y, drag->width, drag->height, False);
2307 if (abort && was_maximized)
2309 /* since we aborted the resize, the window is still maximized */
2310 SET_MAXIMIZED(tmp_win, 1);
2311 /* force redraw */
2312 DrawDecorations(
2313 tmp_win, DRAW_BUTTONS, (tmp_win == Scr.Hilite), True, None);
2316 if (bad_window == tmp_win->w)
2318 XUnmapWindow(dpy, tmp_win->frame);
2319 XBell(dpy, 0);
2322 if (Scr.bo.InstallRootCmap)
2323 UninstallRootColormap();
2324 else
2325 UninstallFvwmColormap();
2326 ResizeWindow = None;
2327 if (!do_resize_opaque)
2329 XEvent event;
2330 /* Throw away some events that dont interest us right now. */
2331 while (XCheckMaskEvent(dpy, EnterWindowMask|LeaveWindowMask,
2332 &event) != False)
2334 Scr.flags.is_wire_frame_displayed = False;
2335 MyXUngrabServer(dpy);
2337 xmotion = 0;
2338 ymotion = 0;
2339 WaitForButtonsUp(True);
2340 UngrabEm(GRAB_NORMAL);
2341 Scr.flags.edge_wrap_x = edge_wrap_x;
2342 Scr.flags.edge_wrap_y = edge_wrap_y;
2343 update_absolute_geometry(tmp_win);
2344 maximize_adjust_offset(tmp_win);
2345 GNOME_SetWinArea(tmp_win);
2347 return;
2352 /***********************************************************************
2354 * Procedure:
2355 * DoResize - move the rubberband around. This is called for
2356 * each motion event when we are resizing
2358 * Inputs:
2359 * x_root - the X corrdinate in the root window
2360 * y_root - the Y corrdinate in the root window
2361 * tmp_win - the current fvwm window
2362 * drag - resize internal structure
2363 * orig - resize internal structure
2364 * xmotionp - pointer to xmotion in resize_window
2365 * ymotionp - pointer to ymotion in resize_window
2367 ************************************************************************/
2368 static void DoResize(
2369 int x_root, int y_root, FvwmWindow *tmp_win, rectangle *drag,
2370 rectangle *orig, int *xmotionp, int *ymotionp, Bool do_resize_opaque)
2372 int action = 0;
2374 if ((y_root <= orig->y) ||
2375 ((*ymotionp == 1)&&(y_root < orig->y+orig->height-1)))
2377 drag->y = y_root;
2378 drag->height = orig->y + orig->height - y_root;
2379 action = 1;
2380 *ymotionp = 1;
2382 else if ((y_root >= orig->y + orig->height - 1)||
2383 ((*ymotionp == -1)&&(y_root > orig->y)))
2385 drag->y = orig->y;
2386 drag->height = 1 + y_root - drag->y;
2387 action = 1;
2388 *ymotionp = -1;
2391 if ((x_root <= orig->x)||
2392 ((*xmotionp == 1)&&(x_root < orig->x + orig->width - 1)))
2394 drag->x = x_root;
2395 drag->width = orig->x + orig->width - x_root;
2396 action = 1;
2397 *xmotionp = 1;
2399 if ((x_root >= orig->x + orig->width - 1)||
2400 ((*xmotionp == -1)&&(x_root > orig->x)))
2402 drag->x = orig->x;
2403 drag->width = 1 + x_root - orig->x;
2404 action = 1;
2405 *xmotionp = -1;
2408 if (action)
2410 /* round up to nearest OK size to keep pointer inside rubberband */
2411 constrain_size(
2412 tmp_win, (unsigned int *)&drag->width, (unsigned int *)&drag->height,
2413 *xmotionp, *ymotionp, True);
2414 if (*xmotionp == 1)
2415 drag->x = orig->x + orig->width - drag->width;
2416 if (*ymotionp == 1)
2417 drag->y = orig->y + orig->height - drag->height;
2419 if(!do_resize_opaque)
2421 MoveOutline(drag->x, drag->y, drag->width - 1, drag->height - 1);
2423 else
2425 SetupFrame(
2426 tmp_win, drag->x, drag->y, drag->width, drag->height, False);
2429 DisplaySize(tmp_win, drag->width, drag->height,False,False);
2434 /***********************************************************************
2436 * Procedure:
2437 * DisplaySize - display the size in the dimensions window
2439 * Inputs:
2440 * tmp_win - the current fvwm window
2441 * width - the width of the rubber band
2442 * height - the height of the rubber band
2444 ***********************************************************************/
2445 static void DisplaySize(FvwmWindow *tmp_win, int width, int height, Bool Init,
2446 Bool resetLast)
2448 char str[100];
2449 int dwidth,dheight,offset;
2450 static int last_width = 0;
2451 static int last_height = 0;
2453 if (Scr.gs.do_hide_resize_window)
2454 return;
2455 if (resetLast)
2457 last_width = 0;
2458 last_height = 0;
2460 if (last_width == width && last_height == height)
2461 return;
2463 last_width = width;
2464 last_height = height;
2466 dheight = height - tmp_win->title_g.height - 2*tmp_win->boundary_width;
2467 dwidth = width - 2*tmp_win->boundary_width;
2469 dwidth -= tmp_win->hints.base_width;
2470 dheight -= tmp_win->hints.base_height;
2471 dwidth /= tmp_win->hints.width_inc;
2472 dheight /= tmp_win->hints.height_inc;
2474 (void) sprintf (str, " %4d x %-4d ", dwidth, dheight);
2475 if(Init)
2477 XClearWindow(dpy,Scr.SizeWindow);
2479 else
2481 /* just clear indside the relief lines to reduce flicker */
2482 XClearArea(dpy,Scr.SizeWindow,2,2,
2483 Scr.SizeStringWidth + SIZE_HINDENT*2 - 3,
2484 Scr.DefaultFont.height + SIZE_VINDENT*2 - 3,False);
2487 if(Pdepth >= 2)
2488 RelieveRectangle(dpy,Scr.SizeWindow,0,0,
2489 Scr.SizeStringWidth+ SIZE_HINDENT*2 - 1,
2490 Scr.DefaultFont.height + SIZE_VINDENT*2 - 1,
2491 Scr.StdReliefGC,
2492 Scr.StdShadowGC, 2);
2493 offset = (Scr.SizeStringWidth + SIZE_HINDENT*2
2494 - XTextWidth(Scr.DefaultFont.font,str,strlen(str)))/2;
2495 #ifdef I18N_MB
2496 XmbDrawString (dpy, Scr.SizeWindow, Scr.DefaultFont.fontset, Scr.StdGC,
2497 offset, Scr.DefaultFont.font->ascent + SIZE_VINDENT, str, 13);
2498 #else
2499 XDrawString (dpy, Scr.SizeWindow, Scr.StdGC,
2500 offset, Scr.DefaultFont.font->ascent + SIZE_VINDENT, str, 13);
2501 #endif
2505 /***********************************************************************
2507 * Procedure:
2508 * MoveOutline - move a window outline
2510 * Inputs:
2511 * root - the window we are outlining
2512 * x - upper left x coordinate
2513 * y - upper left y coordinate
2514 * width - the width of the rectangle
2515 * height - the height of the rectangle
2517 ***********************************************************************/
2518 void MoveOutline(int x, int y, int width, int height)
2520 static int lastx = 0;
2521 static int lasty = 0;
2522 static int lastWidth = 0;
2523 static int lastHeight = 0;
2524 int interleave = 0;
2525 int offset;
2526 XRectangle rects[10];
2528 if (x == lastx && y == lasty && width == lastWidth && height == lastHeight)
2529 return;
2531 /* figure out the ordering */
2532 if (width || height)
2533 interleave += 1;
2534 if (lastWidth || lastHeight)
2535 interleave += 1;
2536 offset = interleave >> 1;
2538 /* place the resize rectangle into the array of rectangles */
2539 /* interleave them for best visual look */
2540 /* draw the new one, if any */
2541 if (width || height)
2543 int i;
2544 for (i=0; i < 4; i++)
2546 rects[i * interleave].x = x + i;
2547 rects[i * interleave].y = y + i;
2548 rects[i * interleave].width = width - (i << 1);
2549 rects[i * interleave].height = height - (i << 1);
2551 rects[3 * interleave].y = y+3 + (height-6)/3;
2552 rects[3 * interleave].height = (height-6)/3;
2553 rects[4 * interleave].x = x+3 + (width-6)/3;
2554 rects[4 * interleave].y = y+3;
2555 rects[4 * interleave].width = (width-6)/3;
2556 rects[4 * interleave].height = (height-6);
2559 /* undraw the old one, if any */
2560 if (lastWidth || lastHeight)
2562 int i;
2563 for (i=0; i < 4; i++)
2565 rects[i * interleave + offset].x = lastx + i;
2566 rects[i * interleave + offset].y = lasty + i;
2567 rects[i * interleave + offset].width = lastWidth - (i << 1);
2568 rects[i * interleave + offset].height = lastHeight - (i << 1);
2570 rects[3 * interleave + offset].y = lasty+3 + (lastHeight-6)/3;
2571 rects[3 * interleave + offset].height = (lastHeight-6)/3;
2572 rects[4 * interleave + offset].x = lastx+3 + (lastWidth-6)/3;
2573 rects[4 * interleave + offset].y = lasty+3;
2574 rects[4 * interleave + offset].width = (lastWidth-6)/3;
2575 rects[4 * interleave + offset].height = (lastHeight-6);
2578 XDrawRectangles(dpy, Scr.Root, Scr.XorGC, rects, interleave * 5);
2580 lastx = x;
2581 lasty = y;
2582 lastWidth = width;
2583 lastHeight = height;
2585 return;
2589 /* ----------------------------- maximizing code --------------------------- */
2591 static void move_sticky_window_to_same_page(
2592 int *x11, int *x12, int *y11, int *y12, int x21, int x22, int y21, int y22)
2594 /* make sure the x coordinate is on the same page as the reference window */
2595 if (*x11 >= x22)
2597 while (*x11 >= x22)
2599 *x11 -= Scr.MyDisplayWidth;
2600 *x12 -= Scr.MyDisplayWidth;
2603 else if (*x12 <= x21)
2605 while (*x12 <= x21)
2607 *x11 += Scr.MyDisplayWidth;
2608 *x12 += Scr.MyDisplayWidth;
2611 /* make sure the y coordinate is on the same page as the reference window */
2612 if (*y11 >= y22)
2614 while (*y11 >= y22)
2616 *y11 -= Scr.MyDisplayHeight;
2617 *y12 -= Scr.MyDisplayHeight;
2620 else if (*y12 <= y21)
2622 while (*y12 <= y21)
2624 *y11 += Scr.MyDisplayHeight;
2625 *y12 += Scr.MyDisplayHeight;
2630 static void MaximizeHeight(
2631 FvwmWindow *win, unsigned int win_width, int win_x, unsigned int *win_height,
2632 int *win_y, Bool grow_up, Bool grow_down)
2634 FvwmWindow *cwin;
2635 int x11, x12, x21, x22;
2636 int y11, y12, y21, y22;
2637 int new_y1, new_y2;
2638 Bool is_sticky;
2640 x11 = win_x; /* Start x */
2641 y11 = *win_y; /* Start y */
2642 x12 = x11 + win_width; /* End x */
2643 y12 = y11 + *win_height; /* End y */
2644 new_y1 = truncate_to_multiple(y11, Scr.MyDisplayHeight);
2645 new_y2 = new_y1 + Scr.MyDisplayHeight;
2647 for (cwin = Scr.FvwmRoot.next; cwin; cwin = cwin->next)
2649 if (IS_STICKY(cwin) || (IS_ICONIFIED(cwin) && IS_ICON_STICKY(cwin)))
2650 is_sticky = True;
2651 else
2652 is_sticky = False;
2653 if (cwin == win || (cwin->Desk != win->Desk && !is_sticky))
2654 continue;
2655 if (IS_ICONIFIED(cwin))
2657 if(cwin->icon_w == None || IS_ICON_UNMAPPED(cwin))
2658 continue;
2659 x21 = cwin->icon_g.x;
2660 y21 = cwin->icon_g.y;
2661 x22 = x21 + cwin->icon_p_width;
2662 y22 = y21 + cwin->icon_p_height + cwin->icon_g.height;
2664 else
2666 x21 = cwin->frame_g.x;
2667 y21 = cwin->frame_g.y;
2668 x22 = x21 + cwin->frame_g.width;
2669 y22 = y21 + cwin->frame_g.height;
2671 if (is_sticky)
2673 move_sticky_window_to_same_page(
2674 &x21, &x22, &new_y1, &new_y2, x11, x12, y11, y12);
2677 /* Are they in the same X space? */
2678 if (!((x22 <= x11) || (x21 >= x12)))
2680 if ((y22 <= y11) && (y22 >= new_y1))
2682 new_y1 = y22;
2684 else if ((y12 <= y21) && (new_y2 >= y21))
2686 new_y2 = y21;
2690 if (!grow_up)
2691 new_y1 = y11;
2692 if (!grow_down)
2693 new_y2 = y12;
2694 *win_height = new_y2 - new_y1;
2695 *win_y = new_y1;
2698 static void MaximizeWidth(
2699 FvwmWindow *win, unsigned int *win_width, int *win_x, unsigned int win_height,
2700 int win_y, Bool grow_left, Bool grow_right)
2702 FvwmWindow *cwin;
2703 int x11, x12, x21, x22;
2704 int y11, y12, y21, y22;
2705 int new_x1, new_x2;
2706 Bool is_sticky;
2708 x11 = *win_x; /* Start x */
2709 y11 = win_y; /* Start y */
2710 x12 = x11 + *win_width; /* End x */
2711 y12 = y11 + win_height; /* End y */
2712 new_x1 = truncate_to_multiple(x11, Scr.MyDisplayWidth);
2713 new_x2 = new_x1 + Scr.MyDisplayWidth;
2715 for (cwin = Scr.FvwmRoot.next; cwin; cwin = cwin->next)
2717 if (IS_STICKY(cwin) || (IS_ICONIFIED(cwin) && IS_ICON_STICKY(cwin)))
2718 is_sticky = True;
2719 else
2720 is_sticky = False;
2721 if (cwin == win || (cwin->Desk != win->Desk && !is_sticky))
2722 continue;
2723 if (IS_ICONIFIED(cwin))
2725 if(cwin->icon_w == None || IS_ICON_UNMAPPED(cwin))
2726 continue;
2727 x21 = cwin->icon_g.x;
2728 y21 = cwin->icon_g.y;
2729 x22 = x21 + cwin->icon_p_width;
2730 y22 = y21 + cwin->icon_p_height + cwin->icon_g.height;
2732 else
2734 x21 = cwin->frame_g.x;
2735 y21 = cwin->frame_g.y;
2736 x22 = x21 + cwin->frame_g.width;
2737 y22 = y21 + cwin->frame_g.height;
2739 if (is_sticky)
2741 move_sticky_window_to_same_page(
2742 &new_x1, &new_x2, &y21, &y22, x11, x12, y11, y12);
2745 /* Are they in the same Y space? */
2746 if (!((y22 <= y11) || (y21 >= y12)))
2748 if ((x22 <= x11) && (x22 >= new_x1))
2750 new_x1 = x22;
2752 else if ((x12 <= x21) && (new_x2 >= x21))
2754 new_x2 = x21;
2758 if (!grow_left)
2759 new_x1 = x11;
2760 if (!grow_right)
2761 new_x2 = x12;
2762 *win_width = new_x2 - new_x1;
2763 *win_x = new_x1;
2766 /***********************************************************************
2768 * Procedure:
2769 * (Un)Maximize a window.
2771 ***********************************************************************/
2772 void Maximize(F_CMD_ARGS)
2774 int page_x, page_y;
2775 int val1, val2, val1_unit, val2_unit;
2776 int toggle;
2777 char *token;
2778 char *taction;
2779 Bool grow_up = False;
2780 Bool grow_down = False;
2781 Bool grow_left = False;
2782 Bool grow_right = False;
2783 Bool do_force_maximize = False;
2784 rectangle new_g;
2786 if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease))
2787 return;
2788 if (tmp_win == NULL || IS_ICONIFIED(tmp_win))
2789 return;
2791 if (check_if_function_allowed(F_MAXIMIZE,tmp_win,True,NULL) == 0)
2793 XBell(dpy, 0);
2794 return;
2796 toggle = ParseToggleArgument(action, &action, -1, 0);
2797 if (toggle == 0 && !IS_MAXIMIZED(tmp_win))
2798 return;
2800 if (toggle == 1 && IS_MAXIMIZED(tmp_win))
2802 /* Fake that the window is not maximized. */
2803 do_force_maximize = True;
2806 /* parse first parameter */
2807 val1_unit = Scr.MyDisplayWidth;
2808 token = PeekToken(action, &taction);
2809 if (token && StrEquals(token, "grow"))
2811 grow_left = True;
2812 grow_right = True;
2813 val1 = 100;
2814 val1_unit = Scr.MyDisplayWidth;
2816 else if (token && StrEquals(token, "growleft"))
2818 grow_left = True;
2819 val1 = 100;
2820 val1_unit = Scr.MyDisplayWidth;
2822 else if (token && StrEquals(token, "growright"))
2824 grow_right = True;
2825 val1 = 100;
2826 val1_unit = Scr.MyDisplayWidth;
2828 else
2830 if (GetOnePercentArgument(token, &val1, &val1_unit) == 0)
2832 val1 = 100;
2833 val1_unit = Scr.MyDisplayWidth;
2835 else if (val1 < 0)
2837 /* handle negative offsets */
2838 if (val1_unit == Scr.MyDisplayWidth)
2840 val1 = 100 + val1;
2842 else
2844 val1 = Scr.MyDisplayWidth + val1;
2849 /* parse second parameter */
2850 val2_unit = Scr.MyDisplayHeight;
2851 token = PeekToken(taction, NULL);
2852 if (token && StrEquals(token, "grow"))
2854 grow_up = True;
2855 grow_down = True;
2856 val2 = 100;
2857 val2_unit = Scr.MyDisplayHeight;
2859 else if (token && StrEquals(token, "growup"))
2861 grow_up = True;
2862 val2 = 100;
2863 val2_unit = Scr.MyDisplayHeight;
2865 else if (token && StrEquals(token, "growdown"))
2867 grow_down = True;
2868 val2 = 100;
2869 val2_unit = Scr.MyDisplayHeight;
2871 else
2873 if (GetOnePercentArgument(token, &val2, &val2_unit) == 0)
2875 val2 = 100;
2876 val2_unit = Scr.MyDisplayHeight;
2878 else if (val2 < 0)
2880 /* handle negative offsets */
2881 if (val2_unit == Scr.MyDisplayHeight)
2883 val2 = 100 + val2;
2885 else
2887 val2 = Scr.MyDisplayHeight + val2;
2892 if (IS_MAXIMIZED(tmp_win) && !do_force_maximize)
2894 SET_MAXIMIZED(tmp_win, 0);
2895 get_relative_geometry(&tmp_win->frame_g, &tmp_win->normal_g);
2896 if (IS_SHADED(tmp_win))
2897 get_shaded_geometry(tmp_win, &tmp_win->frame_g, &tmp_win->frame_g);
2898 ForceSetupFrame(
2899 tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width,
2900 tmp_win->frame_g.height, True);
2901 DrawDecorations(tmp_win, DRAW_ALL, True, True, None);
2903 else /* maximize */
2905 /* find the new page and geometry */
2906 new_g.x = tmp_win->frame_g.x;
2907 new_g.y = tmp_win->frame_g.y;
2908 new_g.width = tmp_win->frame_g.width;
2909 new_g.height = tmp_win->frame_g.height;
2910 if (IsRectangleOnThisPage(&tmp_win->frame_g, tmp_win->Desk))
2912 /* maximize on visible page */
2913 page_x = 0;
2914 page_y = 0;
2916 else
2918 /* maximize on the page where the center of the window is */
2919 page_x = truncate_to_multiple(
2920 tmp_win->frame_g.x + tmp_win->frame_g.width / 2,
2921 Scr.MyDisplayWidth);
2922 page_y = truncate_to_multiple(
2923 tmp_win->frame_g.y + tmp_win->frame_g.height / 2,
2924 Scr.MyDisplayHeight);
2927 /* handle command line arguments */
2928 if (grow_up || grow_down)
2930 MaximizeHeight(
2931 tmp_win, new_g.width, new_g.x, (unsigned int *)&new_g.height,
2932 &new_g.y, grow_up, grow_down);
2934 else if(val2 > 0)
2936 new_g.height = val2 * val2_unit / 100;
2937 new_g.y = page_y;
2939 if (grow_left || grow_right)
2941 MaximizeWidth(
2942 tmp_win, (unsigned int *)&new_g.width, &new_g.x, new_g.height,
2943 new_g.y, grow_left, grow_right);
2945 else if(val1 >0)
2947 new_g.width = val1 * val1_unit / 100;
2948 new_g.x = page_x;
2950 if(val1 == 0 && val2 == 0)
2952 new_g.x = page_x;
2953 new_g.y = page_y;
2954 new_g.height = Scr.MyDisplayHeight;
2955 new_g.width = Scr.MyDisplayWidth;
2957 /* now maximize it */
2958 SET_MAXIMIZED(tmp_win, 1);
2959 constrain_size(tmp_win, (unsigned int *)&new_g.width,
2960 (unsigned int *)&new_g.height, 0, 0, False);
2961 tmp_win->max_g = new_g;
2962 if (IS_SHADED(tmp_win))
2963 get_shaded_geometry(tmp_win, &new_g, &tmp_win->max_g);
2964 SetupFrame(
2965 tmp_win, new_g.x, new_g.y, new_g.width, new_g.height, True);
2966 DrawDecorations(tmp_win, DRAW_ALL, (Scr.Hilite == tmp_win), True, None);
2967 /* remember the offset between old and new position in case the maximized
2968 * window is moved more than the screen width/height. */
2969 if (!do_force_maximize)
2971 update_absolute_geometry(tmp_win);
2973 tmp_win->max_offset.x = tmp_win->normal_g.x - tmp_win->max_g.x;
2974 tmp_win->max_offset.y = tmp_win->normal_g.y - tmp_win->max_g.y;
2975 #if 0
2976 fprintf(stderr,"%d %d %d %d, max_offset.x = %d, max_offset.y = %d\n", tmp_win->max_g.x, tmp_win->max_g.y, tmp_win->max_g.width, tmp_win->max_g.height, tmp_win->max_offset.x, tmp_win->max_offset.y);
2977 #endif
2979 GNOME_SetWinArea(tmp_win);
2982 /* ----------------------------- stick code -------------------------------- */
2984 void handle_stick(F_CMD_ARGS, int toggle)
2986 if ((toggle == 1 && IS_STICKY(tmp_win)) ||
2987 (toggle == 0 && !IS_STICKY(tmp_win)))
2988 return;
2990 if(IS_STICKY(tmp_win))
2992 SET_STICKY(tmp_win, 0);
2993 tmp_win->Desk = Scr.CurrentDesk;
2994 GNOME_SetDeskCount();
2995 GNOME_SetDesk(tmp_win);
2997 else
2999 if (tmp_win->Desk != Scr.CurrentDesk)
3000 do_move_window_to_desk(tmp_win, Scr.CurrentDesk);
3001 SET_STICKY(tmp_win, 1);
3002 if (!IsRectangleOnThisPage(&tmp_win->frame_g, Scr.CurrentDesk))
3004 action = "";
3005 move_window_doit(F_PASS_ARGS, FALSE, TRUE);
3006 /* move_window_doit resets the STICKY flag, so we must set it after the
3007 * call! */
3008 SET_STICKY(tmp_win, 1);
3011 BroadcastConfig(M_CONFIGURE_WINDOW,tmp_win);
3012 DrawDecorations(tmp_win, DRAW_TITLE, (Scr.Hilite==tmp_win), True, None);
3013 GNOME_SetHints(tmp_win);
3016 void stick_function(F_CMD_ARGS)
3018 int toggle;
3020 if (DeferExecution(eventp,&w,&tmp_win,&context,CRS_SELECT,ButtonRelease))
3021 return;
3023 toggle = ParseToggleArgument(action, &action, -1, 0);
3025 handle_stick(F_PASS_ARGS, toggle);