/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/****************************************************************************
* This module is all new
* by Rob Nation
*
* This code does smart-placement initial window placement stuff
*
* Copyright 1994 Robert Nation. No restrictions are placed on this code,
* as long as the copyright notice is preserved . No guarantees or
* warrantees of any sort whatsoever are given or implied or anything.
****************************************************************************/
#include "config.h"
#include <stdio.h>
#include "libs/fvwmlib.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "geometry.h"
/************************************************************************
*
* Procedure:
* gravity_get_offsets - map gravity to (x,y) offset signs for adding
* to x and y when window is mapped to get proper placement.
*
************************************************************************/
struct _gravity_offset
{
int x, y;
};
void gravity_get_offsets(int grav, int *xp,int *yp)
{
static struct _gravity_offset gravity_offsets[11] =
{
{ 0, 0 }, /* ForgetGravity */
{ -1, -1 }, /* NorthWestGravity */
{ 0, -1 }, /* NorthGravity */
{ 1, -1 }, /* NorthEastGravity */
{ -1, 0 }, /* WestGravity */
{ 0, 0 }, /* CenterGravity */
{ 1, 0 }, /* EastGravity */
{ -1, 1 }, /* SouthWestGravity */
{ 0, 1 }, /* SouthGravity */
{ 1, 1 }, /* SouthEastGravity */
{ 0, 0 }, /* StaticGravity */
};
if (grav < ForgetGravity || grav > StaticGravity)
*xp = *yp = 0;
else
{
*xp = (int)gravity_offsets[grav].x;
*yp = (int)gravity_offsets[grav].y;
}
return;
}
/* Move a rectangle while taking gravity into account. */
void gravity_move(int gravity, rectangle *rect, int xdiff, int ydiff)
{
int xoff;
int yoff;
gravity_get_offsets(gravity, &xoff, &yoff);
rect->x -= xoff * xdiff;
rect->y -= yoff * ydiff;
return;
}
/* Resize rectangle while taking gravity into account. */
void gravity_resize(int gravity, rectangle *rect, int wdiff, int hdiff)
{
int xoff;
int yoff;
gravity_get_offsets(gravity, &xoff, &yoff);
rect->x -= (wdiff * (xoff + 1)) / 2;
rect->width += wdiff;
rect->y -= (hdiff * (yoff + 1)) / 2;
rect->height += hdiff;
return;
}
/* Removes decorations from the source rectangle and moves it according to the
* gravity specification. */
void gravity_get_naked_geometry(
int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
{
int xoff;
int yoff;
gravity_get_offsets(gravity, &xoff, &yoff);
dest_g->x = orig_g->x + ((xoff + 1) * (orig_g->width - 1)) / 2;
dest_g->y = orig_g->y + ((yoff + 1) * (orig_g->height - 1)) / 2;
dest_g->width = orig_g->width - 2 * t->boundary_width;
dest_g->height = orig_g->height - 2 * t->boundary_width - t->title_g.height;
return;
}
/* Decorate the rectangle. Resize and shift it according to gravity. */
void gravity_add_decoration(
int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
{
*dest_g = *orig_g;
gravity_resize(
gravity, dest_g, 2 * t->boundary_width,
2 * t->boundary_width + t->title_g.height);
return;
}
void get_relative_geometry(rectangle *rel_g, rectangle *abs_g)
{
rel_g->x = abs_g->x - Scr.Vx;
rel_g->y = abs_g->y - Scr.Vy;
rel_g->width = abs_g->width;
rel_g->height = abs_g->height;
}
void gravity_translate_to_northwest_geometry(
int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
{
int xoff;
int yoff;
gravity_get_offsets(gravity, &xoff, &yoff);
dest_g->x =
orig_g->x - ((xoff + 1) * (orig_g->width - 1 + 2 * t->old_bw)) / 2;
dest_g->y =
orig_g->y - ((yoff + 1) * (orig_g->height - 1 + 2 * t->old_bw)) / 2;
dest_g->width = orig_g->width;
dest_g->height = orig_g->height;
return;
}
void gravity_translate_to_northwest_geometry_no_bw(
int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
{
int bw = t->old_bw;
t->old_bw = 0;
gravity_translate_to_northwest_geometry(gravity, t, dest_g, orig_g);
t->old_bw = bw;
return;
}
void get_shaded_geometry(
FvwmWindow *tmp_win, rectangle *small_g, rectangle *big_g)
{
/* this variable is necessary so the function can be called with
* small_g == big_g */
int big_height = big_g->height;
small_g->width = big_g->width;
small_g->height = 2 * tmp_win->boundary_width + tmp_win->title_g.height;
small_g->x = big_g->x;
small_g->y = big_g->y + ((HAS_BOTTOM_TITLE(tmp_win)) ?
(big_height - small_g->height) : 0);
}
void get_unshaded_geometry(
FvwmWindow *tmp_win, rectangle *ret_g)
{
if (IS_SHADED(tmp_win))
{
if (IS_MAXIMIZED(tmp_win))
{
*ret_g = tmp_win->max_g;
}
else
{
*ret_g = tmp_win->normal_g;
}
get_relative_geometry(ret_g, ret_g);
}
else
{
*ret_g = tmp_win->frame_g;
}
return;
}
/* update the frame_g according to the window's normal_g or max_g and shaded
* state */
void update_relative_geometry(FvwmWindow *tmp_win)
{
get_relative_geometry(
&tmp_win->frame_g,
(IS_MAXIMIZED(tmp_win)) ? &tmp_win->max_g : &tmp_win->normal_g);
if (IS_SHADED(tmp_win))
{
get_shaded_geometry(tmp_win, &tmp_win->frame_g, &tmp_win->frame_g);
}
}
/* update the normal_g or max_g according to the window's current position */
void update_absolute_geometry(FvwmWindow *tmp_win)
{
rectangle *dest_g;
#if 0
fprintf(stderr,"uag: called for window '%s'\n", tmp_win->name);
#endif
/* store orig values in absolute coords */
dest_g = (IS_MAXIMIZED(tmp_win)) ? &tmp_win->max_g : &tmp_win->normal_g;
dest_g->x = tmp_win->frame_g.x + Scr.Vx;
dest_g->y = tmp_win->frame_g.y + Scr.Vy;
dest_g->width = tmp_win->frame_g.width;
if (!IS_SHADED(tmp_win))
dest_g->height = tmp_win->frame_g.height;
else if (HAS_BOTTOM_TITLE(tmp_win))
{
dest_g->y += tmp_win->frame_g.height - dest_g->height;
}
#if 0
fprintf(stderr," frame: %5d %5d, %4d x %4d\n", tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width, tmp_win->frame_g.height);
fprintf(stderr," normal: %5d %5d, %4d x %4d\n", tmp_win->normal_g.x, tmp_win->normal_g.y, tmp_win->normal_g.width, tmp_win->normal_g.height);
if (IS_MAXIMIZED(tmp_win))
fprintf(stderr," max: %5d %5d, %4d x %4d, %5d %5d\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);
#endif
}
/* make sure a maximized window and it's normal version are never a page or
* more apart. */
void maximize_adjust_offset(FvwmWindow *tmp_win)
{
int off_x;
int off_y;
if (!IS_MAXIMIZED(tmp_win))
/* otherwise we might corrupt the normal_g */
return;
off_x = tmp_win->normal_g.x - tmp_win->max_g.x - tmp_win->max_offset.x;
off_y = tmp_win->normal_g.y - tmp_win->max_g.y - tmp_win->max_offset.y;
#if 0
fprintf(stderr,"mao: xo=%d, yo=%d\n", off_x, off_y);
#endif
if (off_x >= Scr.MyDisplayWidth)
{
tmp_win->normal_g.x -=
(off_x / Scr.MyDisplayWidth) * Scr.MyDisplayWidth;
#if 0
fprintf(stderr, "mao: x -= %d\n", (off_x / Scr.MyDisplayWidth) * Scr.MyDisplayWidth);
#endif
}
else if (off_x <= -Scr.MyDisplayWidth)
{
tmp_win->normal_g.x +=
((-off_x) / Scr.MyDisplayWidth) * Scr.MyDisplayWidth;
#if 0
fprintf(stderr, "mao: x += %d\n", ((-off_x) / Scr.MyDisplayWidth) * Scr.MyDisplayWidth);
#endif
}
if (off_y >= Scr.MyDisplayHeight)
{
tmp_win->normal_g.y -=
(off_y / Scr.MyDisplayHeight) * Scr.MyDisplayHeight;
#if 0
fprintf(stderr, "mao: y -= %d\n", (off_y / Scr.MyDisplayHeight) * Scr.MyDisplayHeight);
#endif
}
else if (off_y <= -Scr.MyDisplayHeight)
{
tmp_win->normal_g.y +=
((-off_y) / Scr.MyDisplayHeight) * Scr.MyDisplayHeight;
#if 0
fprintf(stderr, "mao: y += %d\n", ((-off_y) / Scr.MyDisplayHeight) * Scr.MyDisplayHeight);
#endif
}
}
/***********************************************************************
*
* Procedure:
* constrain_size - adjust the given width and height to account for the
* constraints imposed by size hints
*
* The general algorithm, especially the aspect ratio stuff, is
* borrowed from uwm's CheckConsistency routine.
*
***********************************************************************/
void constrain_size(
FvwmWindow *tmp_win, unsigned int *widthp, unsigned int *heightp,
int xmotion, int ymotion, int flags)
{
#define MAKEMULT(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )
int minWidth, minHeight, maxWidth, maxHeight, xinc, yinc, delta;
int baseWidth, baseHeight;
int dwidth = *widthp, dheight = *heightp;
int roundUpX = 0;
int roundUpY = 0;
int old_w = 0;
int old_h = 0;
if (IS_MAXIMIZED(tmp_win) && (flags & CS_UPDATE_MAX_DEFECT))
{
*widthp += tmp_win->max_g_defect.width;
*heightp += tmp_win->max_g_defect.height;
old_w = *widthp;
old_h = *heightp;
}
dwidth -= 2 *tmp_win->boundary_width;
dheight -= (tmp_win->title_g.height + 2 * tmp_win->boundary_width);
minWidth = tmp_win->hints.min_width;
minHeight = tmp_win->hints.min_height;
maxWidth = tmp_win->hints.max_width;
maxHeight = tmp_win->hints.max_height;
if (maxWidth > tmp_win->max_window_width - 2 * tmp_win->boundary_width)
{
maxWidth = tmp_win->max_window_width - 2 * tmp_win->boundary_width;
}
if (maxHeight > tmp_win->max_window_height - 2*tmp_win->boundary_width -
tmp_win->title_g.height)
{
maxHeight =
tmp_win->max_window_height - 2*tmp_win->boundary_width -
tmp_win->title_g.height;
}
baseWidth = tmp_win->hints.base_width;
baseHeight = tmp_win->hints.base_height;
xinc = tmp_win->hints.width_inc;
yinc = tmp_win->hints.height_inc;
/*
* First, clamp to min and max values
*/
if (dwidth < minWidth)
dwidth = minWidth;
if (dheight < minHeight)
dheight = minHeight;
if (dwidth > maxWidth)
dwidth = maxWidth;
if (dheight > maxHeight)
dheight = maxHeight;
/*
* Second, round to base + N * inc (up or down depending on resize type)
* if rounding up store amount
*/
if (!(flags & CS_ROUND_UP))
{
dwidth = (((dwidth - baseWidth) / xinc) * xinc) + baseWidth;
dheight = (((dheight - baseHeight) / yinc) * yinc) + baseHeight;
}
else
{
roundUpX = dwidth;
roundUpY = dheight;
dwidth = (((dwidth - baseWidth + xinc - 1) / xinc) * xinc) + baseWidth;
dheight = (((dheight - baseHeight + yinc - 1) / yinc) * yinc) +
baseHeight;
roundUpX = dwidth - roundUpX;
roundUpY = dheight - roundUpY;
}
/*
* Step 2a: check we didn't move the edge off screen in interactive moves
*/
if ((flags & CS_ROUND_UP) && Event.type == MotionNotify)
{
if (xmotion > 0 && Event.xmotion.x_root < roundUpX)
dwidth -= xinc;
else if (xmotion < 0 &&
Event.xmotion.x_root >= Scr.MyDisplayWidth - roundUpX)
dwidth -= xinc;
if (ymotion > 0 && Event.xmotion.y_root < roundUpY)
dheight -= yinc;
else if (ymotion < 0 &&
Event.xmotion.y_root >= Scr.MyDisplayHeight - roundUpY)
dheight -= yinc;
}
/*
* Step 2b: Check that we didn't violate min and max.
*/
if (dwidth < minWidth)
dwidth += xinc;
if (dheight < minHeight)
dheight += yinc;
if (dwidth > maxWidth)
dwidth -= xinc;
if (dheight > maxHeight)
dheight -= yinc;
/*
* Third, adjust for aspect ratio
*/
#define maxAspectX tmp_win->hints.max_aspect.x
#define maxAspectY tmp_win->hints.max_aspect.y
#define minAspectX tmp_win->hints.min_aspect.x
#define minAspectY tmp_win->hints.min_aspect.y
/*
* The math looks like this:
*
* minAspectX dwidth maxAspectX
* ---------- <= ------- <= ----------
* minAspectY dheight maxAspectY
*
* If that is multiplied out, then the width and height are
* invalid in the following situations:
*
* minAspectX * dheight > minAspectY * dwidth
* maxAspectX * dheight < maxAspectY * dwidth
*
*/
if (tmp_win->hints.flags & PAspect)
{
if (tmp_win->hints.flags & PBaseSize)
{
/*
ICCCM 2 demands that aspect ratio should apply
to width - base_width. To prevent funny results,
we reset PBaseSize in GetWindowSizeHints, if
base is not smaller than min.
*/
dwidth -= baseWidth;
maxWidth -= baseWidth;
minWidth -= baseWidth;
dheight -= baseHeight;
maxHeight -= baseHeight;
minHeight -= baseHeight;
}
if ((minAspectX * dheight > minAspectY * dwidth)&&(xmotion == 0))
{
/* Change width to match */
delta = MAKEMULT(minAspectX * dheight / minAspectY - dwidth,
xinc);
if (dwidth + delta <= maxWidth)
dwidth += delta;
}
if (minAspectX * dheight > minAspectY * dwidth)
{
delta = MAKEMULT(dheight - dwidth*minAspectY/minAspectX,
yinc);
if (dheight - delta >= minHeight)
dheight -= delta;
else
{
delta = MAKEMULT(minAspectX*dheight / minAspectY - dwidth,
xinc);
if (dwidth + delta <= maxWidth)
dwidth += delta;
}
}
if ((maxAspectX * dheight < maxAspectY * dwidth)&&(ymotion == 0))
{
delta = MAKEMULT(dwidth * maxAspectY / maxAspectX - dheight,
yinc);
if (dheight + delta <= maxHeight)
dheight += delta;
}
if ((maxAspectX * dheight < maxAspectY * dwidth))
{
delta = MAKEMULT(dwidth - maxAspectX*dheight/maxAspectY,
xinc);
if (dwidth - delta >= minWidth)
dwidth -= delta;
else
{
delta = MAKEMULT(dwidth * maxAspectY / maxAspectX - dheight, yinc);
if (dheight + delta <= maxHeight)
dheight += delta;
}
}
if (tmp_win->hints.flags & PBaseSize)
{
dwidth += baseWidth;
dheight += baseHeight;
}
}
/*
* Fourth, account for border width and title height
*/
*widthp = dwidth + 2*tmp_win->boundary_width;
*heightp = dheight + tmp_win->title_g.height + 2*tmp_win->boundary_width;
if (IS_MAXIMIZED(tmp_win) && (flags & CS_UPDATE_MAX_DEFECT))
{
/* update size defect for maximized window */
tmp_win->max_g_defect.width = old_w - *widthp;
tmp_win->max_g_defect.height = old_h - *heightp;
}
return;
}
/* This function does roughly the same as constrain_size, but takes into account
* that the window shifts according to gravity if constrain_size actually
* changes the width or height. The frame_g of the window is not changed. The
* target geometry is expected to be in *rect and will be retured through rect.
*/
void gravity_constrain_size(
int gravity, FvwmWindow *t, rectangle *rect, int flags)
{
rectangle old_g = t->frame_g;
rectangle new_g = *rect;
int new_width = new_g.width;
int new_height = new_g.height;
t->frame_g = *rect;
if (IS_MAXIMIZED(t) && (flags & CS_UPDATE_MAX_DEFECT))
{
gravity_resize(
gravity, &new_g, t->max_g_defect.width, t->max_g_defect.height);
t->max_g_defect.width = 0;
t->max_g_defect.height = 0;
new_width = new_g.width;
new_height = new_g.height;
}
constrain_size(
t, (unsigned int *)&new_width, (unsigned int *)&new_height, 0, 0, flags);
if (new_g.width != new_width || new_g.height != new_height)
{
gravity_resize(
gravity, &new_g, new_width - new_g.width, new_height - new_g.height);
}
t->frame_g = old_g;
*rect = new_g;
}
syntax highlighted by Code2HTML, v. 0.9.1