[gnome-games] aisleriot: Add floating exit-fullscreen button



commit 5e05b593f3cb66b5b761952b39f34b9217bf467e
Author: Christian Persch <chpe gnome org>
Date:   Thu Nov 26 20:11:20 2009 +0100

    aisleriot: Add floating exit-fullscreen button
    
    On maemo5, there's no hardware key to exit the fullscreen mode. So if
    the toolbar is hidden, we show a floating exit-fullscreen button in a
    screen corner. It auto-hides after a few seconds, and reappears when
    clicking on the board background.
    
    Bug #584667.

 aisleriot/Makefile.am            |   14 +
 aisleriot/ar-fullscreen-button.c |  596 ++++++++++++++++++++++++++++++++++++++
 aisleriot/ar-fullscreen-button.h |   62 ++++
 aisleriot/window.c               |   76 +++++-
 4 files changed, 742 insertions(+), 6 deletions(-)
---
diff --git a/aisleriot/Makefile.am b/aisleriot/Makefile.am
index 935699f..ec936fe 100644
--- a/aisleriot/Makefile.am
+++ b/aisleriot/Makefile.am
@@ -35,6 +35,13 @@ sol_SOURCES = \
 	window.h	\
 	$(NULL)
 
+if HAVE_MAEMO_5
+sol_SOURCES += \
+	ar-fullscreen-button.c \
+	ar-fullscreen-button.h \
+	$(NULL)
+endif
+
 if !HAVE_GUILE_1_8
 sol_SOURCES += guile16-compat.h
 endif
@@ -111,6 +118,13 @@ sol_clutter_SOURCES = \
 	window.h	\
 	$(NULL)
 
+if HAVE_MAEMO_5
+sol_clutter_SOURCES += \
+	ar-fullscreen-button.c \
+	ar-fullscreen-button.h \
+	$(NULL)
+endif
+
 if !HAVE_GUILE_1_8
 sol_clutter_SOURCES += guile16-compat.h
 endif
diff --git a/aisleriot/ar-fullscreen-button.c b/aisleriot/ar-fullscreen-button.c
new file mode 100644
index 0000000..28216ca
--- /dev/null
+++ b/aisleriot/ar-fullscreen-button.c
@@ -0,0 +1,596 @@
+/*  
+ * Copyright © 2009 Christian Persch <chpe gnome org>
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "ar-fullscreen-button.h"
+
+#include <libgames-support/games-stock.h>
+
+struct _ArFullscreenButtonPrivate {
+  GtkWindow *window;
+
+  GtkCornerType corner;
+  GtkCornerType effective_corner;
+
+  guint autohide_timeout_id;
+  guint active : 1;
+};
+
+enum {
+  PROP_0,
+  PROP_ACTIVE,
+  PROP_CORNER,
+  PROP_WINDOW
+};
+
+#define AUTOHIDE_TIMEOUT (5 /* s */)
+
+/* private functions */
+
+static gboolean
+autohide_cb (ArFullscreenButton *button)
+{
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  priv->autohide_timeout_id = 0;
+
+  gtk_widget_hide (GTK_WIDGET (button));
+
+  return FALSE;
+}
+
+static void
+autohide_cancel (ArFullscreenButton *button)
+{
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  if (priv->autohide_timeout_id == 0)
+    return;
+
+  g_source_remove (priv->autohide_timeout_id);
+  priv->autohide_timeout_id = 0;
+}
+
+static void
+autohide_reschedule (ArFullscreenButton *button)
+{
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  autohide_cancel (button);
+  if (!priv->active)
+    return;
+
+#if GTK_CHECK_VERSION (2, 14, 0)
+  priv->autohide_timeout_id =
+    gdk_threads_add_timeout_seconds (AUTOHIDE_TIMEOUT,
+                                     (GSourceFunc) autohide_cb,
+                                     button);
+#else
+  priv->autohide_timeout_id =
+    g_timeout_add (AUTOHIDE_TIMEOUT * 1000,
+                   (GSourceFunc) autohide_cb,
+                   button);
+#endif
+}
+
+static void
+update_position (ArFullscreenButton *button)
+{
+  ArFullscreenButtonPrivate *priv = button->priv;
+  GtkWidget *widget = GTK_WIDGET (button);
+  GtkRequisition requisition;
+  GdkScreen *screen;
+  GdkRectangle screen_rect;
+
+  if (!gtk_widget_has_screen (widget) ||
+      gtk_widget_get_style (widget) == NULL)
+    return;
+
+  gtk_widget_size_request (widget, &requisition);
+
+  screen = gtk_widget_get_screen (widget);
+  gdk_screen_get_monitor_geometry
+    (screen,
+     GTK_WIDGET_REALIZED (widget)
+        ? gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget))
+        : 0,
+     &screen_rect);
+
+  switch (priv->effective_corner) {
+    case GTK_CORNER_TOP_LEFT:
+    gtk_window_move (GTK_WINDOW (widget),
+                     screen_rect.x, screen_rect.y);
+      break;
+    case GTK_CORNER_TOP_RIGHT:
+      gtk_window_move (GTK_WINDOW (widget),
+                       screen_rect.x + screen_rect.width - requisition.width,
+                       screen_rect.y);
+      break;
+    case GTK_CORNER_BOTTOM_LEFT:
+      gtk_window_move (GTK_WINDOW (widget),
+                       screen_rect.x,
+                       screen_rect.y + screen_rect.height - requisition.height);
+      break;
+    case GTK_CORNER_BOTTOM_RIGHT:
+      gtk_window_move (GTK_WINDOW (widget),
+                       screen_rect.x + screen_rect.width - requisition.width,
+                       screen_rect.y + screen_rect.height - requisition.height);
+      break;
+  }
+}
+
+static void
+update_screen (ArFullscreenButton *button,
+               GdkScreen *previous_screen,
+               GdkScreen *screen)
+{
+  if (screen == previous_screen)
+    return;
+
+  if (previous_screen != NULL) {
+    g_signal_handlers_disconnect_matched (previous_screen, G_SIGNAL_MATCH_DATA,
+                                          0, 0, NULL, NULL, button);
+  }
+
+  if (screen == NULL)
+    return;
+        
+  g_signal_connect_swapped (screen, "size-changed",
+                            G_CALLBACK (update_position), button);
+
+  /* FIXME: connect to composited changed? */
+}
+
+static void
+set_active (ArFullscreenButton *button,
+            gboolean active)
+{
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  priv->active = active;
+
+  if (active) {
+    autohide_reschedule (button);
+  } else {
+    autohide_cancel (button);
+  }
+
+  g_object_set (button, "visible", active, NULL);
+}
+
+static void
+set_corner (ArFullscreenButton *button,
+            GtkCornerType corner)
+{
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  priv->corner = corner;
+
+  if (gtk_widget_get_direction (GTK_WIDGET (button)) == GTK_TEXT_DIR_RTL) {
+    static const GtkCornerType swap_corners[] = {
+      GTK_CORNER_TOP_RIGHT,
+      GTK_CORNER_BOTTOM_RIGHT,
+      GTK_CORNER_TOP_LEFT,
+      GTK_CORNER_BOTTOM_LEFT
+    };
+
+    priv->effective_corner = swap_corners[corner];
+  } else {
+    priv->effective_corner = corner;
+  }
+
+  update_position (button);
+}
+
+static gboolean
+window_button_press_cb (GtkWidget *window,
+                        GdkEventButton *event,
+                        ArFullscreenButton *button)
+{
+  if (event->type == GDK_BUTTON_PRESS) {
+    ArFullscreenButtonPrivate *priv = button->priv;
+
+    if (priv->active) {
+      gtk_widget_show (GTK_WIDGET (button));
+      autohide_reschedule (button);
+    }
+  }
+
+  return FALSE;
+}
+    
+static void
+window_screen_changed_cb (GtkWidget *window,
+                          GdkScreen *previous_screen,
+                          ArFullscreenButton *button)
+{
+  GdkScreen *screen;
+
+  screen = gtk_widget_get_screen (window);
+  if (screen == gtk_widget_get_screen (GTK_WIDGET (button)))
+    return;
+
+  gtk_window_set_screen (GTK_WINDOW (button), screen);
+}
+    
+/* GType impl */
+
+G_DEFINE_TYPE (ArFullscreenButton, ar_fullscreen_button, GTK_TYPE_WINDOW)
+
+/* GtkWidgetClass impl */
+
+static void
+ar_fullscreen_button_size_allocate (GtkWidget *widget,
+                                    GtkAllocation *allocation)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (widget);
+
+  GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->size_allocate (widget, allocation);
+
+  update_position (button);
+}
+
+static void
+ar_fullscreen_button_realize (GtkWidget *widget)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (widget);
+#ifdef GDK_WINDOWING_X11
+  GdkScreen *screen;
+  GdkColormap *colormap;
+
+  screen = gtk_widget_get_screen (widget);
+  if (gdk_screen_is_composited (screen) &&
+      (colormap = gdk_screen_get_rgba_colormap (screen)) != NULL) {
+    gtk_widget_set_colormap (widget, colormap);
+  } else {
+    gtk_widget_set_colormap (widget, gdk_screen_get_default_colormap (screen));
+  }
+#endif
+
+  GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->realize (widget);
+
+  update_position (button);
+}
+
+static void
+ar_fullscreen_button_show (GtkWidget *widget)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (widget);
+
+  update_position (button);
+  autohide_reschedule (button);
+
+  GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->show (widget);
+}
+
+static void
+ar_fullscreen_button_hide (GtkWidget *widget)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (widget);
+
+  autohide_cancel (button);
+
+  GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->hide (widget);
+}
+
+static gboolean
+ar_fullscreen_button_expose (GtkWidget *widget,
+                             GdkEventExpose *event)
+{
+  return GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->expose_event (widget, event);
+}
+
+static gboolean
+ar_fullscreen_button_button_release (GtkWidget *widget,
+                                     GdkEventButton *event)
+{
+  if (event->button == 1) {
+    ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (widget);
+    ArFullscreenButtonPrivate *priv = button->priv;
+
+    gtk_window_unfullscreen (priv->window);
+    return TRUE;
+  }
+
+  return GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->button_press_event (widget, event);
+}
+
+static void
+ar_fullscreen_button_style_set (GtkWidget *widget,
+                                GtkStyle *previous_style)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (widget);
+  void (* style_set) (GtkWidget*, GtkStyle*) =
+    GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->style_set;
+
+  if (style_set) {
+    style_set (widget, previous_style);
+  }
+
+  update_position (button);
+}
+
+static void
+ar_fullscreen_button_direction_changed (GtkWidget *widget,
+                                        GtkTextDirection previous_direction)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (widget);
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->direction_changed (widget, previous_direction);
+
+  if (gtk_widget_get_direction (widget) != previous_direction) {
+    /* Update the effective corner */
+    set_corner (button, priv->corner);
+  }
+}
+
+static void
+ar_fullscreen_button_screen_changed (GtkWidget *widget,
+                                     GdkScreen *previous_screen)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (widget);
+  void (* screen_changed) (GtkWidget*, GdkScreen*) =
+    GTK_WIDGET_CLASS (ar_fullscreen_button_parent_class)->screen_changed;
+
+  if (screen_changed) {
+    screen_changed (widget, previous_screen);
+  }
+
+  update_screen (button, previous_screen, gtk_widget_get_screen (widget));
+}
+
+/* GObjectClass impl */
+
+static void
+ar_fullscreen_button_init (ArFullscreenButton *button)
+{
+  ArFullscreenButtonPrivate *priv;
+  GtkWidget *widget = GTK_WIDGET (button);
+  GtkWindow *window = GTK_WINDOW (button);
+  GtkWidget *ebox, *image;
+
+  priv = button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button, AR_TYPE_FULLSCREEN_BUTTON, ArFullscreenButtonPrivate);
+
+  priv->active = FALSE;
+  priv->corner = priv->effective_corner = GTK_CORNER_BOTTOM_RIGHT;
+  priv->window = NULL;
+        
+  gtk_window_set_resizable (window, FALSE);
+  gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_NOTIFICATION);
+  gtk_window_set_skip_taskbar_hint (window, TRUE);
+  gtk_window_set_skip_pager_hint (window, TRUE);
+  gtk_window_set_focus_on_map (window, FALSE);
+
+  /* Now create the contents */
+  ebox = gtk_event_box_new ();
+  gtk_event_box_set_visible_window (GTK_EVENT_BOX (ebox), FALSE);
+  gtk_container_add (GTK_CONTAINER (button), ebox);
+
+  image = gtk_image_new_from_stock (GAMES_STOCK_LEAVE_FULLSCREEN, GTK_ICON_SIZE_LARGE_TOOLBAR);
+  gtk_container_add (GTK_CONTAINER (ebox), image);
+  gtk_widget_show_all (ebox);
+
+  /* Need to do this explicitly since there's no initial notification */
+  update_screen (button, NULL, gtk_widget_get_screen (widget));
+}
+
+static void
+ar_fullscreen_button_dispose (GObject *object)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (object);
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  update_screen (button, gtk_widget_get_screen (GTK_WIDGET (button)), NULL);
+
+  if (priv->window != NULL) {
+    g_signal_handlers_disconnect_matched (priv->window,
+                                          G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, button);
+
+    priv->window = NULL;
+  }
+
+  autohide_cancel (button);
+
+  G_OBJECT_CLASS (ar_fullscreen_button_parent_class)->dispose (object);
+}
+
+static void
+ar_fullscreen_button_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (ar_fullscreen_button_parent_class)->finalize (object);
+}
+
+static void
+ar_fullscreen_button_get_property (GObject    *object,
+                                   guint       property_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (object);
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  switch (property_id) {
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, priv->active);
+      break;
+
+    case PROP_CORNER:
+      g_value_set_enum (value, priv->corner);
+      break;
+
+    /* not readable */
+    case PROP_WINDOW:
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+  }
+}
+
+static void
+ar_fullscreen_button_set_property (GObject      *object,
+                                   guint         property_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  ArFullscreenButton *button = AR_FULLSCREEN_BUTTON (object);
+  ArFullscreenButtonPrivate *priv = button->priv;
+
+  switch (property_id) {
+    case PROP_ACTIVE:
+      set_active (button, g_value_get_boolean (value));
+      break;
+    case PROP_CORNER:
+      set_corner (button, g_value_get_enum (value));
+      break;
+    case PROP_WINDOW:
+      priv->window = g_value_get_object (value);
+
+      g_signal_connect (priv->window, "button-press-event",
+                        G_CALLBACK (window_button_press_cb), button);
+      g_signal_connect (priv->window, "screen-changed",
+                        G_CALLBACK (window_screen_changed_cb), button);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+  }
+}
+
+static void
+ar_fullscreen_button_class_init (ArFullscreenButtonClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ArFullscreenButtonPrivate));
+
+  object_class->set_property = ar_fullscreen_button_set_property;
+  object_class->get_property = ar_fullscreen_button_get_property;
+  object_class->dispose = ar_fullscreen_button_dispose;
+  object_class->finalize = ar_fullscreen_button_finalize;
+  
+  widget_class->realize = ar_fullscreen_button_realize;
+  widget_class->screen_changed = ar_fullscreen_button_screen_changed;
+  widget_class->style_set = ar_fullscreen_button_style_set;
+  widget_class->direction_changed = ar_fullscreen_button_direction_changed;
+  widget_class->size_allocate = ar_fullscreen_button_size_allocate;
+  widget_class->show = ar_fullscreen_button_show;
+  widget_class->hide = ar_fullscreen_button_hide;
+  widget_class->expose_event = ar_fullscreen_button_expose;
+  widget_class->button_release_event = ar_fullscreen_button_button_release;
+
+  g_object_class_install_property
+    (object_class,
+     PROP_ACTIVE,
+     g_param_spec_boolean ("active", NULL, NULL,
+                           FALSE,
+                           G_PARAM_READWRITE |
+                           G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property
+    (object_class,
+     PROP_CORNER,
+     g_param_spec_enum ("corner", NULL, NULL,
+                        GTK_TYPE_CORNER_TYPE,
+                        GTK_CORNER_BOTTOM_RIGHT,
+                        G_PARAM_READWRITE |
+                        G_PARAM_CONSTRUCT |
+                        G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property
+    (object_class,
+     PROP_WINDOW,
+     g_param_spec_object ("window", NULL, NULL,
+                          GTK_TYPE_WINDOW,
+                          G_PARAM_WRITABLE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+
+}
+
+/* public API */
+
+/**
+ * ar_fullscreen_button_new:
+ * @window: a parent #GtkWindow
+ * @corner: which screen corner to use
+ *
+ * Return value: a new #ArFullscreenButton
+ */
+GtkWidget *
+ar_fullscreen_button_new (GtkWindow *parent,
+                          GtkCornerType corner)
+{
+  return g_object_new (AR_TYPE_FULLSCREEN_BUTTON,
+                       "type", GTK_WINDOW_POPUP,
+#if GTK_CHECK_VERSION (2, 10, 0)
+                       "transient-for", parent,
+#endif
+                       "window", parent,
+                       "corner", corner,
+                       NULL);
+}
+
+/**
+ * ar_fullscreen_button_set_corner:
+ * @button: a #ArFullscreenButton
+ * @corner: which screen corner to use
+ *
+ * Repositions the button to the specified screen corner.
+ */
+void
+ar_fullscreen_button_set_corner (ArFullscreenButton *button,
+                                 GtkCornerType corner)
+{
+  ArFullscreenButtonPrivate *priv;
+
+  g_return_if_fail (AR_IS_FULLSCREEN_BUTTON (button));
+
+  priv = button->priv;
+  if (priv->corner == corner)
+    return;
+
+  set_corner (button, corner);
+  g_object_notify (G_OBJECT (button), "corner");
+}
+
+/**
+ * ar_fullscreen_button_set_active:
+ * @button: a #ArFullscreenButton
+ * @active: whether to activate the button
+ *
+ * When active, the button auto-hides after a certain time, and re-shows
+ * on button-press-event on the parent window.
+ */
+void
+ar_fullscreen_button_set_active (ArFullscreenButton *button,
+                                 gboolean active)
+{
+  ArFullscreenButtonPrivate *priv;
+
+  g_return_if_fail (AR_IS_FULLSCREEN_BUTTON (button));
+
+  priv = button->priv;
+  
+  active = active != FALSE;
+  if (priv->active == active)
+    return;
+
+  set_active (button, active);
+  g_object_notify (G_OBJECT (button), "active");
+}
diff --git a/aisleriot/ar-fullscreen-button.h b/aisleriot/ar-fullscreen-button.h
new file mode 100644
index 0000000..d0f66c3
--- /dev/null
+++ b/aisleriot/ar-fullscreen-button.h
@@ -0,0 +1,62 @@
+/*  
+ * Copyright © 2009 Christian Persch <chpe gnome org>
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AR_FULLSCREEN_BUTTON_H__
+#define __AR_FULLSCREEN_BUTTON_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define AR_TYPE_FULLSCREEN_BUTTON            (ar_fullscreen_button_get_type())     
+#define AR_FULLSCREEN_BUTTON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), AR_TYPE_FULLSCREEN_BUTTON, ArFullscreenButton))     
+#define AR_FULLSCREEN_BUTTON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  AR_TYPE_FULLSCREEN_BUTTON, ArFullscreenButtonClass))
+#define AR_IS_FULLSCREEN_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AR_TYPE_FULLSCREEN_BUTTON))                      
+#define AR_IS_FULLSCREEN_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  AR_TYPE_FULLSCREEN_BUTTON))                      
+#define AR_FULLSCREEN_BUTTON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  AR_TYPE_FULLSCREEN_BUTTON, ArFullscreenButtonClass))
+
+typedef struct _ArFullscreenButton        ArFullscreenButton;
+typedef struct _ArFullscreenButtonClass   ArFullscreenButtonClass;
+typedef struct _ArFullscreenButtonPrivate ArFullscreenButtonPrivate;
+
+struct _ArFullscreenButton
+{
+  GtkWindow parent;
+
+  /*< private >*/
+  ArFullscreenButtonPrivate *priv;
+};
+
+struct _ArFullscreenButtonClass
+{
+  GtkWindowClass parent_class;
+};
+
+GType ar_fullscreen_button_get_type (void);
+
+GtkWidget* ar_fullscreen_button_new (GtkWindow *parent,
+                                     GtkCornerType corner);
+
+void ar_fullscreen_button_set_corner (ArFullscreenButton *button,
+                                      GtkCornerType corner);
+
+void ar_fullscreen_button_set_active (ArFullscreenButton *button,
+                                      gboolean active);
+
+G_END_DECLS
+
+#endif /* __AR_FULLSCREEN_BUTTON_H__ */
diff --git a/aisleriot/window.c b/aisleriot/window.c
index bacc4a1..d892f16 100644
--- a/aisleriot/window.c
+++ b/aisleriot/window.c
@@ -59,6 +59,7 @@
 #include "game.h"
 #include "stats-dialog.h"
 #include "util.h"
+#include "ar-fullscreen-button.h"
 
 #include "window.h"
 
@@ -93,6 +94,13 @@
 #define MAEMO_TOOLBAR_BANNER
 #endif
 
+/* On maemo5, there's no hardware key to exit the fullscreen mode. So we show
+ * an overlay button to restore normal mode, if the toolbar is hidden too.
+ */
+#if defined(HAVE_HILDON) && defined(HAVE_MAEMO_5)
+#define LEAVE_FULLSCREEN_BUTTON
+#endif
+
 /* define this to enable a debug menu */
 /* #undef ENABLE_DEBUG_UI */
 
@@ -161,6 +169,10 @@ struct _AisleriotWindowPrivate
   GtkWidget *hint_dialog;
 #endif
 
+#ifdef LEAVE_FULLSCREEN_BUTTON
+  GtkWidget *fullscreen_button;
+#endif
+
   guint load_idle_id;
 
   guint use_pixbuf_drawing : 1;
@@ -168,6 +180,7 @@ struct _AisleriotWindowPrivate
   guint freecell_mode : 1;
   guint toolbar_visible : 1;
   guint statusbar_visible : 1;
+  guint fullscreen : 1;
 };
 
 enum {
@@ -1019,6 +1032,31 @@ debug_pixbuf_drawing_cb (GtkToggleAction *action,
 #endif /* ENABLE_DEBUG_UI */
 
 static void
+set_fullscreen_button_active (AisleriotWindow *window)
+{
+#ifdef LEAVE_FULLSCREEN_BUTTON
+  AisleriotWindowPrivate *priv = window->priv;
+  gboolean active;
+
+  active = priv->fullscreen && !priv->toolbar_visible;
+  if (!active) {
+    if (priv->fullscreen_button != NULL) {
+      ar_fullscreen_button_set_active (AR_FULLSCREEN_BUTTON (priv->fullscreen_button), FALSE);
+    }
+
+    return;
+  }
+
+  if (active && priv->fullscreen_button == NULL) {
+    priv->fullscreen_button = ar_fullscreen_button_new (GTK_WINDOW (window),
+                                                        GTK_CORNER_TOP_RIGHT);
+  }
+
+  ar_fullscreen_button_set_active (AR_FULLSCREEN_BUTTON (priv->fullscreen_button), TRUE);
+#endif /* LEAVE_FULLSCREEN_BUTTON */
+}
+
+static void
 toolbar_toggled_cb (GtkToggleAction *action,
                     AisleriotWindow *window)
 {
@@ -1032,6 +1070,8 @@ toolbar_toggled_cb (GtkToggleAction *action,
   priv->toolbar_visible = state != FALSE;
 
   games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_TOOLBAR), state);
+
+  set_fullscreen_button_active (window);
 }
 
 #ifndef HAVE_HILDON
@@ -1063,6 +1103,8 @@ set_fullscreen_actions (AisleriotWindow *window,
 {
   AisleriotWindowPrivate *priv = window->priv;
 
+  priv->fullscreen = is_fullscreen;
+
 #ifndef HAVE_MAEMO
   g_object_set (priv->main_menu, "visible", !is_fullscreen, NULL);
 #endif /* HAVE_MAEMO */
@@ -2169,6 +2211,8 @@ aisleriot_window_state_event (GtkWidget *widget,
 
     set_fullscreen_actions (window, is_fullscreen);
 
+    set_fullscreen_button_active (window);
+
 #ifndef HAVE_HILDON
 #if GTK_CHECK_VERSION (2, 11, 0)
     gtk_statusbar_set_has_resize_grip (priv->statusbar, !is_maximised && !is_fullscreen);
@@ -2521,6 +2565,8 @@ aisleriot_window_init (AisleriotWindow *window)
 
   priv = window->priv = AISLERIOT_WINDOW_GET_PRIVATE (window);
 
+  priv->fullscreen = FALSE;
+
   priv->game = aisleriot_game_new ();
 
 #ifdef HAVE_HILDON
@@ -2832,11 +2878,11 @@ aisleriot_window_init (AisleriotWindow *window)
 }
 
 static void
-aisleriot_window_finalize (GObject *object)
+aisleriot_window_dispose (GObject *object)
 {
   AisleriotWindow *window = AISLERIOT_WINDOW (object);
   AisleriotWindowPrivate *priv = window->priv;
-
+  
 #ifdef HAVE_CLUTTER
   g_signal_handlers_disconnect_by_func (gtk_widget_get_settings (GTK_WIDGET (window)),
                                         G_CALLBACK (settings_changed_cb),
@@ -2861,6 +2907,27 @@ aisleriot_window_finalize (GObject *object)
     gtk_widget_destroy (GTK_WIDGET (priv->stats_dialog));
     g_assert (priv->stats_dialog == NULL);
   }
+  
+#ifdef LEAVE_FULLSCREEN_BUTTON
+  if (priv->fullscreen_button != NULL) {
+    gtk_widget_destroy (GTK_WIDGET (priv->fullscreen_button));
+    priv->fullscreen_button = NULL;
+  }
+#endif
+
+  if (priv->load_idle_id != 0) {
+    g_source_remove (priv->load_idle_id);
+    priv->load_idle_id = 0;
+  }
+
+  G_OBJECT_CLASS (aisleriot_window_parent_class)->dispose (object);
+}
+
+static void
+aisleriot_window_finalize (GObject *object)
+{
+  AisleriotWindow *window = AISLERIOT_WINDOW (object);
+  AisleriotWindowPrivate *priv = window->priv;
 
   if (priv->theme) {
     g_object_unref (priv->theme);
@@ -2873,10 +2940,6 @@ aisleriot_window_finalize (GObject *object)
                                         0, 0, NULL, NULL, window);
   g_object_unref (priv->game);
 
-  if (priv->load_idle_id != 0) {
-    g_source_remove (priv->load_idle_id);
-  }
-
   G_OBJECT_CLASS (aisleriot_window_parent_class)->finalize (object);
 }
 
@@ -2886,6 +2949,7 @@ aisleriot_window_class_init (AisleriotWindowClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+  gobject_class->dispose = aisleriot_window_dispose;
   gobject_class->finalize = aisleriot_window_finalize;
 
   widget_class->window_state_event = aisleriot_window_state_event;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]