* Boston, MA 02111-1307, USA.
*/
-#include <gtk/gtkentrycompletion.h>
-#include <gtk/gtkentryprivate.h>
-#include <gtk/gtkcelllayout.h>
-
-#include <gtk/gtkintl.h>
-#include <gtk/gtkcellrenderertext.h>
-#include <gtk/gtktreeselection.h>
-#include <gtk/gtktreeview.h>
-#include <gtk/gtkscrolledwindow.h>
-#include <gtk/gtkvbox.h>
-#include <gtk/gtkwindow.h>
-#include <gtk/gtkentry.h>
-#include <gtk/gtkmain.h>
-#include <gtk/gtksignal.h>
-#include <gtk/gtkmarshalers.h>
+#include <config.h>
+#include "gtkentrycompletion.h"
+#include "gtkentryprivate.h"
+#include "gtkcelllayout.h"
+
+#include "gtkintl.h"
+#include "gtkcellrenderertext.h"
+#include "gtkframe.h"
+#include "gtktreeselection.h"
+#include "gtktreeview.h"
+#include "gtkscrolledwindow.h"
+#include "gtkvbox.h"
+#include "gtkwindow.h"
+#include "gtkentry.h"
+#include "gtkmain.h"
+#include "gtksignal.h"
+#include "gtkmarshalers.h"
#include <string.h>
PROP_MINIMUM_KEY_LENGTH
};
+#define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
static void gtk_entry_completion_class_init (GtkEntryCompletionClass *klass);
static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface);
GDestroyNotify destroy);
static void gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout,
GtkCellRenderer *cell);
+static void gtk_entry_completion_reorder (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gint position);
static gboolean gtk_entry_completion_visible_func (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data);
+static GObjectClass *parent_class = NULL;
static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
{
GObjectClass *object_class;
+ parent_class = g_type_class_peek_parent (klass);
object_class = (GObjectClass *)klass;
object_class->set_property = gtk_entry_completion_set_property;
object_class->get_property = gtk_entry_completion_get_property;
object_class->finalize = gtk_entry_completion_finalize;
+ /**
+ * GtkEntryCompletion::match-selected:
+ * @widget: the object which received the signal
+ * @model: the #GtkTreeModel containing the matches
+ * @iter: a #GtkTreeIter positioned at the selected match
+ *
+ * The ::match-selected signal is emitted when a match from the list
+ * is selected. The default behaviour is to replace the contents of the
+ * entry with the contents of the text column in the row pointed to by
+ * @iter.
+ *
+ * Return value: %TRUE if the signal has been handled
+ */
entry_completion_signals[MATCH_SELECTED] =
g_signal_new ("match_selected",
G_TYPE_FROM_CLASS (klass),
G_TYPE_BOOLEAN, 2,
GTK_TYPE_TREE_MODEL,
GTK_TYPE_TREE_ITER);
+
+ /**
+ * GtkEntryCompletion::action-activated:
+ * @widget: the object which received the signal
+ * @index: the index of the activated action
+ *
+ * The ::action-activated signal is emitted when an action
+ * is activated.
+ */
entry_completion_signals[ACTION_ACTIVATED] =
g_signal_new ("action_activated",
G_TYPE_FROM_CLASS (klass),
g_object_class_install_property (object_class,
PROP_MODEL,
g_param_spec_object ("model",
- _("Completion Model"),
- _("The model to find matches in"),
+ P_("Completion Model"),
+ P_("The model to find matches in"),
GTK_TYPE_TREE_MODEL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_MINIMUM_KEY_LENGTH,
g_param_spec_int ("minimum_key_length",
- _("Minimum Key Length"),
- _("Minimum length of the search key in order to look up matches"),
+ P_("Minimum Key Length"),
+ P_("Minimum length of the search key in order to look up matches"),
-1,
G_MAXINT,
1,
iface->add_attribute = gtk_entry_completion_add_attribute;
iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
iface->clear_attributes = gtk_entry_completion_clear_attributes;
+ iface->reorder = gtk_entry_completion_reorder;
}
static void
GtkCellRenderer *cell;
GtkTreeSelection *sel;
GtkEntryCompletionPrivate *priv;
+ GtkWidget *popup_frame;
/* yes, also priv, need to keep the code readable */
priv = completion->priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
- GTK_SHADOW_ETCHED_IN);
+ GTK_SHADOW_NONE);
+ /* a nasty hack to get the completions treeview to size nicely */
+ gtk_widget_set_size_request (GTK_SCROLLED_WINDOW (priv->scrolled_window)->vscrollbar, -1, 0);
/* actions */
priv->actions = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
gtk_tree_selection_unselect_all (sel);
cell = gtk_cell_renderer_text_new ();
- g_object_set (cell, "cell_background_gdk",
- &priv->tree_view->style->bg[GTK_STATE_NORMAL],
- NULL);
gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (priv->action_view),
0, "",
cell,
G_CALLBACK (gtk_entry_completion_popup_button_press),
completion);
+ popup_frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (popup_frame),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_widget_show (popup_frame);
+ gtk_container_add (GTK_CONTAINER (priv->popup_window), popup_frame);
+
priv->vbox = gtk_vbox_new (FALSE, 0);
- gtk_container_add (GTK_CONTAINER (priv->popup_window), priv->vbox);
+ gtk_container_add (GTK_CONTAINER (popup_frame), priv->vbox);
gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->tree_view);
gtk_box_pack_start (GTK_BOX (priv->vbox), priv->scrolled_window,
if (completion->priv->popup_window)
gtk_widget_destroy (completion->priv->popup_window);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
/* implement cell layout interface */
gtk_tree_view_column_clear_attributes (priv->column, cell);
}
+static void
+gtk_entry_completion_reorder (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gint position)
+{
+ GtkEntryCompletionPrivate *priv;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
+
+ priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
+
+ gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position);
+}
+
/* all those callbacks */
static gboolean
gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
completion->priv->text_column, &item,
-1);
- normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
- case_normalized_string = g_utf8_casefold (normalized_string, -1);
-
- if (!strncmp (key, case_normalized_string, strlen (key)))
- ret = TRUE;
-
- g_free (item);
- g_free (normalized_string);
- g_free (case_normalized_string);
+ if (item != NULL)
+ {
+ normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
+ case_normalized_string = g_utf8_casefold (normalized_string, -1);
+
+ if (!strncmp (key, case_normalized_string, strlen (key)))
+ ret = TRUE;
+
+ g_free (item);
+ g_free (normalized_string);
+ g_free (case_normalized_string);
+ }
return ret;
}
if (!completion->priv->case_normalized_key)
return ret;
- if (completion->priv->text_column >= 0)
- ret = gtk_entry_completion_default_completion_func (completion,
- completion->priv->case_normalized_key,
- iter,
- NULL);
-
- else if (completion->priv->match_func)
+ if (completion->priv->match_func)
ret = (* completion->priv->match_func) (completion,
completion->priv->case_normalized_key,
iter,
completion->priv->match_data);
+ else if (completion->priv->text_column >= 0)
+ ret = gtk_entry_completion_default_completion_func (completion,
+ completion->priv->case_normalized_key,
+ iter,
+ NULL);
return ret;
}
&iter, path);
gtk_tree_path_free (path);
+ g_signal_handler_block (completion->priv->entry,
+ completion->priv->changed_id);
g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
0, GTK_TREE_MODEL (completion->priv->filter_model),
&iter, &entry_set);
+ g_signal_handler_unblock (completion->priv->entry,
+ completion->priv->changed_id);
if (!entry_set)
{
"markup", NULL,
"text", string,
NULL);
+
+ g_free (string);
}
static void
if (completion->priv->first_sel_changed)
{
completion->priv->first_sel_changed = FALSE;
- gtk_tree_selection_unselect_all (selection);
+ if (gtk_widget_is_focus (completion->priv->tree_view))
+ gtk_tree_selection_unselect_all (selection);
}
}
* @completion: A #GtkEntryCompletion.
* @column: The column in the model of @completion to get strings from.
*
- * Conviencefunction for setting up the most used case of this code: a
+ * Convenience function for setting up the most used case of this code: a
* completion list with just strings. This function will set up @completion
* to have a list displaying all (and just) strings in the completion list,
* and to get those strings from @column in the model of @completion.
*
+ * This functions creates and adds a GtkCellRendererText for the selected column.
+
* Since: 2.4
*/
void
}
}
-/* this function is a bit nasty */
-void
-_gtk_entry_completion_popup (GtkEntryCompletion *completion)
+/* some nasty size requisition */
+gboolean
+_gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
{
- gint x, y, x_border, y_border;
- gint items;
- gint height;
-
- if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
- return;
-
- completion->priv->first_sel_changed = TRUE;
-
- gtk_widget_show_all (completion->priv->vbox);
+ gint x, y;
+ gint matches, items, height, x_border, y_border;
+ GdkScreen *screen;
+ gint monitor_num;
+ GdkRectangle monitor;
+ GtkRequisition popup_req;
+ GtkTreePath *path;
+ gboolean above;
gdk_window_get_origin (completion->priv->entry->window, &x, &y);
get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
- items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
+ x += x_border;
+ y += 2 * y_border;
+
+ matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
- items = MIN (items, 15);
+ items = MIN (matches, 15);
gtk_tree_view_column_cell_get_size (completion->priv->column, NULL,
NULL, NULL, NULL, &height);
+ if (items <= 0)
+ gtk_widget_hide (completion->priv->scrolled_window);
+ else
+ gtk_widget_show (completion->priv->scrolled_window);
+
gtk_widget_set_size_request (completion->priv->tree_view,
completion->priv->entry->allocation.width - 2 * x_border,
items * height);
- if (items <= 0)
- gtk_widget_hide (completion->priv->scrolled_window);
-
/* default on no match */
completion->priv->current_selected = -1;
if (items)
{
+ gtk_widget_show (completion->priv->action_view);
+
gtk_tree_view_column_cell_get_size (gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0),
NULL, NULL, NULL, NULL,
&height);
completion->priv->entry->allocation.width - 2 * x_border,
items * height);
}
+ else
+ gtk_widget_hide (completion->priv->action_view);
+
+ gtk_widget_size_request (completion->priv->popup_window, &popup_req);
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (completion->priv->entry));
+ monitor_num = gdk_screen_get_monitor_at_window (screen,
+ GTK_WIDGET (completion->priv->entry)->window);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ if (x < monitor.x)
+ x = monitor.x;
+ else if (x + popup_req.width > monitor.x + monitor.width)
+ x = monitor.x + monitor.width - popup_req.width;
+
+ if (y + height + popup_req.height <= monitor.y + monitor.height)
+ {
+ y += height;
+ above = FALSE;
+ }
+ else
+ {
+ y -= popup_req.height;
+ above = TRUE;
+ }
+
+ if (matches > 0)
+ {
+ path = gtk_tree_path_new_from_indices (above ? matches - 1 : 0, -1);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view), path,
+ NULL, FALSE, 0.0, 0.0);
+ gtk_tree_path_free (path);
+ }
- x += x_border;
- y += 2 * y_border;
+ gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y);
+
+ return above;
+}
+
+void
+_gtk_entry_completion_popup (GtkEntryCompletion *completion)
+{
+ GtkTreeViewColumn *column;
+ GList *renderers;
+
+ if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
+ return;
+
+ completion->priv->may_wrap = TRUE;
+
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
+ renderers = gtk_tree_view_column_get_cell_renderers (column);
+ gtk_widget_ensure_style (completion->priv->tree_view);
+ g_object_set (GTK_CELL_RENDERER (renderers->data), "cell_background_gdk",
+ &completion->priv->tree_view->style->bg[GTK_STATE_NORMAL],
+ NULL);
+ g_list_free (renderers);
+
+ gtk_widget_show_all (completion->priv->vbox);
- gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y + height);
+ _gtk_entry_completion_resize_popup (completion);
gtk_widget_show (completion->priv->popup_window);