summaryrefslogtreecommitdiff
path: root/mate-panel/panel-toplevel.c
diff options
context:
space:
mode:
Diffstat (limited to 'mate-panel/panel-toplevel.c')
-rw-r--r--mate-panel/panel-toplevel.c5298
1 files changed, 5298 insertions, 0 deletions
diff --git a/mate-panel/panel-toplevel.c b/mate-panel/panel-toplevel.c
new file mode 100644
index 00000000..043e2e26
--- /dev/null
+++ b/mate-panel/panel-toplevel.c
@@ -0,0 +1,5298 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * panel-toplevel.c: The panel's toplevel window object.
+ *
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ * Copyright (C) 2004 Rob Adams
+ *
+ * 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.
+ *
+ * Authors:
+ * Mark McLoughlin <[email protected]>
+ */
+
+#include <config.h>
+
+#include "panel-toplevel.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+
+#include "panel-profile.h"
+#include "panel-frame.h"
+#include "panel-xutils.h"
+#include "panel-multiscreen.h"
+#include "panel-a11y.h"
+#include "panel-typebuiltins.h"
+#include "panel-marshal.h"
+#include "panel-widget.h"
+#include "panel-bindings.h"
+#include "panel-struts.h"
+#include "panel-config-global.h"
+#include "panel-lockdown.h"
+
+G_DEFINE_TYPE (PanelToplevel, panel_toplevel, GTK_TYPE_WINDOW)
+
+#define PANEL_TOPLEVEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_TOPLEVEL, PanelToplevelPrivate))
+
+#define DEFAULT_SIZE 48
+#define DEFAULT_AUTO_HIDE_SIZE 1
+#define DEFAULT_HIDE_DELAY 300
+#define DEFAULT_UNHIDE_DELAY 100
+#define DEFAULT_DND_THRESHOLD 8
+#define MINIMUM_WIDTH 100
+#define MAXIMUM_SIZE_SCREEN_RATIO 5
+#define SNAP_TOLERANCE_FACTOR 6
+#define DEFAULT_ARROW_SIZE 20
+#define HANDLE_SIZE 10
+#define N_ATTACH_TOPLEVEL_SIGNALS 5
+#define N_ATTACH_WIDGET_SIGNALS 5
+
+typedef enum {
+ PANEL_GRAB_OP_NONE,
+ PANEL_GRAB_OP_MOVE,
+ PANEL_GRAB_OP_RESIZE,
+ PANEL_GRAB_OP_RESIZE_UP,
+ PANEL_GRAB_OP_RESIZE_DOWN,
+ PANEL_GRAB_OP_RESIZE_LEFT,
+ PANEL_GRAB_OP_RESIZE_RIGHT
+} PanelGrabOpType;
+
+struct _PanelToplevelPrivate {
+ gboolean expand;
+ PanelOrientation orientation;
+ int size;
+
+ /* relative to the monitor origin */
+ int x;
+ int y;
+ /* relative to the bottom right corner, -1 to ignore and use x, y*/
+ int x_right;
+ int y_bottom;
+
+ int monitor;
+ /* this is used when the configured monitor is missing. We keep it so
+ * we can move the toplevel to the right monitor when it becomes
+ * available */
+ int configured_monitor;
+
+ int hide_delay;
+ int unhide_delay;
+ int auto_hide_size;
+ PanelAnimationSpeed animation_speed;
+
+ int snap_tolerance;
+ GtkSettings *gtk_settings;
+
+ PanelState state;
+
+ char *name;
+ char *description;
+
+ guint hide_timeout;
+ guint unhide_timeout;
+
+ GdkRectangle geometry;
+ PanelFrameEdge edges;
+
+ int original_width;
+ int original_height;
+
+ PanelGrabOpType grab_op;
+
+ /* The offset within the panel from which the panel
+ * drag was initiated relative to the screen origin.
+ */
+ int drag_offset_x;
+ int drag_offset_y;
+
+ /* Saved state before for cancelled grab op */
+ int orig_monitor;
+ int orig_x;
+ int orig_y;
+ int orig_x_right;
+ int orig_y_bottom;
+ int orig_size;
+ int orig_orientation;
+
+ /* relative to the monitor origin */
+ int animation_end_x;
+ int animation_end_y;
+ int animation_end_width;
+ int animation_end_height;
+ GTimeVal animation_start_time;
+ GTimeVal animation_end_time;
+ guint animation_timeout;
+
+ PanelWidget *panel_widget;
+ PanelFrame *inner_frame;
+ GtkWidget *table;
+ GtkWidget *hide_button_top;
+ GtkWidget *hide_button_bottom;
+ GtkWidget *hide_button_left;
+ GtkWidget *hide_button_right;
+
+ PanelToplevel *attach_toplevel;
+ gulong attach_toplevel_signals [N_ATTACH_TOPLEVEL_SIGNALS];
+ GtkWidget *attach_widget;
+ gulong attach_widget_signals [N_ATTACH_WIDGET_SIGNALS];
+ gint n_autohide_disablers;
+
+ guint auto_hide : 1;
+ guint animate : 1;
+ guint buttons_enabled : 1;
+ guint arrows_enabled : 1;
+
+ /* The co-ordinates are relative to center screen */
+ guint x_centered : 1;
+ guint y_centered : 1;
+
+ /* The panel is not lined up with th screen edge */
+ guint floating : 1;
+
+ /* We are currently animating a hide/show */
+ guint animating : 1;
+
+ /* This is a keyboard initiated grab operation */
+ guint grab_is_keyboard : 1;
+
+ /* The x-y co-ordinates temporarily specify the panel center.
+ * This is used when the panel is rotating, because the width/height
+ * of the toplevel might change, so we need to compute new values for
+ * those. */
+ guint position_centered : 1;
+
+ /* The toplevel is "attached" to another widget */
+ guint attached : 1;
+
+ /* Hidden temporarily because the attach_toplevel was hidden */
+ guint attach_hidden : 1;
+
+ /* More saved grab op state */
+ guint orig_x_centered : 1;
+ guint orig_y_centered : 1;
+
+ /* flag to see if we have already done geometry updating,
+ if not then we're still loading and can ignore many things */
+ guint updated_geometry_initial : 1;
+ /* flag to see if we have done the initial animation */
+ guint initial_animation_done : 1;
+};
+
+enum {
+ HIDE_SIGNAL,
+ UNHIDE_SIGNAL,
+ POPUP_PANEL_MENU_SIGNAL,
+ TOGGLE_EXPAND_SIGNAL,
+ EXPAND_SIGNAL,
+ UNEXPAND_SIGNAL,
+ TOGGLE_HIDDEN_SIGNAL,
+ BEGIN_MOVE_SIGNAL,
+ BEGIN_RESIZE_SIGNAL,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_EXPAND,
+ PROP_ORIENTATION,
+ PROP_SIZE,
+ PROP_X,
+ PROP_X_RIGHT,
+ PROP_X_CENTERED,
+ PROP_Y,
+ PROP_Y_BOTTOM,
+ PROP_Y_CENTERED,
+ PROP_MONITOR,
+ PROP_AUTOHIDE,
+ PROP_HIDE_DELAY,
+ PROP_UNHIDE_DELAY,
+ PROP_AUTOHIDE_SIZE,
+ PROP_ANIMATE,
+ PROP_ANIMATION_SPEED,
+ PROP_BUTTONS_ENABLED,
+ PROP_ARROWS_ENABLED
+};
+
+static guint toplevel_signals[LAST_SIGNAL] = {0};
+static GSList* toplevel_list = NULL;
+
+static void panel_toplevel_calculate_animation_end_geometry(PanelToplevel *toplevel);
+
+static void panel_toplevel_update_monitor(PanelToplevel* toplevel);
+static void panel_toplevel_set_monitor_internal(PanelToplevel* toplevel, int monitor, gboolean force_resize);
+
+
+GSList* panel_toplevel_list_toplevels(void)
+{
+ return toplevel_list;
+}
+
+/* Is this the last un-attached toplevel? */
+gboolean panel_toplevel_is_last_unattached(PanelToplevel* toplevel)
+{
+ GSList* l;
+
+ if (panel_toplevel_get_is_attached(toplevel))
+ {
+ return FALSE;
+ }
+
+ for (l = toplevel_list; l; l = l->next)
+ {
+ PanelToplevel* t = l->data;
+
+ if (t != toplevel && !panel_toplevel_get_is_attached(t))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static GdkScreen* panel_toplevel_get_screen_geometry(PanelToplevel* toplevel, int* width, int* height)
+{
+ GdkScreen* screen;
+
+ g_return_val_if_fail(PANEL_IS_TOPLEVEL (toplevel), NULL);
+ g_return_val_if_fail(width != NULL && height != NULL, NULL);
+
+ screen = gtk_window_get_screen(GTK_WINDOW(toplevel));
+
+ *width = gdk_screen_get_width(screen);
+ *height = gdk_screen_get_height(screen);
+
+ return screen;
+}
+
+static GdkScreen* panel_toplevel_get_monitor_geometry(PanelToplevel* toplevel, int* x, int* y, int* width, int* height)
+{
+ GdkScreen* screen;
+
+ g_return_val_if_fail(PANEL_IS_TOPLEVEL(toplevel), NULL);
+ g_return_val_if_fail(width != NULL && height != NULL, NULL);
+
+ screen = gtk_window_get_screen(GTK_WINDOW(toplevel));
+
+ if (x) *x = panel_multiscreen_x(screen, toplevel->priv->monitor);
+ if (y) *y = panel_multiscreen_y(screen, toplevel->priv->monitor);
+
+ if (width)
+ {
+ *width = panel_multiscreen_width(screen, toplevel->priv->monitor);
+ }
+
+ if (height)
+ {
+ *height = panel_multiscreen_height(screen, toplevel->priv->monitor);
+ }
+
+ return screen;
+}
+
+static GdkCursorType panel_toplevel_grab_op_cursor(PanelToplevel* toplevel, PanelGrabOpType grab_op)
+{
+ GdkCursorType retval = -1;
+
+ switch (grab_op) {
+ case PANEL_GRAB_OP_MOVE:
+ case PANEL_GRAB_OP_RESIZE:
+ if (toplevel->priv->grab_is_keyboard)
+ retval = GDK_CROSS;
+ else
+ retval = GDK_FLEUR;
+ break;
+ break;
+ case PANEL_GRAB_OP_RESIZE_UP:
+ retval = GDK_TOP_SIDE;
+ break;
+ case PANEL_GRAB_OP_RESIZE_DOWN:
+ retval = GDK_BOTTOM_SIDE;
+ break;
+ case PANEL_GRAB_OP_RESIZE_LEFT:
+ retval = GDK_LEFT_SIDE;
+ break;
+ case PANEL_GRAB_OP_RESIZE_RIGHT:
+ retval = GDK_RIGHT_SIDE;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return retval;
+}
+
+static void panel_toplevel_init_resize_drag_offsets(PanelToplevel* toplevel, PanelGrabOpType grab_op)
+{
+ toplevel->priv->drag_offset_x = 0;
+ toplevel->priv->drag_offset_y = 0;
+
+ switch (grab_op) {
+ case PANEL_GRAB_OP_RESIZE_DOWN:
+ toplevel->priv->drag_offset_y = toplevel->priv->geometry.y;
+ break;
+ case PANEL_GRAB_OP_RESIZE_UP:
+ toplevel->priv->drag_offset_y =
+ toplevel->priv->geometry.y + toplevel->priv->geometry.height;
+ break;
+ case PANEL_GRAB_OP_RESIZE_RIGHT:
+ toplevel->priv->drag_offset_x = toplevel->priv->geometry.x;
+ break;
+ case PANEL_GRAB_OP_RESIZE_LEFT:
+ toplevel->priv->drag_offset_x =
+ toplevel->priv->geometry.x + toplevel->priv->geometry.width;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void panel_toplevel_warp_pointer(PanelToplevel* toplevel)
+{
+ GtkWidget *widget;
+ GdkRectangle geometry;
+ int x, y;
+
+ widget = GTK_WIDGET (toplevel);
+
+ geometry = toplevel->priv->geometry;
+
+ x = y = 0;
+
+ switch (toplevel->priv->grab_op) {
+ case PANEL_GRAB_OP_MOVE:
+ case PANEL_GRAB_OP_RESIZE:
+ x = (geometry.width / 2);
+ y = (geometry.height / 2);
+ break;
+ case PANEL_GRAB_OP_RESIZE_UP:
+ x = (geometry.width / 2);
+ break;
+ case PANEL_GRAB_OP_RESIZE_DOWN:
+ x = (geometry.width / 2);
+ y = geometry.height;
+ break;
+ case PANEL_GRAB_OP_RESIZE_LEFT:
+ y = (geometry.height / 2);
+ break;
+ case PANEL_GRAB_OP_RESIZE_RIGHT:
+ x = geometry.width;
+ y = (geometry.height / 2);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (toplevel->priv->grab_op == PANEL_GRAB_OP_MOVE ||
+ toplevel->priv->grab_op == PANEL_GRAB_OP_RESIZE) {
+ toplevel->priv->drag_offset_x = x;
+ toplevel->priv->drag_offset_y = y;
+ } else
+ panel_toplevel_init_resize_drag_offsets (toplevel, toplevel->priv->grab_op);
+
+ panel_warp_pointer (gtk_widget_get_window (widget), x, y);
+}
+
+static void panel_toplevel_begin_attached_move(PanelToplevel* toplevel, gboolean is_keyboard, guint32 time_)
+{
+ PanelWidget *attached_panel_widget;
+
+ attached_panel_widget = panel_toplevel_get_panel_widget (toplevel->priv->attach_toplevel);
+
+ panel_widget_applet_drag_start (attached_panel_widget,
+ toplevel->priv->attach_widget,
+ is_keyboard ? PW_DRAG_OFF_CENTER : PW_DRAG_OFF_CURSOR,
+ time_);
+}
+
+static void panel_toplevel_begin_grab_op(PanelToplevel* toplevel, PanelGrabOpType op_type, gboolean grab_keyboard, guint32 time_)
+{
+ GtkWidget *widget;
+ GdkWindow *window;
+ GdkCursorType cursor_type;
+ GdkCursor *cursor;
+
+ if (toplevel->priv->state != PANEL_STATE_NORMAL ||
+ toplevel->priv->grab_op != PANEL_GRAB_OP_NONE)
+ return;
+
+ if (panel_lockdown_get_locked_down ())
+ return;
+
+ /* If any of the position/orientation are not writable,
+ then we can't really move freely */
+ if (op_type == PANEL_GRAB_OP_MOVE &&
+ ! panel_profile_can_be_moved_freely (toplevel))
+ return;
+
+ /* If size is not writable, then we can't resize */
+ if ((op_type == PANEL_GRAB_OP_RESIZE ||
+ op_type == PANEL_GRAB_OP_RESIZE_UP ||
+ op_type == PANEL_GRAB_OP_RESIZE_DOWN ||
+ op_type == PANEL_GRAB_OP_RESIZE_LEFT ||
+ op_type == PANEL_GRAB_OP_RESIZE_RIGHT) &&
+ ! panel_profile_is_writable_toplevel_size (toplevel))
+ return;
+
+ if (toplevel->priv->attached && op_type == PANEL_GRAB_OP_MOVE) {
+ panel_toplevel_begin_attached_move (toplevel, grab_keyboard, time_);
+ return;
+ }
+
+ widget = GTK_WIDGET (toplevel);
+ window = gtk_widget_get_window (widget);
+
+ toplevel->priv->grab_op = op_type;
+ toplevel->priv->grab_is_keyboard = grab_keyboard;
+
+ toplevel->priv->orig_monitor = toplevel->priv->monitor;
+ toplevel->priv->orig_x = toplevel->priv->x;
+ toplevel->priv->orig_x_right = toplevel->priv->x_right;
+ toplevel->priv->orig_x_centered = toplevel->priv->x_centered;
+ toplevel->priv->orig_y = toplevel->priv->y;
+ toplevel->priv->orig_y_bottom = toplevel->priv->y_bottom;
+ toplevel->priv->orig_y_centered = toplevel->priv->y_centered;
+ toplevel->priv->orig_size = toplevel->priv->size;
+ toplevel->priv->orig_orientation = toplevel->priv->orientation;
+
+ gtk_grab_add (widget);
+
+ if (toplevel->priv->grab_is_keyboard)
+ panel_toplevel_warp_pointer (toplevel);
+
+ cursor_type = panel_toplevel_grab_op_cursor (
+ toplevel, toplevel->priv->grab_op);
+
+ cursor = gdk_cursor_new (cursor_type);
+ gdk_pointer_grab (window, FALSE,
+ GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+ NULL, cursor, time_);
+ gdk_cursor_unref (cursor);
+
+ if (grab_keyboard)
+ gdk_keyboard_grab (window, FALSE, time_);
+}
+
+static void panel_toplevel_end_grab_op (PanelToplevel* toplevel, guint32 time_)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (toplevel->priv->grab_op != PANEL_GRAB_OP_NONE);
+
+ widget = GTK_WIDGET (toplevel);
+
+ toplevel->priv->grab_op = PANEL_GRAB_OP_NONE;
+ toplevel->priv->grab_is_keyboard = FALSE;
+
+ gtk_grab_remove (widget);
+
+ gdk_pointer_ungrab (time_);
+ gdk_keyboard_ungrab (time_);
+}
+
+static void panel_toplevel_cancel_grab_op(PanelToplevel* toplevel, guint32 time_)
+{
+ panel_toplevel_set_orientation (toplevel, toplevel->priv->orig_orientation);
+ panel_toplevel_set_monitor (toplevel, toplevel->priv->orig_monitor);
+ panel_toplevel_set_size (toplevel, toplevel->priv->orig_size);
+ panel_toplevel_set_x (toplevel,
+ toplevel->priv->orig_x,
+ toplevel->priv->orig_x_right,
+ toplevel->priv->orig_x_centered);
+ panel_toplevel_set_y (toplevel,
+ toplevel->priv->orig_y,
+ toplevel->priv->orig_y_bottom,
+ toplevel->priv->orig_y_centered);
+}
+
+static void panel_toplevel_resize_to_pointer(PanelToplevel* toplevel, int x, int y)
+{
+ int new_size;
+ int new_x, new_y;
+ int new_x_right, new_y_bottom;
+ int new_x_centered, new_y_centered;
+ int monitor_width, monitor_height;
+
+ new_size = toplevel->priv->size;
+ new_x = toplevel->priv->x;
+ new_y = toplevel->priv->y;
+ new_x_right = toplevel->priv->x_right;
+ new_y_bottom = toplevel->priv->y_bottom;
+ new_x_centered = toplevel->priv->x_centered;
+ new_y_centered = toplevel->priv->y_centered;
+
+ panel_toplevel_get_monitor_geometry (toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ switch (toplevel->priv->grab_op) {
+ case PANEL_GRAB_OP_RESIZE_UP:
+ new_size = toplevel->priv->drag_offset_y - y;
+ new_size = CLAMP (new_size, 0, monitor_height / 4);
+ new_y -= (new_size - toplevel->priv->size);
+ if (!toplevel->priv->y_centered && (new_y + new_size / 2) > monitor_height / 2)
+ new_y_bottom = monitor_height - (new_y + new_size);
+ else
+ new_y_bottom = -1;
+ break;
+ case PANEL_GRAB_OP_RESIZE_DOWN:
+ new_size = y - toplevel->priv->drag_offset_y;
+ new_size = CLAMP (new_size, 0, monitor_height / 4);
+ if (!toplevel->priv->y_centered && (new_y + new_size / 2) > monitor_height / 2)
+ new_y_bottom = monitor_height - (new_y + new_size);
+ else
+ new_y_bottom = -1;
+ break;
+ case PANEL_GRAB_OP_RESIZE_LEFT:
+ new_size = toplevel->priv->drag_offset_x - x;
+ new_size = CLAMP (new_size, 0, monitor_width / 4);
+ new_x -= (new_size - toplevel->priv->size);
+ if (!toplevel->priv->x_centered && (new_x + new_size / 2) > monitor_width / 2)
+ new_x_right = monitor_width - (new_x + new_size);
+ else
+ new_x_right = -1;
+ break;
+ case PANEL_GRAB_OP_RESIZE_RIGHT:
+ new_size = x - toplevel->priv->drag_offset_x;
+ new_size = CLAMP (new_size, 0, monitor_width / 4);
+ if (!toplevel->priv->x_centered && (new_x + new_size / 2) > monitor_width / 2)
+ new_x_right = monitor_width - (new_x + new_size);
+ else
+ new_x_right = -1;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (new_size == 0)
+ return;
+
+ panel_toplevel_set_x (toplevel, new_x, new_x_right, new_x_centered);
+ panel_toplevel_set_y (toplevel, new_y, new_y_bottom, new_y_centered);
+ panel_toplevel_set_size (toplevel, new_size);
+}
+
+/* this is called for expanded panels that are dragged around */
+static void panel_toplevel_calc_new_orientation(PanelToplevel* toplevel, int pointer_x, int pointer_y)
+{
+ PanelOrientation new_orientation;
+ GdkScreen *screen;
+ int hborder, vborder;
+ int monitor;
+ int monitor_width, monitor_height;
+ int new_x, new_y;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+
+ monitor = panel_multiscreen_get_monitor_at_point (screen, pointer_x, pointer_y);
+
+ if (toplevel->priv->geometry.height < toplevel->priv->geometry.width)
+ vborder = hborder = (3 * toplevel->priv->geometry.height) >> 1;
+ else
+ vborder = hborder = (3 * toplevel->priv->geometry.width) >> 1;
+
+ new_x = pointer_x - panel_multiscreen_x (screen, monitor);
+ new_y = pointer_y - panel_multiscreen_y (screen, monitor);
+ monitor_width = panel_multiscreen_width (screen, monitor);
+ monitor_height = panel_multiscreen_height (screen, monitor);
+
+ new_orientation = toplevel->priv->orientation;
+
+ switch (toplevel->priv->orientation) {
+ case PANEL_ORIENTATION_TOP:
+ if (new_y > (monitor_height - hborder))
+ new_orientation = PANEL_ORIENTATION_BOTTOM;
+
+ else if (new_y > hborder) {
+ if (new_x > (monitor_width - vborder))
+ new_orientation = PANEL_ORIENTATION_RIGHT;
+ else if (new_x < vborder)
+ new_orientation = PANEL_ORIENTATION_LEFT;
+ }
+ break;
+ case PANEL_ORIENTATION_BOTTOM:
+ if (new_y < hborder)
+ new_orientation = PANEL_ORIENTATION_TOP;
+
+ else if (new_y < (monitor_height - hborder)) {
+ if (new_x > (monitor_width - vborder))
+ new_orientation = PANEL_ORIENTATION_RIGHT;
+ else if (new_x < vborder)
+ new_orientation = PANEL_ORIENTATION_LEFT;
+ }
+ break;
+ case PANEL_ORIENTATION_LEFT:
+ if (new_x > (monitor_width - vborder))
+ new_orientation = PANEL_ORIENTATION_RIGHT;
+
+ else if (new_x > vborder) {
+ if (new_y > (monitor_height - hborder))
+ new_orientation = PANEL_ORIENTATION_BOTTOM;
+ else if (new_y < hborder)
+ new_orientation = PANEL_ORIENTATION_TOP;
+ }
+ break;
+ case PANEL_ORIENTATION_RIGHT:
+ if (new_x < vborder)
+ new_orientation = PANEL_ORIENTATION_LEFT;
+
+ else if (new_x < (monitor_width - vborder)) {
+ if (new_y > (monitor_height - hborder))
+ new_orientation = PANEL_ORIENTATION_BOTTOM;
+ else if (new_y < hborder)
+ new_orientation = PANEL_ORIENTATION_TOP;
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ panel_toplevel_set_monitor (toplevel, monitor);
+ panel_toplevel_set_orientation (toplevel, new_orientation);
+}
+
+static void panel_toplevel_move_to(PanelToplevel* toplevel, int new_x, int new_y)
+{
+ GdkScreen *screen;
+ PanelOrientation new_orientation;
+ gboolean x_centered, y_centered;
+ int screen_width, screen_height;
+ int monitor_width, monitor_height;
+ int width, height;
+ int new_monitor;
+ int x, y, x_right, y_bottom;
+ int snap_tolerance;
+
+ screen = panel_toplevel_get_screen_geometry (
+ toplevel, &screen_width, &screen_height);
+
+ width = toplevel->priv->geometry.width;
+ height = toplevel->priv->geometry.height;
+
+ snap_tolerance = toplevel->priv->snap_tolerance;
+
+ new_x = CLAMP (new_x, 0, screen_width - width);
+ new_y = CLAMP (new_y, 0, screen_height - height);
+
+ new_orientation = toplevel->priv->orientation;
+
+ if (new_x <= snap_tolerance &&
+ toplevel->priv->orientation & PANEL_VERTICAL_MASK)
+ new_orientation = PANEL_ORIENTATION_LEFT;
+
+ else if ((new_x + width) >= (screen_width - snap_tolerance) &&
+ toplevel->priv->orientation & PANEL_VERTICAL_MASK)
+ new_orientation = PANEL_ORIENTATION_RIGHT;
+
+ if (new_y <= snap_tolerance &&
+ toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
+ new_orientation = PANEL_ORIENTATION_TOP;
+
+ else if ((new_y + height) >= (screen_height - snap_tolerance) &&
+ toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
+ new_orientation = PANEL_ORIENTATION_BOTTOM;
+
+ new_monitor = panel_multiscreen_get_monitor_at_point (screen, new_x, new_y);
+
+ panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ x_centered = toplevel->priv->x_centered;
+ y_centered = toplevel->priv->y_centered;
+
+ x = new_x - panel_multiscreen_x (screen, new_monitor);
+ y = new_y - panel_multiscreen_y (screen, new_monitor);
+
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK) {
+ y_centered = FALSE;
+ if (new_y <= snap_tolerance ||
+ new_y + height >= screen_height - snap_tolerance)
+ x_centered = abs (x - ((monitor_width - width) / 2))
+ <= snap_tolerance;
+ else
+ x_centered = FALSE;
+ } else {
+ x_centered = FALSE;
+ if (new_x <= snap_tolerance ||
+ new_x + width >= screen_width - snap_tolerance)
+ y_centered = abs (y - ((monitor_height - height) / 2))
+ <= snap_tolerance;
+ else
+ y_centered = FALSE;
+ }
+
+ if (x_centered)
+ x = (monitor_width - width) / 2;
+ if (y_centered)
+ y = (monitor_height - height) / 2;
+
+ if (!x_centered && (x + width / 2) > monitor_width / 2)
+ x_right = monitor_width - (x + width);
+ else
+ x_right = -1;
+
+ if (!y_centered && (y + height / 2) > monitor_height / 2)
+ y_bottom = monitor_height - (y + height);
+ else
+ y_bottom = -1;
+
+ panel_toplevel_set_monitor (toplevel, new_monitor);
+ panel_toplevel_set_orientation (toplevel, new_orientation);
+ panel_toplevel_set_x (toplevel, x, x_right, x_centered);
+ panel_toplevel_set_y (toplevel, y, y_bottom, y_centered);
+}
+
+static void panel_toplevel_move_to_pointer(PanelToplevel* toplevel, int pointer_x, int pointer_y)
+{
+ int new_x, new_y;
+
+ new_x = pointer_x - toplevel->priv->drag_offset_x;
+ new_y = pointer_y - toplevel->priv->drag_offset_y;
+
+ panel_toplevel_move_to (toplevel, new_x, new_y);
+}
+
+static void panel_toplevel_rotate_to_pointer(PanelToplevel* toplevel, int pointer_x, int pointer_y)
+{
+ int x_diff, y_diff;
+ int x, y;
+ int snap_tolerance;
+
+ x = toplevel->priv->geometry.x;
+ y = toplevel->priv->geometry.y;
+ snap_tolerance = toplevel->priv->snap_tolerance;
+
+ x_diff = pointer_x - (x + toplevel->priv->geometry.width / 2);
+ y_diff = pointer_y - (y + toplevel->priv->geometry.height / 2);
+
+ if (((-y_diff > x_diff + snap_tolerance) && x_diff > 0 && y_diff < 0) ||
+ (( y_diff < x_diff + snap_tolerance) && x_diff < 0 && y_diff < 0))
+ panel_toplevel_set_orientation (toplevel, PANEL_ORIENTATION_RIGHT);
+
+ else if (((-x_diff < y_diff - snap_tolerance) && x_diff > 0 && y_diff < 0) ||
+ (( x_diff > y_diff - snap_tolerance) && x_diff > 0 && y_diff > 0))
+ panel_toplevel_set_orientation (toplevel, PANEL_ORIENTATION_BOTTOM);
+
+ else if ((( y_diff > x_diff + snap_tolerance) && x_diff > 0 && y_diff > 0) ||
+ ((-y_diff < x_diff + snap_tolerance) && x_diff < 0 && y_diff > 0))
+ panel_toplevel_set_orientation (toplevel, PANEL_ORIENTATION_LEFT);
+
+ else if (((-x_diff > y_diff - snap_tolerance) && x_diff < 0 && y_diff > 0) ||
+ (( x_diff < y_diff - snap_tolerance) && x_diff < 0 && y_diff < 0))
+ panel_toplevel_set_orientation (toplevel, PANEL_ORIENTATION_TOP);
+}
+
+static gboolean panel_toplevel_warp_pointer_increment(PanelToplevel* toplevel, int keyval, int increment)
+{
+ GdkScreen *screen;
+ GdkWindow *root_window;
+ int new_x, new_y;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+ root_window = gdk_screen_get_root_window (screen);
+
+ gdk_window_get_pointer (root_window, &new_x, &new_y, NULL);
+
+ switch (keyval) {
+ case GDK_Up:
+ case GDK_KP_Up:
+ new_y -= increment;
+ break;
+ case GDK_Left:
+ case GDK_KP_Left:
+ new_x -= increment;
+ break;
+ case GDK_Down:
+ case GDK_KP_Down:
+ new_y += increment;
+ break;
+ case GDK_Right:
+ case GDK_KP_Right:
+ new_x += increment;
+ break;
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ panel_warp_pointer (root_window, new_x, new_y);
+
+ return TRUE;
+}
+
+static gboolean panel_toplevel_move_keyboard_floating(PanelToplevel* toplevel, GdkEventKey* event)
+{
+#define SMALL_INCREMENT 1
+#define NORMAL_INCREMENT 10
+
+ int increment = NORMAL_INCREMENT;
+
+ if ((event->state & gtk_accelerator_get_default_mod_mask ()) == GDK_SHIFT_MASK)
+ increment = SMALL_INCREMENT;
+
+ return panel_toplevel_warp_pointer_increment (
+ toplevel, event->keyval, increment);
+
+#undef SMALL_INCREMENT
+#undef NORMAL_INCREMENT
+}
+
+static gboolean panel_toplevel_move_keyboard_expanded(PanelToplevel* toplevel, GdkEventKey* event)
+{
+ PanelOrientation new_orientation;
+
+ switch (event->keyval) {
+ case GDK_Up:
+ case GDK_KP_Up:
+ new_orientation = PANEL_ORIENTATION_TOP;
+ break;
+ case GDK_Left:
+ case GDK_KP_Left:
+ new_orientation = PANEL_ORIENTATION_LEFT;
+ break;
+ case GDK_Down:
+ case GDK_KP_Down:
+ new_orientation = PANEL_ORIENTATION_BOTTOM;
+ break;
+ case GDK_Right:
+ case GDK_KP_Right:
+ new_orientation = PANEL_ORIENTATION_RIGHT;
+ break;
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ panel_toplevel_set_orientation (toplevel, new_orientation);
+
+ return TRUE;
+}
+
+static gboolean panel_toplevel_initial_resize_keypress(PanelToplevel* toplevel, GdkEventKey* event)
+{
+ PanelGrabOpType grab_op;
+
+ switch (event->keyval) {
+ case GDK_Up:
+ case GDK_KP_Up:
+ if (!(toplevel->priv->orientation & PANEL_HORIZONTAL_MASK))
+ return FALSE;
+ grab_op = PANEL_GRAB_OP_RESIZE_UP;
+ break;
+ case GDK_Left:
+ case GDK_KP_Left:
+ if (!(toplevel->priv->orientation & PANEL_VERTICAL_MASK))
+ return FALSE;
+ grab_op = PANEL_GRAB_OP_RESIZE_LEFT;
+ break;
+ case GDK_Down:
+ case GDK_KP_Down:
+ if (!(toplevel->priv->orientation & PANEL_HORIZONTAL_MASK))
+ return FALSE;
+ grab_op = PANEL_GRAB_OP_RESIZE_DOWN;
+ break;
+ case GDK_Right:
+ case GDK_KP_Right:
+ if (!(toplevel->priv->orientation & PANEL_VERTICAL_MASK))
+ return FALSE;
+ grab_op = PANEL_GRAB_OP_RESIZE_RIGHT;
+ break;
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ panel_toplevel_end_grab_op (toplevel, event->time);
+ panel_toplevel_begin_grab_op (toplevel, grab_op, TRUE, event->time);
+
+ return TRUE;
+}
+
+static gboolean panel_toplevel_handle_grab_op_key_event(PanelToplevel* toplevel, GdkEventKey* event)
+{
+ gboolean retval = FALSE;
+
+ switch (event->keyval) {
+ case GDK_Up:
+ case GDK_KP_Up:
+ case GDK_Left:
+ case GDK_KP_Left:
+ case GDK_Down:
+ case GDK_KP_Down:
+ case GDK_Right:
+ case GDK_KP_Right:
+ switch (toplevel->priv->grab_op) {
+ case PANEL_GRAB_OP_MOVE:
+ if (toplevel->priv->expand)
+ retval = panel_toplevel_move_keyboard_expanded (
+ toplevel, event);
+ else
+ retval = panel_toplevel_move_keyboard_floating (
+ toplevel, event);
+ break;
+ case PANEL_GRAB_OP_RESIZE:
+ retval = panel_toplevel_initial_resize_keypress (toplevel, event);
+ break;
+ case PANEL_GRAB_OP_RESIZE_UP:
+ case PANEL_GRAB_OP_RESIZE_DOWN:
+ case PANEL_GRAB_OP_RESIZE_LEFT:
+ case PANEL_GRAB_OP_RESIZE_RIGHT:
+ retval = panel_toplevel_warp_pointer_increment (
+ toplevel, event->keyval, 1);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ break;
+ case GDK_Escape:
+ panel_toplevel_cancel_grab_op (toplevel, event->time);
+ case GDK_Return: /* drop through*/
+ case GDK_KP_Enter:
+ case GDK_space:
+ case GDK_KP_Space:
+ panel_toplevel_end_grab_op (toplevel, event->time);
+ retval = TRUE;
+ default: /* drop through*/
+ break;
+ }
+
+ return retval;
+}
+
+static gboolean panel_toplevel_handle_grab_op_motion_event(PanelToplevel* toplevel, GdkEventMotion* event)
+{
+ switch (toplevel->priv->grab_op) {
+ case PANEL_GRAB_OP_MOVE:
+ if (toplevel->priv->expand)
+ panel_toplevel_calc_new_orientation (
+ toplevel, event->x_root, event->y_root);
+
+ else if ((event->state & gtk_accelerator_get_default_mod_mask ()) == GDK_CONTROL_MASK)
+ panel_toplevel_rotate_to_pointer (
+ toplevel, event->x_root, event->y_root);
+
+ else
+ panel_toplevel_move_to_pointer (
+ toplevel, event->x_root, event->y_root);
+ return TRUE;
+ case PANEL_GRAB_OP_RESIZE_UP:
+ case PANEL_GRAB_OP_RESIZE_DOWN:
+ case PANEL_GRAB_OP_RESIZE_LEFT:
+ case PANEL_GRAB_OP_RESIZE_RIGHT:
+ panel_toplevel_resize_to_pointer (toplevel, event->x_root, event->y_root);
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void panel_toplevel_calc_floating(PanelToplevel* toplevel)
+{
+ int screen_width, screen_height;
+ int monitor_x, monitor_y;
+ int monitor_width, monitor_height;
+ int x, y;
+ int snap_tolerance;
+
+ if (toplevel->priv->expand) {
+ toplevel->priv->floating = FALSE;
+ return;
+ }
+
+ panel_toplevel_get_screen_geometry (toplevel,
+ &screen_width, &screen_height);
+ panel_toplevel_get_monitor_geometry (toplevel, &monitor_x, &monitor_y,
+ &monitor_width, &monitor_height);
+
+ if (toplevel->priv->x_right == -1)
+ x = monitor_x + toplevel->priv->x;
+ else
+ x = monitor_x + (monitor_width - (toplevel->priv->x_right + toplevel->priv->geometry.width));
+ if (toplevel->priv->y_bottom == -1)
+ y = monitor_y + toplevel->priv->y;
+ else
+ y = monitor_y + (monitor_height - (toplevel->priv->y_bottom + toplevel->priv->geometry.height));
+
+ snap_tolerance = toplevel->priv->snap_tolerance;
+
+ //FIXME? everywhere else, snap_tolerance is relative to the monitor,
+ //not the screen
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
+ toplevel->priv->floating =
+ (y > snap_tolerance) && (y < (screen_height - toplevel->priv->geometry.height - snap_tolerance));
+ else
+ toplevel->priv->floating =
+ (x > snap_tolerance) && (x < (screen_width - toplevel->priv->geometry.width - snap_tolerance));
+}
+
+void panel_toplevel_push_autohide_disabler(PanelToplevel* toplevel)
+{
+ g_return_if_fail (toplevel != NULL);
+
+ if (!toplevel->priv->n_autohide_disablers++)
+ panel_toplevel_queue_auto_hide (toplevel);
+}
+
+void panel_toplevel_pop_autohide_disabler(PanelToplevel* toplevel)
+{
+ g_return_if_fail (toplevel != NULL);
+ g_return_if_fail (toplevel->priv->n_autohide_disablers > 0);
+
+ if (!--toplevel->priv->n_autohide_disablers)
+ panel_toplevel_queue_auto_hide (toplevel);
+}
+
+static gboolean panel_toplevel_get_autohide_disabled(PanelToplevel* toplevel)
+{
+ return toplevel->priv->n_autohide_disablers > 0 ? TRUE : FALSE;
+}
+
+static gboolean panel_toplevel_hide_button_event(PanelToplevel* toplevel, GdkEventButton* event, GtkButton* button)
+{
+ if (event->button == 1)
+ return FALSE;
+
+ return gtk_widget_event (GTK_WIDGET (toplevel), (GdkEvent *) event);
+}
+
+static void panel_toplevel_hide_button_clicked(PanelToplevel* toplevel, GtkButton* button)
+{
+ GtkArrowType arrow_type;
+ gboolean ltr;
+
+ if (toplevel->priv->animating ||
+ toplevel->priv->state == PANEL_STATE_AUTO_HIDDEN)
+ return;
+
+ ltr = gtk_widget_get_direction (GTK_WIDGET (toplevel)) == GTK_TEXT_DIR_LTR;
+ arrow_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "arrow-type"));
+
+ if (toplevel->priv->state == PANEL_STATE_NORMAL) {
+ GtkDirectionType direction = -1;
+
+ switch (arrow_type) {
+ case GTK_ARROW_UP:
+ direction = GTK_DIR_UP;
+ break;
+ case GTK_ARROW_DOWN:
+ direction = GTK_DIR_DOWN;
+ break;
+ case GTK_ARROW_LEFT:
+ direction = ltr ? GTK_DIR_LEFT : GTK_DIR_RIGHT;
+ break;
+ case GTK_ARROW_RIGHT:
+ direction = ltr ? GTK_DIR_RIGHT : GTK_DIR_LEFT;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ panel_toplevel_hide (toplevel, FALSE, direction);
+ } else
+ panel_toplevel_unhide (toplevel);
+}
+
+static GtkWidget *
+panel_toplevel_add_hide_button (PanelToplevel *toplevel,
+ GtkArrowType arrow_type,
+ int left_attach,
+ int right_attach,
+ int top_attach,
+ int bottom_attach)
+{
+ GtkWidget *button;
+ AtkObject *obj;
+ GtkWidget *arrow;
+ int arrow_size;
+
+ button = gtk_button_new ();
+ obj = gtk_widget_get_accessible (button);
+ atk_object_set_name (obj, _("Hide Panel"));
+ gtk_widget_set_can_default (button, FALSE);
+
+ gtk_widget_style_get (GTK_WIDGET (toplevel),
+ "arrow-size", &arrow_size,
+ NULL);
+
+ switch (arrow_type) {
+ case GTK_ARROW_UP:
+ gtk_widget_set_size_request (button, -1, arrow_size);
+ break;
+ case GTK_ARROW_DOWN:
+ gtk_widget_set_size_request (button, -1, arrow_size);
+ break;
+ case GTK_ARROW_LEFT:
+ gtk_widget_set_size_request (button, arrow_size, -1);
+ break;
+ case GTK_ARROW_RIGHT:
+ gtk_widget_set_size_request (button, arrow_size, -1);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ arrow = gtk_arrow_new (arrow_type, GTK_SHADOW_NONE);
+ gtk_misc_set_padding (GTK_MISC (arrow), 0, 0);
+ gtk_container_add (GTK_CONTAINER (button), arrow);
+ gtk_widget_show (arrow);
+
+ g_object_set_data (G_OBJECT (button),
+ "arrow-type",
+ GINT_TO_POINTER (arrow_type));
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (panel_toplevel_hide_button_clicked), toplevel);
+ g_signal_connect_swapped (button, "button_press_event",
+ G_CALLBACK (panel_toplevel_hide_button_event), toplevel);
+ g_signal_connect_swapped (button, "button_release_event",
+ G_CALLBACK (panel_toplevel_hide_button_event), toplevel);
+
+ gtk_table_attach (GTK_TABLE (toplevel->priv->table),
+ button,
+ left_attach,
+ right_attach,
+ top_attach,
+ bottom_attach,
+ GTK_FILL,
+ GTK_FILL,
+ 0,
+ 0);
+
+ return button;
+}
+
+static void panel_toplevel_update_buttons_showing(PanelToplevel* toplevel)
+{
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK) {
+ gtk_widget_hide (toplevel->priv->hide_button_top);
+ gtk_widget_hide (toplevel->priv->hide_button_bottom);
+ gtk_widget_show (toplevel->priv->hide_button_left);
+ gtk_widget_show (toplevel->priv->hide_button_right);
+ } else {
+ gtk_widget_show (toplevel->priv->hide_button_top);
+ gtk_widget_show (toplevel->priv->hide_button_bottom);
+ gtk_widget_hide (toplevel->priv->hide_button_left);
+ gtk_widget_hide (toplevel->priv->hide_button_right);
+ }
+
+ if (toplevel->priv->attached) {
+ switch (panel_toplevel_get_orientation (toplevel->priv->attach_toplevel)) {
+ case PANEL_ORIENTATION_TOP:
+ gtk_widget_hide (toplevel->priv->hide_button_top);
+ break;
+ case PANEL_ORIENTATION_BOTTOM:
+ gtk_widget_hide (toplevel->priv->hide_button_bottom);
+ break;
+ case PANEL_ORIENTATION_LEFT:
+ gtk_widget_hide (toplevel->priv->hide_button_left);
+ break;
+ case PANEL_ORIENTATION_RIGHT:
+ gtk_widget_hide (toplevel->priv->hide_button_right);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+}
+
+static void panel_toplevel_update_hide_buttons(PanelToplevel* toplevel)
+{
+ if (toplevel->priv->buttons_enabled)
+ panel_toplevel_update_buttons_showing (toplevel);
+ else {
+ g_object_set (
+ G_OBJECT (toplevel->priv->hide_button_top),
+ "visible", toplevel->priv->state == PANEL_STATE_HIDDEN_DOWN,
+ NULL);
+ g_object_set (
+ G_OBJECT (toplevel->priv->hide_button_bottom),
+ "visible", toplevel->priv->state == PANEL_STATE_HIDDEN_UP,
+ NULL);
+ g_object_set (
+ G_OBJECT (toplevel->priv->hide_button_left),
+ "visible", toplevel->priv->state == PANEL_STATE_HIDDEN_RIGHT,
+ NULL);
+ g_object_set (
+ G_OBJECT (toplevel->priv->hide_button_right),
+ "visible", toplevel->priv->state == PANEL_STATE_HIDDEN_LEFT,
+ NULL);
+ }
+
+ if (toplevel->priv->arrows_enabled) {
+ int arrow_size;
+
+ gtk_widget_show (gtk_bin_get_child (GTK_BIN (toplevel->priv->hide_button_top)));
+ gtk_widget_show (gtk_bin_get_child (GTK_BIN (toplevel->priv->hide_button_bottom)));
+ gtk_widget_show (gtk_bin_get_child (GTK_BIN (toplevel->priv->hide_button_left)));
+ gtk_widget_show (gtk_bin_get_child (GTK_BIN (toplevel->priv->hide_button_right)));
+
+ gtk_widget_style_get (GTK_WIDGET (toplevel),
+ "arrow-size", &arrow_size,
+ NULL);
+
+ gtk_widget_set_size_request (toplevel->priv->hide_button_top,
+ -1, arrow_size);
+ gtk_widget_set_size_request (toplevel->priv->hide_button_bottom,
+ -1, arrow_size);
+ gtk_widget_set_size_request (toplevel->priv->hide_button_left,
+ arrow_size, -1);
+ gtk_widget_set_size_request (toplevel->priv->hide_button_right,
+ arrow_size, -1);
+ } else {
+ gtk_widget_hide (gtk_bin_get_child (GTK_BIN (toplevel->priv->hide_button_top)));
+ gtk_widget_hide (gtk_bin_get_child (GTK_BIN (toplevel->priv->hide_button_bottom)));
+ gtk_widget_hide (gtk_bin_get_child (GTK_BIN (toplevel->priv->hide_button_left)));
+ gtk_widget_hide (gtk_bin_get_child (GTK_BIN (toplevel->priv->hide_button_right)));
+
+ gtk_widget_set_size_request (toplevel->priv->hide_button_top, -1, -1);
+ gtk_widget_set_size_request (toplevel->priv->hide_button_bottom, -1, -1);
+ gtk_widget_set_size_request (toplevel->priv->hide_button_left, -1, -1);
+ gtk_widget_set_size_request (toplevel->priv->hide_button_right, -1, -1);
+ }
+}
+
+static gboolean panel_toplevel_contains_pointer(PanelToplevel* toplevel)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ GtkWidget *widget;
+ int x, y;
+
+ display = gdk_display_get_default ();
+ widget = GTK_WIDGET (toplevel);
+
+ if (!gtk_widget_get_realized (widget))
+ return FALSE;
+
+ screen = NULL;
+ x = y = -1;
+ gdk_display_get_pointer (display, &screen, &x, &y, NULL);
+
+ if (screen != gtk_window_get_screen (GTK_WINDOW (toplevel)))
+ return FALSE;
+
+ if (x == -1 || y == -1)
+ return FALSE;
+
+ if (x < toplevel->priv->geometry.x || x >= (toplevel->priv->geometry.x + toplevel->priv->geometry.width) ||
+ y < toplevel->priv->geometry.y || y >= (toplevel->priv->geometry.y + toplevel->priv->geometry.height))
+ return FALSE;
+
+ return TRUE;
+}
+
+static inline int panel_toplevel_get_effective_auto_hide_size(PanelToplevel* toplevel)
+{
+ int size;
+
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
+ size = CLAMP (toplevel->priv->auto_hide_size,
+ 1, toplevel->priv->original_height / 2);
+ else
+ size = CLAMP (toplevel->priv->auto_hide_size,
+ 1, toplevel->priv->original_width / 2);
+
+ /* paranoia */
+ return (size <= 0) ? DEFAULT_AUTO_HIDE_SIZE : size;
+}
+
+static gboolean panel_toplevel_update_struts(PanelToplevel* toplevel, gboolean end_of_animation)
+{
+ PanelOrientation orientation;
+ GdkScreen *screen;
+ gboolean geometry_changed = FALSE;
+ int strut, strut_start, strut_end;
+ int x, y, width, height;
+ int monitor_x, monitor_y;
+ int monitor_width, monitor_height;
+
+ if (!toplevel->priv->updated_geometry_initial)
+ return FALSE;
+
+ if (toplevel->priv->attached) {
+ panel_struts_unregister_strut (toplevel);
+ panel_struts_set_window_hint (toplevel);
+ return FALSE;
+ }
+
+ /* In the case of the initial animation, we really want the struts to
+ * represent what is at the end of the animation, to avoid desktop
+ * icons jumping around. */
+ if (!toplevel->priv->initial_animation_done) {
+ end_of_animation = TRUE;
+
+ /* We've not started the animation yet, so we have to compute
+ * where we want to end. Note that we don't want to compute
+ * this everytime, since the struts conflict resolution will be
+ * overridden if we do so */
+ if (!toplevel->priv->animating)
+ panel_toplevel_calculate_animation_end_geometry (toplevel);
+ }
+
+ screen = panel_toplevel_get_monitor_geometry (toplevel,
+ &monitor_x,
+ &monitor_y,
+ &monitor_width,
+ &monitor_height);
+
+ if (end_of_animation) {
+ x = toplevel->priv->animation_end_x;
+ y = toplevel->priv->animation_end_y;
+ x += panel_multiscreen_x (screen, toplevel->priv->monitor);
+ y += panel_multiscreen_y (screen, toplevel->priv->monitor);
+ if (toplevel->priv->animation_end_width != -1)
+ width = toplevel->priv->animation_end_width;
+ else
+ width = toplevel->priv->geometry.width;
+ if (toplevel->priv->animation_end_height != -1)
+ height = toplevel->priv->animation_end_height;
+ else
+ height = toplevel->priv->geometry.height;
+ } else {
+ x = toplevel->priv->geometry.x;
+ y = toplevel->priv->geometry.y;
+ width = toplevel->priv->geometry.width;
+ height = toplevel->priv->geometry.height;
+ }
+
+ orientation = toplevel->priv->orientation;
+
+ strut = strut_start = strut_end = 0;
+
+ if (orientation & PANEL_HORIZONTAL_MASK) {
+ if (y <= monitor_y) {
+ orientation = PANEL_ORIENTATION_TOP;
+ strut = y + height - monitor_y;
+ } else if (y >= monitor_y + monitor_height - height) {
+ orientation = PANEL_ORIENTATION_BOTTOM;
+ strut = monitor_y + monitor_height - y;
+ }
+
+ if (strut > 0) {
+ strut_start = MAX (x, monitor_x);
+ strut_end = MIN (x + width, monitor_x + monitor_width) - 1;
+ }
+ } else {
+ if (x <= monitor_x) {
+ orientation = PANEL_ORIENTATION_LEFT;
+ strut = x + width - monitor_x;
+ } else if (x >= monitor_x + monitor_width - width) {
+ orientation = PANEL_ORIENTATION_RIGHT;
+ strut = monitor_x + monitor_width - x;
+ }
+
+ if (strut > 0) {
+ strut_start = MAX (y, monitor_y);
+ strut_end = MIN (y + height, monitor_y + monitor_height) - 1;
+ }
+ }
+
+ if (orientation != toplevel->priv->orientation) {
+ toplevel->priv->orientation = orientation;
+ g_object_notify (G_OBJECT (toplevel), "orientation");
+ }
+
+ if (toplevel->priv->auto_hide && strut > 0)
+ strut = panel_toplevel_get_effective_auto_hide_size (toplevel);
+
+ if (strut > 0)
+ geometry_changed = panel_struts_register_strut (toplevel,
+ screen,
+ toplevel->priv->monitor,
+ orientation,
+ strut,
+ strut_start,
+ strut_end);
+ else
+ panel_struts_unregister_strut (toplevel);
+
+ if (toplevel->priv->state == PANEL_STATE_NORMAL ||
+ toplevel->priv->state == PANEL_STATE_AUTO_HIDDEN ||
+ toplevel->priv->animating)
+ panel_struts_set_window_hint (toplevel);
+ else
+ panel_struts_unset_window_hint (toplevel);
+
+ return geometry_changed;
+}
+
+void panel_toplevel_update_edges(PanelToplevel* toplevel)
+{
+ GtkWidget *widget;
+ PanelFrameEdge edges;
+ PanelFrameEdge inner_edges;
+ PanelFrameEdge outer_edges;
+ PanelBackground *background;
+ int monitor_width, monitor_height;
+ int width, height;
+ gboolean inner_frame = FALSE;
+
+ widget = GTK_WIDGET (toplevel);
+
+ panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ width = toplevel->priv->geometry.width;
+ height = toplevel->priv->geometry.height;
+
+ edges = PANEL_EDGE_NONE;
+
+ background = &toplevel->priv->panel_widget->background;
+
+ /* We don't want any bevels with a color/image background */
+ if (panel_background_effective_type (background) == PANEL_BACK_NONE) {
+ if (toplevel->priv->geometry.y > 0)
+ edges |= PANEL_EDGE_TOP;
+
+ if (toplevel->priv->geometry.x > 0)
+ edges |= PANEL_EDGE_LEFT;
+
+ if (toplevel->priv->geometry.y < (monitor_height - height))
+ edges |= PANEL_EDGE_BOTTOM;
+
+ if (toplevel->priv->geometry.x < (monitor_width - width))
+ edges |= PANEL_EDGE_RIGHT;
+
+ /* There is a conflict in the position algorithm when a
+ * non-expanded centered panel is nearly the size of the
+ * screen. This is similar to the one we have in
+ * panel_toplevel_update_position(). A simple solution is
+ * to keep the bevels in this case. */
+ if (!toplevel->priv->expand &&
+ toplevel->priv->orientation & PANEL_HORIZONTAL_MASK &&
+ toplevel->priv->x_centered)
+ edges |= PANEL_EDGE_LEFT | PANEL_EDGE_RIGHT;
+
+ if (!toplevel->priv->expand &&
+ toplevel->priv->orientation & PANEL_VERTICAL_MASK &&
+ toplevel->priv->y_centered)
+ edges |= PANEL_EDGE_TOP | PANEL_EDGE_BOTTOM;
+
+ if (gtk_widget_get_visible (toplevel->priv->hide_button_left) ||
+ gtk_widget_get_visible (toplevel->priv->hide_button_right)) {
+ inner_frame = TRUE;
+ edges |= PANEL_EDGE_LEFT | PANEL_EDGE_RIGHT;
+ }
+
+ if (gtk_widget_get_visible (toplevel->priv->hide_button_top) ||
+ gtk_widget_get_visible (toplevel->priv->hide_button_bottom)) {
+ inner_frame = TRUE;
+ edges |= PANEL_EDGE_TOP | PANEL_EDGE_BOTTOM;
+ }
+ }
+
+ if (!inner_frame) {
+ inner_edges = PANEL_EDGE_NONE;
+ outer_edges = edges;
+ } else {
+ inner_edges = edges;
+ outer_edges = PANEL_EDGE_NONE;
+ }
+
+ panel_frame_set_edges (toplevel->priv->inner_frame, inner_edges);
+
+ if (toplevel->priv->edges != outer_edges) {
+ toplevel->priv->edges = outer_edges;
+ gtk_widget_queue_resize (widget);
+ }
+}
+
+static const char* panel_toplevel_construct_description(PanelToplevel *toplevel)
+{
+ int orientation, type;
+
+ static const char *description[4][4] = {
+ {
+ /* translators: these string will be shown in MetaCity's switch window
+ * popup when you pass the focus to a panel */
+ N_("Top Expanded Edge Panel"),
+ N_("Top Centered Panel"),
+ N_("Top Floating Panel"),
+ N_("Top Edge Panel"),
+ },
+
+ {
+ N_("Bottom Expanded Edge Panel"),
+ N_("Bottom Centered Panel"),
+ N_("Bottom Floating Panel"),
+ N_("Bottom Edge Panel"),
+ },
+
+ {
+ N_("Left Expanded Edge Panel"),
+ N_("Left Centered Panel"),
+ N_("Left Floating Panel"),
+ N_("Left Edge Panel"),
+ },
+
+ {
+ N_("Right Expanded Edge Panel"),
+ N_("Right Centered Panel"),
+ N_("Right Floating Panel"),
+ N_("Right Edge Panel"),
+ },
+ };
+
+ if (toplevel->priv->attached)
+ return N_("Drawer");
+
+ switch (toplevel->priv->orientation) {
+ case PANEL_ORIENTATION_TOP:
+ orientation = 0;
+ break;
+ case PANEL_ORIENTATION_BOTTOM:
+ orientation = 1;
+ break;
+ case PANEL_ORIENTATION_LEFT:
+ orientation = 2;
+ break;
+ case PANEL_ORIENTATION_RIGHT:
+ orientation = 3;
+ break;
+ default:
+ orientation = 0;
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (toplevel->priv->expand)
+ type = 0;
+ else if (toplevel->priv->x_centered ||
+ toplevel->priv->y_centered)
+ type = 1;
+ else if (toplevel->priv->floating)
+ type = 2;
+ else
+ type = 3;
+
+ return description[orientation][type];
+}
+
+static void panel_toplevel_update_description(PanelToplevel* toplevel)
+{
+ const char *description;
+
+ description = panel_toplevel_construct_description (toplevel);
+
+ if (toplevel->priv->description &&
+ !strcmp (toplevel->priv->description, description))
+ return;
+
+ if (toplevel->priv->description)
+ g_free (toplevel->priv->description);
+ toplevel->priv->description = g_strdup (_(description));
+
+ if (!toplevel->priv->name)
+ gtk_window_set_title (GTK_WINDOW (toplevel),
+ toplevel->priv->description);
+
+ panel_a11y_set_atk_name_desc (
+ GTK_WIDGET (toplevel->priv->panel_widget),
+ toplevel->priv->name ? toplevel->priv->name :
+ _(toplevel->priv->description),
+ _(toplevel->priv->description));
+}
+
+static void panel_toplevel_update_attached_position(PanelToplevel* toplevel, gboolean hidden, int* x, int* y, int* w, int* h)
+{
+ GtkAllocation attach_allocation;
+ PanelOrientation attach_orientation;
+ GdkRectangle toplevel_box;
+ GdkRectangle parent_box;
+ GdkRectangle attach_box;
+ int x_origin = 0, y_origin = 0;
+ int monitor_x, monitor_y;
+ int monitor_width, monitor_height;
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (toplevel->priv->attach_toplevel)) ||
+ !gtk_widget_get_realized (toplevel->priv->attach_widget))
+ return;
+
+ gtk_widget_get_allocation (GTK_WIDGET (toplevel->priv->attach_widget), &attach_allocation);
+
+ toplevel_box = toplevel->priv->geometry;
+ parent_box = toplevel->priv->attach_toplevel->priv->geometry;
+ attach_box = attach_allocation;
+
+ if (attach_box.x != -1) {
+ gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (toplevel->priv->attach_widget)),
+ &x_origin, &y_origin);
+
+ attach_box.x += x_origin;
+ attach_box.y += y_origin;
+ } else {
+ /* attach_widget isn't allocated. Put the toplevel
+ * off screen.
+ */
+ attach_box.x = -toplevel_box.width;
+ attach_box.y = -toplevel_box.height;
+ }
+
+ attach_orientation = panel_toplevel_get_orientation (
+ toplevel->priv->attach_toplevel);
+
+ if (attach_orientation & PANEL_HORIZONTAL_MASK)
+ *x = attach_box.x + attach_box.width / 2 - toplevel_box.width / 2;
+ else
+ *y = attach_box.y + attach_box.height / 2 - toplevel_box.height / 2;
+
+ switch (attach_orientation) {
+ case PANEL_ORIENTATION_TOP:
+ *y = parent_box.y;
+ if (!hidden)
+ *y += parent_box.height;
+ else
+ *h = parent_box.height;
+ break;
+ case PANEL_ORIENTATION_BOTTOM:
+ *y = parent_box.y;
+ if (!hidden)
+ *y -= toplevel_box.height;
+ else
+ *h = parent_box.height;
+ break;
+ case PANEL_ORIENTATION_LEFT:
+ *x = parent_box.x;
+ if (!hidden)
+ *x += parent_box.width;
+ else
+ *w = parent_box.width;
+ break;
+ case PANEL_ORIENTATION_RIGHT:
+ *x = parent_box.x;
+ if (!hidden)
+ *x -= toplevel_box.width;
+ else
+ *w = parent_box.width;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ panel_toplevel_get_monitor_geometry (toplevel,
+ &monitor_x,
+ &monitor_y,
+ &monitor_width,
+ &monitor_height);
+
+ *x -= monitor_x;
+ *y -= monitor_y;
+
+ if (toplevel->priv->orientation & PANEL_VERTICAL_MASK)
+ *x = CLAMP (*x, 0, monitor_width - toplevel->priv->original_width);
+ else
+ *y = CLAMP (*y, 0, monitor_height - toplevel->priv->original_height);
+}
+
+static void panel_toplevel_update_normal_position(PanelToplevel* toplevel, int* x, int* y, int* w, int* h)
+{
+ int monitor_width, monitor_height;
+ int width, height;
+ int snap_tolerance;
+
+ g_assert (x != NULL && y != NULL);
+
+ if (toplevel->priv->attached) {
+ panel_toplevel_update_attached_position (toplevel, FALSE, x, y, w, h);
+ return;
+ }
+
+ panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ width = toplevel->priv->original_width;
+ height = toplevel->priv->original_height;
+ snap_tolerance = toplevel->priv->snap_tolerance;
+
+ *x = CLAMP (*x, 0, monitor_width - width);
+ *y = CLAMP (*y, 0, monitor_height - height);
+
+ if (toplevel->priv->x <= snap_tolerance &&
+ toplevel->priv->x_right == -1 &&
+ !toplevel->priv->x_centered)
+ *x = 0;
+ else if (toplevel->priv->x_right != -1 &&
+ toplevel->priv->x_right <= snap_tolerance &&
+ !toplevel->priv->x_centered)
+ *x = monitor_width - width;
+
+ if (toplevel->priv->y <= snap_tolerance &&
+ toplevel->priv->y_bottom == -1 &&
+ !toplevel->priv->y_centered)
+ *y = 0;
+ else if (toplevel->priv->y_bottom != -1 &&
+ toplevel->priv->y_bottom <= snap_tolerance &&
+ !toplevel->priv->y_centered)
+ *y = monitor_height - height;
+}
+
+static void
+panel_toplevel_update_auto_hide_position (PanelToplevel *toplevel,
+ int *x,
+ int *y,
+ int *w,
+ int *h,
+ gboolean for_end_position)
+{
+ int width, height;
+ int monitor_width, monitor_height;
+ int auto_hide_size;
+ int snap_tolerance;
+
+ g_assert (x != NULL && y != NULL);
+
+ if (toplevel->priv->floating) {
+ panel_toplevel_update_normal_position (toplevel, x, y, w, h);
+ return;
+ }
+
+ panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ width = toplevel->priv->original_width;
+ height = toplevel->priv->original_height;
+ snap_tolerance = toplevel->priv->snap_tolerance;
+
+ /* For the initial animation, we animate from outside the screen, and
+ * so we don't want the toplevel to be visible at all. But when the
+ * request is for the end position, then we give the real result (it's
+ * useful for struts) */
+ if (for_end_position || toplevel->priv->initial_animation_done) {
+ auto_hide_size = panel_toplevel_get_effective_auto_hide_size (toplevel);
+ } else {
+ auto_hide_size = 0;
+ }
+
+ switch (toplevel->priv->orientation) {
+ case PANEL_ORIENTATION_TOP:
+ *y = - (height - auto_hide_size);
+ break;
+ case PANEL_ORIENTATION_BOTTOM:
+ *y = monitor_height - auto_hide_size;
+ break;
+ case PANEL_ORIENTATION_LEFT:
+ *x = - (width - auto_hide_size);
+ break;
+ case PANEL_ORIENTATION_RIGHT:
+ *x = monitor_width - auto_hide_size;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK) {
+ if (toplevel->priv->x <= snap_tolerance &&
+ toplevel->priv->x_right == -1 &&
+ !toplevel->priv->x_centered)
+ *x = 0;
+ else if (toplevel->priv->x_right != -1 &&
+ toplevel->priv->x_right <= snap_tolerance &&
+ !toplevel->priv->x_centered)
+ *x = monitor_width - width;
+ } else /* if (toplevel->priv->orientation & PANEL_VERTICAL_MASK) */ {
+ if (toplevel->priv->y <= snap_tolerance &&
+ toplevel->priv->y_bottom == -1 &&
+ !toplevel->priv->y_centered)
+ *y = 0;
+ else if (toplevel->priv->y_bottom != -1 &&
+ toplevel->priv->y_bottom <= snap_tolerance &&
+ !toplevel->priv->y_centered)
+ *y = monitor_height - height;
+ }
+}
+
+/* FIXME: this is wrong for Xinerama. In the Xinerama case
+ * I think if hiding it requires it to go onto the
+ * next monitor then it should just move it on to
+ * the next monitor and set its state back to normal
+ */
+static void
+panel_toplevel_update_hidden_position (PanelToplevel *toplevel,
+ int *x,
+ int *y,
+ int *w,
+ int *h)
+{
+ int width, height;
+ int min_hide_size;
+ int monitor_height, monitor_width;
+ GtkAllocation hide_allocation;
+
+ g_assert (x != NULL && y != NULL);
+
+ g_assert (toplevel->priv->state == PANEL_STATE_HIDDEN_UP ||
+ toplevel->priv->state == PANEL_STATE_HIDDEN_DOWN ||
+ toplevel->priv->state == PANEL_STATE_HIDDEN_LEFT ||
+ toplevel->priv->state == PANEL_STATE_HIDDEN_RIGHT);
+
+ if (toplevel->priv->attached) {
+ panel_toplevel_update_attached_position (toplevel, TRUE, x, y, w, h);
+ return;
+ }
+
+ panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ width = toplevel->priv->original_width;
+ height = toplevel->priv->original_height;
+
+ //FIXME should find a better default
+ min_hide_size = DEFAULT_AUTO_HIDE_SIZE;
+
+ switch (toplevel->priv->state) {
+ case PANEL_STATE_HIDDEN_UP:
+ gtk_widget_get_allocation (toplevel->priv->hide_button_bottom,
+ &hide_allocation);
+ *y = - (height - MAX (hide_allocation.height, min_hide_size));
+ break;
+ case PANEL_STATE_HIDDEN_DOWN:
+ gtk_widget_get_allocation (toplevel->priv->hide_button_top,
+ &hide_allocation);
+ *y = monitor_height - MAX (hide_allocation.height, min_hide_size);
+ break;
+ case PANEL_STATE_HIDDEN_LEFT:
+ gtk_widget_get_allocation (toplevel->priv->hide_button_right,
+ &hide_allocation);
+ *x = - (width - MAX (hide_allocation.width, min_hide_size));
+ break;
+ case PANEL_STATE_HIDDEN_RIGHT:
+ gtk_widget_get_allocation (toplevel->priv->hide_button_left,
+ &hide_allocation);
+ *x = monitor_width - MAX (hide_allocation.width, min_hide_size);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+/*
+ * This is "almost" like the double sine movement
+ * from the original panel except that it uses
+ * a cubic (twice again). I suppose it looks less
+ * mathematical now :) -- _v_
+ */
+static int
+get_delta (int src,
+ int dest,
+ GTimeVal *start_time,
+ GTimeVal *end_time,
+ GTimeVal *cur_time)
+{
+ double x, s, n, d, percentage;
+
+ s = start_time->tv_sec + ((double)start_time->tv_usec / G_USEC_PER_SEC);
+ n = cur_time->tv_sec + ((double)cur_time->tv_usec / G_USEC_PER_SEC);
+ d = end_time->tv_sec + ((double)end_time->tv_usec / G_USEC_PER_SEC);
+
+ n -= s;
+ d -= s;
+
+ if (abs (dest - src) <= 1 || n >= d)
+ return dest - src;
+
+ /* The cubic is: p(x) = (-2) x^2 (x-1.5) */
+ /* running p(p(x)) to make it more "pronounced",
+ * effectively making it a ninth-degree polynomial */
+
+ x = (double)n/d;
+ x = -2 * (x*x) * (x-1.5);
+ /* run it again */
+ percentage = -2 * (x*x) * (x-1.5);
+
+ percentage = CLAMP (percentage, 0.0, 1.0);
+
+ return ((dest - src) * percentage);
+}
+
+static void
+panel_toplevel_update_animating_position (PanelToplevel *toplevel)
+{
+ GdkScreen *screen;
+ GTimeVal time_val;
+ int deltax, deltay, deltaw = 0, deltah = 0;
+ int monitor_offset_x, monitor_offset_y;
+
+ g_get_current_time (&time_val);
+
+ screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+
+ monitor_offset_x = panel_multiscreen_x (screen, toplevel->priv->monitor);
+ monitor_offset_y = panel_multiscreen_y (screen, toplevel->priv->monitor);
+
+ if (toplevel->priv->animation_end_width != -1)
+ deltaw = get_delta (toplevel->priv->geometry.width,
+ toplevel->priv->animation_end_width,
+ &toplevel->priv->animation_start_time,
+ &toplevel->priv->animation_end_time,
+ &time_val);
+
+ if (toplevel->priv->animation_end_height != -1)
+ deltah = get_delta (toplevel->priv->geometry.height,
+ toplevel->priv->animation_end_height,
+ &toplevel->priv->animation_start_time,
+ &toplevel->priv->animation_end_time,
+ &time_val);
+
+ deltax = get_delta (toplevel->priv->geometry.x - monitor_offset_x,
+ toplevel->priv->animation_end_x,
+ &toplevel->priv->animation_start_time,
+ &toplevel->priv->animation_end_time,
+ &time_val);
+
+ deltay = get_delta (toplevel->priv->geometry.y - monitor_offset_y,
+ toplevel->priv->animation_end_y,
+ &toplevel->priv->animation_start_time,
+ &toplevel->priv->animation_end_time,
+ &time_val);
+
+ if (deltaw != 0 && abs (deltaw) > abs (deltax))
+ deltax = deltaw;
+ if (deltah != 0 && abs (deltah) > abs (deltay))
+ deltay = deltah;
+
+ toplevel->priv->geometry.x += deltax;
+ toplevel->priv->geometry.y += deltay;
+
+ toplevel->priv->geometry.width += deltaw;
+ toplevel->priv->geometry.height += deltah;
+
+ if (toplevel->priv->geometry.x - monitor_offset_x == toplevel->priv->animation_end_x &&
+ toplevel->priv->geometry.y - monitor_offset_y == toplevel->priv->animation_end_y) {
+ toplevel->priv->animating = FALSE;
+ /* Note: it's important to set initial_animation_done to TRUE
+ * as soon as possible (hence, here) since we don't want to
+ * have a wrong value in a size request event */
+ toplevel->priv->initial_animation_done = TRUE;
+
+ if (toplevel->priv->attached && panel_toplevel_get_is_hidden (toplevel))
+ gtk_widget_unmap (GTK_WIDGET (toplevel));
+ else
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ if (toplevel->priv->state == PANEL_STATE_NORMAL)
+ g_signal_emit (toplevel, toplevel_signals [UNHIDE_SIGNAL], 0);
+ }
+}
+
+static void
+panel_toplevel_update_expanded_position (PanelToplevel *toplevel)
+{
+ GdkScreen *screen;
+ int monitor_width, monitor_height;
+ int screen_width, screen_height;
+ int monitor_x, monitor_y;
+ int x, y;
+ int x_right, y_bottom;
+ int monitor;
+
+ if (!toplevel->priv->expand)
+ return;
+
+ screen = panel_toplevel_get_screen_geometry (toplevel,
+ &screen_width,
+ &screen_height);
+
+ panel_toplevel_get_monitor_geometry (toplevel, &monitor_x, &monitor_y,
+ &monitor_width, &monitor_height);
+
+ x = -1;
+ y = -1;
+ x_right = -1;
+ y_bottom = -1;
+
+ switch (toplevel->priv->orientation) {
+ case PANEL_ORIENTATION_TOP:
+ x = monitor_x;
+ y = monitor_y;
+ break;
+ case PANEL_ORIENTATION_LEFT:
+ x = monitor_x;
+ y = monitor_y;
+ break;
+ case PANEL_ORIENTATION_BOTTOM:
+ x = monitor_x;
+ y = monitor_y + monitor_height - toplevel->priv->geometry.height;
+ y_bottom = 0;
+ break;
+ case PANEL_ORIENTATION_RIGHT:
+ x = monitor_x + monitor_width - toplevel->priv->geometry.width;
+ y = monitor_y;
+ x_right = 0;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ monitor = panel_multiscreen_get_monitor_at_point (screen, x, y);
+
+ panel_toplevel_set_monitor_internal (toplevel, monitor, TRUE);
+
+ x -= panel_multiscreen_x (screen, monitor);
+ y -= panel_multiscreen_y (screen, monitor);
+
+ g_object_freeze_notify (G_OBJECT (toplevel));
+
+ if (toplevel->priv->x != x) {
+ toplevel->priv->x = x;
+ g_object_notify (G_OBJECT (toplevel), "x");
+ }
+
+ if (toplevel->priv->y != y) {
+ toplevel->priv->y = y;
+ g_object_notify (G_OBJECT (toplevel), "y");
+ }
+
+ if (toplevel->priv->x_right != x_right) {
+ toplevel->priv->x_right = x_right;
+ g_object_notify (G_OBJECT (toplevel), "x_right");
+ }
+
+ if (toplevel->priv->y_bottom != y_bottom) {
+ toplevel->priv->y_bottom = y_bottom;
+ g_object_notify (G_OBJECT (toplevel), "y_bottom");
+ }
+
+ g_object_thaw_notify (G_OBJECT (toplevel));
+}
+
+static void
+panel_toplevel_update_position (PanelToplevel *toplevel)
+{
+ PanelBackground *background;
+ GdkScreen *screen;
+ int x, y;
+ int w, h;
+ int screen_width, screen_height;
+ int monitor_width, monitor_height;
+
+ screen = panel_toplevel_get_screen_geometry (
+ toplevel, &screen_width, &screen_height);
+
+ panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ if (toplevel->priv->animating) {
+ panel_toplevel_update_animating_position (toplevel);
+ return;
+ }
+
+ if (toplevel->priv->position_centered) {
+ toplevel->priv->position_centered = FALSE;
+
+ g_object_freeze_notify (G_OBJECT (toplevel));
+
+ if (!toplevel->priv->x_centered) {
+ int x_right;
+
+ toplevel->priv->x -= toplevel->priv->geometry.width / 2;
+ g_object_notify (G_OBJECT (toplevel), "x");
+
+ if ((toplevel->priv->x + toplevel->priv->geometry.width / 2) > monitor_width / 2)
+ x_right = monitor_width - (toplevel->priv->x + toplevel->priv->geometry.width);
+ else
+ x_right = -1;
+ if (toplevel->priv->x_right != x_right) {
+ toplevel->priv->x_right = x_right;
+ g_object_notify (G_OBJECT (toplevel),
+ "x-right");
+ }
+ }
+
+ if (!toplevel->priv->y_centered) {
+ int y_bottom;
+
+ toplevel->priv->y -= toplevel->priv->geometry.height / 2;
+ g_object_notify (G_OBJECT (toplevel), "y");
+
+ if ((toplevel->priv->y + toplevel->priv->geometry.height / 2) > monitor_height / 2)
+ y_bottom = monitor_height - (toplevel->priv->y + toplevel->priv->geometry.height);
+ else
+ y_bottom = -1;
+ if (toplevel->priv->y_bottom != y_bottom) {
+ toplevel->priv->y_bottom = y_bottom;
+ g_object_notify (G_OBJECT (toplevel),
+ "y-bottom");
+ }
+ }
+
+ g_object_thaw_notify (G_OBJECT (toplevel));
+ }
+
+ panel_toplevel_update_expanded_position (toplevel);
+ panel_toplevel_calc_floating (toplevel); //FIXME should probably be done after panel_toplevel_update_normal_position() too
+
+ if (toplevel->priv->x_right == -1)
+ x = toplevel->priv->x;
+ else
+ x = monitor_width - (toplevel->priv->x_right + toplevel->priv->geometry.width);
+ if (toplevel->priv->y_bottom == -1)
+ y = toplevel->priv->y;
+ else
+ y = monitor_height - (toplevel->priv->y_bottom + toplevel->priv->geometry.height);
+
+ if (!toplevel->priv->expand) {
+ if (toplevel->priv->x_centered)
+ x = (monitor_width - toplevel->priv->geometry.width) / 2;
+ if (toplevel->priv->y_centered)
+ y = (monitor_height - toplevel->priv->geometry.height) / 2;
+ }
+
+ w = h = -1;
+
+ if (toplevel->priv->state == PANEL_STATE_NORMAL)
+ panel_toplevel_update_normal_position (toplevel, &x, &y, &w, &h);
+
+ else if (toplevel->priv->state == PANEL_STATE_AUTO_HIDDEN)
+ panel_toplevel_update_auto_hide_position (toplevel, &x, &y, &w, &h, FALSE);
+
+ else
+ panel_toplevel_update_hidden_position (toplevel, &x, &y, &w, &h);
+
+ if (w != -1)
+ toplevel->priv->geometry.width = w;
+ if (h != -1)
+ toplevel->priv->geometry.height = h;
+
+ /* This is some kind of snap: there's a possibility of an infinite loop
+ * because of the bevels of the frame that are set in
+ * panel_toplevel_update_edges(). The bevels change the width/height of
+ * the toplevel. The typical loop is:
+ * x = 1 => outer bevel => x = 0 => no outer bevel = > x = 1 => ...
+ * FIXME: maybe the real bug is that we enter into this loop (see bug
+ * #160748 to learn how to reproduce.) */
+ background = &toplevel->priv->panel_widget->background;
+ /* There's no bevels with a color/image background */
+ if (panel_background_effective_type (background) == PANEL_BACK_NONE) {
+ GtkStyle *style;
+ GdkRectangle *geometry;
+ int max_size;
+
+ style = gtk_widget_get_style (GTK_WIDGET (toplevel->priv->inner_frame));
+ geometry = &toplevel->priv->geometry;
+
+ if (x <= style->xthickness && x > 0 &&
+ !toplevel->priv->x_centered)
+ x = 0;
+
+ if (y <= style->ythickness && y > 0 &&
+ !toplevel->priv->y_centered)
+ y = 0;
+
+ max_size = monitor_width - geometry->width - style->xthickness;
+ if (x + style->xthickness >= max_size && x < max_size &&
+ !toplevel->priv->x_centered)
+ x = max_size;
+
+ max_size = monitor_height - geometry->height - style->ythickness;
+ if (y + style->ythickness >= max_size && y < max_size &&
+ !toplevel->priv->y_centered)
+ y = max_size;
+ }
+
+ x += panel_multiscreen_x (screen, toplevel->priv->monitor);
+ y += panel_multiscreen_y (screen, toplevel->priv->monitor);
+
+ toplevel->priv->geometry.x = x;
+ toplevel->priv->geometry.y = y;
+}
+
+static int
+calculate_minimum_height (GtkWidget *widget,
+ PanelOrientation orientation)
+{
+ GtkStyle *style;
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ int focus_width = 0;
+ int focus_pad = 0;
+ int ascent;
+ int descent;
+ int thickness;
+
+ style = gtk_widget_get_style (widget);
+ context = gtk_widget_get_pango_context (widget);
+ metrics = pango_context_get_metrics (context,
+ style->font_desc,
+ pango_context_get_language (context));
+
+ ascent = pango_font_metrics_get_ascent (metrics);
+ descent = pango_font_metrics_get_descent (metrics);
+
+ pango_font_metrics_unref (metrics);
+
+ gtk_widget_style_get (widget,
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
+ thickness = orientation & PANEL_HORIZONTAL_MASK ?
+ style->ythickness :
+ style->xthickness;
+
+ return PANGO_PIXELS (ascent + descent) + 2 * (focus_width + focus_pad + thickness);
+}
+
+static int
+panel_toplevel_update_size_from_hints (PanelToplevel *toplevel,
+ int requisition_size,
+ int monitor_size,
+ int non_panel_widget_size)
+{
+ int nb_size_hints;
+ AppletSizeHints *applets_hints;
+ AppletSizeHintsAlloc *using_hint;
+
+ int i;
+ int total_size;
+ int full_hints;
+
+ total_size = non_panel_widget_size + requisition_size;
+
+ nb_size_hints = toplevel->priv->panel_widget->nb_applets_size_hints;
+ if (nb_size_hints <= 0)
+ return total_size;
+
+ applets_hints = toplevel->priv->panel_widget->applets_hints;
+ using_hint = toplevel->priv->panel_widget->applets_using_hint;
+
+ for (i = 0; i < nb_size_hints; i++) {
+ using_hint[i].index = applets_hints[i].len - 2;
+ using_hint[i].size = applets_hints[i].hints[applets_hints[i].len - 1];
+ total_size += using_hint[i].size;
+ }
+
+ if (total_size > monitor_size)
+ return monitor_size;
+
+ full_hints = 0;
+ while (full_hints != nb_size_hints && total_size < monitor_size) {
+ int bonus;
+ int extra_bonus;
+
+ bonus = (monitor_size - total_size)
+ / (nb_size_hints - full_hints);
+ extra_bonus = (monitor_size - total_size)
+ % (nb_size_hints - full_hints);
+ full_hints = 0;
+
+ for (i = 0; i < nb_size_hints; i++) {
+ int new_size;
+ int current_bonus;
+
+ current_bonus = bonus;
+
+ while (using_hint[i].index > 0 && applets_hints[i].hints[using_hint[i].index - 1] < using_hint[i].size + current_bonus) {
+ new_size = applets_hints[i].hints[using_hint[i].index - 1];
+ current_bonus = using_hint[i].size
+ + current_bonus - new_size;
+ total_size = total_size - using_hint[i].size
+ + new_size;
+
+ using_hint[i].index -= 2;
+ using_hint[i].size = new_size;
+ }
+
+ new_size = MIN (applets_hints[i].hints[using_hint[i].index],
+ using_hint[i].size + current_bonus);
+ if (new_size > using_hint[i].size) {
+ total_size += (new_size - using_hint[i].size);
+ using_hint[i].size = new_size;
+ }
+
+ if (extra_bonus > 0) {
+ new_size = MIN (applets_hints[i].hints[using_hint[i].index],
+ using_hint[i].size + extra_bonus);
+ if (new_size > using_hint[i].size) {
+ total_size += (new_size
+ - using_hint[i].size);
+ extra_bonus -= (new_size
+ - using_hint[i].size);
+ using_hint[i].size = new_size;
+ }
+ }
+
+ if (using_hint[i].size == applets_hints[i].hints[using_hint[i].index])
+ full_hints++;
+ }
+ }
+
+ return total_size;
+}
+
+static void
+panel_toplevel_update_size (PanelToplevel *toplevel,
+ GtkRequisition *requisition)
+{
+ GtkWidget *widget;
+ GtkStyle *style;
+ int monitor_width, monitor_height;
+ int width, height;
+ int minimum_height;
+ int non_panel_widget_size;
+
+ if (toplevel->priv->animating)
+ return;
+
+ widget = GTK_WIDGET (toplevel);
+ style = gtk_widget_get_style (widget);
+
+ panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ width = requisition->width;
+ height = requisition->height;
+
+ if (!toplevel->priv->expand &&
+ !toplevel->priv->buttons_enabled && !toplevel->priv->attached)
+ non_panel_widget_size = 2 * HANDLE_SIZE;
+ else
+ non_panel_widget_size = 0;
+
+ minimum_height = calculate_minimum_height (GTK_WIDGET (toplevel),
+ toplevel->priv->orientation);
+
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK) {
+
+ height = MAX (MIN (MAX (height, toplevel->priv->size),
+ panel_toplevel_get_maximum_size (toplevel)),
+ minimum_height);
+
+ if (toplevel->priv->expand)
+ width = monitor_width;
+ else {
+ int max_width;
+
+ if (!toplevel->priv->attached)
+ max_width = monitor_width;
+ else {
+ if (panel_toplevel_get_orientation (toplevel->priv->attach_toplevel) == PANEL_ORIENTATION_LEFT)
+ max_width = monitor_width
+ - toplevel->priv->geometry.x;
+ else
+ max_width = toplevel->priv->geometry.x +
+ toplevel->priv->geometry.width;
+ }
+
+ width = panel_toplevel_update_size_from_hints (
+ toplevel,
+ requisition->width,
+ max_width,
+ non_panel_widget_size);
+ }
+
+ width = MAX (MINIMUM_WIDTH, width);
+ } else {
+ width = MAX (MIN (MAX (width, toplevel->priv->size),
+ panel_toplevel_get_maximum_size (toplevel)),
+ minimum_height);
+
+ if (toplevel->priv->expand)
+ height = monitor_height;
+ else {
+ int max_height;
+
+ if (!toplevel->priv->attached)
+ max_height = monitor_height;
+ else {
+ if (panel_toplevel_get_orientation (toplevel->priv->attach_toplevel) == PANEL_ORIENTATION_TOP)
+ max_height = monitor_height
+ - toplevel->priv->geometry.y;
+ else
+ max_height = toplevel->priv->geometry.y +
+ toplevel->priv->geometry.height;
+ }
+
+ height = panel_toplevel_update_size_from_hints (
+ toplevel,
+ requisition->height,
+ max_height,
+ non_panel_widget_size);
+ }
+
+ height = MAX (MINIMUM_WIDTH, height);
+ }
+
+ if (toplevel->priv->edges & PANEL_EDGE_TOP)
+ height += style->ythickness;
+ if (toplevel->priv->edges & PANEL_EDGE_BOTTOM)
+ height += style->ythickness;
+ if (toplevel->priv->edges & PANEL_EDGE_LEFT)
+ width += style->ythickness;
+ if (toplevel->priv->edges & PANEL_EDGE_RIGHT)
+ width += style->ythickness;
+
+ toplevel->priv->geometry.width = CLAMP (width, 0, monitor_width);
+ toplevel->priv->geometry.height = CLAMP (height, 0, monitor_height);
+ toplevel->priv->original_width = toplevel->priv->geometry.width;
+ toplevel->priv->original_height = toplevel->priv->geometry.height;
+}
+
+static void
+panel_toplevel_update_geometry (PanelToplevel *toplevel,
+ GtkRequisition *requisition)
+{
+ toplevel->priv->updated_geometry_initial = TRUE;
+ panel_toplevel_update_size (toplevel, requisition);
+ panel_toplevel_update_position (toplevel);
+
+ panel_toplevel_update_struts (toplevel, FALSE);
+
+ if (toplevel->priv->state == PANEL_STATE_NORMAL ||
+ toplevel->priv->state == PANEL_STATE_AUTO_HIDDEN) {
+ panel_struts_update_toplevel_geometry (toplevel,
+ &toplevel->priv->geometry.x,
+ &toplevel->priv->geometry.y,
+ &toplevel->priv->geometry.width,
+ &toplevel->priv->geometry.height);
+ } else {
+ panel_struts_update_toplevel_geometry (toplevel,
+ &toplevel->priv->geometry.x,
+ &toplevel->priv->geometry.y,
+ NULL, NULL);
+ }
+
+ panel_toplevel_update_edges (toplevel);
+ panel_toplevel_update_description (toplevel);
+}
+
+static void
+panel_toplevel_attach_widget_destroyed (PanelToplevel *toplevel)
+{
+ panel_toplevel_detach (toplevel);
+}
+
+static gboolean
+panel_toplevel_attach_widget_configure (PanelToplevel *toplevel)
+{
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ return FALSE;
+}
+
+static void
+panel_toplevel_update_attach_orientation (PanelToplevel *toplevel)
+{
+ PanelOrientation attach_orientation;
+ PanelOrientation orientation;
+
+ attach_orientation =
+ panel_toplevel_get_orientation (toplevel->priv->attach_toplevel);
+
+ orientation = toplevel->priv->orientation;
+
+ switch (attach_orientation) {
+ case PANEL_ORIENTATION_TOP:
+ orientation = PANEL_ORIENTATION_LEFT;
+ break;
+ case PANEL_ORIENTATION_BOTTOM:
+ orientation = PANEL_ORIENTATION_RIGHT;
+ break;
+ case PANEL_ORIENTATION_LEFT:
+ orientation = PANEL_ORIENTATION_TOP;
+ break;
+ case PANEL_ORIENTATION_RIGHT:
+ orientation = PANEL_ORIENTATION_BOTTOM;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ panel_toplevel_set_orientation (toplevel, orientation);
+}
+
+static void
+panel_toplevel_attach_widget_parent_set (PanelToplevel *toplevel,
+ GtkWidget *previous_parent,
+ GtkWidget *attach_widget)
+{
+ GtkWidget *panel_widget;
+
+ panel_widget = gtk_widget_get_parent (GTK_WIDGET (attach_widget));
+ if (!panel_widget)
+ return;
+
+ g_assert (PANEL_IS_WIDGET (panel_widget));
+
+ toplevel->priv->attach_toplevel = PANEL_WIDGET (panel_widget)->toplevel;
+ panel_toplevel_update_attach_orientation (toplevel);
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+}
+
+static void
+panel_toplevel_attach_toplevel_hiding (PanelToplevel *toplevel)
+{
+ if (!panel_toplevel_get_is_hidden (toplevel)) {
+ panel_toplevel_hide (toplevel, FALSE, -1);
+
+ toplevel->priv->attach_hidden = TRUE;
+ }
+}
+
+static void
+panel_toplevel_attach_toplevel_unhiding (PanelToplevel *toplevel)
+{
+ if (!toplevel->priv->attach_hidden)
+ return;
+
+ toplevel->priv->attach_hidden = FALSE;
+
+ panel_toplevel_unhide (toplevel);
+}
+
+static void
+panel_toplevel_reverse_arrow (PanelToplevel *toplevel,
+ GtkWidget *button)
+{
+ GtkArrowType arrow_type;
+
+ arrow_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "arrow-type"));
+
+ switch (arrow_type) {
+ case GTK_ARROW_UP:
+ arrow_type = GTK_ARROW_DOWN;
+ break;
+ case GTK_ARROW_DOWN:
+ arrow_type = GTK_ARROW_UP;
+ break;
+ case GTK_ARROW_LEFT:
+ arrow_type = GTK_ARROW_RIGHT;
+ break;
+ case GTK_ARROW_RIGHT:
+ arrow_type = GTK_ARROW_LEFT;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_object_set_data (G_OBJECT (button), "arrow-type", GINT_TO_POINTER (arrow_type));
+
+ gtk_arrow_set (GTK_ARROW (gtk_bin_get_child (GTK_BIN (button))), arrow_type, GTK_SHADOW_NONE);
+}
+
+static void
+panel_toplevel_reverse_arrows (PanelToplevel *toplevel)
+{
+ panel_toplevel_reverse_arrow (toplevel, toplevel->priv->hide_button_top);
+ panel_toplevel_reverse_arrow (toplevel, toplevel->priv->hide_button_bottom);
+ panel_toplevel_reverse_arrow (toplevel, toplevel->priv->hide_button_left);
+ panel_toplevel_reverse_arrow (toplevel, toplevel->priv->hide_button_right);
+}
+
+static void
+panel_toplevel_disconnect_attached (PanelToplevel *toplevel)
+{
+ int i;
+
+ for (i = 0; i < N_ATTACH_TOPLEVEL_SIGNALS; i++) {
+ if (!toplevel->priv->attach_toplevel_signals [i])
+ continue;
+
+ g_signal_handler_disconnect (
+ toplevel->priv->attach_toplevel,
+ toplevel->priv->attach_toplevel_signals [i]);
+ toplevel->priv->attach_toplevel_signals [i] = 0;
+ }
+
+ for (i = 0; i < N_ATTACH_WIDGET_SIGNALS; i++) {
+ if (!toplevel->priv->attach_widget_signals [i])
+ continue;
+
+ g_signal_handler_disconnect (
+ toplevel->priv->attach_widget,
+ toplevel->priv->attach_widget_signals [i]);
+ toplevel->priv->attach_widget_signals [i] = 0;
+ }
+}
+
+static void
+panel_toplevel_connect_attached (PanelToplevel *toplevel)
+{
+ gulong *signals;
+ int i = 0;
+
+ signals = toplevel->priv->attach_toplevel_signals;
+
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_toplevel, "destroy",
+ G_CALLBACK (panel_toplevel_attach_widget_destroyed), toplevel);
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_toplevel, "notify::orientation",
+ G_CALLBACK (panel_toplevel_update_attach_orientation), toplevel);
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_toplevel, "configure-event",
+ G_CALLBACK (panel_toplevel_attach_widget_configure), toplevel);
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_toplevel, "hiding",
+ G_CALLBACK (panel_toplevel_attach_toplevel_hiding), toplevel);
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_toplevel, "unhiding",
+ G_CALLBACK (panel_toplevel_attach_toplevel_unhiding), toplevel);
+
+ g_assert (i == N_ATTACH_TOPLEVEL_SIGNALS);
+
+ signals = toplevel->priv->attach_widget_signals;
+ i = 0;
+
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_widget, "destroy",
+ G_CALLBACK (panel_toplevel_attach_widget_destroyed), toplevel);
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_widget, "configure-event",
+ G_CALLBACK (panel_toplevel_attach_widget_configure), toplevel);
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_widget, "parent-set",
+ G_CALLBACK (panel_toplevel_attach_widget_parent_set), toplevel);
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_widget, "show",
+ G_CALLBACK (gtk_widget_show), toplevel);
+ signals [i++] = g_signal_connect_swapped (
+ toplevel->priv->attach_widget, "hide",
+ G_CALLBACK (gtk_widget_hide), toplevel);
+
+ g_assert (i == N_ATTACH_WIDGET_SIGNALS);
+}
+
+void
+panel_toplevel_attach_to_widget (PanelToplevel *toplevel,
+ PanelToplevel *attach_toplevel,
+ GtkWidget *attach_widget)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+ g_return_if_fail (PANEL_IS_TOPLEVEL (attach_toplevel));
+ g_return_if_fail (GTK_IS_WIDGET (attach_widget));
+
+ if (toplevel->priv->attached)
+ panel_toplevel_disconnect_attached (toplevel);
+
+ toplevel->priv->attached = TRUE;
+
+ /* Cancelling the initial animation for drawers in
+ * panel_toplevel_initially_hide() is not enough, since this will
+ * happen only when the toplevel is realized, which might be too late
+ * for drawers (since it's realized when the drawer is clicked) */
+ toplevel->priv->initial_animation_done = TRUE;
+
+ toplevel->priv->attach_toplevel = attach_toplevel;
+ toplevel->priv->attach_widget = attach_widget;
+
+ panel_toplevel_connect_attached (toplevel);
+
+ panel_toplevel_reverse_arrows (toplevel);
+ panel_toplevel_set_expand (toplevel, FALSE);
+ panel_toplevel_update_attach_orientation (toplevel);
+ panel_toplevel_update_hide_buttons (toplevel);
+
+ gtk_window_set_screen (GTK_WINDOW (toplevel),
+ gtk_widget_get_screen (GTK_WIDGET (attach_toplevel)));
+ panel_toplevel_set_monitor (toplevel,
+ panel_toplevel_get_monitor (attach_toplevel));
+ if (toplevel->priv->state == PANEL_STATE_NORMAL)
+ panel_toplevel_push_autohide_disabler (toplevel->priv->attach_toplevel);
+
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+}
+
+void
+panel_toplevel_detach (PanelToplevel *toplevel)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (!toplevel->priv->attached)
+ return;
+
+ if (toplevel->priv->state == PANEL_STATE_NORMAL)
+ panel_toplevel_pop_autohide_disabler (toplevel->priv->attach_toplevel);
+
+ panel_toplevel_disconnect_attached (toplevel);
+
+ panel_toplevel_reverse_arrows (toplevel);
+
+ toplevel->priv->attached = FALSE;
+
+ toplevel->priv->attach_toplevel = NULL;
+ toplevel->priv->attach_widget = NULL;
+
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+}
+
+gboolean
+panel_toplevel_get_is_attached (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ return toplevel->priv->attached;
+}
+
+PanelToplevel *
+panel_toplevel_get_attach_toplevel (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), NULL);
+
+ return toplevel->priv->attach_toplevel;
+}
+
+GtkWidget *
+panel_toplevel_get_attach_widget (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), NULL);
+
+ return toplevel->priv->attach_widget;
+}
+
+static gboolean
+panel_toplevel_popup_panel_menu (PanelToplevel *toplevel)
+{
+ gboolean retval = FALSE;
+
+ g_signal_emit_by_name (toplevel, "popup_menu", &retval);
+
+ return retval;
+}
+
+static gboolean
+panel_toplevel_toggle_expand (PanelToplevel *toplevel)
+{
+ panel_toplevel_set_expand (toplevel, !toplevel->priv->expand);
+
+ return TRUE;
+}
+
+static gboolean
+panel_toplevel_expand (PanelToplevel *toplevel)
+{
+ panel_toplevel_set_expand (toplevel, TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+panel_toplevel_unexpand (PanelToplevel *toplevel)
+{
+ panel_toplevel_set_expand (toplevel, FALSE);
+
+ return TRUE;
+}
+
+static gboolean
+panel_toplevel_toggle_hidden (PanelToplevel *toplevel)
+{
+ if (toplevel->priv->state == PANEL_STATE_NORMAL)
+ panel_toplevel_hide (toplevel, toplevel->priv->auto_hide, -1);
+ else
+ panel_toplevel_unhide (toplevel);
+
+ return FALSE;
+}
+
+static gboolean
+panel_toplevel_begin_move (PanelToplevel *toplevel)
+{
+ if (toplevel->priv->grab_op != PANEL_GRAB_OP_NONE)
+ return FALSE;
+
+ panel_toplevel_begin_grab_op (
+ toplevel, PANEL_GRAB_OP_MOVE, TRUE, GDK_CURRENT_TIME);
+
+ return TRUE;
+}
+
+static gboolean
+panel_toplevel_begin_resize (PanelToplevel *toplevel)
+{
+ if (toplevel->priv->grab_op != PANEL_GRAB_OP_NONE)
+ return FALSE;
+
+ panel_toplevel_begin_grab_op (
+ toplevel, PANEL_GRAB_OP_RESIZE, TRUE, GDK_CURRENT_TIME);
+
+ return TRUE;
+}
+
+static void
+panel_toplevel_move_resize_window (PanelToplevel *toplevel,
+ gboolean move,
+ gboolean resize)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (toplevel);
+
+ g_assert (gtk_widget_get_realized (widget));
+
+ if (move && resize)
+ gdk_window_move_resize (gtk_widget_get_window (widget),
+ toplevel->priv->geometry.x,
+ toplevel->priv->geometry.y,
+ toplevel->priv->geometry.width,
+ toplevel->priv->geometry.height);
+ else if (move)
+ gdk_window_move (gtk_widget_get_window (widget),
+ toplevel->priv->geometry.x,
+ toplevel->priv->geometry.y);
+ else if (resize)
+ gdk_window_resize (gtk_widget_get_window (widget),
+ toplevel->priv->geometry.width,
+ toplevel->priv->geometry.height);
+}
+
+static void
+panel_toplevel_initially_hide (PanelToplevel *toplevel)
+{
+ if (!toplevel->priv->attached) {
+ toplevel->priv->initial_animation_done = FALSE;
+
+ /* We start the panel off hidden until all the applets are
+ * loaded, and then finally slide it down when it's ready to be
+ * used */
+ toplevel->priv->state = PANEL_STATE_AUTO_HIDDEN;
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+ } else
+ toplevel->priv->initial_animation_done = TRUE;
+}
+
+static void
+panel_toplevel_realize (GtkWidget *widget)
+{
+ PanelToplevel *toplevel = (PanelToplevel *) widget;
+ GdkWindow *window;
+
+ gtk_window_set_decorated (GTK_WINDOW (widget), FALSE);
+ gtk_window_stick (GTK_WINDOW (widget));
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->realize)
+ GTK_WIDGET_CLASS (panel_toplevel_parent_class)->realize (widget);
+
+ window = gtk_widget_get_window (widget);
+
+ panel_struts_set_window_hint (toplevel);
+ panel_xutils_set_window_type (window, PANEL_XUTILS_TYPE_DOCK);
+
+ gdk_window_set_group (window, window);
+ gdk_window_set_geometry_hints (window, NULL, GDK_HINT_POS);
+
+ panel_toplevel_initially_hide (toplevel);
+
+ panel_toplevel_move_resize_window (toplevel, TRUE, TRUE);
+}
+
+static void
+panel_toplevel_disconnect_timeouts (PanelToplevel *toplevel)
+{
+ if (toplevel->priv->hide_timeout)
+ g_source_remove (toplevel->priv->hide_timeout);
+ toplevel->priv->hide_timeout = 0;
+
+ if (toplevel->priv->unhide_timeout)
+ g_source_remove (toplevel->priv->unhide_timeout);
+ toplevel->priv->unhide_timeout = 0;
+
+ if (toplevel->priv->animation_timeout)
+ g_source_remove (toplevel->priv->animation_timeout);
+ toplevel->priv->animation_timeout = 0;
+}
+
+static void
+panel_toplevel_unrealize (GtkWidget *widget)
+{
+ panel_toplevel_disconnect_timeouts (PANEL_TOPLEVEL (widget));
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->unrealize)
+ GTK_WIDGET_CLASS (panel_toplevel_parent_class)->unrealize (widget);
+}
+
+static void
+panel_toplevel_destroy (GtkObject *widget)
+{
+ PanelToplevel *toplevel = (PanelToplevel *) widget;
+
+ if (toplevel->priv->attached) {
+ panel_toplevel_disconnect_attached (toplevel);
+ toplevel->priv->attached = FALSE;
+
+ toplevel->priv->attach_toplevel = NULL;
+ toplevel->priv->attach_widget = NULL;
+ }
+
+ panel_toplevel_disconnect_timeouts (toplevel);
+
+ if (GTK_OBJECT_CLASS (panel_toplevel_parent_class)->destroy)
+ GTK_OBJECT_CLASS (panel_toplevel_parent_class)->destroy (widget);
+}
+
+static void
+panel_toplevel_check_resize (GtkContainer *container)
+{
+ GtkAllocation allocation;
+ GtkAllocation widget_allocation;
+ GtkRequisition requisition;
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (container);
+
+ if (!gtk_widget_get_visible (widget))
+ return;
+
+ requisition.width = -1;
+ requisition.height = -1;
+
+ gtk_widget_size_request (widget, &requisition);
+ gtk_widget_get_allocation (widget, &widget_allocation);
+
+ if (widget_allocation.width != requisition.width ||
+ widget_allocation.height != requisition.height)
+ return;
+
+ allocation = widget_allocation;
+ gtk_widget_size_allocate (widget, &allocation);
+}
+
+static void
+panel_toplevel_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ PanelToplevel *toplevel;
+ GtkBin *bin;
+ GtkWidget *child;
+ GdkRectangle old_geometry;
+ int position_changed = FALSE;
+ int size_changed = FALSE;
+
+ toplevel = PANEL_TOPLEVEL (widget);
+ bin = GTK_BIN (widget);
+
+ /* we get a size request when there are new monitors, so first try to
+ * see if we need to move to a new monitor */
+ panel_toplevel_update_monitor (toplevel);
+
+ child = gtk_bin_get_child (bin);
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_size_request (child, requisition);
+
+ old_geometry = toplevel->priv->geometry;
+
+ panel_toplevel_update_geometry (toplevel, requisition);
+
+ requisition->width = toplevel->priv->geometry.width;
+ requisition->height = toplevel->priv->geometry.height;
+
+ if (!gtk_widget_get_realized (widget))
+ return;
+
+ if (old_geometry.width != toplevel->priv->geometry.width ||
+ old_geometry.height != toplevel->priv->geometry.height)
+ size_changed = TRUE;
+
+ if (old_geometry.x != toplevel->priv->geometry.x ||
+ old_geometry.y != toplevel->priv->geometry.y)
+ position_changed = TRUE;
+
+ panel_toplevel_move_resize_window (toplevel, position_changed, size_changed);
+}
+
+static void
+panel_toplevel_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ PanelToplevel *toplevel = (PanelToplevel *) widget;
+ GtkBin *bin = (GtkBin *) widget;
+ GtkStyle *style;
+ GtkWidget *child;
+ GtkAllocation challoc;
+ GtkAllocation child_allocation;
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ if (toplevel->priv->expand ||
+ toplevel->priv->buttons_enabled ||
+ toplevel->priv->attached)
+ challoc = *allocation;
+ else {
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK) {
+ challoc.x = HANDLE_SIZE;
+ challoc.y = 0;
+ challoc.width = allocation->width - 2 * HANDLE_SIZE;
+ challoc.height = allocation->height;
+ } else {
+ challoc.x = 0;
+ challoc.y = HANDLE_SIZE;
+ challoc.width = allocation->width;
+ challoc.height = allocation->height - 2 * HANDLE_SIZE;
+ }
+ }
+
+ style = gtk_widget_get_style (widget);
+
+ if (toplevel->priv->edges & PANEL_EDGE_TOP) {
+ challoc.y += style->ythickness;
+ challoc.height -= style->ythickness;
+ }
+
+ if (toplevel->priv->edges & PANEL_EDGE_LEFT) {
+ challoc.x += style->xthickness;
+ challoc.width -= style->xthickness;
+ }
+
+ if (toplevel->priv->edges & PANEL_EDGE_BOTTOM)
+ challoc.height -= style->ythickness;
+
+ if (toplevel->priv->edges & PANEL_EDGE_RIGHT)
+ challoc.width -= style->xthickness;
+
+ challoc.width = MAX (1, challoc.width);
+ challoc.height = MAX (1, challoc.height);
+
+ child = gtk_bin_get_child (bin);
+ gtk_widget_get_allocation (child, &child_allocation);
+
+ if (gtk_widget_get_mapped (widget) &&
+ (challoc.x != child_allocation.x ||
+ challoc.y != child_allocation.y ||
+ challoc.width != child_allocation.width ||
+ challoc.height != child_allocation.height)) {
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ gdk_window_invalidate_rect (gtk_widget_get_window (widget), &allocation, FALSE);
+ }
+
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_size_allocate (child, &challoc);
+}
+
+static gboolean panel_toplevel_expose(GtkWidget* widget, GdkEventExpose* event)
+{
+ PanelToplevel* toplevel = (PanelToplevel*) widget;
+ PanelFrameEdge edges;
+ gboolean retval = FALSE;
+ GdkWindow *window;
+ GtkStyle *style;
+ GtkStateType state;
+ GtkAllocation allocation;
+
+ if (!gtk_widget_is_drawable (widget))
+ return retval;
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->expose_event)
+ retval = GTK_WIDGET_CLASS (panel_toplevel_parent_class)->expose_event (widget, event);
+
+ edges = toplevel->priv->edges;
+ panel_frame_draw (widget, edges);
+
+ if (toplevel->priv->expand ||
+ toplevel->priv->buttons_enabled ||
+ toplevel->priv->attached)
+ return retval;
+
+ window = gtk_widget_get_window (widget);
+ style = gtk_widget_get_style (widget);
+ state = gtk_widget_get_state (widget);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK) {
+ int x, y, width, height;
+ int xthickness, ythickness;
+
+ x = allocation.x;
+ y = allocation.y;
+ width = HANDLE_SIZE;
+ height = allocation.height;
+
+ xthickness = style->xthickness;
+ ythickness = style->ythickness;
+
+ if (edges & PANEL_EDGE_TOP) {
+ y += ythickness;
+ height -= ythickness;
+ }
+ if (edges & PANEL_EDGE_BOTTOM)
+ height -= ythickness;
+ if (edges & PANEL_EDGE_LEFT)
+ x += xthickness;
+
+ gtk_paint_handle (style, window, state,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "handlebox",
+ x, y, width, height,
+ GTK_ORIENTATION_VERTICAL);
+
+ x = allocation.width - HANDLE_SIZE;
+ if (edges & PANEL_EDGE_RIGHT)
+ x -= xthickness;
+
+ gtk_paint_handle (style, window, state,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "handlebox",
+ x, y, width, height,
+ GTK_ORIENTATION_VERTICAL);
+ } else {
+ int x, y, width, height;
+ int xthickness, ythickness;
+
+ x = allocation.x;
+ y = allocation.y;
+ width = allocation.width;
+ height = HANDLE_SIZE;
+
+ xthickness = style->xthickness;
+ ythickness = style->ythickness;
+
+ if (edges & PANEL_EDGE_LEFT) {
+ x += xthickness;
+ width -= xthickness;
+ }
+ if (edges & PANEL_EDGE_RIGHT)
+ width -= xthickness;
+ if (edges & PANEL_EDGE_TOP)
+ y += ythickness;
+
+ gtk_paint_handle (style, window, state,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "handlebox",
+ x, y, width, height,
+ GTK_ORIENTATION_HORIZONTAL);
+
+ y = allocation.height - HANDLE_SIZE;
+ if (edges & PANEL_EDGE_BOTTOM)
+ y -= ythickness;
+
+ gtk_paint_handle (style, window, state,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "handlebox",
+ x, y, width, height,
+ GTK_ORIENTATION_HORIZONTAL);
+ }
+
+ return retval;
+}
+
+static gboolean
+panel_toplevel_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ PanelToplevel *toplevel;
+ GtkWidget *event_widget;
+
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (widget), FALSE);
+
+ toplevel = PANEL_TOPLEVEL (widget);
+
+ if (event->button != 1 && event->button != 2)
+ return FALSE;
+
+ if (toplevel->priv->animating)
+ return FALSE;
+
+ /* Get the mouse-button modifier from marco so that only intentional
+ * moves are considered. We don't this for non-expanded panels since we
+ * only have the handles that the user can grab. */
+ if ((toplevel->priv->expand || toplevel->priv->attached) &&
+ (event->state & GDK_MODIFIER_MASK) != panel_bindings_get_mouse_button_modifier_keymask ())
+ return FALSE;
+
+ gdk_window_get_user_data (event->window, (gpointer)&event_widget);
+ g_assert (GTK_IS_WIDGET (event_widget));
+ gtk_widget_translate_coordinates (event_widget,
+ widget,
+ event->x,
+ event->y,
+ &toplevel->priv->drag_offset_x,
+ &toplevel->priv->drag_offset_y);
+
+ panel_toplevel_begin_grab_op (toplevel, PANEL_GRAB_OP_MOVE, FALSE, event->time);
+
+ return TRUE;
+}
+
+static gboolean
+panel_toplevel_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ PanelToplevel *toplevel;
+
+ if (event->button != 1 && event->button != 2)
+ return FALSE;
+
+ toplevel = PANEL_TOPLEVEL (widget);
+
+ if (toplevel->priv->grab_op == PANEL_GRAB_OP_NONE)
+ return FALSE;
+
+ if (toplevel->priv->grab_is_keyboard)
+ return FALSE;
+
+ panel_toplevel_end_grab_op (toplevel, event->time);
+
+ return TRUE;
+}
+
+static gboolean
+panel_toplevel_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ PanelToplevel *toplevel = (PanelToplevel *) widget;
+
+ if (toplevel->priv->grab_op != PANEL_GRAB_OP_NONE &&
+ panel_toplevel_handle_grab_op_key_event (toplevel, event))
+ return TRUE;
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->key_press_event)
+ return GTK_WIDGET_CLASS (panel_toplevel_parent_class)->key_press_event (widget, event);
+
+ return FALSE;
+}
+
+static gboolean
+panel_toplevel_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ if (gdk_event_get_screen ((GdkEvent *)event) ==
+ gtk_window_get_screen (GTK_WINDOW (widget)))
+ return panel_toplevel_handle_grab_op_motion_event (
+ PANEL_TOPLEVEL (widget), event);
+ else
+ return FALSE;
+}
+
+static gboolean
+panel_toplevel_animation_timeout (PanelToplevel *toplevel)
+{
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ if (!toplevel->priv->animating) {
+ toplevel->priv->animation_end_x = 0xdead;
+ toplevel->priv->animation_end_y = 0xdead;
+ toplevel->priv->animation_end_width = 0xdead;
+ toplevel->priv->animation_end_height = 0xdead;
+ toplevel->priv->animation_start_time.tv_sec = 0xdead;
+ toplevel->priv->animation_start_time.tv_usec = 0xdead;
+ toplevel->priv->animation_end_time.tv_sec = 0xdead;
+ toplevel->priv->animation_end_time.tv_usec = 0xdead;
+ toplevel->priv->animation_timeout = 0;
+ toplevel->priv->initial_animation_done = TRUE;
+ }
+
+ return toplevel->priv->animating;
+}
+
+static long
+panel_toplevel_get_animation_time (PanelToplevel *toplevel)
+{
+ /* The number of seconds to complete the animation.
+ */
+#define ANIMATION_TIME_FAST 0.4
+#define ANIMATION_TIME_MEDIUM 1.2
+#define ANIMATION_TIME_SLOW 2.0
+
+ long t;
+
+ switch (toplevel->priv->animation_speed) {
+ case PANEL_ANIMATION_SLOW:
+ t = ANIMATION_TIME_SLOW * G_USEC_PER_SEC;
+ break;
+ case PANEL_ANIMATION_MEDIUM:
+ t = ANIMATION_TIME_MEDIUM * G_USEC_PER_SEC;
+ break;
+ case PANEL_ANIMATION_FAST:
+ t = ANIMATION_TIME_FAST * G_USEC_PER_SEC;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return t;
+
+#undef ANIMATION_TIME_FAST
+#undef ANIMATION_TIME_MEDIUM
+#undef ANIMATION_TIME_SLOW
+}
+
+static void
+panel_toplevel_calculate_animation_end_geometry (PanelToplevel *toplevel)
+{
+ GdkScreen *screen;
+ int monitor_width, monitor_height;
+
+ toplevel->priv->animation_end_x = toplevel->priv->x;
+ toplevel->priv->animation_end_y = toplevel->priv->y;
+ toplevel->priv->animation_end_width = -1;
+ toplevel->priv->animation_end_height = -1;
+
+ screen = panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ if (!toplevel->priv->expand) {
+
+ if (toplevel->priv->x_centered)
+ toplevel->priv->animation_end_x =
+ (monitor_width - toplevel->priv->geometry.width) / 2;
+ if (toplevel->priv->y_centered)
+ toplevel->priv->animation_end_y =
+ (monitor_height - toplevel->priv->geometry.height) / 2;
+ }
+
+ /* we consider the toplevels which are in the initial animation stage
+ * as in a normal state */
+ if (toplevel->priv->state == PANEL_STATE_NORMAL ||
+ (!toplevel->priv->initial_animation_done &&
+ !toplevel->priv->auto_hide))
+ panel_toplevel_update_normal_position (toplevel,
+ &toplevel->priv->animation_end_x,
+ &toplevel->priv->animation_end_y,
+ &toplevel->priv->animation_end_width,
+ &toplevel->priv->animation_end_height);
+
+ else if (toplevel->priv->state == PANEL_STATE_AUTO_HIDDEN)
+ panel_toplevel_update_auto_hide_position (toplevel,
+ &toplevel->priv->animation_end_x,
+ &toplevel->priv->animation_end_y,
+ &toplevel->priv->animation_end_width,
+ &toplevel->priv->animation_end_height,
+ TRUE);
+ else
+ panel_toplevel_update_hidden_position (toplevel,
+ &toplevel->priv->animation_end_x,
+ &toplevel->priv->animation_end_y,
+ &toplevel->priv->animation_end_width,
+ &toplevel->priv->animation_end_height);
+}
+
+static void
+panel_toplevel_start_animation (PanelToplevel *toplevel)
+{
+ GdkScreen *screen;
+ GtkRequisition requisition;
+ int deltax, deltay, deltaw = 0, deltah = 0;
+ int cur_x = -1, cur_y = -1;
+ long t;
+
+ panel_toplevel_calculate_animation_end_geometry (toplevel);
+
+ toplevel->priv->animating = TRUE;
+
+ panel_toplevel_update_struts (toplevel, TRUE);
+ panel_struts_update_toplevel_geometry (toplevel,
+ &toplevel->priv->animation_end_x,
+ &toplevel->priv->animation_end_y,
+ &toplevel->priv->animation_end_width,
+ &toplevel->priv->animation_end_height);
+ panel_toplevel_update_struts (toplevel, FALSE);
+
+ gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (toplevel)), &cur_x, &cur_y);
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (toplevel));
+
+ cur_x -= panel_multiscreen_x (screen, toplevel->priv->monitor);
+ cur_y -= panel_multiscreen_y (screen, toplevel->priv->monitor);
+
+ deltax = toplevel->priv->animation_end_x - cur_x;
+ deltay = toplevel->priv->animation_end_y - cur_y;
+
+ gtk_widget_get_requisition (GTK_WIDGET (toplevel), &requisition);
+
+ if (toplevel->priv->animation_end_width != -1)
+ deltaw = toplevel->priv->animation_end_width - requisition.width;
+
+ if (toplevel->priv->animation_end_height != -1)
+ deltah = toplevel->priv->animation_end_height - requisition.height;
+
+ if (deltax == 0 && deltay == 0 && deltaw == 0 && deltah == 0) {
+ toplevel->priv->animation_end_x = -1;
+ toplevel->priv->animation_end_y = -1;
+ toplevel->priv->animation_end_width = -1;
+ toplevel->priv->animation_end_height = -1;
+ toplevel->priv->animating = FALSE;
+ return;
+ }
+
+ if (toplevel->priv->attached) {
+ /* Re-map unmapped attached toplevels */
+ if (!gtk_widget_get_mapped (GTK_WIDGET (toplevel)))
+ gtk_widget_map (GTK_WIDGET (toplevel));
+
+ gtk_window_present (GTK_WINDOW (toplevel->priv->attach_toplevel));
+ }
+
+ g_get_current_time (&toplevel->priv->animation_start_time);
+
+ t = panel_toplevel_get_animation_time (toplevel);
+ g_get_current_time (&toplevel->priv->animation_end_time);
+ g_time_val_add (&toplevel->priv->animation_end_time, t);
+
+ if (!toplevel->priv->animation_timeout)
+ toplevel->priv->animation_timeout =
+ g_timeout_add (20, (GSourceFunc) panel_toplevel_animation_timeout, toplevel);
+}
+
+void
+panel_toplevel_hide (PanelToplevel *toplevel,
+ gboolean auto_hide,
+ GtkDirectionType direction)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->state != PANEL_STATE_NORMAL)
+ return;
+
+ g_signal_emit (toplevel, toplevel_signals [HIDE_SIGNAL], 0);
+
+ if (toplevel->priv->attach_toplevel)
+ panel_toplevel_pop_autohide_disabler (toplevel->priv->attach_toplevel);
+
+ if (auto_hide)
+ toplevel->priv->state = PANEL_STATE_AUTO_HIDDEN;
+ else {
+ if (direction == -1) {
+ if (toplevel->priv->orientation & PANEL_VERTICAL_MASK)
+ direction = GTK_DIR_UP;
+ else
+ direction = GTK_DIR_LEFT;
+ }
+
+ switch (direction) {
+ case GTK_DIR_UP:
+ g_return_if_fail (toplevel->priv->orientation & PANEL_VERTICAL_MASK);
+ toplevel->priv->state = PANEL_STATE_HIDDEN_UP;
+ break;
+ case GTK_DIR_DOWN:
+ g_return_if_fail (toplevel->priv->orientation & PANEL_VERTICAL_MASK);
+ toplevel->priv->state = PANEL_STATE_HIDDEN_DOWN;
+ break;
+ case GTK_DIR_LEFT:
+ g_return_if_fail (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK);
+ toplevel->priv->state = PANEL_STATE_HIDDEN_LEFT;
+ break;
+ case GTK_DIR_RIGHT:
+ g_return_if_fail (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK);
+ toplevel->priv->state = PANEL_STATE_HIDDEN_RIGHT;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ panel_toplevel_update_hide_buttons (toplevel);
+ }
+
+ if (toplevel->priv->animate && gtk_widget_get_realized (GTK_WIDGET (toplevel)))
+ panel_toplevel_start_animation (toplevel);
+ else if (toplevel->priv->attached)
+ gtk_widget_hide (GTK_WIDGET (toplevel));
+
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+}
+
+static gboolean
+panel_toplevel_auto_hide_timeout_handler (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ if (panel_toplevel_get_autohide_disabled (toplevel)) {
+ toplevel->priv->hide_timeout = 0;
+ return FALSE;
+ }
+
+ /* keep coming back until the animation has finished.
+ * FIXME: we should really remove the timeout/idle
+ * completely and re-instate it when the
+ * animation has finished.
+ */
+ if (toplevel->priv->animating)
+ return TRUE;
+
+ panel_toplevel_hide (toplevel, TRUE, -1);
+
+ toplevel->priv->hide_timeout = 0;
+
+ return FALSE;
+}
+
+void
+panel_toplevel_unhide (PanelToplevel *toplevel)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->state == PANEL_STATE_NORMAL)
+ return;
+
+ toplevel->priv->state = PANEL_STATE_NORMAL;
+
+ panel_toplevel_update_hide_buttons (toplevel);
+
+ if (toplevel->priv->attach_toplevel)
+ panel_toplevel_push_autohide_disabler (toplevel->priv->attach_toplevel);
+
+ if (toplevel->priv->animate && gtk_widget_get_realized (GTK_WIDGET (toplevel)))
+ panel_toplevel_start_animation (toplevel);
+ else if (toplevel->priv->attached)
+ gtk_widget_show (GTK_WIDGET (toplevel));
+
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ if (!toplevel->priv->animate)
+ g_signal_emit (toplevel, toplevel_signals [UNHIDE_SIGNAL], 0);
+}
+
+static gboolean
+panel_toplevel_auto_unhide_timeout_handler (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ /* keep coming back until the animation has finished.
+ * FIXME: we should really remove the timeout/idle
+ * completely and re-instate it when the
+ * animation has finished.
+ */
+ if (toplevel->priv->animating)
+ return TRUE;
+
+ if (!toplevel->priv->animate)
+ toplevel->priv->initial_animation_done = TRUE;
+
+ /* initial animation for auto-hidden panels: we need to unhide and hide
+ * again to get at the right size */
+ if (!toplevel->priv->initial_animation_done &&
+ toplevel->priv->auto_hide) {
+ toplevel->priv->unhide_timeout = 0;
+ panel_toplevel_unhide (toplevel);
+ panel_toplevel_hide (toplevel, TRUE, -1);
+ return FALSE;
+ }
+
+ if (!panel_toplevel_contains_pointer (toplevel) &&
+ toplevel->priv->auto_hide) {
+ toplevel->priv->unhide_timeout = 0;
+ return FALSE;
+ }
+
+ panel_toplevel_unhide (toplevel);
+
+ toplevel->priv->unhide_timeout = 0;
+
+ return FALSE;
+}
+
+void
+panel_toplevel_queue_auto_hide (PanelToplevel *toplevel)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (!toplevel->priv->auto_hide ||
+ panel_toplevel_contains_pointer (toplevel) ||
+ panel_toplevel_get_autohide_disabled (toplevel))
+ return;
+
+ if (toplevel->priv->unhide_timeout)
+ g_source_remove (toplevel->priv->unhide_timeout);
+ toplevel->priv->unhide_timeout = 0;
+
+ if (toplevel->priv->hide_timeout ||
+ toplevel->priv->state != PANEL_STATE_NORMAL)
+ return;
+
+ if (toplevel->priv->hide_delay > 0)
+ toplevel->priv->hide_timeout =
+ g_timeout_add (toplevel->priv->hide_delay,
+ (GSourceFunc) panel_toplevel_auto_hide_timeout_handler,
+ toplevel);
+ else
+ toplevel->priv->hide_timeout =
+ g_idle_add ((GSourceFunc) panel_toplevel_auto_hide_timeout_handler,
+ toplevel);
+}
+
+void
+panel_toplevel_queue_auto_unhide (PanelToplevel *toplevel)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->unhide_timeout)
+ return;
+
+ if (toplevel->priv->hide_timeout)
+ g_source_remove (toplevel->priv->hide_timeout);
+ toplevel->priv->hide_timeout = 0;
+
+ if (toplevel->priv->state != PANEL_STATE_AUTO_HIDDEN)
+ return;
+
+ if (toplevel->priv->unhide_delay > 0)
+ toplevel->priv->unhide_timeout =
+ g_timeout_add (toplevel->priv->unhide_delay,
+ (GSourceFunc) panel_toplevel_auto_unhide_timeout_handler,
+ toplevel);
+ else
+ toplevel->priv->unhide_timeout =
+ g_idle_add ((GSourceFunc) panel_toplevel_auto_unhide_timeout_handler,
+ toplevel);
+}
+
+void
+panel_toplevel_queue_initial_unhide (PanelToplevel *toplevel)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->initial_animation_done)
+ return;
+
+ if (toplevel->priv->unhide_timeout)
+ return;
+
+ toplevel->priv->unhide_timeout =
+ g_idle_add ((GSourceFunc) panel_toplevel_auto_unhide_timeout_handler,
+ toplevel);
+}
+
+static gboolean
+panel_toplevel_enter_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ PanelToplevel *toplevel;
+
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (widget), FALSE);
+
+ toplevel = PANEL_TOPLEVEL (widget);
+
+ if (toplevel->priv->auto_hide && event->detail != GDK_NOTIFY_INFERIOR)
+ panel_toplevel_queue_auto_unhide (toplevel);
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->enter_notify_event)
+ return GTK_WIDGET_CLASS (panel_toplevel_parent_class)->enter_notify_event (widget, event);
+
+ return FALSE;
+}
+
+static gboolean
+panel_toplevel_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ PanelToplevel *toplevel;
+
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (widget), FALSE);
+
+ toplevel = PANEL_TOPLEVEL (widget);
+
+ if (toplevel->priv->auto_hide && event->detail != GDK_NOTIFY_INFERIOR)
+ panel_toplevel_queue_auto_hide (toplevel);
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->leave_notify_event)
+ return GTK_WIDGET_CLASS (panel_toplevel_parent_class)->leave_notify_event (widget, event);
+
+ return FALSE;
+}
+
+static gboolean
+panel_toplevel_focus_in_event (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ PanelToplevel *toplevel = PANEL_TOPLEVEL (widget);
+
+ if (toplevel->priv->state == PANEL_STATE_AUTO_HIDDEN)
+ panel_toplevel_unhide (toplevel);
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->focus_in_event)
+ return GTK_WIDGET_CLASS (panel_toplevel_parent_class)->focus_in_event (widget, event);
+
+ return FALSE;
+}
+
+static gboolean
+panel_toplevel_focus_out_event (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ PanelToplevel *toplevel = PANEL_TOPLEVEL (widget);
+
+ /* It appears that sometimes we don't get a leave notify event,
+ but just a focus in/out, so queue the autohide in that case.
+ If the pointer is inside the panel then obviously we won't hide */
+ if (toplevel->priv->auto_hide)
+ panel_toplevel_queue_auto_hide (toplevel);
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->focus_out_event)
+ return GTK_WIDGET_CLASS (panel_toplevel_parent_class)->focus_out_event (widget, event);
+
+ return FALSE;
+}
+
+static void
+panel_toplevel_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ panel_toplevel_update_hide_buttons (PANEL_TOPLEVEL (widget));
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->style_set)
+ GTK_WIDGET_CLASS (panel_toplevel_parent_class)->style_set (widget, previous_style);
+}
+
+static void
+panel_toplevel_drag_threshold_changed (PanelToplevel *toplevel)
+{
+ int threshold;
+
+ threshold = 0;
+ g_object_get (G_OBJECT (toplevel->priv->gtk_settings),
+ "gtk-dnd-drag-threshold", &threshold,
+ NULL);
+
+ if (threshold)
+ toplevel->priv->snap_tolerance = threshold * SNAP_TOLERANCE_FACTOR;
+}
+
+static void
+panel_toplevel_update_gtk_settings (PanelToplevel *toplevel)
+{
+ if (toplevel->priv->gtk_settings)
+ g_signal_handlers_disconnect_by_func (toplevel->priv->gtk_settings,
+ G_CALLBACK (panel_toplevel_drag_threshold_changed),
+ toplevel);
+
+ toplevel->priv->gtk_settings = gtk_widget_get_settings (GTK_WIDGET (toplevel->priv->panel_widget));
+
+ g_signal_connect_swapped (G_OBJECT (toplevel->priv->gtk_settings),
+ "notify::gtk-dnd-drag-threshold",
+ G_CALLBACK (panel_toplevel_drag_threshold_changed),
+ toplevel);
+
+ panel_toplevel_drag_threshold_changed (toplevel);
+}
+
+static void
+panel_toplevel_screen_changed (GtkWidget *widget,
+ GdkScreen *previous_screen)
+{
+ panel_toplevel_update_gtk_settings (PANEL_TOPLEVEL (widget));
+
+ if (GTK_WIDGET_CLASS (panel_toplevel_parent_class)->screen_changed)
+ GTK_WIDGET_CLASS (panel_toplevel_parent_class)->screen_changed (widget, previous_screen);
+
+ gtk_widget_queue_resize (widget);
+}
+
+static void
+panel_toplevel_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PanelToplevel *toplevel;
+
+ g_return_if_fail (PANEL_IS_TOPLEVEL (object));
+
+ toplevel = PANEL_TOPLEVEL (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ panel_toplevel_set_name (toplevel, g_value_get_string (value));
+ break;
+ case PROP_EXPAND:
+ panel_toplevel_set_expand (toplevel, g_value_get_boolean (value));
+ break;
+ case PROP_ORIENTATION:
+ panel_toplevel_set_orientation (toplevel, g_value_get_enum (value));
+ break;
+ case PROP_SIZE:
+ panel_toplevel_set_size (toplevel, g_value_get_int (value));
+ break;
+ case PROP_X:
+ panel_toplevel_set_x (toplevel,
+ g_value_get_int (value),
+ toplevel->priv->x_right,
+ toplevel->priv->x_centered);
+ break;
+ case PROP_X_RIGHT:
+ panel_toplevel_set_x (toplevel,
+ toplevel->priv->x,
+ g_value_get_int (value),
+ toplevel->priv->x_centered);
+ break;
+ case PROP_X_CENTERED:
+ panel_toplevel_set_x (toplevel,
+ toplevel->priv->x,
+ toplevel->priv->x_right,
+ g_value_get_boolean (value));
+ break;
+ case PROP_Y:
+ panel_toplevel_set_y (toplevel,
+ g_value_get_int (value),
+ toplevel->priv->y_bottom,
+ toplevel->priv->y_centered);
+ break;
+ case PROP_Y_BOTTOM:
+ panel_toplevel_set_y (toplevel,
+ toplevel->priv->y,
+ g_value_get_int (value),
+ toplevel->priv->y_centered);
+ break;
+ case PROP_Y_CENTERED:
+ panel_toplevel_set_y (toplevel,
+ toplevel->priv->y,
+ toplevel->priv->y_bottom,
+ g_value_get_boolean (value));
+ break;
+ case PROP_MONITOR:
+ panel_toplevel_set_monitor (toplevel, g_value_get_int (value));
+ break;
+ case PROP_AUTOHIDE:
+ panel_toplevel_set_auto_hide (toplevel, g_value_get_boolean (value));
+ break;
+ case PROP_HIDE_DELAY:
+ panel_toplevel_set_hide_delay (toplevel, g_value_get_int (value));
+ break;
+ case PROP_UNHIDE_DELAY:
+ panel_toplevel_set_unhide_delay (toplevel, g_value_get_int (value));
+ break;
+ case PROP_AUTOHIDE_SIZE:
+ panel_toplevel_set_auto_hide_size (toplevel, g_value_get_int (value));
+ break;
+ case PROP_ANIMATE:
+ panel_toplevel_set_animate (toplevel, g_value_get_boolean (value));
+ break;
+ case PROP_ANIMATION_SPEED:
+ panel_toplevel_set_animation_speed (toplevel, g_value_get_enum (value));
+ break;
+ case PROP_BUTTONS_ENABLED:
+ panel_toplevel_set_enable_buttons (toplevel, g_value_get_boolean (value));
+ break;
+ case PROP_ARROWS_ENABLED:
+ panel_toplevel_set_enable_arrows (toplevel, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+panel_toplevel_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PanelToplevel *toplevel;
+
+ g_return_if_fail (PANEL_IS_TOPLEVEL (object));
+
+ toplevel = PANEL_TOPLEVEL (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, panel_toplevel_get_name (toplevel));
+ break;
+ case PROP_EXPAND:
+ g_value_set_boolean (value, toplevel->priv->expand);
+ break;
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, toplevel->priv->orientation);
+ break;
+ case PROP_SIZE:
+ g_value_set_int (value, toplevel->priv->size);
+ break;
+ case PROP_X:
+ g_value_set_int (value, toplevel->priv->x);
+ break;
+ case PROP_X_RIGHT:
+ g_value_set_int (value, toplevel->priv->x_right);
+ break;
+ case PROP_X_CENTERED:
+ g_value_set_boolean (value, toplevel->priv->x_centered);
+ break;
+ case PROP_Y:
+ g_value_set_int (value, toplevel->priv->y);
+ break;
+ case PROP_Y_BOTTOM:
+ g_value_set_int (value, toplevel->priv->y_bottom);
+ break;
+ case PROP_Y_CENTERED:
+ g_value_set_boolean (value, toplevel->priv->y_centered);
+ break;
+ case PROP_MONITOR:
+ g_value_set_int (value, toplevel->priv->monitor);
+ break;
+ case PROP_AUTOHIDE:
+ g_value_set_boolean (value, toplevel->priv->auto_hide);
+ break;
+ case PROP_HIDE_DELAY:
+ g_value_set_int (value, toplevel->priv->hide_delay);
+ break;
+ case PROP_UNHIDE_DELAY:
+ g_value_set_int (value, toplevel->priv->unhide_delay);
+ break;
+ case PROP_AUTOHIDE_SIZE:
+ g_value_set_int (value, toplevel->priv->auto_hide_size);
+ break;
+ case PROP_ANIMATE:
+ g_value_set_boolean (value, toplevel->priv->animate);
+ break;
+ case PROP_ANIMATION_SPEED:
+ g_value_set_enum (value, toplevel->priv->animation_speed);
+ break;
+ case PROP_BUTTONS_ENABLED:
+ g_value_set_boolean (value, toplevel->priv->buttons_enabled);
+ break;
+ case PROP_ARROWS_ENABLED:
+ g_value_set_boolean (value, toplevel->priv->arrows_enabled);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+panel_toplevel_finalize (GObject *object)
+{
+ PanelToplevel *toplevel = (PanelToplevel *) object;
+
+ panel_struts_unregister_strut (toplevel);
+
+ toplevel_list = g_slist_remove (toplevel_list, toplevel);
+
+ if (toplevel->priv->gtk_settings) {
+ g_signal_handlers_disconnect_by_func (toplevel->priv->gtk_settings,
+ G_CALLBACK (panel_toplevel_drag_threshold_changed),
+ toplevel);
+ toplevel->priv->gtk_settings = NULL;
+ }
+
+ if (toplevel->priv->attached) {
+ panel_toplevel_disconnect_attached (toplevel);
+
+ toplevel->priv->attached = FALSE;
+
+ toplevel->priv->attach_toplevel = NULL;
+ toplevel->priv->attach_widget = NULL;
+ }
+
+ if (toplevel->priv->description)
+ g_free (toplevel->priv->description);
+ toplevel->priv->description = NULL;
+
+ if (toplevel->priv->name)
+ g_free (toplevel->priv->name);
+ toplevel->priv->name = NULL;
+
+ G_OBJECT_CLASS (panel_toplevel_parent_class)->finalize (object);
+}
+
+static void
+panel_toplevel_class_init (PanelToplevelClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GtkObjectClass *gtkobject_class = (GtkObjectClass *) klass;
+ GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+ GtkContainerClass *container_class = (GtkContainerClass *) klass;
+ GtkBindingSet *binding_set;
+
+ binding_set = gtk_binding_set_by_class (klass);
+
+ gobject_class->set_property = panel_toplevel_set_property;
+ gobject_class->get_property = panel_toplevel_get_property;
+ gobject_class->finalize = panel_toplevel_finalize;
+
+ gtkobject_class->destroy = panel_toplevel_destroy;
+
+ widget_class->realize = panel_toplevel_realize;
+ widget_class->unrealize = panel_toplevel_unrealize;
+ widget_class->size_request = panel_toplevel_size_request;
+ widget_class->size_allocate = panel_toplevel_size_allocate;
+ widget_class->expose_event = panel_toplevel_expose;
+ widget_class->button_press_event = panel_toplevel_button_press_event;
+ widget_class->button_release_event = panel_toplevel_button_release_event;
+ widget_class->key_press_event = panel_toplevel_key_press_event;
+ widget_class->motion_notify_event = panel_toplevel_motion_notify_event;
+ widget_class->enter_notify_event = panel_toplevel_enter_notify_event;
+ widget_class->leave_notify_event = panel_toplevel_leave_notify_event;
+ widget_class->screen_changed = panel_toplevel_screen_changed;
+ widget_class->focus_in_event = panel_toplevel_focus_in_event;
+ widget_class->focus_out_event = panel_toplevel_focus_out_event;
+ widget_class->style_set = panel_toplevel_style_set;
+
+ container_class->check_resize = panel_toplevel_check_resize;
+
+ klass->hiding = NULL;
+ klass->unhiding = NULL;
+ klass->popup_panel_menu = panel_toplevel_popup_panel_menu;
+ klass->toggle_expand = panel_toplevel_toggle_expand;
+ klass->expand = panel_toplevel_expand;
+ klass->unexpand = panel_toplevel_unexpand;
+ klass->toggle_hidden = panel_toplevel_toggle_hidden;
+ klass->begin_move = panel_toplevel_begin_move;
+ klass->begin_resize = panel_toplevel_begin_resize;
+
+ g_type_class_add_private (klass, sizeof (PanelToplevelPrivate));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_NAME,
+ g_param_spec_string (
+ "name",
+ "Name",
+ "The name of this panel",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_EXPAND,
+ g_param_spec_boolean (
+ "expand",
+ "Expand",
+ "Expand to take up the full monitor width/height",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_ORIENTATION,
+ g_param_spec_enum (
+ "orientation",
+ "Orientation",
+ "The orientation of the panel",
+ PANEL_TYPE_ORIENTATION,
+ PANEL_ORIENTATION_TOP,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_SIZE,
+ g_param_spec_int (
+ "size",
+ "Size",
+ "The height (or width when vertical) of the panel",
+ 0,
+ G_MAXINT,
+ DEFAULT_SIZE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_X,
+ g_param_spec_int (
+ "x",
+ "X position",
+ "The X position of the panel",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_X_RIGHT,
+ g_param_spec_int (
+ "x-right",
+ "X position, from the right",
+ "The X position of the panel, starting from the right of the screen",
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_X_CENTERED,
+ g_param_spec_boolean (
+ "x-centered",
+ "X centered",
+ "The x co-ordinate is relative to center screen",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_Y,
+ g_param_spec_int (
+ "y",
+ "Y position",
+ "The Y position of the panel",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_Y_BOTTOM,
+ g_param_spec_int (
+ "y_bottom",
+ "Y position, from the bottom",
+ "The Y position of the panel, starting from the bottom of the screen",
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_Y_CENTERED,
+ g_param_spec_boolean (
+ "y-centered",
+ "Y centered",
+ "The y co-ordinate is relative to center screen",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_MONITOR,
+ g_param_spec_int (
+ "monitor",
+ "Xinerama monitor",
+ "The monitor (in terms of Xinerama) which the panel is on",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_AUTOHIDE,
+ g_param_spec_boolean (
+ "auto-hide",
+ "Auto hide",
+ "Automatically hide the panel when the mouse leaves the panel",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_HIDE_DELAY,
+ g_param_spec_int (
+ "hide-delay",
+ "Hide delay",
+ "The number of milliseconds to delay before automatically hiding",
+ 0,
+ G_MAXINT,
+ DEFAULT_HIDE_DELAY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_UNHIDE_DELAY,
+ g_param_spec_int (
+ "unhide-delay",
+ "Un-hide delay",
+ "The number of milliseconds to delay before automatically un-hiding",
+ 0,
+ G_MAXINT,
+ DEFAULT_UNHIDE_DELAY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_AUTOHIDE_SIZE,
+ g_param_spec_int (
+ "auto-hide-size",
+ "Auto-hide size",
+ "The number of pixels visible when the panel has been automatically hidden",
+ 1,
+ G_MAXINT,
+ DEFAULT_AUTO_HIDE_SIZE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_ANIMATE,
+ g_param_spec_boolean (
+ "animate",
+ "Animate",
+ "Enable hiding/showing animations",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_ANIMATION_SPEED,
+ g_param_spec_enum (
+ "animation-speed",
+ "Animation Speed",
+ "The speed at which to animate panel hiding/showing",
+ PANEL_TYPE_ANIMATION_SPEED,
+ PANEL_ANIMATION_MEDIUM,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_BUTTONS_ENABLED,
+ g_param_spec_boolean (
+ "buttons-enabled",
+ "Buttons Enabled",
+ "Enable hide/show buttons",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_ARROWS_ENABLED,
+ g_param_spec_boolean (
+ "arrows-enabled",
+ "Arrows Enabled",
+ "Enable arrows on hide/show buttons",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ gtk_widget_class_install_style_property (
+ widget_class,
+ g_param_spec_int (
+ "arrow-size",
+ "Arrow Size",
+ "The size of the arrows on the hide/show buttons",
+ 0,
+ G_MAXINT,
+ DEFAULT_ARROW_SIZE,
+ G_PARAM_READABLE));
+
+ toplevel_signals [HIDE_SIGNAL] =
+ g_signal_new ("hiding",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, hiding),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ toplevel_signals [UNHIDE_SIGNAL] =
+ g_signal_new ("unhiding",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, unhiding),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ toplevel_signals [POPUP_PANEL_MENU_SIGNAL] =
+ g_signal_new ("popup-panel-menu",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, popup_panel_menu),
+ NULL,
+ NULL,
+ panel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+
+ toplevel_signals [TOGGLE_EXPAND_SIGNAL] =
+ g_signal_new ("toggle-expand",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, toggle_expand),
+ NULL,
+ NULL,
+ panel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+
+ toplevel_signals [EXPAND_SIGNAL] =
+ g_signal_new ("expand",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, expand),
+ NULL,
+ NULL,
+ panel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+
+ toplevel_signals [UNEXPAND_SIGNAL] =
+ g_signal_new ("unexpand",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, unexpand),
+ NULL,
+ NULL,
+ panel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+
+ toplevel_signals [TOGGLE_HIDDEN_SIGNAL] =
+ g_signal_new ("toggle-hidden",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, toggle_hidden),
+ NULL,
+ NULL,
+ panel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+
+ toplevel_signals [BEGIN_MOVE_SIGNAL] =
+ g_signal_new ("begin-move",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, begin_move),
+ NULL,
+ NULL,
+ panel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+
+ toplevel_signals [BEGIN_RESIZE_SIGNAL] =
+ g_signal_new ("begin-resize",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (PanelToplevelClass, begin_resize),
+ NULL,
+ NULL,
+ panel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_F10, GDK_CONTROL_MASK,
+ "popup_panel_menu", 0);
+
+ panel_bindings_set_entries (binding_set);
+}
+
+static void
+panel_toplevel_setup_widgets (PanelToplevel *toplevel)
+{
+ GtkWidget* container;
+
+ toplevel->priv->table = gtk_table_new(3, 3, FALSE);
+
+ toplevel->priv->hide_button_top = panel_toplevel_add_hide_button(toplevel, GTK_ARROW_UP, 1, 2, 0, 1);
+
+ toplevel->priv->hide_button_bottom = panel_toplevel_add_hide_button(toplevel, GTK_ARROW_DOWN, 1, 2, 2, 3);
+
+ toplevel->priv->hide_button_left = panel_toplevel_add_hide_button(toplevel, GTK_ARROW_LEFT, 0, 1, 1, 2);
+
+ toplevel->priv->hide_button_right = panel_toplevel_add_hide_button(toplevel, GTK_ARROW_RIGHT, 2, 3, 1, 2);
+
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
+ {
+ gtk_widget_show(toplevel->priv->hide_button_left);
+ gtk_widget_show(toplevel->priv->hide_button_right);
+ }
+ else
+ {
+ gtk_widget_show(toplevel->priv->hide_button_top);
+ gtk_widget_show(toplevel->priv->hide_button_bottom);
+ }
+
+ toplevel->priv->inner_frame = g_object_new(PANEL_TYPE_FRAME, NULL);
+
+ gtk_table_attach (GTK_TABLE (toplevel->priv->table),
+ GTK_WIDGET (toplevel->priv->inner_frame),
+ 1, 2,
+ 1, 2,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+ 0, 0);
+ gtk_widget_show (GTK_WIDGET (toplevel->priv->inner_frame));
+
+ container = panel_widget_new (toplevel,
+ !toplevel->priv->expand,
+ toplevel->priv->orientation & PANEL_HORIZONTAL_MASK ?
+ GTK_ORIENTATION_HORIZONTAL :
+ GTK_ORIENTATION_VERTICAL,
+ toplevel->priv->size);
+
+ toplevel->priv->panel_widget = PANEL_WIDGET(container);
+
+ gtk_container_add(GTK_CONTAINER(toplevel->priv->inner_frame), container);
+ gtk_widget_show(container);
+
+ gtk_container_add(GTK_CONTAINER(toplevel), toplevel->priv->table);
+ gtk_widget_show(toplevel->priv->table);
+}
+
+static void
+panel_toplevel_init (PanelToplevel *toplevel)
+{
+ int i;
+
+ /* This is a hack for the default resize grip on Ubuntu.
+ * Once again, thank you Ubuntu.
+ *
+ * We need to add a --enable-ubuntu for this.
+ */
+ #ifdef UBUNTU
+ gtk_window_set_has_resize_grip(&toplevel->window_instance, FALSE);
+ #endif
+ toplevel->priv = PANEL_TOPLEVEL_GET_PRIVATE (toplevel);
+
+ toplevel->priv->expand = TRUE;
+ toplevel->priv->orientation = PANEL_ORIENTATION_BOTTOM;
+ toplevel->priv->size = DEFAULT_SIZE;
+ toplevel->priv->x = 0;
+ toplevel->priv->y = 0;
+ toplevel->priv->x_right = -1;
+ toplevel->priv->y_bottom = -1;
+ toplevel->priv->monitor = 0;
+ toplevel->priv->configured_monitor = -1;
+ toplevel->priv->hide_delay = DEFAULT_HIDE_DELAY;
+ toplevel->priv->unhide_delay = DEFAULT_UNHIDE_DELAY;
+ toplevel->priv->auto_hide_size = DEFAULT_AUTO_HIDE_SIZE;
+ toplevel->priv->animation_speed = PANEL_ANIMATION_FAST;
+
+ toplevel->priv->snap_tolerance = DEFAULT_DND_THRESHOLD * SNAP_TOLERANCE_FACTOR;
+ toplevel->priv->gtk_settings = NULL;
+
+ toplevel->priv->state = PANEL_STATE_NORMAL;
+
+ toplevel->priv->name = NULL;
+ toplevel->priv->description = NULL;
+
+ toplevel->priv->hide_timeout = 0;
+ toplevel->priv->unhide_timeout = 0;
+
+ toplevel->priv->geometry.x = -1;
+ toplevel->priv->geometry.y = -1;
+ toplevel->priv->geometry.width = -1;
+ toplevel->priv->geometry.height = -1;
+
+ toplevel->priv->original_width = -1;
+ toplevel->priv->original_height = -1;
+
+ toplevel->priv->grab_op = PANEL_GRAB_OP_NONE;
+
+ toplevel->priv->drag_offset_x = 0;
+ toplevel->priv->drag_offset_y = 0;
+
+ toplevel->priv->animation_end_x = 0;
+ toplevel->priv->animation_end_y = 0;
+ toplevel->priv->animation_end_width = 0;
+ toplevel->priv->animation_end_height = 0;
+ toplevel->priv->animation_start_time.tv_sec = 0;
+ toplevel->priv->animation_start_time.tv_usec = 0;
+ toplevel->priv->animation_end_time.tv_sec = 0;
+ toplevel->priv->animation_end_time.tv_usec = 0;
+ toplevel->priv->animation_timeout = 0;
+
+ toplevel->priv->panel_widget = NULL;
+ toplevel->priv->inner_frame = NULL;
+ toplevel->priv->table = NULL;
+ toplevel->priv->hide_button_top = NULL;
+ toplevel->priv->hide_button_bottom = NULL;
+ toplevel->priv->hide_button_left = NULL;
+ toplevel->priv->hide_button_right = NULL;
+
+ toplevel->priv->attach_toplevel = NULL;
+ toplevel->priv->attach_widget = NULL;
+ toplevel->priv->n_autohide_disablers = 0;
+
+ for (i = 0; i < N_ATTACH_TOPLEVEL_SIGNALS; i++)
+ toplevel->priv->attach_toplevel_signals [i] = 0;
+ for (i = 0; i < N_ATTACH_WIDGET_SIGNALS; i++)
+ toplevel->priv->attach_widget_signals [i] = 0;
+
+ toplevel->priv->auto_hide = FALSE;
+ toplevel->priv->buttons_enabled = TRUE;
+ toplevel->priv->arrows_enabled = TRUE;
+ toplevel->priv->x_centered = FALSE;
+ toplevel->priv->y_centered = FALSE;
+ toplevel->priv->animating = FALSE;
+ toplevel->priv->grab_is_keyboard = FALSE;
+ toplevel->priv->position_centered = FALSE;
+ toplevel->priv->attached = FALSE;
+ toplevel->priv->attach_hidden = FALSE;
+ toplevel->priv->updated_geometry_initial = FALSE;
+ toplevel->priv->initial_animation_done = FALSE;
+
+ gtk_widget_add_events (GTK_WIDGET (toplevel),
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK);
+
+ panel_toplevel_setup_widgets (toplevel);
+ panel_toplevel_update_description (toplevel);
+ panel_toplevel_update_gtk_settings (toplevel);
+
+ toplevel_list = g_slist_prepend (toplevel_list, toplevel);
+
+ /* Prevent the window from being deleted via Alt+F4 by accident. This
+ * happens with "alternative" window managers such as Sawfish or XFWM4.
+ */
+ g_signal_connect(GTK_WIDGET(toplevel), "delete-event", G_CALLBACK(gtk_true), NULL);
+}
+
+PanelWidget *
+panel_toplevel_get_panel_widget (PanelToplevel *toplevel)
+{
+
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), NULL);
+
+ return toplevel->priv->panel_widget;
+}
+
+static void
+panel_toplevel_update_name (PanelToplevel *toplevel)
+{
+ char *title;
+
+ g_assert (toplevel->priv->description != NULL);
+
+ title = toplevel->priv->name ? toplevel->priv->name : toplevel->priv->description;
+
+ gtk_window_set_title (GTK_WINDOW (toplevel), title);
+
+ panel_a11y_set_atk_name_desc (
+ GTK_WIDGET (toplevel->priv->panel_widget),
+ title, toplevel->priv->description);
+}
+
+void
+panel_toplevel_set_name (PanelToplevel *toplevel,
+ const char *name)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (!toplevel->priv->name && (!name || !name [0]))
+ return;
+
+ if (toplevel->priv->name && name && name [0] &&
+ !strcmp (toplevel->priv->name, name))
+ return;
+
+ if (toplevel->priv->name)
+ g_free (toplevel->priv->name);
+ toplevel->priv->name = NULL;
+
+ if (name && name [0])
+ toplevel->priv->name = g_strdup (name);
+
+ panel_toplevel_update_name (toplevel);
+
+ g_object_notify (G_OBJECT (toplevel), "name");
+}
+
+const char* panel_toplevel_get_name(PanelToplevel* toplevel)
+{
+ g_return_val_if_fail(PANEL_IS_TOPLEVEL(toplevel), NULL);
+
+ return toplevel->priv->name;
+}
+
+const char* panel_toplevel_get_description(PanelToplevel* toplevel)
+{
+ g_return_val_if_fail(PANEL_IS_TOPLEVEL(toplevel), NULL);
+
+ return toplevel->priv->description;
+}
+
+void
+panel_toplevel_set_expand (PanelToplevel *toplevel,
+ gboolean expand)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ expand = expand != FALSE;
+
+ if (toplevel->priv->expand == expand)
+ return;
+
+ toplevel->priv->expand = expand;
+
+ if (!toplevel->priv->expand) {
+ switch (toplevel->priv->orientation) {
+ case PANEL_ORIENTATION_TOP:
+ panel_toplevel_set_x (toplevel, 0, -1, TRUE);
+ break;
+ case PANEL_ORIENTATION_BOTTOM:
+ panel_toplevel_set_x (toplevel, 0, 0, TRUE);
+ break;
+ case PANEL_ORIENTATION_LEFT:
+ panel_toplevel_set_y (toplevel, 0, -1, TRUE);
+ break;
+ case PANEL_ORIENTATION_RIGHT:
+ panel_toplevel_set_y (toplevel, 0, 0, TRUE);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ panel_widget_set_packed (toplevel->priv->panel_widget, !toplevel->priv->expand);
+
+ g_object_notify (G_OBJECT (toplevel), "expand");
+}
+
+gboolean
+panel_toplevel_get_expand (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), TRUE);
+
+ return toplevel->priv->expand;
+}
+
+gboolean
+panel_toplevel_get_is_floating (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), TRUE);
+
+ return toplevel->priv->floating;
+}
+
+void
+panel_toplevel_set_orientation (PanelToplevel *toplevel,
+ PanelOrientation orientation)
+{
+ gboolean rotate;
+ int monitor_width;
+ int monitor_height;
+
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->orientation == orientation)
+ return;
+
+ g_object_freeze_notify (G_OBJECT (toplevel));
+
+ panel_toplevel_get_monitor_geometry (
+ toplevel, NULL, NULL, &monitor_width, &monitor_height);
+
+ /* Un-snap from center if no longer along screen edge */
+ if (toplevel->priv->x_centered &&
+ (orientation & PANEL_VERTICAL_MASK)) {
+ toplevel->priv->x_centered = FALSE;
+ toplevel->priv->x = (monitor_width - toplevel->priv->geometry.width) / 2;
+ g_object_notify (G_OBJECT (toplevel), "x");
+ g_object_notify (G_OBJECT (toplevel), "x-centered");
+
+ if (toplevel->priv->x_right != -1) {
+ toplevel->priv->x_right = -1;
+ g_object_notify (G_OBJECT (toplevel), "x-right");
+ }
+ }
+
+ if (toplevel->priv->y_centered &&
+ (orientation & PANEL_HORIZONTAL_MASK)) {
+ toplevel->priv->y_centered = FALSE;
+ toplevel->priv->y = (monitor_height - toplevel->priv->geometry.height) / 2;
+ g_object_notify (G_OBJECT (toplevel), "y");
+ g_object_notify (G_OBJECT (toplevel), "y-centered");
+
+ if (toplevel->priv->y_bottom != -1) {
+ toplevel->priv->y_bottom = -1;
+ g_object_notify (G_OBJECT (toplevel), "y-bottom");
+ }
+ }
+
+ rotate = FALSE;
+ if ((orientation & PANEL_HORIZONTAL_MASK) &&
+ (toplevel->priv->orientation & PANEL_VERTICAL_MASK))
+ rotate = TRUE;
+ else if ((orientation & PANEL_VERTICAL_MASK) &&
+ (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK))
+ rotate = TRUE;
+
+ /* rotate around the center */
+ if (rotate && !toplevel->priv->position_centered && !toplevel->priv->expand &&
+ toplevel->priv->updated_geometry_initial) {
+ toplevel->priv->position_centered = TRUE;
+
+ /* x, y temporary refer to the panel center, so we don't care
+ * about x_right, y_bottom. Those will get updated in
+ * panel_toplevel_update_position() accordingly. */
+ if (!toplevel->priv->x_centered) {
+ toplevel->priv->x += toplevel->priv->geometry.width / 2;
+ g_object_notify (G_OBJECT (toplevel), "x");
+ }
+
+ if (!toplevel->priv->y_centered) {
+ toplevel->priv->y += toplevel->priv->geometry.height / 2;
+ g_object_notify (G_OBJECT (toplevel), "y");
+ }
+
+ }
+
+ toplevel->priv->orientation = orientation;
+
+ panel_toplevel_update_hide_buttons (toplevel);
+
+ panel_widget_set_orientation (
+ toplevel->priv->panel_widget,
+ toplevel->priv->orientation & PANEL_HORIZONTAL_MASK ?
+ GTK_ORIENTATION_HORIZONTAL :
+ GTK_ORIENTATION_VERTICAL);
+
+ switch (toplevel->priv->state) {
+ case PANEL_STATE_HIDDEN_UP:
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
+ toplevel->priv->state = PANEL_STATE_HIDDEN_LEFT;
+ break;
+ case PANEL_STATE_HIDDEN_DOWN:
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
+ toplevel->priv->state = PANEL_STATE_HIDDEN_RIGHT;
+ break;
+ case PANEL_STATE_HIDDEN_LEFT:
+ if (toplevel->priv->orientation & PANEL_VERTICAL_MASK)
+ toplevel->priv->state = PANEL_STATE_HIDDEN_UP;
+ break;
+ case PANEL_STATE_HIDDEN_RIGHT:
+ if (toplevel->priv->orientation & PANEL_VERTICAL_MASK)
+ toplevel->priv->state = PANEL_STATE_HIDDEN_DOWN;
+ break;
+ default:
+ break;
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ g_object_notify (G_OBJECT (toplevel), "orientation");
+
+ g_object_thaw_notify (G_OBJECT (toplevel));
+}
+
+PanelOrientation
+panel_toplevel_get_orientation (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), GTK_ORIENTATION_HORIZONTAL);
+
+ return toplevel->priv->orientation;
+}
+
+void
+panel_toplevel_set_size (PanelToplevel *toplevel,
+ int size)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+ g_return_if_fail (size >= 0);
+
+ if (toplevel->priv->size == size)
+ return;
+
+ toplevel->priv->size = size;
+
+ panel_widget_set_size (toplevel->priv->panel_widget, toplevel->priv->size);
+
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ g_object_notify (G_OBJECT (toplevel), "size");
+}
+
+int
+panel_toplevel_get_size (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), DEFAULT_SIZE);
+
+ return toplevel->priv->size;
+}
+
+void
+panel_toplevel_set_auto_hide_size (PanelToplevel *toplevel,
+ int auto_hide_size)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->auto_hide_size == auto_hide_size)
+ return;
+
+ toplevel->priv->auto_hide_size = auto_hide_size;
+
+ if (toplevel->priv->state == PANEL_STATE_AUTO_HIDDEN) {
+ if (panel_toplevel_update_struts (toplevel, FALSE)) {
+ if (toplevel->priv->animate) {
+ panel_toplevel_unhide (toplevel);
+ panel_toplevel_hide (toplevel, TRUE, -1);
+ } else
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+ }
+ }
+
+ g_object_notify (G_OBJECT (toplevel), "auto-hide-size");
+}
+
+int
+panel_toplevel_get_auto_hide_size (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), DEFAULT_AUTO_HIDE_SIZE);
+
+ return toplevel->priv->auto_hide_size;
+}
+
+void
+panel_toplevel_set_x (PanelToplevel *toplevel,
+ int x,
+ int x_right,
+ gboolean x_centered)
+{
+ gboolean changed = FALSE;
+
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ x_centered = x_centered != FALSE;
+
+ g_object_freeze_notify (G_OBJECT (toplevel));
+
+ if (toplevel->priv->x != x) {
+ toplevel->priv->x = x;
+ changed = TRUE;
+ g_object_notify (G_OBJECT (toplevel), "x");
+ }
+
+ if (toplevel->priv->x_right != x_right) {
+ toplevel->priv->x_right = x_right;
+ changed = TRUE;
+ g_object_notify (G_OBJECT (toplevel), "x-right");
+ }
+
+ if (toplevel->priv->x_centered != x_centered) {
+ toplevel->priv->x_centered = x_centered;
+ changed = TRUE;
+ g_object_notify (G_OBJECT (toplevel), "x-centered");
+ }
+
+ if (changed)
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ g_object_thaw_notify (G_OBJECT (toplevel));
+}
+
+void
+panel_toplevel_set_y (PanelToplevel *toplevel,
+ int y,
+ int y_bottom,
+ gboolean y_centered)
+{
+ gboolean changed = FALSE;
+
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ y_centered = y_centered != FALSE;
+
+ g_object_freeze_notify (G_OBJECT (toplevel));
+
+ if (toplevel->priv->y != y) {
+ toplevel->priv->y = y;
+ changed = TRUE;
+ g_object_notify (G_OBJECT (toplevel), "y");
+ }
+
+ if (toplevel->priv->y_bottom != y_bottom) {
+ toplevel->priv->y_bottom = y_bottom;
+ changed = TRUE;
+ g_object_notify (G_OBJECT (toplevel), "y-bottom");
+ }
+
+ if (toplevel->priv->y_centered != y_centered) {
+ toplevel->priv->y_centered = y_centered;
+ changed = TRUE;
+ g_object_notify (G_OBJECT (toplevel), "y-centered");
+ }
+
+ if (changed)
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ g_object_thaw_notify (G_OBJECT (toplevel));
+}
+
+void
+panel_toplevel_get_position (PanelToplevel *toplevel,
+ int *x,
+ int *x_right,
+ int *y,
+ int *y_bottom)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (x)
+ *x = toplevel->priv->x;
+
+ if (y)
+ *y = toplevel->priv->y;
+
+ if (x_right)
+ *x_right = toplevel->priv->x_right;
+
+ if (y_bottom)
+ *y_bottom = toplevel->priv->y_bottom;
+}
+
+gboolean
+panel_toplevel_get_x_centered (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ return toplevel->priv->x_centered;
+}
+
+gboolean
+panel_toplevel_get_y_centered (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ return toplevel->priv->y_centered;
+}
+
+/**
+ * panel_toplevel_set_monitor_internal:
+ *
+ * Sets the monitor of the toplevel, but only the internal state. We need to
+ * make the difference between the internal state and the configuration of the
+ * user because internal constraints might affect the monitor of the toplevel.
+ *
+ * panel_toplevel_set_monitor_internal() won't update the configuration of the
+ * user.
+ **/
+static void
+panel_toplevel_set_monitor_internal (PanelToplevel *toplevel,
+ int monitor,
+ gboolean force_resize)
+{
+ if (toplevel->priv->monitor == monitor)
+ return;
+
+ toplevel->priv->monitor = monitor;
+
+ if (force_resize)
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+}
+
+/**
+ * panel_toplevel_update_monitor:
+ *
+ * Moves the toplevel to its configured monitor or the first one, if needed.
+ * This generally happens when the configured monitor was non-existing before,
+ * and it appeared at runtime, or if it was existing and disappeared.
+ *
+ * This must only be called at the beginning of the size request of the
+ * toplevel because it doesn't queue a size request.
+ **/
+static void
+panel_toplevel_update_monitor (PanelToplevel *toplevel)
+{
+ GdkScreen *screen;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+
+ /* If we were not using the configured monitor, can we use it now? */
+ if ((toplevel->priv->configured_monitor != -1) &&
+ (toplevel->priv->configured_monitor != toplevel->priv->monitor) &&
+ toplevel->priv->configured_monitor < panel_multiscreen_monitors (screen)) {
+ panel_toplevel_set_monitor_internal (toplevel,
+ toplevel->priv->configured_monitor,
+ FALSE);
+
+ /* else, can we still use the monitor we were using? */
+ } else if (toplevel->priv->monitor >= panel_multiscreen_monitors (screen)) {
+ panel_toplevel_set_monitor_internal (toplevel,
+ 0,
+ FALSE);
+ }
+}
+
+void
+panel_toplevel_set_monitor (PanelToplevel *toplevel,
+ int monitor)
+{
+ GdkScreen *screen;
+
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->configured_monitor == monitor)
+ return;
+
+ toplevel->priv->configured_monitor = monitor;
+
+ /* Only use the configured monitor if it's existing. Else, we ignore
+ * the non-existing monitor, and keep the old one. The main use case is
+ * when logging in after having used a multiscreen environment.
+ * We will put the panel on the monitor 0 for this session, and it will
+ * move back to the right monitor next time. */
+ screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+ if (monitor < panel_multiscreen_monitors (screen))
+ panel_toplevel_set_monitor_internal (toplevel, monitor, TRUE);
+
+ g_object_notify (G_OBJECT (toplevel), "monitor");
+}
+
+int
+panel_toplevel_get_monitor (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), -1);
+
+ return toplevel->priv->monitor;
+}
+
+void
+panel_toplevel_set_auto_hide (PanelToplevel *toplevel,
+ gboolean auto_hide)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ auto_hide = auto_hide != FALSE;
+
+ if (toplevel->priv->auto_hide == auto_hide)
+ return;
+
+ toplevel->priv->auto_hide = auto_hide;
+
+ if (toplevel->priv->auto_hide)
+ panel_toplevel_queue_auto_hide (toplevel);
+ else
+ panel_toplevel_queue_auto_unhide (toplevel);
+
+ if (panel_toplevel_update_struts (toplevel, FALSE))
+ gtk_widget_queue_resize (GTK_WIDGET (toplevel));
+
+ g_object_notify (G_OBJECT (toplevel), "auto-hide");
+}
+
+gboolean
+panel_toplevel_get_auto_hide (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ return toplevel->priv->auto_hide;
+}
+
+void
+panel_toplevel_set_hide_delay (PanelToplevel *toplevel,
+ int hide_delay)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->hide_delay == hide_delay)
+ return;
+
+ toplevel->priv->hide_delay = hide_delay;
+
+ g_object_notify (G_OBJECT (toplevel), "hide-delay");
+}
+
+int
+panel_toplevel_get_hide_delay (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), -1);
+
+ return toplevel->priv->hide_delay;
+}
+
+void
+panel_toplevel_set_unhide_delay (PanelToplevel *toplevel,
+ int unhide_delay)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->unhide_delay == unhide_delay)
+ return;
+
+ toplevel->priv->unhide_delay = unhide_delay;
+
+ g_object_notify (G_OBJECT (toplevel), "unhide-delay");
+}
+
+int
+panel_toplevel_get_unhide_delay (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), -1);
+
+ return toplevel->priv->unhide_delay;
+}
+
+void
+panel_toplevel_set_animate (PanelToplevel *toplevel,
+ gboolean animate)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ animate = animate != FALSE;
+
+ if (toplevel->priv->animate == animate)
+ return;
+
+ toplevel->priv->animate = animate;
+
+ g_object_notify (G_OBJECT (toplevel), "animate");
+}
+
+gboolean
+panel_toplevel_get_animate (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ return toplevel->priv->animate;
+}
+
+void
+panel_toplevel_set_animation_speed (PanelToplevel *toplevel,
+ PanelAnimationSpeed animation_speed)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ if (toplevel->priv->animation_speed == animation_speed)
+ return;
+
+ toplevel->priv->animation_speed = animation_speed;
+
+ g_object_notify (G_OBJECT (toplevel), "animation-speed");
+}
+
+PanelAnimationSpeed
+panel_toplevel_get_animation_speed (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), 0);
+
+ return toplevel->priv->animation_speed;
+}
+
+void
+panel_toplevel_set_enable_buttons (PanelToplevel *toplevel,
+ gboolean enable_buttons)
+{
+ enable_buttons = enable_buttons != FALSE;
+
+ if (toplevel->priv->buttons_enabled == enable_buttons)
+ return;
+
+ toplevel->priv->buttons_enabled = enable_buttons;
+
+ panel_toplevel_update_hide_buttons (toplevel);
+
+ g_object_notify (G_OBJECT (toplevel), "buttons-enabled");
+}
+
+gboolean
+panel_toplevel_get_enable_buttons (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ return toplevel->priv->buttons_enabled;
+}
+
+void
+panel_toplevel_set_enable_arrows (PanelToplevel *toplevel,
+ gboolean enable_arrows)
+{
+ g_return_if_fail (PANEL_IS_TOPLEVEL (toplevel));
+
+ enable_arrows = enable_arrows != FALSE;
+
+ if (toplevel->priv->arrows_enabled == enable_arrows)
+ return;
+
+ toplevel->priv->arrows_enabled = enable_arrows;
+
+ panel_toplevel_update_hide_buttons (toplevel);
+
+ g_object_notify (G_OBJECT (toplevel), "arrows-enabled");
+}
+
+gboolean
+panel_toplevel_get_enable_arrows (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ return toplevel->priv->arrows_enabled;
+}
+
+void
+panel_toplevel_rotate (PanelToplevel *toplevel,
+ gboolean clockwise)
+{
+ PanelOrientation orientation;
+
+ /* Relies on PanelOrientation definition:
+ *
+ * typedef enum {
+ * PANEL_ORIENTATION_TOP = 1 << 0,
+ * PANEL_ORIENTATION_RIGHT = 1 << 1,
+ * PANEL_ORIENTATION_BOTTOM = 1 << 2,
+ * PANEL_ORIENTATION_LEFT = 1 << 3
+ * } PanelOrientation;
+ */
+
+ orientation = toplevel->priv->orientation;
+
+ if (clockwise)
+ orientation <<= 1;
+ else
+ orientation >>= 1;
+
+ if (orientation == 0)
+ orientation = PANEL_ORIENTATION_LEFT;
+
+ else if (orientation > PANEL_ORIENTATION_LEFT)
+ orientation = PANEL_ORIENTATION_TOP;
+
+ panel_toplevel_set_orientation (toplevel, orientation);
+}
+
+PanelState
+panel_toplevel_get_state (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), 0);
+
+ return toplevel->priv->state;
+}
+
+gboolean
+panel_toplevel_get_is_hidden (PanelToplevel *toplevel)
+{
+ g_return_val_if_fail (PANEL_IS_TOPLEVEL (toplevel), FALSE);
+
+ if (toplevel->priv->state == PANEL_STATE_HIDDEN_UP ||
+ toplevel->priv->state == PANEL_STATE_HIDDEN_DOWN ||
+ toplevel->priv->state == PANEL_STATE_HIDDEN_LEFT ||
+ toplevel->priv->state == PANEL_STATE_HIDDEN_RIGHT)
+ return TRUE;
+
+ return FALSE;
+}
+
+int
+panel_toplevel_get_minimum_size (PanelToplevel *toplevel)
+{
+ return calculate_minimum_height (GTK_WIDGET (toplevel),
+ toplevel->priv->orientation);
+}
+
+int
+panel_toplevel_get_maximum_size (PanelToplevel *toplevel)
+{
+ int monitor_width, monitor_height;
+
+ panel_toplevel_get_monitor_geometry (toplevel, NULL, NULL,
+ &monitor_width, &monitor_height);
+
+ if (toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
+ return monitor_height / MAXIMUM_SIZE_SCREEN_RATIO;
+ else
+ return monitor_width / MAXIMUM_SIZE_SCREEN_RATIO;
+}