#include <math.h>
#include <string.h>
#include "gtklabel.h"
+#include "gtkaccellabel.h"
#include "gtkdnd.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkimagemenuitem.h"
#include "gtkintl.h"
#include "gtkseparatormenuitem.h"
+#include "gtktextutil.h"
#include "gtkmenuitem.h"
#include "gtknotebook.h"
#include "gtkstock.h"
typedef struct
{
+ gint wrap_width;
gint width_chars;
gint max_width_chars;
- guint single_line_mode : 1;
- guint have_transform : 1;
- gdouble angle;
}
GtkLabelPrivate;
PROP_JUSTIFY,
PROP_PATTERN,
PROP_WRAP,
+ PROP_WRAP_MODE,
PROP_SELECTABLE,
PROP_MNEMONIC_KEYVAL,
PROP_MNEMONIC_WIDGET,
static guint signals[LAST_SIGNAL] = { 0 };
-static void gtk_label_class_init (GtkLabelClass *klass);
-static void gtk_label_init (GtkLabel *label);
static void gtk_label_set_property (GObject *object,
guint prop_id,
const GValue *value,
GdkEventButton *event);
static gboolean gtk_label_motion (GtkWidget *widget,
GdkEventMotion *event);
+static void gtk_label_grab_focus (GtkWidget *widget);
static void gtk_label_set_text_internal (GtkLabel *label,
static void gtk_label_destroy_window (GtkLabel *label);
static void gtk_label_clear_layout (GtkLabel *label);
static void gtk_label_ensure_layout (GtkLabel *label);
+static void gtk_label_invalidate_wrap_width (GtkLabel *label);
static void gtk_label_select_region_index (GtkLabel *label,
gint anchor_index,
gint end_index);
static gint gtk_label_move_backward_word (GtkLabel *label,
gint start);
-static GtkMiscClass *parent_class = NULL;
+static GQuark quark_angle = 0;
-
-GType
-gtk_label_get_type (void)
-{
- static GType label_type = 0;
-
- if (!label_type)
- {
- static const GTypeInfo label_info =
- {
- sizeof (GtkLabelClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) gtk_label_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GtkLabel),
- 32, /* n_preallocs */
- (GInstanceInitFunc) gtk_label_init,
- };
-
- label_type = g_type_register_static (GTK_TYPE_MISC, "GtkLabel",
- &label_info, 0);
- }
-
- return label_type;
-}
+G_DEFINE_TYPE (GtkLabel, gtk_label, GTK_TYPE_MISC)
static void
add_move_binding (GtkBindingSet *binding_set,
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
GtkBindingSet *binding_set;
- parent_class = g_type_class_peek_parent (class);
-
+ quark_angle = g_quark_from_static_string ("angle");
+
gobject_class->set_property = gtk_label_set_property;
gobject_class->get_property = gtk_label_get_property;
gobject_class->finalize = gtk_label_finalize;
widget_class->screen_changed = gtk_label_screen_changed;
widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
widget_class->drag_data_get = gtk_label_drag_data_get;
+ widget_class->grab_focus = gtk_label_grab_focus;
class->move_cursor = gtk_label_move_cursor;
class->copy_clipboard = gtk_label_copy_clipboard;
signals[MOVE_CURSOR] =
- g_signal_new ("move_cursor",
+ g_signal_new (I_("move_cursor"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
G_TYPE_BOOLEAN);
signals[COPY_CLIPBOARD] =
- g_signal_new ("copy_clipboard",
+ g_signal_new (I_("copy_clipboard"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
G_TYPE_NONE, 0);
signals[POPULATE_POPUP] =
- g_signal_new ("populate_popup",
+ g_signal_new (I_("populate_popup"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkLabelClass, populate_popup),
P_("If set, wrap lines if the text becomes too wide"),
FALSE,
GTK_PARAM_READWRITE));
+ /**
+ * GtkLabel:wrap-mode:
+ *
+ * If line wrapping is on (see the wrap property) this controls how
+ * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
+ * wrap on word boundaries.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_WRAP_MODE,
+ g_param_spec_enum ("wrap-mode",
+ P_("Line wrap mode"),
+ P_("If wrap is set, controls how linewrapping is done"),
+ PANGO_TYPE_WRAP_MODE,
+ PANGO_WRAP_WORD,
+ GTK_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_SELECTABLE,
g_param_spec_boolean ("selectable",
PROP_ELLIPSIZE,
g_param_spec_enum ("ellipsize",
P_("Ellipsize"),
- P_("The preferred place to ellipsize the string, if the label does not have enough room to display the entire string, if at all"),
+ P_("The preferred place to ellipsize the string, if the label does not have enough room to display the entire string"),
PANGO_TYPE_ELLIPSIZE_MODE,
PANGO_ELLIPSIZE_NONE,
GTK_PARAM_READWRITE));
add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
GTK_MOVEMENT_WORDS, -1);
-
- add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
- GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
- add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
- GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
+ /* select all */
+ gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
+ "move_cursor", 3,
+ G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
+ G_TYPE_INT, -1,
+ G_TYPE_BOOLEAN, FALSE);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
+ "move_cursor", 3,
+ G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
+ G_TYPE_INT, 1,
+ G_TYPE_BOOLEAN, TRUE);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
+ "move_cursor", 3,
+ G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
+ G_TYPE_INT, -1,
+ G_TYPE_BOOLEAN, FALSE);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
+ "move_cursor", 3,
+ G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
+ G_TYPE_INT, 1,
+ G_TYPE_BOOLEAN, TRUE);
+
+ /* unselect all */
+ gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+ "move_cursor", 3,
+ G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
+ G_TYPE_INT, 0,
+ G_TYPE_BOOLEAN, FALSE);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK,
+ "move_cursor", 3,
+ G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
+ G_TYPE_INT, 0,
+ G_TYPE_BOOLEAN, FALSE);
add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
GTK_MOVEMENT_WORDS, 1);
/* copy */
gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
"copy_clipboard", 0);
+
+ gtk_settings_install_property (g_param_spec_boolean ("gtk-label-select-on-focus",
+ P_("Select on focus"),
+ P_("Whether to select the contents of a selectable label when it is focused"),
+ TRUE,
+ GTK_PARAM_READWRITE));
+
g_type_class_add_private (class, sizeof (GtkLabelPrivate));
}
case PROP_WRAP:
gtk_label_set_line_wrap (label, g_value_get_boolean (value));
break;
+ case PROP_WRAP_MODE:
+ gtk_label_set_line_wrap_mode (label, g_value_get_enum (value));
+ break;
case PROP_SELECTABLE:
gtk_label_set_selectable (label, g_value_get_boolean (value));
break;
case PROP_WRAP:
g_value_set_boolean (value, label->wrap);
break;
+ case PROP_WRAP_MODE:
+ g_value_set_enum (value, label->wrap_mode);
+ break;
case PROP_SELECTABLE:
g_value_set_boolean (value, gtk_label_get_selectable (label));
break;
priv = GTK_LABEL_GET_PRIVATE (label);
priv->width_chars = -1;
- priv->angle = 0.0;
priv->max_width_chars = -1;
+ priv->wrap_width = -1;
label->label = NULL;
label->jtype = GTK_JUSTIFY_LEFT;
label->wrap = FALSE;
+ label->wrap_mode = PANGO_WRAP_WORD;
label->ellipsize = PANGO_ELLIPSIZE_NONE;
label->use_underline = FALSE;
/* barf if there was nothing to activate */
g_warning ("Couldn't find a target for a mnemonic activation.");
- gdk_display_beep (gtk_widget_get_display (widget));
-
+ gtk_widget_error_bell (widget);
+
return FALSE;
}
}
done:
- g_object_set_data (G_OBJECT (label), "gtk-mnemonic-menu", mnemonic_menu);
+ g_object_set_data (G_OBJECT (label), I_("gtk-mnemonic-menu"), mnemonic_menu);
}
static void
gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
}
+static void
+label_shortcut_setting_apply (GtkLabel *label)
+{
+ gtk_label_recalculate (label);
+ if (GTK_IS_ACCEL_LABEL (label))
+ gtk_accel_label_refetch (GTK_ACCEL_LABEL (label));
+}
+
+static void
+label_shortcut_setting_traverse_container (GtkWidget *widget,
+ gpointer data)
+{
+ if (GTK_IS_LABEL (widget))
+ label_shortcut_setting_apply (GTK_LABEL (widget));
+ else if (GTK_IS_CONTAINER (widget))
+ gtk_container_forall (GTK_CONTAINER (widget),
+ label_shortcut_setting_traverse_container, data);
+}
+
+static void
+label_shortcut_setting_changed (GtkSettings *settings)
+{
+ GList *list, *l;
+
+ list = gtk_window_list_toplevels ();
+
+ for (l = list; l ; l = l->next)
+ {
+ GtkWidget *widget = l->data;
+
+ if (gtk_widget_get_settings (widget) == settings)
+ gtk_container_forall (GTK_CONTAINER (widget),
+ label_shortcut_setting_traverse_container, NULL);
+ }
+
+ g_list_free (list);
+}
+
static void
gtk_label_screen_changed (GtkWidget *widget,
GdkScreen *old_screen)
{
- gtk_label_clear_layout (GTK_LABEL (widget));
+ GtkSettings *settings;
+ gboolean shortcuts_connected;
+
+ if (!gtk_widget_has_screen (widget))
+ return;
+
+ settings = gtk_widget_get_settings (widget);
+
+ shortcuts_connected =
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings),
+ "gtk-label-shortcuts-connected"));
+
+ if (! shortcuts_connected)
+ {
+ g_signal_connect (settings, "notify::gtk-enable-mnemonics",
+ G_CALLBACK (label_shortcut_setting_changed),
+ NULL);
+ g_signal_connect (settings, "notify::gtk-enable-accels",
+ G_CALLBACK (label_shortcut_setting_changed),
+ NULL);
+
+ g_object_set_data (G_OBJECT (settings), "gtk-label-shortcuts-connected",
+ GINT_TO_POINTER (TRUE));
+ }
+
+ label_shortcut_setting_apply (GTK_LABEL (widget));
}
+
static void
label_mnemonic_widget_weak_notify (gpointer data,
GObject *where_the_object_was)
val = val != FALSE;
if (label->use_markup != val)
{
- g_object_notify (G_OBJECT (label), "use-markup");
label->use_markup = val;
+
+ g_object_notify (G_OBJECT (label), "use-markup");
}
}
val = val != FALSE;
if (label->use_underline != val)
{
- g_object_notify (G_OBJECT (label), "use-underline");
label->use_underline = val;
+
+ g_object_notify (G_OBJECT (label), "use-underline");
}
}
with_uline ? &accel_char : NULL,
&error))
{
- g_warning ("Failed to set label from markup due to error parsing markup: %s",
+ g_warning ("Failed to set text from markup due to error parsing markup: %s",
error->message);
g_error_free (error);
return;
{
g_return_if_fail (GTK_IS_LABEL (label));
+ g_object_freeze_notify (G_OBJECT (label));
+
gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
gtk_label_set_use_markup_internal (label, TRUE);
gtk_label_set_use_underline_internal (label, FALSE);
gtk_label_recalculate (label);
+
+ g_object_thaw_notify (G_OBJECT (label));
}
/**
guint last_keyval;
g_return_if_fail (GTK_IS_LABEL (label));
+ g_object_freeze_notify (G_OBJECT (label));
+
last_keyval = label->mnemonic_keyval;
gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
gtk_label_set_use_markup_internal (label, TRUE);
gtk_label_recalculate (label);
gtk_label_setup_mnemonic (label, last_keyval);
+
+ g_object_thaw_notify (G_OBJECT (label));
}
/**
const gchar *pattern)
{
PangoAttrList *attrs;
+ gboolean enable_mnemonics;
+
g_return_if_fail (GTK_IS_LABEL (label));
-
- if (pattern)
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
+ "gtk-enable-mnemonics", &enable_mnemonics,
+ NULL);
+
+ if (enable_mnemonics && pattern)
attrs = gtk_label_pattern_to_attrs (label, pattern);
else
attrs = NULL;
{
priv->width_chars = n_chars;
g_object_notify (G_OBJECT (label), "width-chars");
+ gtk_label_invalidate_wrap_width (label);
gtk_widget_queue_resize (GTK_WIDGET (label));
}
}
priv->max_width_chars = n_chars;
g_object_notify (G_OBJECT (label), "max-width-chars");
+ gtk_label_invalidate_wrap_width (label);
gtk_widget_queue_resize (GTK_WIDGET (label));
}
}
* Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
* lines if text exceeds the widget's size. %FALSE lets the text get cut off
* by the edge of the widget if it exceeds the widget size.
+ *
+ * Note that setting line wrapping to %TRUE does not make the label
+ * wrap at its parent container's width, because GTK+ widgets
+ * conceptually can't make their requisition depend on the parent
+ * container's size. For a label that wraps at a specific position,
+ * set the label's width using gtk_widget_set_size_request().
**/
void
gtk_label_set_line_wrap (GtkLabel *label,
if (label->wrap != wrap)
{
label->wrap = wrap;
- g_object_notify (G_OBJECT (label), "wrap");
-
+
+ gtk_label_clear_layout (label);
gtk_widget_queue_resize (GTK_WIDGET (label));
+ g_object_notify (G_OBJECT (label), "wrap");
}
}
return label->wrap;
}
+/**
+ * gtk_label_set_line_wrap_mode:
+ * @label: a #GtkLabel
+ * @wrap_mode: the line wrapping mode
+ *
+ * If line wrapping is on (see gtk_label_set_line_wrap()) this controls how
+ * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
+ * wrap on word boundaries.
+ *
+ * Since: 2.10
+ **/
+void
+gtk_label_set_line_wrap_mode (GtkLabel *label,
+ PangoWrapMode wrap_mode)
+{
+ g_return_if_fail (GTK_IS_LABEL (label));
+
+ if (label->wrap_mode != wrap_mode)
+ {
+ label->wrap_mode = wrap_mode;
+ g_object_notify (G_OBJECT (label), "wrap-mode");
+
+ gtk_widget_queue_resize (GTK_WIDGET (label));
+ }
+}
+
+/**
+ * gtk_label_get_line_wrap_mode:
+ * @label: a #GtkLabel
+ *
+ * Returns line wrap mode used by the label. See gtk_label_set_line_wrap_mode ().
+ *
+ * Return value: %TRUE if the lines of the label are automatically wrapped.
+ *
+ * Since: 2.10
+ */
+PangoWrapMode
+gtk_label_get_line_wrap_mode (GtkLabel *label)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
+
+ return label->wrap_mode;
+}
+
+
void
gtk_label_get (GtkLabel *label,
gchar **str)
gtk_label_set_mnemonic_widget (label, NULL);
- GTK_OBJECT_CLASS (parent_class)->destroy (object);
+ GTK_OBJECT_CLASS (gtk_label_parent_class)->destroy (object);
}
static void
g_free (label->select_info);
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
}
static void
}
}
-typedef struct _LabelWrapWidth LabelWrapWidth;
-struct _LabelWrapWidth
+static gint
+get_label_char_width (GtkLabel *label)
{
- gint width;
- PangoFontDescription *font_desc;
-};
+ GtkLabelPrivate *priv;
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ gint char_width, digit_width, char_pixels, w;
+
+ priv = GTK_LABEL_GET_PRIVATE (label);
+
+ context = pango_layout_get_context (label->layout);
+ metrics = pango_context_get_metrics (context, GTK_WIDGET (label)->style->font_desc,
+ pango_context_get_language (context));
+
+ char_width = pango_font_metrics_get_approximate_char_width (metrics);
+ digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
+ char_pixels = MAX (char_width, digit_width);
+ pango_font_metrics_unref (metrics);
+
+ if (priv->width_chars < 0)
+ {
+ PangoRectangle rect;
+
+ pango_layout_set_width (label->layout, -1);
+ pango_layout_get_extents (label->layout, NULL, &rect);
+
+ w = char_pixels * MAX (priv->max_width_chars, 3);
+ w = MIN (rect.width, w);
+ }
+ else
+ {
+ /* enforce minimum width for ellipsized labels at ~3 chars */
+ w = char_pixels * MAX (priv->width_chars, 3);
+ }
+
+ return w;
+}
static void
-label_wrap_width_free (gpointer data)
+gtk_label_invalidate_wrap_width (GtkLabel *label)
{
- LabelWrapWidth *wrap_width = data;
- pango_font_description_free (wrap_width->font_desc);
- g_free (wrap_width);
+ GtkLabelPrivate *priv;
+
+ priv = GTK_LABEL_GET_PRIVATE (label);
+
+ priv->wrap_width = -1;
}
static gint
get_label_wrap_width (GtkLabel *label)
{
- PangoLayout *layout;
- GtkStyle *style = GTK_WIDGET (label)->style;
+ GtkLabelPrivate *priv;
- LabelWrapWidth *wrap_width = g_object_get_data (G_OBJECT (style), "gtk-label-wrap-width");
- if (!wrap_width)
+ priv = GTK_LABEL_GET_PRIVATE (label);
+
+ if (priv->wrap_width < 0)
{
- wrap_width = g_new0 (LabelWrapWidth, 1);
- g_object_set_data_full (G_OBJECT (style), "gtk-label-wrap-width",
- wrap_width, label_wrap_width_free);
+ if (priv->width_chars > 0 || priv->max_width_chars > 0)
+ priv->wrap_width = get_label_char_width (label);
+ else
+ {
+ PangoLayout *layout;
+
+ layout = gtk_widget_create_pango_layout (GTK_WIDGET (label),
+ "This long string gives a good enough length for any line to have.");
+ pango_layout_get_size (layout, &priv->wrap_width, NULL);
+ g_object_unref (layout);
+ }
}
- if (wrap_width->font_desc && pango_font_description_equal (wrap_width->font_desc, style->font_desc))
- return wrap_width->width;
-
- if (wrap_width->font_desc)
- pango_font_description_free (wrap_width->font_desc);
-
- wrap_width->font_desc = pango_font_description_copy (style->font_desc);
-
- layout = gtk_widget_create_pango_layout (GTK_WIDGET (label),
- "This long string gives a good enough length for any line to have.");
- pango_layout_get_size (layout, &wrap_width->width, NULL);
- g_object_unref (layout);
-
- return wrap_width->width;
+ return priv->wrap_width;
}
static void
{
GtkWidget *widget;
PangoRectangle logical_rect;
- gint rwidth, rheight;
gboolean rtl;
widget = GTK_WIDGET (label);
rtl = gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL;
- rwidth = label->misc.xpad * 2;
- rheight = label->misc.ypad * 2;
if (!label->layout)
{
PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
- GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
+ gdouble angle = gtk_label_get_angle (label);
- if (priv->angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
+ if (angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
{
/* We rotate the standard singleton PangoContext for the widget,
* depending on the fact that it's meant pretty much exclusively
*/
PangoMatrix matrix = PANGO_MATRIX_INIT;
- pango_matrix_rotate (&matrix, priv->angle);
+ pango_matrix_rotate (&matrix, angle);
pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
- priv->have_transform = TRUE;
+ label->have_transform = TRUE;
}
else
{
- if (priv->have_transform)
+ if (label->have_transform)
pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
- priv->have_transform = FALSE;
+ label->have_transform = FALSE;
}
label->layout = gtk_widget_create_pango_layout (widget, label->text);
pango_layout_set_alignment (label->layout, align);
pango_layout_set_ellipsize (label->layout, label->ellipsize);
- pango_layout_set_single_paragraph_mode (label->layout, priv->single_line_mode);
+ pango_layout_set_single_paragraph_mode (label->layout, label->single_line_mode);
if (label->ellipsize)
pango_layout_set_width (label->layout,
GtkWidgetAuxInfo *aux_info;
gint longest_paragraph;
gint width, height;
+
+ pango_layout_set_wrap (label->layout, label->wrap_mode);
aux_info = _gtk_widget_get_aux_info (widget, FALSE);
if (aux_info && aux_info->width > 0)
}
}
-/* Gets the bounds of a layout in device coordinates. Note cut-and-paste
- * between here and gdkpango.c */
-static void
-get_rotated_layout_bounds (PangoLayout *layout,
- GdkRectangle *rect)
-{
- PangoContext *context = pango_layout_get_context (layout);
- const PangoMatrix *matrix = pango_context_get_matrix (context);
- gdouble x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* quiet gcc */
- PangoRectangle logical_rect;
- gint i, j;
-
- pango_layout_get_extents (layout, NULL, &logical_rect);
-
- for (i = 0; i < 2; i++)
- {
- gdouble x = (i == 0) ? logical_rect.x : logical_rect.x + logical_rect.width;
- for (j = 0; j < 2; j++)
- {
- gdouble y = (j == 0) ? logical_rect.y : logical_rect.y + logical_rect.height;
-
- gdouble xt = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0;
- gdouble yt = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0;
-
- if (i == 0 && j == 0)
- {
- x_min = x_max = xt;
- y_min = y_max = yt;
- }
- else
- {
- if (xt < x_min)
- x_min = xt;
- if (yt < y_min)
- y_min = yt;
- if (xt > x_max)
- x_max = xt;
- if (yt > y_max)
- y_max = yt;
- }
- }
- }
-
- rect->x = floor (x_min);
- rect->width = ceil (x_max) - rect->x;
- rect->y = floor (y_min);
- rect->height = floor (y_max) - rect->y;
-}
-
static void
gtk_label_size_request (GtkWidget *widget,
GtkRequisition *requisition)
aux_info = _gtk_widget_get_aux_info (widget, FALSE);
- if (priv->have_transform)
+ if (label->have_transform)
{
- GdkRectangle rect;
+ PangoRectangle rect;
+ PangoContext *context = pango_layout_get_context (label->layout);
+ const PangoMatrix *matrix = pango_context_get_matrix (context);
- get_rotated_layout_bounds (label->layout, &rect);
+ pango_layout_get_extents (label->layout, NULL, &rect);
+ pango_matrix_transform_rectangle (matrix, &rect);
+ pango_extents_to_pixels (&rect, NULL);
requisition->width = width + rect.width;
requisition->height = height + rect.height;
width += aux_info->width;
else if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
{
- PangoContext *context;
- PangoFontMetrics *metrics;
- gint char_width, digit_width, char_pixels, w;
-
- context = pango_layout_get_context (label->layout);
- metrics = pango_context_get_metrics (context, widget->style->font_desc,
- pango_context_get_language (context));
-
- char_width = pango_font_metrics_get_approximate_char_width (metrics);
- digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
- char_pixels = MAX (char_width, digit_width);
- pango_font_metrics_unref (metrics);
-
- if (priv->width_chars < 0)
- {
- PangoRectangle rect;
-
- pango_layout_set_width (label->layout, -1);
- pango_layout_get_extents (label->layout, NULL, &rect);
-
- w = char_pixels * MAX (priv->max_width_chars, 3);
- w = MIN (rect.width, w);
- }
- else
- {
- /* enforce minimum width for ellipsized labels at ~3 chars */
- w = char_pixels * MAX (priv->width_chars, 3);
- }
-
- width += PANGO_PIXELS (w);
+ width += PANGO_PIXELS (get_label_char_width (label));
}
else
width += PANGO_PIXELS (logical_rect.width);
- if (priv->single_line_mode)
+ if (label->single_line_mode)
{
PangoContext *context;
PangoFontMetrics *metrics;
GtkAllocation *allocation)
{
GtkLabel *label;
- GtkLabelPrivate *priv;
label = GTK_LABEL (widget);
- priv = GTK_LABEL_GET_PRIVATE (widget);
- (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+ (* GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate) (widget, allocation);
if (label->ellipsize)
{
if (label->layout)
- pango_layout_set_width (label->layout, allocation->width * PANGO_SCALE);
+ {
+ gint width;
+ PangoRectangle logical;
+
+ width = (allocation->width - label->misc.xpad * 2) * PANGO_SCALE;
+
+ pango_layout_set_width (label->layout, -1);
+ pango_layout_get_extents (label->layout, NULL, &logical);
+
+ if (logical.width > width)
+ pango_layout_set_width (label->layout, width);
+ }
}
if (label->select_info && label->select_info->window)
GtkStateType prev_state)
{
GtkLabel *label;
+ GdkCursor *cursor;
label = GTK_LABEL (widget);
if (label->select_info)
- gtk_label_select_region (label, 0, 0);
+ {
+ gtk_label_select_region (label, 0, 0);
- if (GTK_WIDGET_CLASS (parent_class)->state_changed)
- GTK_WIDGET_CLASS (parent_class)->state_changed (widget, prev_state);
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ if (GTK_WIDGET_IS_SENSITIVE (widget))
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), GDK_XTERM);
+ else
+ cursor = NULL;
+
+ gdk_window_set_cursor (label->select_info->window, cursor);
+
+ if (cursor)
+ gdk_cursor_unref (cursor);
+ }
+ }
+
+ if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed)
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed (widget, prev_state);
}
static void
/* We have to clear the layout, fonts etc. may have changed */
gtk_label_clear_layout (label);
+ gtk_label_invalidate_wrap_width (label);
}
static void
if (label->layout)
pango_layout_context_changed (label->layout);
- GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
}
static void
gint *yp)
{
GtkMisc *misc;
- GtkWidget *widget;
+ GtkWidget *widget;
+ GtkLabelPrivate *priv;
gfloat xalign;
gint req_width, x, y;
misc = GTK_MISC (label);
widget = GTK_WIDGET (label);
-
+ priv = GTK_LABEL_GET_PRIVATE (label);
+
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
xalign = misc->xalign;
else
xalign = 1.0 - misc->xalign;
- if (label->ellipsize)
+ if (label->ellipsize || priv->width_chars > 0)
{
- PangoRectangle ink_rect;
+ int width;
+ PangoRectangle logical;
- pango_layout_get_extents (label->layout, &ink_rect, NULL);
+ width = pango_layout_get_width (label->layout);
+ pango_layout_get_pixel_extents (label->layout, NULL, &logical);
- req_width = PANGO_PIXELS (ink_rect.width);
+ req_width = logical.width;
+ if (width != -1)
+ req_width = MIN(PANGO_PIXELS (width), req_width);
+ req_width += 2 * misc->xpad;
}
else
req_width = widget->requisition.width;
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
x = MAX (x, widget->allocation.x + misc->xpad);
else
- x = MIN (x,
- widget->allocation.x + widget->allocation.width -
- req_width - misc->xpad);
+ x = MIN (x, widget->allocation.x + widget->allocation.width - misc->xpad);
y = floor (widget->allocation.y + (gint)misc->ypad
+ MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign),
gtk_label_ensure_layout (label);
- for (l = pango_layout_get_lines (label->layout); l; l = l->next)
+ for (l = pango_layout_get_lines_readonly (label->layout); l; l = l->next)
{
PangoLayoutLine *line = l->data;
label = GTK_LABEL (widget);
- (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
+ (* GTK_WIDGET_CLASS (gtk_label_parent_class)->realize) (widget);
if (label->select_info)
gtk_label_create_window (label);
if (label->select_info)
gtk_label_destroy_window (label);
- (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+ (* GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize) (widget);
}
static void
label = GTK_LABEL (widget);
- (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
+ (* GTK_WIDGET_CLASS (gtk_label_parent_class)->map) (widget);
if (label->select_info)
gdk_window_show (label->select_info->window);
if (label->select_info)
gdk_window_hide (label->select_info->window);
- (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
+ (* GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap) (widget);
}
static void
gtk_label_select_region_index (label, min, max);
}
+static void
+gtk_label_grab_focus (GtkWidget *widget)
+{
+ GtkLabel *label;
+ gboolean select_on_focus;
+
+ label = GTK_LABEL (widget);
+
+ if (label->select_info == NULL)
+ return;
+
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
+
+ g_object_get (gtk_widget_get_settings (widget),
+ "gtk-label-select-on-focus",
+ &select_on_focus,
+ NULL);
+
+ if (select_on_focus && !label->in_click)
+ gtk_label_select_region (label, 0, -1);
+}
+
static gboolean
gtk_label_button_press (GtkWidget *widget,
GdkEventButton *event)
label->select_info->in_drag = FALSE;
if (event->button == 1)
{
- if (!GTK_WIDGET_HAS_FOCUS (widget))
- gtk_widget_grab_focus (widget);
+ if (!GTK_WIDGET_HAS_FOCUS (widget))
+ {
+ label->in_click = TRUE;
+ gtk_widget_grab_focus (widget);
+ label->in_click = FALSE;
+ }
if (event->type == GDK_3BUTTON_PRESS)
{
return TRUE;
}
+static void
+drag_begin_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer data)
+{
+ GtkLabel *label;
+ GdkPixmap *pixmap = NULL;
+
+ g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
+
+ label = GTK_LABEL (widget);
+
+ if ((label->select_info->selection_anchor !=
+ label->select_info->selection_end) &&
+ label->text)
+ {
+ gint start, end;
+ gint len;
+
+ start = MIN (label->select_info->selection_anchor,
+ label->select_info->selection_end);
+ end = MAX (label->select_info->selection_anchor,
+ label->select_info->selection_end);
+
+ len = strlen (label->text);
+
+ if (end > len)
+ end = len;
+
+ if (start > len)
+ start = len;
+
+ pixmap = _gtk_text_util_create_drag_icon (widget,
+ label->text + start,
+ end - start);
+ }
+
+ if (pixmap)
+ gtk_drag_set_icon_pixmap (context,
+ gdk_drawable_get_colormap (pixmap),
+ pixmap,
+ NULL,
+ -2, -2);
+ else
+ gtk_drag_set_icon_default (context);
+
+ if (pixmap)
+ g_object_unref (pixmap);
+}
+
static gboolean
gtk_label_motion (GtkWidget *widget,
GdkEventMotion *event)
label->select_info->drag_start_y,
event->x, event->y))
{
- GdkDragContext *context;
GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
gtk_target_list_add_text_targets (target_list, 0);
-
- context = gtk_drag_begin (widget, target_list,
- GDK_ACTION_COPY,
- 1, (GdkEvent *)event);
-
+
+ g_signal_connect (widget, "drag_begin",
+ G_CALLBACK (drag_begin_cb), NULL);
+ gtk_drag_begin (widget, target_list,
+ GDK_ACTION_COPY,
+ 1, (GdkEvent *)event);
label->select_info->in_drag = FALSE;
gtk_target_list_unref (target_list);
- gtk_drag_set_icon_default (context);
}
}
else
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
- attributes.window_type = GDK_WINDOW_TEMP;
+ attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_ONLY;
attributes.override_redirect = TRUE;
- attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
- GDK_XTERM);
attributes.event_mask = gtk_widget_get_events (widget) |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON_MOTION_MASK;
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
+ if (GTK_WIDGET_IS_SENSITIVE (widget))
+ {
+ attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_XTERM);
+ attributes_mask |= GDK_WA_CURSOR;
+ }
- attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR | GDK_WA_CURSOR;
label->select_info->window = gdk_window_new (widget->window,
&attributes, attributes_mask);
gdk_window_set_user_data (label->select_info->window, widget);
- gdk_cursor_unref (attributes.cursor);
+ if (attributes_mask & GDK_WA_CURSOR)
+ gdk_cursor_unref (attributes.cursor);
}
static void
return label->select_info != NULL;
}
+static void
+free_angle (gpointer angle)
+{
+ g_slice_free (gdouble, angle);
+}
+
/**
* gtk_label_set_angle:
* @label: a #GtkLabel
gtk_label_set_angle (GtkLabel *label,
gdouble angle)
{
- GtkLabelPrivate *priv;
+ gdouble *label_angle;
g_return_if_fail (GTK_IS_LABEL (label));
- priv = GTK_LABEL_GET_PRIVATE (label);
+ label_angle = (gdouble *)g_object_get_qdata (G_OBJECT (label), quark_angle);
+ if (!label_angle)
+ {
+ label_angle = g_slice_new (gdouble);
+ *label_angle = 0.0;
+ g_object_set_qdata_full (G_OBJECT (label), quark_angle,
+ label_angle, free_angle);
+ }
+
/* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
* double property ranges are inclusive, and changing 360 to 0 would
* make a property editor behave strangely.
if (angle < 0 || angle > 360.0)
angle = angle - 360. * floor (angle / 360.);
- if (angle != priv->angle)
+ if (*label_angle != angle)
{
- priv->angle = angle;
+ *label_angle = angle;
gtk_label_clear_layout (label);
gtk_widget_queue_resize (GTK_WIDGET (label));
gdouble
gtk_label_get_angle (GtkLabel *label)
{
- GtkLabelPrivate *priv;
+ gdouble *angle;
g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
+
+ angle = (gdouble *)g_object_get_qdata (G_OBJECT (label), quark_angle);
- priv = GTK_LABEL_GET_PRIVATE (label);
-
- return priv->angle;
+ if (angle)
+ return *angle;
+ else
+ return 0.0;
}
static void
gtk_label_set_single_line_mode (GtkLabel *label,
gboolean single_line_mode)
{
- GtkLabelPrivate *priv;
-
g_return_if_fail (GTK_IS_LABEL (label));
single_line_mode = single_line_mode != FALSE;
- priv = GTK_LABEL_GET_PRIVATE (label);
- if (priv->single_line_mode != single_line_mode)
+ if (label->single_line_mode != single_line_mode)
{
- priv->single_line_mode = single_line_mode;
+ label->single_line_mode = single_line_mode;
gtk_label_clear_layout (label);
gtk_widget_queue_resize (GTK_WIDGET (label));
gboolean
gtk_label_get_single_line_mode (GtkLabel *label)
{
- GtkLabelPrivate *priv;
-
g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
- priv = GTK_LABEL_GET_PRIVATE (label);
-
- return priv->single_line_mode;
+ return label->single_line_mode;
}
/* Compute the X position for an offset that corresponds to the "more important
{
gint new_pos = g_utf8_pointer_to_offset (label->text,
label->text + start);
- gint length;
- length = g_utf8_strlen (label->text, -1);
-
if (new_pos > 0)
{
PangoLogAttr *log_attrs;
gint count,
gboolean extend_selection)
{
+ gint old_pos;
gint new_pos;
if (label->select_info == NULL)
return;
-
- new_pos = label->select_info->selection_end;
+
+ old_pos = new_pos = label->select_info->selection_end;
if (label->select_info->selection_end != label->select_info->selection_anchor &&
!extend_selection)
new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
else
new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
-
break;
}
case GTK_MOVEMENT_LOGICAL_POSITIONS:
break;
case GTK_MOVEMENT_VISUAL_POSITIONS:
new_pos = gtk_label_move_visually (label, new_pos, count);
+ if (new_pos == old_pos)
+ {
+ if (!extend_selection)
+ {
+ if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
+ count > 0 ?
+ GTK_DIR_RIGHT : GTK_DIR_LEFT))
+ {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
+
+ if (toplevel)
+ gtk_widget_child_focus (toplevel,
+ count > 0 ?
+ GTK_DIR_RIGHT : GTK_DIR_LEFT);
+ }
+ }
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (label));
+ }
+ }
break;
case GTK_MOVEMENT_WORDS:
while (count > 0)
new_pos = gtk_label_move_backward_word (label, new_pos);
count++;
}
+ if (new_pos == old_pos)
+ gtk_widget_error_bell (GTK_WIDGET (label));
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
case GTK_MOVEMENT_PARAGRAPH_ENDS:
case GTK_MOVEMENT_BUFFER_ENDS:
/* FIXME: Can do better here */
new_pos = count < 0 ? 0 : strlen (label->text);
+ if (new_pos == old_pos)
+ gtk_widget_error_bell (GTK_WIDGET (label));
break;
case GTK_MOVEMENT_DISPLAY_LINES:
case GTK_MOVEMENT_PARAGRAPHS:
{
GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
- g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal);
+ g_object_set_data (G_OBJECT (menuitem), I_("gtk-signal"), (char *)signal);
g_signal_connect (menuitem, "activate",
G_CALLBACK (activate_cb), label);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
- menuitem = gtk_menu_item_new_with_label (_("Select All"));
+ menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
g_signal_connect_swapped (menuitem, "activate",
G_CALLBACK (gtk_label_select_all), label);
gtk_widget_show (menuitem);