You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1545 lines
42 KiB

/* $Id$
*
* Copyright (c) 2005 Jasper Huijsmans <jasper@xfce.org>
*
* 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 Library 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.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_MATH_H
#include <math.h>
#endif
#include <gtk/gtk.h>
#include <libxfce4util/libxfce4util.h>
#include <libxfce4panel/xfce-panel-window.h>
#include <libxfce4panel/xfce-itembar.h>
#include <libxfce4panel/xfce-panel-item-iface.h>
#include <libxfce4panel/xfce-panel-enums.h>
#include <libxfce4panel/xfce-panel-macros.h>
#include "panel-properties.h"
#include "panel-private.h"
#include "panel-dnd.h"
#define HIDDEN_SIZE 2
#define OPAQUE 0xffffffff
#define HIDE_TIMEOUT 350
#define UNHIDE_TIMEOUT 200
/* prototypes */
static void panel_move_end (XfcePanelWindow *window,
gint x,
gint y);
static void panel_move_function (XfcePanelWindow *window,
Panel *panel,
gint *x,
gint *y);
static void panel_resize_function (XfcePanelWindow *window,
Panel *panel,
GtkAllocation *alloc_old,
GtkAllocation *alloc_new,
gint *x,
gint *y);
static void panel_enter (Panel *p,
GdkEventCrossing *event);
static void panel_leave (Panel *p,
GdkEventCrossing *event);
/* moving and resizing */
static void
_calculate_coordinates (XfceScreenPosition position, /* screen position */
GdkRectangle *screen, /* monitor geometry */
gint width, /* panel size */
gint height,
gint *x, /* output coordinates */
gint *y)
{
switch (position)
{
/* floating */
case XFCE_SCREEN_POSITION_NONE:
case XFCE_SCREEN_POSITION_FLOATING_H:
case XFCE_SCREEN_POSITION_FLOATING_V:
*x = CLAMP (*x, screen->x,
MAX (screen->x, screen->x + screen->width - width));
*y = CLAMP (*y, screen->y,
MAX (screen->y, screen->y + screen->height - height));
break;
/* top */
case XFCE_SCREEN_POSITION_NW_H:
*x = screen->x;
*y = screen->y;
break;
case XFCE_SCREEN_POSITION_N:
*x = MAX (screen->x, screen->x + (screen->width - width) / 2);
*y = screen->y;
break;
case XFCE_SCREEN_POSITION_NE_H:
*x = MAX (screen->x, screen->x + screen->width - width);
*y = screen->y;
break;
/* left */
case XFCE_SCREEN_POSITION_NW_V:
*x = screen->x;
*y = screen->y;
break;
case XFCE_SCREEN_POSITION_W:
*x = screen->x;
*y = MAX (screen->y, screen->y + (screen->height - height) / 2);
break;
case XFCE_SCREEN_POSITION_SW_V:
*x = screen->x;
*y = MAX (screen->y, screen->y + screen->height - height);
break;
/* right */
case XFCE_SCREEN_POSITION_NE_V:
*x = screen->x + screen->width - width;
*y = screen->y;
break;
case XFCE_SCREEN_POSITION_E:
*x = screen->x + screen->width - width;
*y = MAX (screen->y, screen->y + (screen->height - height) / 2);
break;
case XFCE_SCREEN_POSITION_SE_V:
*x = screen->x + screen->width - width;
*y = MAX (screen->y, screen->y + screen->height - height);
break;
/* bottom */
case XFCE_SCREEN_POSITION_SW_H:
*x = screen->x;
*y = screen->y + screen->height - height;
break;
case XFCE_SCREEN_POSITION_S:
*x = MAX (screen->x, screen->x + (screen->width - width) / 2);
*y = screen->y + screen->height - height;
break;
case XFCE_SCREEN_POSITION_SE_H:
*x = MAX (screen->x, screen->x + screen->width - width);
*y = screen->y + screen->height - height;
break;
}
}
static void
_set_borders (Panel *panel)
{
PanelPrivate *priv;
gboolean top, bottom, left, right;
priv = panel->priv;
top = bottom = left = right = TRUE;
switch (priv->screen_position)
{
/* floating */
case XFCE_SCREEN_POSITION_NONE:
case XFCE_SCREEN_POSITION_FLOATING_H:
case XFCE_SCREEN_POSITION_FLOATING_V:
break;
/* top */
case XFCE_SCREEN_POSITION_NW_H:
top = left = FALSE;
right = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
case XFCE_SCREEN_POSITION_N:
top = FALSE;
left = right = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
case XFCE_SCREEN_POSITION_NE_H:
top = right = FALSE;
left = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
/* left */
case XFCE_SCREEN_POSITION_NW_V:
left = top = FALSE;
bottom = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
case XFCE_SCREEN_POSITION_W:
left = FALSE;
top = bottom = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
case XFCE_SCREEN_POSITION_SW_V:
left = bottom = FALSE;
top = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
/* right */
case XFCE_SCREEN_POSITION_NE_V:
right = top = FALSE;
bottom = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
case XFCE_SCREEN_POSITION_E:
right = FALSE;
top = bottom = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
case XFCE_SCREEN_POSITION_SE_V:
right = bottom = FALSE;
top = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
/* bottom */
case XFCE_SCREEN_POSITION_SW_H:
bottom = left = FALSE;
right = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
case XFCE_SCREEN_POSITION_S:
bottom = FALSE;
left = right = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
case XFCE_SCREEN_POSITION_SE_H:
bottom = right = FALSE;
left = (priv->full_width == XFCE_PANEL_NORMAL_WIDTH);
break;
}
xfce_panel_window_set_show_border (XFCE_PANEL_WINDOW (panel),
top, bottom, left, right);
}
/* fairly arbitrary maximum strut size */
static gboolean
_validate_strut( gint width,
gint offset )
{
return ( offset < width / 4 );
}
static void
_set_struts (Panel *panel,
XfceMonitor *xmon,
gint x,
gint y,
gint w,
gint h)
{
PanelPrivate *priv;
gulong data[12] = { 0, };
gint i;
gboolean update = FALSE;
/* _NET_WM_STRUT_PARTIAL: CARDINAL[12]/32
*
* 0: left 1: right 2: top 3: bottom
* 4: left_start_y 5: left_end_y 6: right_start_y 7: right_end_y
* 8: top_start_x 9: top_end_x 10: bottom_start_x 11: bottom_end_x
*
* Note: In xinerama use struts relative to combined screen dimensions,
* not just the current monitor.
*/
priv = panel->priv;
/* use edit_mode property to test if we are changing position */
if (G_UNLIKELY (priv->edit_mode))
return;
if (!priv->autohide &&
!xfce_screen_position_is_floating (priv->screen_position))
{
if (xfce_screen_position_is_left (priv->screen_position))
{
/* no struts possible on Xinerama screens when this monitor
* has neighbors on the left (see fd.o spec).
*/
if (!xmon->has_neighbor_left
&& _validate_strut (xmon->geometry.width, w))
{
data[0] = xmon->geometry.x + w; /* left */
data[4] = y; /* left_start_y */
data[5] = y + h - 1; /* left_end_y */
}
}
else if (xfce_screen_position_is_right (priv->screen_position))
{
/* no struts possible on Xinerama screens when this monitor
* has neighbors on the right (see fd.o spec).
*/
if (!xmon->has_neighbor_right
&& _validate_strut (xmon->geometry.width, w))
{
data[1] = gdk_screen_get_width (xmon->screen)
- xmon->geometry.x - xmon->geometry.width
+ w; /* right */
data[6] = y; /* right_start_y */
data[7] = y + h - 1; /* right_end_y */
}
}
else if (xfce_screen_position_is_top (priv->screen_position))
{
/* no struts possible on Xinerama screens when this monitor
* has neighbors on the top (see fd.o spec).
*/
if (!xmon->has_neighbor_above
&& _validate_strut (xmon->geometry.height, h))
{
data[2] = xmon->geometry.y
+ h; /* top */
data[8] = x; /* top_start_x */
data[9] = x + w - 1; /* top_end_x */
}
}
else
{
/* no struts possible on Xinerama screens when this monitor
* has neighbors on the bottom (see fd.o spec).
*/
if (!xmon->has_neighbor_below
&& _validate_strut (xmon->geometry.height, h))
{
data[3] = gdk_screen_get_height (xmon->screen)
- xmon->geometry.y - xmon->geometry.height
+ h; /* bottom */
data[10] = x; /* bottom_start_x */
data[11] = x + w - 1; /* bottom_end_x */
}
}
}
DBG ("\nStruts\n"
"%ld\t%ld\t%ld\t%ld\n"
"%ld\t%ld\t%ld\t%ld\n"
"%ld\t%ld\t%ld\t%ld\n",
data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7],
data[8], data[9], data[10], data[11]);
/* Check if values have changed. */
for (i = 0; i < 12; i++)
{
if (data[i] != priv->struts[i])
{
update = TRUE;
priv->struts[i] = data[i];
}
}
if (update)
{
gdk_error_trap_push ();
gdk_property_change (GTK_WIDGET (panel)->window,
gdk_atom_intern ("_NET_WM_STRUT_PARTIAL", FALSE),
gdk_atom_intern ("CARDINAL", FALSE), 32,
GDK_PROP_MODE_REPLACE, (guchar *) & data, 12);
gdk_error_trap_pop ();
gdk_error_trap_push ();
gdk_property_change (GTK_WIDGET (panel)->window,
gdk_atom_intern ("_NET_WM_STRUT", FALSE),
gdk_atom_intern ("CARDINAL", FALSE), 32,
GDK_PROP_MODE_REPLACE, (guchar *) & data, 4);
gdk_error_trap_pop ();
}
DBG ("all struts are checked and updated");
}
static gboolean
unblock_struts (Panel *panel)
{
PanelPrivate *priv;
XfceMonitor *xmon;
gint x, y, w, h;
priv = PANEL (panel)->priv;
priv->edit_mode = FALSE;
gtk_window_get_position (GTK_WINDOW (panel), &x, &y);
gtk_window_get_size (GTK_WINDOW (panel), &w, &h);
xmon = panel_app_get_monitor (priv->monitor);
_set_struts (panel, xmon, x, y, w, h);
return FALSE;
}
static void
panel_move_end (XfcePanelWindow *window,
gint x,
gint y)
{
PanelPrivate *priv;
XfceMonitor *xmon;
priv = PANEL (window)->priv;
if (xfce_screen_position_is_floating (priv->screen_position))
{
/* Emit this signal to allow plugins to do something based on the
* new position. E.g. the launcher plugin adjusts the arrow type
* of the associated menu button. */
gtk_container_foreach (GTK_CONTAINER (priv->itembar),
(GtkCallback)xfce_panel_item_set_screen_position,
GINT_TO_POINTER (priv->screen_position));
}
xmon = panel_app_get_monitor (priv->monitor);
priv->xoffset = x - xmon->geometry.x;
priv->yoffset = y - xmon->geometry.y;
DBG ("\n + Position: %d\n + Offset: (%d, %d)",
priv->screen_position, priv->xoffset, priv->yoffset);
panel_app_queue_save ();
}
static void
panel_move_function (XfcePanelWindow *window,
Panel *panel,
gint *x,
gint *y)
{
PanelPrivate *priv;
XfceMonitor *xmon;
GdkScreen *screen;
priv = panel->priv;
screen = gtk_widget_get_screen (GTK_WIDGET (window));
priv->monitor = gdk_screen_get_monitor_at_point (screen, *x, *y);
DBG (" + Monitor at (%d, %d) = %d\n", *x, *y, priv->monitor);
xmon = panel_app_get_monitor (priv->monitor);
_calculate_coordinates (priv->screen_position,
&(xmon->geometry),
GTK_WIDGET (window)->allocation.width,
GTK_WIDGET (window)->allocation.height,
x, y);
}
static void
panel_resize_function (XfcePanelWindow *window,
Panel *panel,
GtkAllocation *alloc_old,
GtkAllocation *alloc_new,
gint *x,
gint *y)
{
PanelPrivate *priv;
XfceMonitor *xmon;
GdkRectangle rect;
DBG ("old: %dx%d\tnew: %dx%d",
alloc_old ? alloc_old->width : 0, alloc_old ? alloc_old->height : 0,
alloc_new->width, alloc_new->height );
if (!GTK_WIDGET_VISIBLE (panel))
return;
priv = panel->priv;
xmon = panel_app_get_monitor (priv->monitor);
if (priv->full_width < XFCE_PANEL_SPAN_MONITORS)
{
_calculate_coordinates (priv->screen_position,
&(xmon->geometry),
alloc_new->width,
alloc_new->height,
x, y);
priv->xoffset = *x - xmon->geometry.x;
priv->yoffset = *y - xmon->geometry.y;
}
else
{
rect.x = 0;
rect.y = 0;
rect.width = gdk_screen_get_width (xmon->screen);
rect.height = gdk_screen_get_height (xmon->screen);
_calculate_coordinates (priv->screen_position,
&rect,
alloc_new->width,
alloc_new->height,
x, y);
priv->xoffset = *x;
priv->yoffset = *y;
}
_set_struts (panel, xmon, *x, *y, alloc_new->width, alloc_new->height);
}
static void
panel_set_position (Panel *panel,
XfceScreenPosition position,
gint xoffset,
gint yoffset)
{
PanelPrivate *priv;
GtkRequisition req;
gint x, y;
XfceMonitor *xmon;
GdkRectangle rect;
if (!GTK_WIDGET_VISIBLE (panel))
return;
priv = panel->priv;
xmon = panel_app_get_monitor (priv->monitor);
priv->screen_position = position;
_set_borders (panel);
gtk_widget_size_request (GTK_WIDGET (panel), &req);
x = y = 0;
/* NOTE: xoffset and yoffset are only used for floating panels */
if (xfce_screen_position_is_floating (position))
{
if (G_UNLIKELY (xoffset < 0 || yoffset < 0))
{
/* This means we switched to being a floating panel;
* calculate the actual position here. */
gtk_window_get_position (GTK_WINDOW (panel), &x, &y);
}
else
{
x = xmon->geometry.x + xoffset;
y = xmon->geometry.y + yoffset;
}
}
if (priv->full_width < XFCE_PANEL_SPAN_MONITORS)
{
_calculate_coordinates (position,
&(xmon->geometry),
req.width,
req.height,
&x, &y);
priv->xoffset = x - xmon->geometry.x;
priv->yoffset = y - xmon->geometry.y;
}
else
{
rect.x = 0;
rect.y = 0;
rect.width = gdk_screen_get_width (xmon->screen);
rect.height = gdk_screen_get_height (xmon->screen);
_calculate_coordinates (position,
&rect,
req.width,
req.height,
&x, &y);
priv->xoffset = x;
priv->yoffset = y;
}
DBG ("\n + Position: %d\n + Offset: (%d, %d)",
priv->screen_position, priv->xoffset, priv->yoffset);
DBG (" + coordinates: (%d, %d)\n", x, y);
gtk_window_move (GTK_WINDOW (panel), x, y);
_set_struts (panel, xmon, x, y, req.width, req.height);
}
/* screen size */
void
panel_screen_size_changed (GdkScreen *screen,
Panel *panel)
{
PanelPrivate *priv;
XfceMonitor *xmon;
XfcePanelWidthType width;
priv = panel->priv;
xmon = panel_app_get_monitor (priv->monitor);
gdk_screen_get_monitor_geometry (screen,
xmon->num,
&(xmon->geometry));
/* new size constraints */
if ((width = priv->full_width) != XFCE_PANEL_NORMAL_WIDTH)
{
priv->full_width = XFCE_PANEL_NORMAL_WIDTH;
panel_set_full_width (panel, width);
}
else
{
gtk_widget_queue_resize (GTK_WIDGET (panel));
}
}
/* transparency and autohide */
static void
_set_transparent (Panel *panel,
gboolean transparent)
{
PanelPrivate *priv;
gulong opacity;
if (!GTK_WIDGET (panel)->window)
return;
priv = panel->priv;
if (G_UNLIKELY (priv->opacity == 0))
{
if (priv->transparency != 0)
{
priv->opacity =
OPAQUE - rint ((gdouble)priv->transparency * OPAQUE / 100);
}
else
{
priv->opacity = OPAQUE;
}
}
opacity = (transparent || priv->activetrans) ? priv->opacity : OPAQUE;
if (opacity != priv->saved_opacity)
{
priv->saved_opacity = opacity;