#include "gtkcellview.h"
#include "gtkeventbox.h"
#include "gtkframe.h"
-#include "gtkhbox.h"
+#include "gtkbox.h"
#include "gtkliststore.h"
#include "gtkmain.h"
#include "gtkmenuprivate.h"
#include "gtkmenushellprivate.h"
#include "gtkscrolledwindow.h"
#include "gtkseparatormenuitem.h"
-#include "gtktearoffmenuitem.h"
+#include "deprecated/gtktearoffmenuitem.h"
#include "gtktogglebutton.h"
#include "gtktreeselection.h"
-#include "gtkvseparator.h"
+#include "gtkseparator.h"
#include "gtkwindow.h"
#include "gtktypebuiltins.h"
#include "gtkprivate.h"
#include "gtkentryprivate.h"
#include "gtktreeprivate.h"
+#include "a11y/gtkcomboboxaccessible.h"
/**
MOVE_ACTIVE,
POPUP,
POPDOWN,
+ FORMAT_ENTRY_TEXT,
LAST_SIGNAL
};
static guint combo_box_signals[LAST_SIGNAL] = {0,};
-#define BONUS_PADDING 4
#define SCROLL_TIME 100
/* common */
gpointer user_data);
static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
gpointer user_data);
-
+static gchar *gtk_combo_box_format_entry_text (GtkComboBox *combo_box,
+ const gchar *path);
/* GtkBuildable method implementation */
static GtkBuildableIface *parent_buildable_iface;
gint avail_size,
gint *minimum_size,
gint *natural_size);
-
+static GtkWidgetPath *gtk_combo_box_get_path_for_child (GtkContainer *container,
+ GtkWidget *child);
+static void gtk_combo_box_direction_changed (GtkWidget *widget,
+ GtkTextDirection previous_direction);
G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
container_class->forall = gtk_combo_box_forall;
container_class->add = gtk_combo_box_add;
container_class->remove = gtk_combo_box_remove;
+ container_class->get_path_for_child = gtk_combo_box_get_path_for_child;
widget_class = (GtkWidgetClass *)klass;
widget_class->size_allocate = gtk_combo_box_size_allocate;
widget_class->get_preferred_height_for_width = gtk_combo_box_get_preferred_height_for_width;
widget_class->get_preferred_width_for_height = gtk_combo_box_get_preferred_width_for_height;
widget_class->destroy = gtk_combo_box_destroy;
+ widget_class->direction_changed = gtk_combo_box_direction_changed;
object_class = (GObjectClass *)klass;
object_class->constructor = gtk_combo_box_constructor;
object_class->set_property = gtk_combo_box_set_property;
object_class->get_property = gtk_combo_box_get_property;
+ klass->format_entry_text = gtk_combo_box_format_entry_text;
+
/* signals */
/**
* GtkComboBox::changed:
_gtk_marshal_BOOLEAN__VOID,
G_TYPE_BOOLEAN, 0);
+ /**
+ * GtkComboBox::format-entry-text:
+ * @combo: the object which received the signal
+ * @path: the GtkTreePath string from the combo box's current model to format text for
+ *
+ * For combo boxes that are created with an entry (See GtkComboBox:has-entry).
+ *
+ * A signal which allows you to change how the text displayed in a combo box's
+ * entry is displayed.
+ *
+ * Connect a signal handler which returns an allocated string representing
+ * @path. That string will then be used to set the text in the combo box's entry.
+ * The default signal handler uses the text from the GtkComboBox::entry-text-column
+ * model column.
+ *
+ * Here's an example signal handler which fetches data from the model and
+ * displays it in the entry.
+ * |[
+ * static gchar*
+ * format_entry_text_callback (GtkComboBox *combo,
+ * const gchar *path,
+ * gpointer user_data)
+ * {
+ * GtkTreeIter iter;
+ * GtkTreeModel model;
+ * gdouble value;
+ *
+ * model = gtk_combo_box_get_model (combo);
+ *
+ * gtk_tree_model_get_iter_from_string (model, &iter, path);
+ * gtk_tree_model_get (model, &iter,
+ * THE_DOUBLE_VALUE_COLUMN, &value,
+ * -1);
+ *
+ * return g_strdup_printf ("%g", value);
+ * }
+ * ]|
+ *
+ * Return value: (transfer full): a newly allocated string representing @path
+ * for the current GtkComboBox model.
+ *
+ * Since: 3.4
+ */
+ combo_box_signals[FORMAT_ENTRY_TEXT] =
+ g_signal_new (I_("format-entry-text"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkComboBoxClass, format_entry_text),
+ _gtk_single_string_accumulator, NULL,
+ _gtk_marshal_STRING__STRING,
+ G_TYPE_STRING, 1, G_TYPE_STRING);
+
/* key bindings */
binding_set = gtk_binding_set_by_class (widget_class);
GTK_PARAM_READABLE));
g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
+
+ gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COMBO_BOX_ACCESSIBLE);
}
static void
{
GtkStyleContext *context;
GtkStateFlags state;
- GdkRGBA *color;
+ GdkRGBA color;
context = gtk_widget_get_style_context (widget);
state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_background_color (context, state, &color);
- gtk_style_context_get (context, state,
- "background-color", &color,
- NULL);
-
- gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
- gdk_rgba_free (color);
+ gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
}
}
gtk_widget_queue_draw (widget);
}
+static void
+gtk_combo_box_invalidate_order (GtkComboBox *combo_box)
+{
+ gtk_container_forall (GTK_CONTAINER (combo_box),
+ (GtkCallback) gtk_widget_reset_style,
+ NULL);
+}
+
+static void
+gtk_combo_box_direction_changed (GtkWidget *widget,
+ GtkTextDirection previous_direction)
+{
+ gtk_combo_box_invalidate_order (GTK_COMBO_BOX (widget));
+}
+
+static GtkWidgetPath *
+gtk_combo_box_get_path_for_child (GtkContainer *container,
+ GtkWidget *child)
+{
+ GtkComboBoxPrivate *priv = GTK_COMBO_BOX (container)->priv;
+ GtkWidgetPath *path;
+ GtkWidget *widget;
+ gboolean found = FALSE;
+ GList *visible_children, *l;
+ GtkWidgetPath *sibling_path;
+ int pos;
+
+ path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (container)));
+
+ if (gtk_widget_get_visible (child))
+ {
+ visible_children = NULL;
+
+ if (priv->button && gtk_widget_get_visible (priv->button))
+ visible_children = g_list_prepend (visible_children, priv->button);
+
+ if (priv->cell_view_frame && gtk_widget_get_visible (priv->cell_view_frame))
+ visible_children = g_list_prepend (visible_children, priv->cell_view_frame);
+
+ widget = gtk_bin_get_child (GTK_BIN (container));
+ if (widget && gtk_widget_get_visible (widget))
+ visible_children = g_list_prepend (visible_children, widget);
+
+ if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL)
+ visible_children = g_list_reverse (visible_children);
+
+ pos = 0;
+
+ for (l = visible_children; l; l = l->next)
+ {
+ widget = l->data;
+
+ if (widget == child)
+ {
+ found = TRUE;
+ break;
+ }
+
+ pos++;
+ }
+ }
+
+ if (found)
+ {
+ sibling_path = gtk_widget_path_new ();
+
+ for (l = visible_children; l; l = l->next)
+ gtk_widget_path_append_for_widget (sibling_path, l->data);
+
+ gtk_widget_path_append_with_siblings (path, sibling_path, pos);
+
+ g_list_free (visible_children);
+ gtk_widget_path_unref (sibling_path);
+ }
+ else
+ {
+ gtk_widget_path_append_for_widget (path, child);
+ }
+
+ return path;
+}
+
static void
gtk_combo_box_check_appearance (GtkComboBox *combo_box)
{
if (priv->tree_view && priv->cell_view)
{
GtkStyleContext *context;
- GdkRGBA *color;
-
- context = gtk_widget_get_style_context (widget);
- gtk_style_context_get (context, 0,
- "background-color", &color,
- NULL);
+ GtkStateFlags state;
+ GdkRGBA color;
- gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view),
- color);
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_background_color (context, state, &color);
- gdk_rgba_free (color);
+ gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
}
child = gtk_bin_get_child (GTK_BIN (combo_box));
screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
monitor_num = gdk_screen_get_monitor_at_window (screen,
gtk_widget_get_window (GTK_WIDGET (combo_box)));
- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
if (*x < monitor.x)
*x = monitor.x;
screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
monitor_num = gdk_screen_get_monitor_at_window (screen, window);
- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
*x = *x + allocation.width - *width;
if (priv->shadow_type != GTK_SHADOW_NONE)
{
GtkStyleContext *context;
- GtkStateFlags state;
context = gtk_widget_get_style_context (widget);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_set_state (context, state);
gtk_render_background (context, cr, 0, 0,
gtk_widget_get_allocated_width (widget),
{
GtkStyleContext *context;
GtkStateFlags state;
- GdkRGBA *color;
+ GdkRGBA color;
- context = gtk_widget_get_style_context (widget);
+ context = gtk_widget_get_style_context (widget);
state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_background_color (context, state, &color);
- gtk_style_context_get (context, state,
- "background-color", &color,
- NULL);
-
- gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
- gdk_rgba_free (color);
+ gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
priv->box = gtk_event_box_new ();
gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
NULL);
if (cell_visible && cell_sensitive)
- break;
+ {
+ sensitive = TRUE;
+ break;
+ }
cell = cell->next;
}
- g_list_free (cells);
- sensitive = cell_sensitive;
+ g_list_free (cells);
}
g_list_free (columns);
gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
gpointer user_data)
{
- GtkComboBoxPrivate *priv = combo_box->priv;
GtkTreeModel *model;
GtkTreeIter iter;
if (entry)
{
- GValue value = {0,};
+ GtkTreePath *path;
+ gchar *path_str;
+ gchar *text = NULL;
+
+ model = gtk_combo_box_get_model (combo_box);
+ path = gtk_tree_model_get_path (model, &iter);
+ path_str = gtk_tree_path_to_string (path);
g_signal_handlers_block_by_func (entry,
gtk_combo_box_entry_contents_changed,
combo_box);
- model = gtk_combo_box_get_model (combo_box);
- gtk_tree_model_get_value (model, &iter,
- priv->text_column, &value);
- g_object_set_property (G_OBJECT (entry), "text", &value);
- g_value_unset (&value);
+ g_signal_emit (combo_box, combo_box_signals[FORMAT_ENTRY_TEXT], 0,
+ path_str, &text);
+
+ gtk_entry_set_text (entry, text);
g_signal_handlers_unblock_by_func (entry,
gtk_combo_box_entry_contents_changed,
combo_box);
+
+ gtk_tree_path_free (path);
+ g_free (text);
+ g_free (path_str);
}
}
}
+static gchar *
+gtk_combo_box_format_entry_text (GtkComboBox *combo_box,
+ const gchar *path)
+{
+ GtkComboBoxPrivate *priv = combo_box->priv;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *text = NULL;
+
+ if (priv->text_column >= 0)
+ {
+ model = gtk_combo_box_get_model (combo_box);
+ gtk_tree_model_get_iter_from_string (model, &iter, path);
+
+ gtk_tree_model_get (model, &iter,
+ priv->text_column, &text,
+ -1);
+ }
+
+ return text;
+}
+
+
static GObject *
gtk_combo_box_constructor (GType type,
guint n_construct_properties,
if (priv->has_entry)
{
GtkWidget *entry;
+ GtkStyleContext *context;
entry = gtk_entry_new ();
gtk_widget_show (entry);
gtk_container_add (GTK_CONTAINER (combo_box), entry);
+ context = gtk_widget_get_style_context (GTK_WIDGET (combo_box));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_COMBOBOX_ENTRY);
+
priv->text_renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
priv->text_renderer, TRUE);
*
* Since: 2.10
*/
-G_CONST_RETURN gchar*
+const gchar*
gtk_combo_box_get_title (GtkComboBox *combo_box)
{
g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
gint font_size, arrow_size;
PangoContext *context;
PangoFontMetrics *metrics;
- PangoFontDescription *font_desc;
+ const PangoFontDescription *font_desc;
GtkWidget *child;
gint minimum_width = 0, natural_width = 0;
gint child_min, child_nat;
state = gtk_widget_get_state_flags (widget);
get_widget_padding (widget, &padding);
- gtk_style_context_get (style_context, state,
- "font", &font_desc,
- NULL);
+ font_desc = gtk_style_context_get_font (style_context, state);
context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
metrics = pango_context_get_metrics (context, font_desc,
font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
pango_font_metrics_get_descent (metrics));
pango_font_metrics_unref (metrics);
- pango_font_description_free (font_desc);
arrow_size = MAX (arrow_size, font_size) * arrow_scaling;
* @combo_box: a #GtkComboBox
*
* Returns the ID of the active row of @combo_box. This value is taken
- * from the active row and the column specified by the 'id-column'
+ * from the active row and the column specified by the #GtkComboBox:id-column
* property of @combo_box (see gtk_combo_box_set_id_column()).
*
* The returned value is an interned string which means that you can
* compare the pointer by value to other interned strings and that you
* must not free it.
*
- * If the 'id-column' property of @combo_box is not set or if no row is
- * selected then %NULL is returned.
+ * If the #GtkComboBox:id-column property of @combo_box is not set, or if
+ * no row is active, or if the active row has a %NULL ID value, then %NULL
+ * is returned.
*
* Return value: the ID of the active row, or %NULL
*
GtkTreeIter iter;
gint column;
- g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
+ g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
column = combo_box->priv->id_column;
/**
* gtk_combo_box_set_active_id:
* @combo_box: a #GtkComboBox
- * @active_id: the ID of the row to select
+ * @active_id: (allow-none): the ID of the row to select, or %NULL
*
- * Changes the active row of @combo_box to the one that has an ID equal to @id.
+ * Changes the active row of @combo_box to the one that has an ID equal to
+ * @active_id, or unsets the active row if @active_id is %NULL. Rows having
+ * a %NULL ID string cannot be made active by this function.
*
- * If the 'id-column' property of @combo_box is unset or if no row has
- * the given ID then nothing happens.
+ * If the #GtkComboBox:id-column property of @combo_box is unset or if no
+ * row has the given ID then the function does nothing and returns %FALSE.
+ *
+ * Returns: %TRUE if a row with a matching ID was found. If a %NULL
+ * @active_id was given to unset the active row, the function
+ * always returns %TRUE.
*
* Since: 3.0
**/
-void
+gboolean
gtk_combo_box_set_active_id (GtkComboBox *combo_box,
const gchar *active_id)
{
GtkTreeModel *model;
GtkTreeIter iter;
+ gboolean match = FALSE;
gint column;
- g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
+ g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
+
+ if (active_id == NULL)
+ {
+ gtk_combo_box_set_active (combo_box, -1);
+ return TRUE; /* active row was successfully unset */
+ }
column = combo_box->priv->id_column;
if (column < 0)
- return;
+ return FALSE;
model = gtk_combo_box_get_model (combo_box);
- g_return_if_fail (gtk_tree_model_get_column_type (model, column) ==
- G_TYPE_STRING);
+ g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) ==
+ G_TYPE_STRING, FALSE);
if (gtk_tree_model_get_iter_first (model, &iter))
do {
- gboolean match;
gchar *id;
gtk_tree_model_get (model, &iter, column, &id, -1);
- match = strcmp (id, active_id) == 0;
+ if (id != NULL)
+ match = strcmp (id, active_id) == 0;
g_free (id);
if (match)
break;
}
} while (gtk_tree_model_iter_next (model, &iter));
+
+ return match;
}