#include "gtklabel.h"
#include "gtkaccellabel.h"
#include "gtkdnd.h"
-#include "gtkmainprivate.h"
#include "gtkmarshalers.h"
#include "gtkpango.h"
#include "gtkwindow.h"
#include "gtkintl.h"
#include "gtkseparatormenuitem.h"
#include "gtktextutil.h"
+#include "gtkmain.h"
#include "gtkmenuitem.h"
#include "gtkmenushellprivate.h"
#include "gtknotebook.h"
#include "gtktooltip.h"
#include "gtkprivate.h"
#include "gtktypebuiltins.h"
+#include "gtkmain.h"
#include "a11y/gtklabelaccessible.h"
+/* this is in case rint() is not provided by the compiler,
+ * such as in the case of C89 compilers, like MSVC
+ */
+#include "fallback-c89.c"
+
/**
* SECTION:gtklabel
* @Short_description: A widget that displays a small to medium amount of text
* </refsect2>
*/
-
-/*rint() is only available in GCC and/or C99*/
-#if (__STDC_VERSION__ < 199901L && !defined __GNUC__)
-double rint(double x)
-{
- if (ceil(x+0.5) == floor(x+0.5))
- {
- int a = (int)ceil(x);
- if (a%2 == 0)
- return ceil(x);
- else
- return floor(x);
- }
- else
- return floor(x+0.5);
-}
-#endif
-
-
-
struct _GtkLabelPrivate
{
GtkLabelSelectionInfo *select_info;
gdouble angle;
- guint mnemonics_visible : 1;
+ guint mnemonics_visible : 1;
guint jtype : 2;
guint wrap : 1;
guint use_underline : 1;
static void gtk_label_destroy (GtkWidget *widget);
static void gtk_label_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
-static void gtk_label_state_changed (GtkWidget *widget,
- GtkStateType state);
+static void gtk_label_state_flags_changed (GtkWidget *widget,
+ GtkStateFlags prev_state);
static void gtk_label_style_updated (GtkWidget *widget);
static void gtk_label_direction_changed (GtkWidget *widget,
GtkTextDirection previous_dir);
gboolean val);
static void gtk_label_set_use_underline_internal (GtkLabel *label,
gboolean val);
-static void gtk_label_set_attributes_internal (GtkLabel *label,
- PangoAttrList *attrs);
static void gtk_label_set_uline_text_internal (GtkLabel *label,
const gchar *str);
static void gtk_label_set_pattern_internal (GtkLabel *label,
gint anchor_index,
gint end_index);
+
static gboolean gtk_label_mnemonic_activate (GtkWidget *widget,
gboolean group_cycling);
static void gtk_label_setup_mnemonic (GtkLabel *label,
gint start);
/* For links: */
-static void gtk_label_rescan_links (GtkLabel *label);
static void gtk_label_clear_links (GtkLabel *label);
static gboolean gtk_label_activate_link (GtkLabel *label,
const gchar *uri);
widget_class->destroy = gtk_label_destroy;
widget_class->size_allocate = gtk_label_size_allocate;
- widget_class->state_changed = gtk_label_state_changed;
+ widget_class->state_flags_changed = gtk_label_state_flags_changed;
widget_class->style_updated = gtk_label_style_updated;
widget_class->query_tooltip = gtk_label_query_tooltip;
widget_class->direction_changed = gtk_label_direction_changed;
g_value_set_object (value, (GObject*) priv->mnemonic_widget);
break;
case PROP_CURSOR_POSITION:
- if (priv->select_info && priv->select_info->selectable)
- {
- gint offset = g_utf8_pointer_to_offset (priv->text,
- priv->text + priv->select_info->selection_end);
- g_value_set_int (value, offset);
- }
- else
- g_value_set_int (value, 0);
+ g_value_set_int (value, _gtk_label_get_cursor_position (label));
break;
case PROP_SELECTION_BOUND:
- if (priv->select_info && priv->select_info->selectable)
- {
- gint offset = g_utf8_pointer_to_offset (priv->text,
- priv->text + priv->select_info->selection_anchor);
- g_value_set_int (value, offset);
- }
- else
- g_value_set_int (value, 0);
+ g_value_set_int (value, _gtk_label_get_selection_bound (label));
break;
case PROP_ELLIPSIZE:
g_value_set_enum (value, priv->ellipsize);
PangoLanguage *language;
PangoFontDescription *font_desc;
GdkColor *color;
- GValue val = { 0, };
+ GValue val = G_VALUE_INIT;
if (!gtk_builder_value_from_string_type (builder, PANGO_TYPE_ATTR_TYPE, name, &val, error))
return NULL;
GError **error)
{
PangoParserData *data = (PangoParserData*)user_data;
- GValue val = { 0, };
+ GValue val = G_VALUE_INIT;
guint i;
gint line_number, char_number;
}
static void
-gtk_label_compose_effective_attrs (GtkLabel *label)
+my_pango_attr_list_merge (PangoAttrList *into,
+ PangoAttrList *from)
{
- GtkLabelPrivate *priv = label->priv;
PangoAttrIterator *iter;
PangoAttribute *attr;
GSList *iter_attrs, *l;
- if (priv->attrs)
+ iter = pango_attr_list_get_iterator (into);
+
+ if (iter)
{
- if (priv->effective_attrs)
- {
- if ((iter = pango_attr_list_get_iterator (priv->attrs)))
- {
- do
- {
- iter_attrs = pango_attr_iterator_get_attrs (iter);
- for (l = iter_attrs; l; l = l->next)
- {
- attr = l->data;
- pango_attr_list_insert (priv->effective_attrs, attr);
- }
- g_slist_free (iter_attrs);
- }
- while (pango_attr_iterator_next (iter));
- pango_attr_iterator_destroy (iter);
- }
- }
- else
- priv->effective_attrs =
- pango_attr_list_ref (priv->attrs);
+ do
+ {
+ iter_attrs = pango_attr_iterator_get_attrs (iter);
+ for (l = iter_attrs; l; l = l->next)
+ {
+ attr = l->data;
+ pango_attr_list_insert (from, attr);
+ }
+ g_slist_free (iter_attrs);
+ }
+ while (pango_attr_iterator_next (iter));
+ pango_attr_iterator_destroy (iter);
}
}
static void
-gtk_label_set_attributes_internal (GtkLabel *label,
- PangoAttrList *attrs)
+gtk_label_compose_effective_attrs (GtkLabel *label)
{
GtkLabelPrivate *priv = label->priv;
- if (attrs)
- pango_attr_list_ref (attrs);
-
if (priv->attrs)
- pango_attr_list_unref (priv->attrs);
- priv->attrs = attrs;
-
- g_object_notify (G_OBJECT (label), "attributes");
+ {
+ if (priv->effective_attrs)
+ my_pango_attr_list_merge (priv->effective_attrs, priv->attrs);
+ else
+ priv->effective_attrs =
+ pango_attr_list_ref (priv->attrs);
+ }
}
-
/* Calculates text, attrs and mnemonic_keyval from
* label, use_underline and use_markup
*/
GtkLabelPrivate *priv = label->priv;
guint keyval = priv->mnemonic_keyval;
+ gtk_label_clear_links (label);
+
if (priv->use_markup)
gtk_label_set_markup_internal (label, priv->label, priv->use_underline);
+ else if (priv->use_underline)
+ gtk_label_set_uline_text_internal (label, priv->label);
else
{
- if (priv->use_underline)
- gtk_label_set_uline_text_internal (label, priv->label);
- else
+ if (!priv->pattern_set)
{
if (priv->effective_attrs)
pango_attr_list_unref (priv->effective_attrs);
priv->effective_attrs = NULL;
- gtk_label_set_text_internal (label, g_strdup (priv->label));
}
+ gtk_label_set_text_internal (label, g_strdup (priv->label));
}
gtk_label_compose_effective_attrs (label);
gtk_label_set_attributes (GtkLabel *label,
PangoAttrList *attrs)
{
+ GtkLabelPrivate *priv = label->priv;
+
g_return_if_fail (GTK_IS_LABEL (label));
- gtk_label_set_attributes_internal (label, attrs);
+ if (attrs)
+ pango_attr_list_ref (attrs);
+
+ if (priv->attrs)
+ pango_attr_list_unref (priv->attrs);
+ priv->attrs = attrs;
+
+ g_object_notify (G_OBJECT (label), "attributes");
gtk_label_recalculate (label);
GtkLabel *label;
GList *links;
GString *new_str;
+ gsize text_len;
GdkColor *link_color;
GdkColor *visited_link_color;
} UriParserData;
link->uri = g_strdup (uri);
link->title = g_strdup (title);
link->visited = visited;
- pdata->links = g_list_append (pdata->links, link);
+ link->start = pdata->text_len;
+ pdata->links = g_list_prepend (pdata->links, link);
}
else
{
UriParserData *pdata = user_data;
if (!strcmp (element_name, "a"))
- g_string_append (pdata->new_str, "</span>");
+ {
+ GtkLabelLink *link = pdata->links->data;
+ link->end = pdata->text_len;
+ g_string_append (pdata->new_str, "</span>");
+ }
else
{
g_string_append (pdata->new_str, "</");
newtext = g_markup_escape_text (text, text_len);
g_string_append (pdata->new_str, newtext);
+ pdata->text_len += text_len;
g_free (newtext);
}
pdata.label = label;
pdata.links = NULL;
pdata.new_str = g_string_sized_new (length);
+ pdata.text_len = 0;
gtk_label_get_link_colors (GTK_WIDGET (label), &pdata.link_color, &pdata.visited_link_color);
failed:
g_markup_parse_context_free (context);
g_string_free (pdata.new_str, TRUE);
- g_list_foreach (pdata.links, (GFunc)link_free, NULL);
- g_list_free (pdata.links);
+ g_list_free_full (pdata.links, (GDestroyNotify) link_free);
gdk_color_free (pdata.link_color);
gdk_color_free (pdata.visited_link_color);
return;
}
- gtk_label_clear_links (label);
if (links)
{
gtk_label_ensure_select_info (label);
- priv->select_info->links = links;
+ priv->select_info->links = g_list_reverse (links);
gtk_label_ensure_has_tooltip (label);
}
gtk_label_set_pattern (GtkLabel *label,
const gchar *pattern)
{
- GtkLabelPrivate *priv = label->priv;
+ GtkLabelPrivate *priv;
g_return_if_fail (GTK_IS_LABEL (label));
{
g_object_unref (priv->layout);
priv->layout = NULL;
-
- //gtk_label_clear_links (label);
}
}
if (priv->effective_attrs)
pango_layout_set_attributes (priv->layout, priv->effective_attrs);
- gtk_label_rescan_links (label);
-
switch (priv->jtype)
{
case GTK_JUSTIFY_LEFT:
}
static void
-gtk_label_state_changed (GtkWidget *widget,
- GtkStateType prev_state)
+gtk_label_state_flags_changed (GtkWidget *widget,
+ GtkStateFlags prev_state)
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = label->priv;
gtk_label_update_cursor (label);
}
- if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed)
- GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed (widget, prev_state);
+ /* We have to clear the layout, fonts etc. may have changed */
+ gtk_label_clear_layout (label);
+
+ if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
}
static void
*yp = y;
}
-static void
-draw_insertion_cursor (GtkLabel *label,
- cairo_t *cr,
- GdkRectangle *cursor_location,
- gboolean is_primary,
- PangoDirection direction,
- gboolean draw_arrow)
-{
- GtkWidget *widget = GTK_WIDGET (label);
- GtkTextDirection text_dir;
-
- if (direction == PANGO_DIRECTION_LTR)
- text_dir = GTK_TEXT_DIR_LTR;
- else
- text_dir = GTK_TEXT_DIR_RTL;
-
- gtk_draw_insertion_cursor (widget, cr, cursor_location,
- is_primary, text_dir, draw_arrow);
-}
-
static PangoDirection
get_cursor_direction (GtkLabel *label)
{
return PANGO_DIRECTION_LTR;
}
-static void
-gtk_label_draw_cursor (GtkLabel *label, cairo_t *cr, gint xoffset, gint yoffset)
-{
- GtkLabelPrivate *priv = label->priv;
- GtkWidget *widget;
-
- if (priv->select_info == NULL)
- return;
-
- widget = GTK_WIDGET (label);
-
- if (gtk_widget_is_drawable (widget))
- {
- PangoDirection keymap_direction;
- PangoDirection cursor_direction;
- PangoRectangle strong_pos, weak_pos;
- gboolean split_cursor;
- PangoRectangle *cursor1 = NULL;
- PangoRectangle *cursor2 = NULL;
- GdkRectangle cursor_location;
- PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
- PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
-
- keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
- cursor_direction = get_cursor_direction (label);
-
- gtk_label_ensure_layout (label);
-
- pango_layout_get_cursor_pos (priv->layout, priv->select_info->selection_end,
- &strong_pos, &weak_pos);
-
- g_object_get (gtk_widget_get_settings (widget),
- "gtk-split-cursor", &split_cursor,
- NULL);
-
- dir1 = cursor_direction;
-
- if (split_cursor)
- {
- cursor1 = &strong_pos;
-
- if (strong_pos.x != weak_pos.x ||
- strong_pos.y != weak_pos.y)
- {
- dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
- cursor2 = &weak_pos;
- }
- }
- else
- {
- if (keymap_direction == cursor_direction)
- cursor1 = &strong_pos;
- else
- cursor1 = &weak_pos;
- }
-
- cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
- cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
- cursor_location.width = 0;
- cursor_location.height = PANGO_PIXELS (cursor1->height);
-
- draw_insertion_cursor (label, cr,
- &cursor_location, TRUE, dir1,
- dir2 != PANGO_DIRECTION_NEUTRAL);
-
- if (dir2 != PANGO_DIRECTION_NEUTRAL)
- {
- cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
- cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
- cursor_location.width = 0;
- cursor_location.height = PANGO_PIXELS (cursor2->height);
-
- draw_insertion_cursor (label, cr,
- &cursor_location, FALSE, dir2,
- TRUE);
- }
- }
-}
-
static GtkLabelLink *
gtk_label_get_focus_link (GtkLabel *label)
{
if (priv->text && (*priv->text != '\0'))
{
- GdkRGBA bg_color, fg_color;
-
get_layout_location (label, &x, &y);
context = gtk_widget_get_style_context (widget);
cairo_translate (cr, -allocation.x, -allocation.y);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_set_state (context, state);
-
gtk_render_layout (context, cr,
x, y,
priv->layout);
+ state = gtk_widget_get_state_flags (widget);
+
if (info &&
(info->selection_anchor != info->selection_end))
{
gint range[2];
cairo_region_t *clip;
+ GdkRGBA bg_color, fg_color;
range[0] = info->selection_anchor;
range[1] = info->selection_end;
gdk_cairo_region (cr, clip);
cairo_clip (cr);
- state = GTK_STATE_FLAG_SELECTED;
-
- if (gtk_widget_has_focus (widget))
- state |= GTK_STATE_FLAG_FOCUSED;
+ state |= GTK_STATE_FLAG_SELECTED;
gtk_style_context_get_color (context, state, &fg_color);
gtk_style_context_get_background_color (context, state, &bg_color);
GdkColor *link_color;
GdkColor *visited_link_color;
- if (info->selectable && gtk_widget_has_focus (widget))
- gtk_label_draw_cursor (label, cr, x, y);
+ if (info->selectable &&
+ gtk_widget_has_focus (widget) &&
+ gtk_widget_is_drawable (widget))
+ {
+ PangoDirection cursor_direction;
+
+ cursor_direction = get_cursor_direction (label);
+ gtk_render_insertion_cursor (context, cr,
+ x, y,
+ priv->layout, priv->select_info->selection_end,
+ cursor_direction);
+ }
focus_link = gtk_label_get_focus_link (label);
active_link = info->active_link;
-
if (active_link)
{
GdkRGBA bg_color;
text_color = link_color;
if (info->link_clicked)
- state = GTK_STATE_FLAG_ACTIVE;
+ state |= GTK_STATE_FLAG_ACTIVE;
else
- state = GTK_STATE_FLAG_PRELIGHT;
+ state |= GTK_STATE_FLAG_PRELIGHT;
gtk_style_context_get_background_color (context, state, &bg_color);
cairo_restore (cr);
}
- if (focus_link && gtk_widget_has_focus (widget))
+ if (focus_link && gtk_widget_has_visible_focus (widget))
{
range[0] = focus_link->start;
range[1] = focus_link->end;
1);
cairo_region_get_extents (clip, &rect);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_set_state (context, state);
-
gtk_render_focus (context, cr,
rect.x, rect.y,
rect.width, rect.height);
if (info->active_link)
{
- if (event->button == 1)
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
{
info->link_clicked = 1;
- gtk_widget_queue_draw (widget);
+ gtk_label_do_popup (label, event);
+ return TRUE;
}
- else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ else if (event->button == 1)
{
info->link_clicked = 1;
- gtk_label_do_popup (label, event);
- return TRUE;
+ gtk_widget_queue_draw (widget);
}
}
info->in_drag = FALSE;
info->select_words = FALSE;
- if (event->button == 1)
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ {
+ gtk_label_do_popup (label, event);
+
+ return TRUE;
+ }
+ else if (event->button == 1)
{
if (!gtk_widget_has_focus (widget))
{
return TRUE;
}
- else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
- {
- gtk_label_do_popup (label, event);
- return TRUE;
- }
return FALSE;
}
{
GtkLabelPrivate *priv = label->priv;
- if ((priv->select_info->selection_anchor !=
+ if (priv->select_info &&
+ (priv->select_info->selection_anchor !=
priv->select_info->selection_end) &&
priv->text)
{
GtkClipboard *clipboard;
if (priv->select_info->selection_anchor == anchor_index &&
- priv->select_info->selection_end == end_index)
- return;
+ priv->select_info->selection_end == end_index)
+ return;
+
+ g_object_freeze_notify (G_OBJECT (label));
+
+ if (priv->select_info->selection_anchor != anchor_index)
+ g_object_notify (G_OBJECT (label), "selection-bound");
+ if (priv->select_info->selection_end != end_index)
+ g_object_notify (G_OBJECT (label), "cursor-position");
priv->select_info->selection_anchor = anchor_index;
priv->select_info->selection_end = end_index;
- clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
- GDK_SELECTION_PRIMARY);
+ if (gtk_widget_has_screen (GTK_WIDGET (label)))
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
+ GDK_SELECTION_PRIMARY);
+ else
+ clipboard = NULL;
if (anchor_index != end_index)
{
gtk_target_list_add_text_targets (list, 0);
targets = gtk_target_table_new_from_list (list, &n_targets);
- gtk_clipboard_set_with_owner (clipboard,
- targets, n_targets,
- get_text_callback,
- clear_text_callback,
- G_OBJECT (label));
+ if (clipboard)
+ gtk_clipboard_set_with_owner (clipboard,
+ targets, n_targets,
+ get_text_callback,
+ clear_text_callback,
+ G_OBJECT (label));
gtk_target_table_free (targets, n_targets);
gtk_target_list_unref (list);
}
else
{
- if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
+ if (clipboard &&
+ gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
gtk_clipboard_clear (clipboard);
}
gtk_widget_queue_draw (GTK_WIDGET (label));
- g_object_freeze_notify (G_OBJECT (label));
- g_object_notify (G_OBJECT (label), "cursor-position");
- g_object_notify (G_OBJECT (label), "selection-bound");
g_object_thaw_notify (G_OBJECT (label));
}
}
{
g_return_if_fail (GTK_IS_LABEL (label));
+ g_object_freeze_notify (G_OBJECT (label));
+
gtk_label_set_use_markup_internal (label, setting);
gtk_label_recalculate (label);
+
+ g_object_thaw_notify (G_OBJECT (label));
}
/**
{
g_return_if_fail (GTK_IS_LABEL (label));
+ g_object_freeze_notify (G_OBJECT (label));
+
gtk_label_set_use_underline_internal (label, setting);
gtk_label_recalculate (label);
+
+ g_object_thaw_notify (G_OBJECT (label));
}
/**
if (!priv->select_info)
return;
- g_list_foreach (priv->select_info->links, (GFunc)link_free, NULL);
- g_list_free (priv->select_info->links);
+ g_list_free_full (priv->select_info->links, (GDestroyNotify) link_free);
priv->select_info->links = NULL;
priv->select_info->active_link = NULL;
}
-static void
-gtk_label_rescan_links (GtkLabel *label)
-{
- GtkLabelPrivate *priv = label->priv;
- PangoLayout *layout = priv->layout;
- PangoAttrList *attlist;
- PangoAttrIterator *iter;
- GList *links;
-
- if (!priv->select_info || !priv->select_info->links)
- return;
-
- attlist = pango_layout_get_attributes (layout);
-
- if (attlist == NULL)
- return;
-
- iter = pango_attr_list_get_iterator (attlist);
-
- links = priv->select_info->links;
-
- do
- {
- PangoAttribute *underline;
- PangoAttribute *color;
-
- underline = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
- color = pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
-
- if (underline != NULL && color != NULL)
- {
- gint start, end;
- PangoRectangle start_pos;
- PangoRectangle end_pos;
- GtkLabelLink *link;
-
- pango_attr_iterator_range (iter, &start, &end);
- pango_layout_index_to_pos (layout, start, &start_pos);
- pango_layout_index_to_pos (layout, end, &end_pos);
-
- if (links == NULL)
- {
- g_warning ("Ran out of links");
- break;
- }
- link = links->data;
- links = links->next;
- link->start = start;
- link->end = end;
- }
- } while (pango_attr_iterator_next (iter));
-
- pango_attr_iterator_destroy (iter);
-}
-
static gboolean
gtk_label_activate_link (GtkLabel *label,
const gchar *uri)
keyboard_tip,
tooltip);
}
+
+gint
+_gtk_label_get_cursor_position (GtkLabel *label)
+{
+ GtkLabelPrivate *priv = label->priv;
+
+ if (priv->select_info && priv->select_info->selectable)
+ return g_utf8_pointer_to_offset (priv->text,
+ priv->text + priv->select_info->selection_end);
+
+ return 0;
+}
+
+gint
+_gtk_label_get_selection_bound (GtkLabel *label)
+{
+ GtkLabelPrivate *priv = label->priv;
+
+ if (priv->select_info && priv->select_info->selectable)
+ return g_utf8_pointer_to_offset (priv->text,
+ priv->text + priv->select_info->selection_anchor);
+
+ return 0;
+}