Commit ef57e639 authored by Christian Persch's avatar Christian Persch

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.
parent 40cc20a3
......@@ -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;
......
......@@ -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
......@@ -681,6 +726,34 @@ ar_style_get_rtl (ArStyle *style)
return priv->rtl;
}
/**
* 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
......
......@@ -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);
......
......@@ -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;
......
......@@ -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,
......
......@@ -1379,6 +1379,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:
*
......
......@@ -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 ())
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment