[gnome-games] aisleriot: Add tooltips and status message to AisleriotBoard



commit 7261d56baff97083d1b2744b2cbab6b6579199cc
Author: Christian Persch <chpe gnome org>
Date:   Sun Jan 10 21:34:51 2010 +0100

    aisleriot: Add tooltips and status message to AisleriotBoard
    
    Add style properties for showing tooltips and status messages, and make
    them show tooltips or status message in the window when moving the mouse
    over the cards and slots.

 aisleriot/ar-style-private.h |    5 ++
 aisleriot/ar-style.c         |   75 ++++++++++++++++++++++++++-
 aisleriot/ar-style.h         |    4 ++
 aisleriot/board-noclutter.c  |  119 ++++++++++++++++++++++++++++++++++++++++-
 aisleriot/board-noclutter.h  |    3 +
 aisleriot/game.c             |  106 +++++++++++++++++++++++++++++++++++++
 aisleriot/game.h             |    7 +++
 aisleriot/window.c           |   25 +++++++++
 8 files changed, 340 insertions(+), 4 deletions(-)
---
diff --git a/aisleriot/ar-style-private.h b/aisleriot/ar-style-private.h
index af4df04..e2c62be 100644
--- a/aisleriot/ar-style-private.h
+++ b/aisleriot/ar-style-private.h
@@ -43,6 +43,9 @@ static const GdkColor default_selection_color = { 0, 0 /* red */, 0 /* green */,
 #define DEFAULT_PIXBUF_DRAWING (TRUE)
 #endif
 
+#define DEFAULT_SHOW_TOOLTIPS (FALSE)
+#define DEFAULT_SHOW_STATUS_MESSAGES (TRUE)
+
 struct _ArStylePrivate
 {
   GamesCardTheme* card_theme;
@@ -66,6 +69,8 @@ struct _ArStylePrivate
   guint enable_animations       : 1;
   guint enable_sound_gtk        : 1;
   guint enable_sound            : 1;
+  guint enable_tooltips         : 1;
+  guint enable_status_messages  : 1;
   guint touchscreen_mode        : 1;
 
   guint rtl                     : 1;
diff --git a/aisleriot/ar-style.c b/aisleriot/ar-style.c
index a7f924b..e5f5164 100644
--- a/aisleriot/ar-style.c
+++ b/aisleriot/ar-style.c
@@ -43,6 +43,8 @@ enum
 #endif
   PROP_RTL,
   PROP_SELECTION_COLOR,
+  PROP_SHOW_TOOLTIPS,
+  PROP_SHOW_STATUS_MESSAGES,
   PROP_TOUCHSCREEN_MODE
 };
 
@@ -80,6 +82,8 @@ ar_style_init (ArStyle *style)
   priv->rtl = FALSE;
   priv->interior_focus = FALSE;
   priv->click_to_move = FALSE;
+  priv->enable_tooltips = DEFAULT_SHOW_TOOLTIPS;
+  priv->enable_status_messages = DEFAULT_SHOW_STATUS_MESSAGES;
 
 #ifndef HAVE_CLUTTER
 
@@ -188,6 +192,14 @@ ar_style_get_property (GObject    *object,
       g_value_set_boxed (value, &priv->selection_color);
       break;
 
+    case PROP_SHOW_TOOLTIPS:
+      g_value_set_boolean (value, ar_style_get_show_tooltips (style));
+      break;
+
+    case PROP_SHOW_STATUS_MESSAGES:
+      g_value_set_boolean (value, ar_style_get_show_status_messages (style));
+      break;
+
     case PROP_TOUCHSCREEN_MODE:
       g_value_set_boolean (value, ar_style_get_touchscreen_mode (style));
       break;
@@ -285,6 +297,14 @@ ar_style_set_property (GObject      *object,
       ar_style_set_enable_sound (style, g_value_get_boolean (value));
       break;
 
+    case PROP_SHOW_TOOLTIPS:
+      priv->enable_tooltips = g_value_get_boolean (value) != FALSE;
+      break;
+
+    case PROP_SHOW_STATUS_MESSAGES:
+      priv->enable_status_messages = g_value_get_boolean (value) != FALSE;
+      break;
+
     case PROP_TOUCHSCREEN_MODE:
       priv->touchscreen_mode = g_value_get_boolean (value) != FALSE;
       break;
@@ -453,6 +473,32 @@ ar_style_class_init (ArStyleClass *klass)
                          G_PARAM_STATIC_STRINGS));
 #endif /* HAVE_CLUTTER */
 
+  /**
+   * ArStyle:show-tooltips:
+   *
+   * Whether to show tooltips on the cards and slots.
+   */
+  g_object_class_install_property
+    (object_class,
+     PROP_SHOW_TOOLTIPS,
+     g_param_spec_boolean (AR_STYLE_PROP_SHOW_TOOLTIPS, NULL, NULL,
+                           DEFAULT_SHOW_TOOLTIPS,
+                           G_PARAM_READWRITE |
+                           G_PARAM_STATIC_STRINGS));
+
+  /**
+   * ArStyle:show-status-messages:
+   *
+   * Whether to show status messages on motion over the cards and slots.
+   */
+  g_object_class_install_property
+    (object_class,
+     PROP_SHOW_STATUS_MESSAGES,
+     g_param_spec_boolean (AR_STYLE_PROP_SHOW_STATUS_MESSAGES, NULL, NULL,
+                           DEFAULT_SHOW_STATUS_MESSAGES,
+                           G_PARAM_READWRITE |
+                           G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_property
     (object_class,
      PROP_TOUCHSCREEN_MODE,
@@ -666,7 +712,6 @@ ar_style_get_interior_focus (ArStyle *style)
   return priv->interior_focus;
 }
 
-
 /**
  * ar_style_get_rtl:
  * @style: an #ArStyle
@@ -682,6 +727,34 @@ ar_style_get_rtl (ArStyle *style)
 }
 
 /**
+ * ar_style_get_show_tooltips:
+ * @style: an #ArStyle
+ *
+ * Returns:
+ */
+gboolean
+ar_style_get_show_tooltips (ArStyle *style)
+{
+  ArStylePrivate *priv = style->priv;
+
+  return priv->enable_tooltips;
+}
+
+/**
+ * ar_style_get_show_status_messages:
+ * @style: an #ArStyle
+ *
+ * Returns:
+ */
+gboolean
+ar_style_get_show_status_messages (ArStyle *style)
+{
+  ArStylePrivate *priv = style->priv;
+
+  return priv->enable_status_messages;
+}
+
+/**
  * ar_style_get_double_click_time:
  * @style: an #ArStyle
  *
diff --git a/aisleriot/ar-style.h b/aisleriot/ar-style.h
index 54f735c..ae9e3c3 100644
--- a/aisleriot/ar-style.h
+++ b/aisleriot/ar-style.h
@@ -72,6 +72,8 @@ ArStyle* ar_style_new (void);
 #define AR_STYLE_PROP_INTERIOR_FOCUS      "interior-focus"
 #define AR_STYLE_PROP_RTL                 "rtl"
 #define AR_STYLE_PROP_SELECTION_COLOR     "selection-color"
+#define AR_STYLE_PROP_SHOW_TOOLTIPS       "show-tooltips"
+#define AR_STYLE_PROP_SHOW_STATUS_MESSAGES "show-status-messages"
 #define AR_STYLE_PROP_TOUCHSCREEN_MODE    "touchscreen-mode"
 
 gboolean ar_style_get_enable_animations (ArStyle *style);
@@ -94,6 +96,8 @@ void            ar_style_set_card_theme (ArStyle *style,
 gboolean ar_style_get_touchscreen_mode (ArStyle *style);
 gboolean ar_style_get_interior_focus   (ArStyle *style);
 gboolean ar_style_get_rtl              (ArStyle *style);
+gboolean ar_style_get_show_tooltips    (ArStyle *style);
+gboolean ar_style_get_show_status_messages (ArStyle *style);
 
 int ar_style_get_double_click_time  (ArStyle *style);
 int ar_style_get_focus_line_width   (ArStyle *style);
diff --git a/aisleriot/board-noclutter.c b/aisleriot/board-noclutter.c
index 5570d4c..f5441ac 100644
--- a/aisleriot/board-noclutter.c
+++ b/aisleriot/board-noclutter.c
@@ -152,6 +152,9 @@ struct _AisleriotBoardPrivate
   int tap_and_hold_card_id;
 #endif
 
+  /* Status message */
+  const char *status_message; /* interned */
+
   /* Bit field */
   guint droppable_supported : 1;
   guint use_pixbuf_drawing : 1;
@@ -167,6 +170,7 @@ struct _AisleriotBoardPrivate
 
   guint show_selection : 1;
   guint show_highlight : 1;
+  guint show_status_messages : 1;
 
   guint force_geometry_update : 1;
 };
@@ -180,19 +184,20 @@ enum
   PROP_STYLE
 };
 
-#ifdef ENABLE_KEYNAV
 enum
 {
+  STATUS_MESSAGE,
+#ifdef ENABLE_KEYNAV
   ACTIVATE,
   MOVE_CURSOR,
   TOGGLE_SELECTION,
   SELECT_ALL,
   DESELECT_ALL,
+#endif /* ENABLE_KEYNAV */
   LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL];
-#endif /* ENABLE_KEYNAV */
 
 static void get_slot_and_card_from_point (AisleriotBoard *board,
                                           int x,
@@ -267,6 +272,22 @@ set_cursor_by_location (AisleriotBoard *board,
 #endif /* !HAVE_HILDON */
 }
 
+/* status message */
+
+static void
+set_status_message (AisleriotBoard *board,
+                    const char *message)
+{
+  AisleriotBoardPrivate *priv = board->priv;
+
+  if (g_strcmp0 (priv->status_message, message) == 0)
+    return;
+
+  priv->status_message = g_intern_string (message);
+
+  g_signal_emit (board, signals[STATUS_MESSAGE], 0, priv->status_message);
+}
+
 /* card drawing functions */
 
 static void
@@ -1367,6 +1388,40 @@ aisleriot_board_move_selected_cards_to_slot (AisleriotBoard *board,
   return moved;
 }
 
+/* Tooltips */
+
+#if GTK_CHECK_VERSION (2, 12, 0) && !defined(HAVE_HILDON)
+
+static gboolean
+aisleriot_board_query_tooltip_cb (GtkWidget *widget,
+                                  int x,
+                                  int y,
+                                  gboolean keyboard_mode,
+                                  GtkTooltip *tooltip,
+                                  AisleriotBoard *board)
+{
+  ArSlot *slot;
+  int cardid;
+  char *text;
+  GdkRectangle rect;
+
+  get_slot_and_card_from_point (board, x, y, &slot, &cardid);
+  if (!slot || ar_slot_get_slot_type (slot) == AR_SLOT_UNKNOWN)
+    return FALSE;
+
+  text = ar_slot_get_hint_string (slot, cardid);
+  gtk_tooltip_set_text (tooltip, text);
+  g_free (text);
+
+  get_rect_by_slot_and_card (board, slot, cardid, 1, &rect);
+  /* FIXMEchpe: subtract the rect of the next card, if there is one! */
+  gtk_tooltip_set_tip_area (tooltip, &rect);
+
+  return TRUE;
+}
+
+#endif /* GTK >= 2.12.0 && !HAVE_HILDON */
+
 /* Keynav */
 
 #ifdef ENABLE_KEYNAV
@@ -2486,6 +2541,29 @@ aisleriot_board_sync_style (ArStyle *style,
     queue_redraw |= TRUE;
   }
 
+#if GTK_CHECK_VERSION (2, 12, 0) && !defined(HAVE_HILDON)
+  if (pspec_name == NULL || pspec_name == I_(AR_STYLE_PROP_SHOW_TOOLTIPS)) {
+    gtk_widget_set_has_tooltip (widget, ar_style_get_show_tooltips (priv->style));
+  }
+#endif
+
+#ifndef HAVE_HILDON
+  if (pspec_name == NULL || pspec_name == I_(AR_STYLE_PROP_SHOW_STATUS_MESSAGES)) {
+    gboolean show_status_messages;
+
+    show_status_messages = ar_style_get_show_status_messages (priv->style);
+
+    if (show_status_messages != priv->show_status_messages) {
+      priv->show_status_messages = show_status_messages;
+
+      if (!show_status_messages) {
+        /* Clear message */
+        set_status_message (board, NULL);
+      }
+    }
+  }
+#endif
+
   if (update_geometry && GTK_WIDGET_REALIZED (widget)) {
     aisleriot_board_setup_geometry (board);
   }
@@ -2869,7 +2947,7 @@ aisleriot_board_button_release (GtkWidget *widget,
 
 static gboolean
 aisleriot_board_motion_notify (GtkWidget *widget,
-                               GdkEventMotion * event)
+                               GdkEventMotion *event)
 {
   AisleriotBoard *board = AISLERIOT_BOARD (widget);
   AisleriotBoardPrivate *priv = board->priv;
@@ -2878,6 +2956,24 @@ aisleriot_board_motion_notify (GtkWidget *widget,
    * parent class has no class closure for this event.
    */
 
+#ifndef HAVE_HILDON
+  if (priv->show_status_messages) {
+    ArSlot *slot = NULL;
+    int cardid = -1;
+
+    get_slot_and_card_from_point (board, event->x, event->y, &slot, &cardid);
+    if (slot != NULL && ar_slot_get_slot_type (slot) != AR_SLOT_UNKNOWN) {
+      char *text;
+
+      text = ar_slot_get_hint_string (slot, cardid);
+      set_status_message (board, text);
+      g_free (text);
+    } else {
+      set_status_message (board, NULL);
+    }
+  }
+#endif /* !HAVE_HILDON */
+
   if (priv->click_status == STATUS_IS_DRAG) {
     ArSlot *slot;
     int x, y;
@@ -3249,6 +3345,7 @@ aisleriot_board_init (AisleriotBoard *board)
 
   priv->click_to_move = FALSE;
   priv->show_selection = FALSE;
+  priv->show_status_messages = FALSE;
 
   priv->show_card_id = -1;
 
@@ -3271,6 +3368,11 @@ aisleriot_board_init (AisleriotBoard *board)
   g_signal_connect (widget, "tap-and-hold",
                     G_CALLBACK (aisleriot_board_tap_and_hold_cb), board);
 #endif /* HAVE_MAEMO */
+
+#if GTK_CHECK_VERSION (2, 12, 0) && !defined(HAVE_HILDON)
+  g_signal_connect (widget, "query-tooltip",
+                    G_CALLBACK (aisleriot_board_query_tooltip_cb), board);
+#endif
 }
 
 static void
@@ -3376,6 +3478,17 @@ aisleriot_board_class_init (AisleriotBoardClass *klass)
   widget_class->key_press_event = aisleriot_board_key_press;
   widget_class->expose_event = aisleriot_board_expose_event;
 
+  signals[STATUS_MESSAGE] =
+    g_signal_new (I_("status-message"),
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (AisleriotBoardClass, status_message),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__STRING,
+                  G_TYPE_NONE,
+                  1,
+                  G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
+
 #ifdef ENABLE_KEYNAV
   klass->activate = aisleriot_board_activate;
   klass->move_cursor = aisleriot_board_move_cursor;
diff --git a/aisleriot/board-noclutter.h b/aisleriot/board-noclutter.h
index e312ff1..bda4a56 100644
--- a/aisleriot/board-noclutter.h
+++ b/aisleriot/board-noclutter.h
@@ -49,6 +49,9 @@ struct _AisleriotBoard {
 struct _AisleriotBoardClass {
   GtkDrawingAreaClass parent_class;
 
+  void (* status_message)   (AisleriotBoard *board,
+                             const char *message);
+
   /* keybinding signals */
   gboolean (* move_cursor)  (AisleriotBoard *board,
                              GtkMovementStep step,
diff --git a/aisleriot/game.c b/aisleriot/game.c
index c141750..f5e1a0c 100644
--- a/aisleriot/game.c
+++ b/aisleriot/game.c
@@ -1380,6 +1380,112 @@ aisleriot_game_class_init (AisleriotGameClass *klass)
 /* public API */
 
 /**
+ * ar_slot_get_slot_type:
+ * @slot: a #ArSlot
+ *
+ * Returns: the slot type of @slot
+ */
+ArSlotType
+ar_slot_get_slot_type (ArSlot *slot)
+{
+  g_return_val_if_fail (slot != NULL, AR_SLOT_UNKNOWN);
+
+  return slot->type;
+}
+
+/**
+ * ar_slot_get_type_string:
+ * @slot: a #ArSlot
+ *
+ * Returns: a string describing the slot type
+ */
+const char *
+ar_slot_get_type_string (ArSlot *slot)
+{
+  const char *text = NULL;
+
+  g_return_val_if_fail (slot != NULL, NULL);
+
+  switch (slot->type) {
+    case AR_SLOT_UNKNOWN:
+      text = NULL;
+      break;
+    case AR_SLOT_FOUNDATION:
+      /* Translators: this is the name of a type of card slot */
+      text = C_("slot type", "foundation");
+      break;
+    case AR_SLOT_RESERVE:
+      /* Translators: this is the name of a type of card slot */
+      text = C_("slot type", "reserve");
+      break;
+    case AR_SLOT_STOCK:
+      /* Translators: this is the name of a type of card slot */
+      text = C_("slot type", "stock");
+      break;
+    case AR_SLOT_TABLEAU:
+      /* Translators: this is the name of a type of card slot */
+      text = C_("slot type", "tableau");
+      break;
+    case AR_SLOT_WASTE:
+      /* Translators: this is the name of a type of card slot */
+      text = C_("slot type", "waste");
+      break;
+  }
+
+  return text;
+}
+
+/**
+ * ar_slot_get_hint_string:
+ * @slot: a #ArSlot
+ *
+ * Returns: a string describing the slot type
+ */
+char *
+ar_slot_get_hint_string (ArSlot *slot,
+                         int cardid)
+{
+  const char *card_name;
+
+  g_return_val_if_fail (slot != NULL, NULL);
+
+  if (cardid < 0)
+    return g_strdup (ar_slot_get_type_string (slot));
+
+  card_name = games_card_get_locale_name (CARD (slot->cards->data[cardid]));
+
+  switch (slot->type) {
+    case AR_SLOT_UNKNOWN:
+      return g_strdup (card_name);
+
+    case AR_SLOT_FOUNDATION:
+      /* Translators: %s is the name of the card; foundation is the name of a type of card slot */
+      return g_strdup_printf (C_("slot hint", "%s on foundation"), card_name);
+
+    case AR_SLOT_RESERVE:
+      /* Translators: this is the name of a type of card slot */
+      return g_strdup_printf (C_("slot hint", "%s on reserve"), card_name);
+
+    case AR_SLOT_STOCK:
+      /* Translators: this is the name of a type of card slot */
+      return g_strdup_printf (C_("slot hint", "%s on stock"), card_name);
+
+    case AR_SLOT_TABLEAU:
+      /* Translators: this is the name of a type of card slot */
+      return g_strdup_printf (C_("slot hint", "%s on tableau"), card_name);
+
+    case AR_SLOT_WASTE:
+      /* Translators: this is the name of a type of card slot */
+      return g_strdup_printf (C_("slot hint", "%s on waste"), card_name);
+
+    default:
+      g_assert_not_reached ();
+  }
+
+  return NULL;
+}
+
+/**
  * aisleriot_game_error_quark:
  *
  * Returns: the #GQuark used for errors
diff --git a/aisleriot/game.h b/aisleriot/game.h
index f76c262..87c1a18 100644
--- a/aisleriot/game.h
+++ b/aisleriot/game.h
@@ -87,6 +87,13 @@ typedef struct {
 
 #define SLOT_CARDS_N_PREALLOC (32)
 
+ArSlotType ar_slot_get_slot_type (ArSlot *slot);
+
+const char *ar_slot_get_type_string (ArSlot *slot);
+
+char *ar_slot_get_hint_string (ArSlot *slot,
+                               int cardid);
+
 /* The game */
 
 #define AISLERIOT_TYPE_GAME         (aisleriot_game_get_type ())
diff --git a/aisleriot/window.c b/aisleriot/window.c
index e8491b8..e6fbb6b 100644
--- a/aisleriot/window.c
+++ b/aisleriot/window.c
@@ -154,6 +154,7 @@ struct _AisleriotWindowPrivate
 #else
   GtkStatusbar *statusbar;
   guint game_message_id;
+  guint board_message_id;
   GtkWidget *score_box;
   GtkWidget *score_label;
   GtkWidget *clock;
@@ -1765,6 +1766,7 @@ game_type_changed_cb (AisleriotGame *game,
   games_clock_reset (GAMES_CLOCK (priv->clock));
 
   gtk_statusbar_pop (priv->statusbar, priv->game_message_id);
+  gtk_statusbar_pop (priv->statusbar, priv->board_message_id);
 
   show_scores = (features & FEATURE_SCORE_HIDDEN) == 0;
   g_object_set (priv->score_box, "visible", show_scores, NULL);
@@ -1791,6 +1793,7 @@ game_new_cb (AisleriotGame *game,
   games_clock_reset (GAMES_CLOCK (priv->clock));
 
   gtk_statusbar_pop (priv->statusbar, priv->game_message_id);
+  gtk_statusbar_pop (priv->statusbar, priv->board_message_id);
 #endif /* HAVE_HILDON */
 }
 
@@ -2042,6 +2045,24 @@ aisleriot_window_set_freecell_mode (AisleriotWindow *window,
   }
 }
 
+#ifndef HAVE_HILDON
+
+static void
+board_status_message_cb (AisleriotBoard *board,
+                         const char *status_message,
+                         AisleriotWindow *window)
+{
+  AisleriotWindowPrivate *priv = window->priv;
+
+  gtk_statusbar_pop (priv->statusbar, priv->board_message_id);
+
+  if (status_message != NULL) {
+    gtk_statusbar_push (priv->statusbar, priv->board_message_id, status_message);
+  }
+}
+
+#endif /* !HAVE_HILDON */
+
 #ifdef HAVE_CLUTTER
 
 static void
@@ -2560,6 +2581,10 @@ aisleriot_window_init (AisleriotWindow *window)
   games_stock_prepare_for_statusbar_tooltips (priv->ui_manager,
                                               GTK_WIDGET (priv->statusbar));
 
+  priv->game_message_id = gtk_statusbar_get_context_id (priv->statusbar, "board-message");
+  g_signal_connect (priv->board, "status-message",
+                    G_CALLBACK (board_status_message_cb), window);
+
 #if GTK_CHECK_VERSION (2, 11, 0)
   gtk_statusbar_set_has_resize_grip (priv->statusbar, TRUE);
 #else



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