* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
*/
/*
#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"
-/*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
+#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
+ * @Title: GtkLabel
+ *
+ * The #GtkLabel widget displays a small amount of text. As the name
+ * implies, most labels are used to label another widget such as a
+ * #GtkButton, a #GtkMenuItem, or a #GtkComboBox.
+ *
+ * <refsect2 id="GtkLabel-BUILDER-UI">
+ * <title>GtkLabel as GtkBuildable</title>
+ * <para>
+ * The GtkLabel implementation of the GtkBuildable interface supports a
+ * custom <attributes> element, which supports any number of <attribute>
+ * elements. the <attribute> element has attributes named name, value,
+ * start and end and allows you to specify #PangoAttribute values for this label.
+ *
+ * <example>
+ * <title>A UI definition fragment specifying Pango attributes</title>
+ * <programlisting><![CDATA[
+ * <object class="GtkLabel">
+ * <attributes>
+ * <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+ * <attribute name="background" value="red" start="5" end="10"/>"
+ * </attributes>
+ * </object>
+ * ]]></programlisting>
+ * </example>
+ * The start and end attributes specify the range of characters to which the
+ * Pango attribute applies. If start and end are not specified, the attribute is
+ * applied to the whole text. Note that specifying ranges does not make much
+ * sense with translatable attributes. Use markup embedded in the translatable
+ * content instead.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Mnemonics</title>
+ * <para>
+ * Labels may contain <firstterm>mnemonics</firstterm>. Mnemonics are
+ * underlined characters in the label, used for keyboard navigation.
+ * Mnemonics are created by providing a string with an underscore before
+ * the mnemonic character, such as <literal>"_File"</literal>, to the
+ * functions gtk_label_new_with_mnemonic() or
+ * gtk_label_set_text_with_mnemonic().
+ *
+ * Mnemonics automatically activate any activatable widget the label is
+ * inside, such as a #GtkButton; if the label is not inside the
+ * mnemonic's target widget, you have to tell the label about the target
+ * using gtk_label_set_mnemonic_widget(). Here's a simple example where
+ * the label is inside a button:
+ *
+ * <informalexample>
+ * <programlisting>
+ * // Pressing Alt+H will activate this button
+ * button = gtk_button_new (<!-- -->);
+ * label = gtk_label_new_with_mnemonic ("_Hello");
+ * gtk_container_add (GTK_CONTAINER (button), label);
+ * </programlisting>
+ * </informalexample>
+ *
+ * There's a convenience function to create buttons with a mnemonic label
+ * already inside:
+ *
+ * <informalexample>
+ * <programlisting>
+ * // Pressing Alt+H will activate this button
+ * button = gtk_button_new_with_mnemonic ("_Hello");
+ * </programlisting>
+ * </informalexample>
+ *
+ * To create a mnemonic for a widget alongside the label, such as a
+ * #GtkEntry, you have to point the label at the entry with
+ * gtk_label_set_mnemonic_widget():
+ *
+ * <informalexample>
+ * <programlisting>
+ * // Pressing Alt+H will focus the entry
+ * entry = gtk_entry_new (<!-- -->);
+ * label = gtk_label_new_with_mnemonic ("_Hello");
+ * gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+ * </programlisting>
+ * </informalexample>
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Markup (styled text)</title>
+ * <para>
+ * To make it easy to format text in a label (changing colors, fonts,
+ * etc.), label text can be provided in a simple <link
+ * linkend="PangoMarkupFormat">markup format</link>.
+ * Here's how to create a label with a small font:
+ *
+ * <informalexample>
+ * <programlisting>
+ * label = gtk_label_new (NULL);
+ * gtk_label_set_markup (GTK_LABEL (label), "<small>Small text</small>");
+ * </programlisting>
+ * </informalexample>
+ *
+ * (See <link
+ * linkend="PangoMarkupFormat">complete documentation</link> of available
+ * tags in the Pango manual.)
+ *
+ * The markup passed to gtk_label_set_markup() must be valid; for example,
+ * literal <, > and & characters must be escaped as \<,
+ * \gt;, and \&. If you pass text obtained from the user, file,
+ * or a network to gtk_label_set_markup(), you'll want to escape it with
+ * g_markup_escape_text() or g_markup_printf_escaped().
+ *
+ * Markup strings are just a convenient way to set the #PangoAttrList on
+ * a label; gtk_label_set_attributes() may be a simpler way to set
+ * attributes in some cases. Be careful though; #PangoAttrList tends to
+ * cause internationalization problems, unless you're applying attributes
+ * to the entire string (i.e. unless you set the range of each attribute
+ * to [0, %G_MAXINT)). The reason is that specifying the start_index and
+ * end_index for a #PangoAttribute requires knowledge of the exact string
+ * being displayed, so translations will cause problems.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Selectable labels</title>
+ * Labels can be made selectable with gtk_label_set_selectable().
+ * Selectable labels allow the user to copy the label contents to
+ * the clipboard. Only labels that contain useful-to-copy information
+ * — such as error messages — should be made selectable.
+ * </refsect2>
+ * <refsect2 id="label-text-layout">
+ * <title>Text layout</title>
+ * <para>
+ * A label can contain any number of paragraphs, but will have
+ * performance problems if it contains more than a small number.
+ * Paragraphs are separated by newlines or other paragraph separators
+ * understood by Pango.
+ *
+ * Labels can automatically wrap text if you call
+ * gtk_label_set_line_wrap().
+ *
+ * gtk_label_set_justify() sets how the lines in a label align
+ * with one another. If you want to set how the label as a whole
+ * aligns in its available space, see gtk_misc_set_alignment().
+ *
+ * The #GtkLabel:width-chars and #GtkLabel:max-width-chars properties
+ * can be used to control the size allocation of ellipsized or wrapped
+ * labels. For ellipsizing labels, if either is specified (and less
+ * than the actual text size), it is used as the minimum width, and the actual
+ * text size is used as the natural width of the label. For wrapping labels,
+ * width-chars is used as the minimum width, if specified, and max-width-chars
+ * is used as the natural width. Even if max-width-chars specified, wrapping
+ * labels will be rewrapped to use all of the available width.
+ *
+ * <note><para>
+ * Note that the interpretation of #GtkLabel:width-chars and
+ * #GtkLabel:max-width-chars has changed a bit with the introduction of
+ * <link linkend="geometry-management">width-for-height geometry management.</link>
+ * </para></note>
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Links</title>
+ * <para>
+ * Since 2.18, GTK+ supports markup for clickable hyperlinks in addition
+ * to regular Pango markup. The markup for links is borrowed from HTML, using the
+ * <tag>a</tag> with href and title attributes. GTK+ renders links similar to the
+ * way they appear in web browsers, with colored, underlined text. The title
+ * attribute is displayed as a tooltip on the link. An example looks like this:
+ *
+ * <informalexample><programlisting>
+ * gtk_label_set_markup (label, "Go to the <a href="http://www.gtk.org" title="<i>Our</i> website">GTK+ website</a> for more...");
+ * </programlisting></informalexample>
+ *
+ * It is possible to implement custom handling for links and their tooltips with
+ * the #GtkLabel::activate-link signal and the gtk_label_get_current_uri() function.
+ * </para>
+ * </refsect2>
+ */
struct _GtkLabelPrivate
{
GtkWindow *mnemonic_window;
PangoAttrList *attrs;
- PangoAttrList *effective_attrs;
+ PangoAttrList *markup_attrs;
PangoLayout *layout;
- gboolean mnemonics_visible;
-
gchar *label;
gchar *text;
- guint jtype : 2;
- guint wrap : 1;
- guint use_underline : 1;
- guint use_markup : 1;
- guint ellipsize : 3;
- guint single_line_mode : 1;
- guint have_transform : 1;
- guint in_click : 1;
- guint wrap_mode : 3;
- guint pattern_set : 1;
- guint track_links : 1;
+ gdouble angle;
+
+ guint mnemonics_visible : 1;
+ guint jtype : 2;
+ guint wrap : 1;
+ guint use_underline : 1;
+ guint use_markup : 1;
+ guint ellipsize : 3;
+ guint single_line_mode : 1;
+ guint have_transform : 1;
+ guint in_click : 1;
+ guint wrap_mode : 3;
+ guint pattern_set : 1;
+ guint track_links : 1;
guint mnemonic_keyval;
- gint wrap_width;
gint width_chars;
gint max_width_chars;
-
- gdouble angle;
};
/* Notes about the handling of links:
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_style_updated (GtkWidget *widget);
-static void gtk_label_direction_changed (GtkWidget *widget,
- GtkTextDirection previous_dir);
+static void gtk_label_state_flags_changed (GtkWidget *widget,
+ GtkStateFlags prev_state);
static gint gtk_label_draw (GtkWidget *widget,
cairo_t *cr);
static gboolean gtk_label_focus (GtkWidget *widget,
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,
static void gtk_label_clear_select_info (GtkLabel *label);
static void gtk_label_update_cursor (GtkLabel *label);
static void gtk_label_clear_layout (GtkLabel *label);
-static void gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width);
-static void gtk_label_invalidate_wrap_width (GtkLabel *label);
+static void gtk_label_ensure_layout (GtkLabel *label);
static void gtk_label_select_region_index (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);
static void gtk_label_activate_current_link (GtkLabel *label);
static GtkLabelLink *gtk_label_get_current_link (GtkLabel *label);
static void gtk_label_get_link_colors (GtkWidget *widget,
- GdkColor **link_color,
- GdkColor **visited_link_color);
+ GdkColor *link_color,
+ GdkColor *visited_link_color);
static void emit_activate_link (GtkLabel *label,
GtkLabelLink *link);
widget_class->destroy = gtk_label_destroy;
widget_class->size_allocate = gtk_label_size_allocate;
- widget_class->state_changed = gtk_label_state_changed;
- widget_class->style_updated = gtk_label_style_updated;
+ widget_class->state_flags_changed = gtk_label_state_flags_changed;
widget_class->query_tooltip = gtk_label_query_tooltip;
- widget_class->direction_changed = gtk_label_direction_changed;
widget_class->draw = gtk_label_draw;
widget_class->realize = gtk_label_realize;
widget_class->unrealize = gtk_label_unrealize;
*
* The preferred place to ellipsize the string, if the label does
* not have enough room to display the entire string, specified as a
- * #PangoEllisizeMode.
+ * #PangoEllipsizeMode.
*
* Note that setting this property to a value other than
* %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
"activate-current-link", 0);
g_type_class_add_private (class, sizeof (GtkLabelPrivate));
+
+ gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LABEL_ACCESSIBLE);
}
static void
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);
priv->width_chars = -1;
priv->max_width_chars = -1;
- priv->wrap_width = -1;
priv->label = NULL;
priv->jtype = GTK_JUSTIFY_LEFT;
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;
attribute = pango_attr_stretch_new (g_value_get_enum (&val));
break;
case PANGO_ATTR_UNDERLINE:
- if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
- attribute = pango_attr_underline_new (g_value_get_boolean (&val));
+ if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_UNDERLINE, value, &val, NULL))
+ attribute = pango_attr_underline_new (g_value_get_enum (&val));
+ else
+ {
+ /* XXX: allow boolean for backwards compat, so ignore error */
+ /* Deprecate this somehow */
+ g_value_unset (&val);
+ if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
+ attribute = pango_attr_underline_new (g_value_get_boolean (&val));
+ }
break;
case PANGO_ATTR_STRIKETHROUGH:
if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
GError **error)
{
PangoParserData *data = (PangoParserData*)user_data;
- GValue val = { 0, };
+ GValue val = G_VALUE_INIT;
guint i;
gint line_number, char_number;
}
attr = attribute_from_text (data->builder, name, value, error);
- attr->start_index = start_val;
- attr->end_index = end_val;
if (attr)
{
+ attr->start_index = start_val;
+ attr->end_index = end_val;
+
if (!data->attrs)
data->attrs = pango_attr_list_new ();
GtkSettings *settings;
gboolean shortcuts_connected;
+ /* The PangoContext is replaced when the screen changes, so clear the layouts */
+ gtk_label_clear_layout (GTK_LABEL (widget));
+
if (!gtk_widget_has_screen (widget))
return;
*
* If the label has been set so that it has an mnemonic key this function
* returns the keyval used for the mnemonic accelerator. If there is no
- * mnemonic set up it returns #GDK_VoidSymbol.
+ * mnemonic set up it returns #GDK_KEY_VoidSymbol.
*
- * Returns: GDK keyval usable for accelerators, or #GDK_VoidSymbol
+ * Returns: GDK keyval usable for accelerators, or #GDK_KEY_VoidSymbol
**/
guint
gtk_label_get_mnemonic_keyval (GtkLabel *label)
static void
gtk_label_set_text_internal (GtkLabel *label,
- gchar *str)
+ gchar *str)
{
GtkLabelPrivate *priv = label->priv;
+ gboolean text_changed;
- g_free (priv->text);
+ text_changed = g_strcmp0 (priv->text, str) != 0;
+ g_free (priv->text);
priv->text = str;
- gtk_label_select_region_index (label, 0, 0);
+ if (text_changed)
+ gtk_label_select_region_index (label, 0, 0);
}
static void
}
}
-static void
-gtk_label_compose_effective_attrs (GtkLabel *label)
+static gboolean
+my_pango_attr_list_merge_filter (PangoAttribute *attribute,
+ gpointer list)
{
- GtkLabelPrivate *priv = label->priv;
- PangoAttrIterator *iter;
- PangoAttribute *attr;
- GSList *iter_attrs, *l;
-
- if (priv->attrs)
- {
- 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);
- }
+ pango_attr_list_change (list, pango_attribute_copy (attribute));
+ return FALSE;
}
static void
-gtk_label_set_attributes_internal (GtkLabel *label,
- PangoAttrList *attrs)
+my_pango_attr_list_merge (PangoAttrList *into,
+ PangoAttrList *from)
{
- 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");
+ pango_attr_list_filter (from, my_pango_attr_list_merge_filter, into);
}
-
/* 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));
+ if (priv->markup_attrs)
+ pango_attr_list_unref (priv->markup_attrs);
+ priv->markup_attrs = NULL;
}
+ gtk_label_set_text_internal (label, g_strdup (priv->label));
}
- gtk_label_compose_effective_attrs (label);
-
if (!priv->use_underline)
priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
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;
- gtk_label_recalculate (label);
+ g_object_notify (G_OBJECT (label), "attributes");
gtk_label_clear_layout (label);
gtk_widget_queue_resize (GTK_WIDGET (label));
* Return value: the text of the label widget. This string is
* owned by the widget and must not be modified or freed.
**/
-G_CONST_RETURN gchar *
+const gchar *
gtk_label_get_label (GtkLabel *label)
{
g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
GtkLabel *label;
GList *links;
GString *new_str;
- GdkColor *link_color;
- GdkColor *visited_link_color;
+ gsize text_len;
} UriParserData;
static void
gint line_number;
gint char_number;
gint i;
- GdkColor *color = NULL;
g_markup_parse_context_get_position (context, &line_number, &char_number);
}
}
- if (visited)
- color = pdata->visited_link_color;
- else
- color = pdata->link_color;
-
- g_string_append_printf (pdata->new_str,
- "<span color=\"#%04x%04x%04x\" underline=\"single\">",
- color->red,
- color->green,
- color->blue);
-
link = g_new0 (GtkLabelLink, 1);
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;
+ }
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);
}
}
static void
-gtk_label_get_link_colors (GtkWidget *widget,
- GdkColor **link_color,
- GdkColor **visited_link_color)
+gtk_label_get_link_colors (GtkWidget *widget,
+ GdkColor *link_color,
+ GdkColor *visited_link_color)
{
GtkStyleContext *context;
+ GdkColor *link, *visited;
context = gtk_widget_get_style_context (widget);
gtk_style_context_get_style (context,
- "link-color", link_color,
- "visited-link-color", visited_link_color,
+ "link-color", &link,
+ "visited-link-color", &visited,
NULL);
- if (!*link_color)
- *link_color = gdk_color_copy (&default_link_color);
- if (!*visited_link_color)
- *visited_link_color = gdk_color_copy (&default_visited_link_color);
+ if (link)
+ {
+ *link_color = *link;
+ gdk_color_free (link);
+ }
+ else
+ *link_color = default_link_color;
+
+ if (visited)
+ {
+ *visited_link_color = *visited;
+ gdk_color_free (visited);
+ }
+ else
+ *visited_link_color = default_visited_link_color;
}
static gboolean
pdata.label = label;
pdata.links = NULL;
pdata.new_str = g_string_sized_new (length);
-
- gtk_label_get_link_colors (GTK_WIDGET (label), &pdata.link_color, &pdata.visited_link_color);
+ pdata.text_len = 0;
while (p != end && xml_isspace (*p))
p++;
*new_str = g_string_free (pdata.new_str, FALSE);
*links = pdata.links;
- gdk_color_free (pdata.link_color);
- gdk_color_free (pdata.visited_link_color);
-
return TRUE;
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);
- gdk_color_free (pdata.link_color);
- gdk_color_free (pdata.visited_link_color);
+ g_list_free_full (pdata.links, (GDestroyNotify) link_free);
return FALSE;
}
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);
}
if (attrs)
{
- if (priv->effective_attrs)
- pango_attr_list_unref (priv->effective_attrs);
- priv->effective_attrs = attrs;
+ if (priv->markup_attrs)
+ pango_attr_list_unref (priv->markup_attrs);
+ priv->markup_attrs = attrs;
}
if (accel_char != 0)
* Return value: the text in the label widget. This is the internal
* string used by the label, and must not be modified.
**/
-G_CONST_RETURN gchar *
+const gchar *
gtk_label_get_text (GtkLabel *label)
{
g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
else
attrs = gtk_label_pattern_to_attrs (label, pattern);
- if (priv->effective_attrs)
- pango_attr_list_unref (priv->effective_attrs);
- priv->effective_attrs = attrs;
+ if (priv->markup_attrs)
+ pango_attr_list_unref (priv->markup_attrs);
+ priv->markup_attrs = attrs;
}
+/**
+ * gtk_label_set_pattern:
+ * @label: The #GtkLabel you want to set the pattern to.
+ * @pattern: The pattern as described above.
+ *
+ * The pattern of underlines you want under the existing text within the
+ * #GtkLabel widget. For example if the current text of the label says
+ * "FooBarBaz" passing a pattern of "___ ___" will underline
+ * "Foo" and "Baz" but not "Bar".
+ */
void
gtk_label_set_pattern (GtkLabel *label,
const gchar *pattern)
{
- GtkLabelPrivate *priv = label->priv;
+ GtkLabelPrivate *priv;
g_return_if_fail (GTK_IS_LABEL (label));
{
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));
}
}
if (priv->attrs)
pango_attr_list_unref (priv->attrs);
- if (priv->effective_attrs)
- pango_attr_list_unref (priv->effective_attrs);
+ if (priv->markup_attrs)
+ pango_attr_list_unref (priv->markup_attrs);
gtk_label_clear_links (label);
g_free (priv->select_info);
{
g_object_unref (priv->layout);
priv->layout = NULL;
-
- //gtk_label_clear_links (label);
}
}
-static PangoFontMetrics *
-get_font_metrics (PangoContext *context, GtkWidget *widget)
-{
- GtkStyleContext *style_context;
- PangoFontDescription *font;
- PangoFontMetrics *retval;
-
- style_context = gtk_widget_get_style_context (widget);
- gtk_style_context_get (style_context, 0, "font", &font, NULL);
-
- retval = pango_context_get_metrics (context,
- font,
- pango_context_get_language (context));
-
- if (font != NULL)
- pango_font_description_free (font);
-
- return retval;
-}
-
/**
* gtk_label_get_measuring_layout:
* @label: the label
int width)
{
GtkLabelPrivate *priv = label->priv;
+ PangoRectangle rect;
PangoLayout *copy;
if (existing_layout != NULL)
g_object_unref (existing_layout);
}
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
if (pango_layout_get_width (priv->layout) == width)
{
return priv->layout;
}
- copy = pango_layout_copy (priv->layout);
- pango_layout_set_width (copy, width);
- return copy;
-}
-
-static void
-get_label_width (GtkLabel *label,
- PangoLayout *guess_layout,
- gint *minimum,
- gint *natural)
-{
- GtkLabelPrivate *priv;
- PangoLayout *layout;
- PangoContext *context;
- PangoFontMetrics *metrics;
- PangoRectangle rect;
- gint char_width, digit_width, char_pixels, text_width, ellipsize_chars, guess_width;
-
- priv = label->priv;
-
- layout = gtk_label_get_measuring_layout (label, NULL, -1);
- context = pango_layout_get_context (layout);
- metrics = get_font_metrics (context, GTK_WIDGET (label));
- 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);
-
- /* Fetch the length of the complete unwrapped text */
- pango_layout_get_extents (layout, NULL, &rect);
- text_width = rect.width;
-
- /* Fetch the width that was guessed */
- pango_layout_get_extents (guess_layout, NULL, &rect);
- guess_width = rect.width;
-
- /* enforce minimum width for ellipsized labels at ~3 chars */
- if (priv->ellipsize)
- ellipsize_chars = 3;
- else
- ellipsize_chars = 0;
-
- /* "width-chars" Hard-coded minimum width:
- * - minimum size should be MAX (width-chars, strlen ("..."));
- * - natural size should be MAX (width-chars, strlen (priv->text));
- *
- * "max-width-chars" User specified maximum size requisition
- * - minimum size should be MAX (width-chars, 0)
- * - natural size should be MIN (max-width-chars, strlen (priv->text))
- *
- * For ellipsizing labels; if max-width-chars is specified: either it is used as
- * a minimum size or the label text as a minimum size (natural size still overflows).
- *
- * For wrapping labels; A reasonable minimum size is useful to naturally layout
- * interfaces automatically. In this case if no "width-chars" is specified, the minimum
- * width will default to the wrap guess that gtk_label_ensure_layout() does.
- *
- * In *any* case the minimum width is completely overridden if an explicit width
- * request was provided.
+ /* We can use the label's own layout if we're not allocated a size yet,
+ * because we don't need it to be properly setup at that point.
+ * This way we can make use of caching upon the label's creation.
*/
-
- if (priv->ellipsize || priv->wrap)
- {
- *minimum = char_pixels * MAX (priv->width_chars, ellipsize_chars);
-
- /* Default to the minimum width regularly guessed by GTK+ if no minimum
- * width was specified, only allow unwrapping of these labels.
- *
- * Note that when specifying a small width_chars for a long text;
- * an accordingly large size will be required for the label height.
- */
- if (priv->wrap && priv->width_chars <= 0)
- *minimum = guess_width;
-
- if (priv->max_width_chars < 0)
- {
- *natural = MAX (*minimum, text_width);
- }
- else
- {
- gint max_char_width = char_pixels * priv->max_width_chars;
- gint max_width = MIN (text_width, max_char_width);
-
- /* With max-char-width specified, we let the minimum widths of
- * ellipsized text crawl up to the max-char-width
- * (note that we dont want to limit the minimum width for wrapping text).
- */
- if (priv->ellipsize)
- *minimum = MIN (text_width, max_width);
-
- *natural = MAX (*minimum, max_width);
- }
- }
- else
+ if (gtk_widget_get_allocated_width (GTK_WIDGET (label)) <= 1)
{
- *minimum = text_width;
- *natural = *minimum;
+ g_object_ref (priv->layout);
+ pango_layout_set_width (priv->layout, width);
+ return priv->layout;
}
- /* if a width-request is set, use that as the requested label width */
- if (priv->wrap || priv->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
+ /* oftentimes we want to measure a width that is far wider than the current width,
+ * even though the layout would not change if we made it wider. In that case, we
+ * can just return the current layout, because for measuring purposes, it will be
+ * identical.
+ */
+ pango_layout_get_extents (priv->layout, NULL, &rect);
+ if ((width == -1 || rect.width <= width) &&
+ !pango_layout_is_wrapped (priv->layout) &&
+ !pango_layout_is_ellipsized (priv->layout))
{
- GtkWidgetAuxInfo *aux_info;
-
- aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
- if (aux_info && aux_info->width > 0)
- {
- *minimum = aux_info->width * PANGO_SCALE;
- *natural = MAX (*natural, *minimum);
- }
+ g_object_ref (priv->layout);
+ return priv->layout;
}
- g_object_unref (layout);
+ copy = pango_layout_copy (priv->layout);
+ pango_layout_set_width (copy, width);
+ return copy;
}
static void
-gtk_label_invalidate_wrap_width (GtkLabel *label)
+gtk_label_update_layout_width (GtkLabel *label)
{
GtkLabelPrivate *priv = label->priv;
+ GtkWidget *widget = GTK_WIDGET (label);
- priv->wrap_width = -1;
-}
-
-static gint
-get_label_wrap_width (GtkLabel *label)
-{
- GtkLabelPrivate *priv = label->priv;
+ g_assert (priv->layout);
- if (priv->wrap_width < 0)
+ if (priv->ellipsize || priv->wrap)
{
- if (priv->width_chars > 0)
- {
- PangoLayout *layout;
- PangoContext *context;
- PangoFontMetrics *metrics;
- PangoRectangle rect;
- gint char_width, digit_width, char_pixels, text_width;
-
- layout = gtk_label_get_measuring_layout (label, NULL, -1);
- context = pango_layout_get_context (layout);
- metrics = get_font_metrics (context, GTK_WIDGET (label));
- 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);
-
- pango_layout_get_extents (layout, NULL, &rect);
- g_object_unref (layout);
-
- text_width = rect.width;
-
- priv->wrap_width = PANGO_PIXELS (MAX (text_width, char_pixels * priv->width_chars));
- }
- else
- {
- PangoLayout *layout;
-
- layout = gtk_widget_create_pango_layout (GTK_WIDGET (label),
- "This string is just about long enough.");
- pango_layout_get_size (layout, &priv->wrap_width, NULL);
- g_object_unref (layout);
- }
- }
+ GtkBorder border;
+ PangoRectangle logical;
+ gint width, height;
- return priv->wrap_width;
-}
-
-static PangoLayout *
-gtk_label_get_layout_with_guessed_wrap_width (GtkLabel *label)
-{
- GdkScreen *screen;
- PangoRectangle logical_rect;
- gint wrap_width, width, height, longest_paragraph;
- PangoLayout *layout;
+ _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
- if (!label->priv->wrap)
- return gtk_label_get_measuring_layout (label, NULL, -1);
+ width = gtk_widget_get_allocated_width (GTK_WIDGET (label)) - border.left - border.right;
+ height = gtk_widget_get_allocated_height (GTK_WIDGET (label)) - border.top - border.bottom;
- screen = gtk_widget_get_screen (GTK_WIDGET (label));
-
- layout = gtk_label_get_measuring_layout (label, NULL, -1);
- pango_layout_get_extents (layout, NULL, &logical_rect);
+ if (priv->have_transform)
+ {
+ PangoContext *context = gtk_widget_get_pango_context (widget);
+ const PangoMatrix *matrix = pango_context_get_matrix (context);
+ const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
+ const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
- width = logical_rect.width;
- /* Try to guess a reasonable maximum width */
- longest_paragraph = width;
+ pango_layout_set_width (priv->layout, -1);
+ pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
- wrap_width = get_label_wrap_width (label);
- width = MIN (width, wrap_width);
- width = MIN (width,
- PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
+ if (fabs (dy) < 0.01)
+ {
+ if (logical.width > width)
+ pango_layout_set_width (priv->layout, width * PANGO_SCALE);
+ }
+ else if (fabs (dx) < 0.01)
+ {
+ if (logical.width > height)
+ pango_layout_set_width (priv->layout, height * PANGO_SCALE);
+ }
+ else
+ {
+ gdouble x0, y0, x1, y1, length;
+ gboolean vertical;
+ gint cy;
- layout = gtk_label_get_measuring_layout (label, layout, width);
+ x0 = width / 2;
+ y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
+ vertical = fabs (y0) > height / 2;
- pango_layout_get_extents (layout, NULL, &logical_rect);
- width = logical_rect.width;
- height = logical_rect.height;
+ if (vertical)
+ {
+ y0 = height/2;
+ x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
+ }
- /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
- * so we try short search for a narrower width that leaves us with the same height
- */
- if (longest_paragraph > 0)
- {
- gint nlines, perfect_width;
+ length = 2 * sqrt (x0 * x0 + y0 * y0);
+ pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
+ pango_layout_get_pixel_size (priv->layout, NULL, &cy);
- nlines = pango_layout_get_line_count (layout);
- perfect_width = (longest_paragraph + nlines - 1) / nlines;
-
- if (perfect_width < width)
- {
- layout = gtk_label_get_measuring_layout (label, layout, perfect_width);
- pango_layout_get_extents (layout, NULL, &logical_rect);
+ x1 = +dy * cy/2;
+ y1 = -dx * cy/2;
- if (logical_rect.height <= height)
- width = logical_rect.width;
- else
- {
- gint mid_width = (perfect_width + width) / 2;
-
- if (mid_width > perfect_width)
+ if (vertical)
{
- layout = gtk_label_get_measuring_layout (label, layout, mid_width);
- pango_layout_get_extents (layout, NULL, &logical_rect);
-
- if (logical_rect.height <= height)
- width = logical_rect.width;
+ y0 = height/2 + y1 - y0;
+ x0 = -y0 * dx/dy;
+ }
+ else
+ {
+ x0 = width/2 + x1 - x0;
+ y0 = -x0 * dy/dx;
}
+
+ length = length - sqrt (x0 * x0 + y0 * y0) * 2;
+ pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
}
}
+ else
+ {
+ pango_layout_set_width (priv->layout, width * PANGO_SCALE);
+ }
+ }
+ else
+ {
+ pango_layout_set_width (priv->layout, -1);
}
-
- return gtk_label_get_measuring_layout (label, layout, width);
}
static void
-gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
+gtk_label_ensure_layout (GtkLabel *label)
{
GtkLabelPrivate *priv = label->priv;
- PangoRectangle logical_rect;
GtkWidget *widget;
gboolean rtl;
if (!priv->layout)
{
- GtkAllocation allocation;
PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
+ PangoAttrList *attrs;
gdouble angle = gtk_label_get_angle (label);
if (angle != 0.0 && !priv->select_info)
priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
- if (priv->effective_attrs)
- pango_layout_set_attributes (priv->layout, priv->effective_attrs);
+ if (priv->select_info && priv->select_info->links)
+ {
+ GdkColor link_color, visited_color;
+ PangoAttribute *attribute;
+ GList *list;
+
+ gtk_label_get_link_colors (widget, &link_color, &visited_color);
+ attrs = pango_attr_list_new ();
+
+ for (list = priv->select_info->links; list; list = list->next)
+ {
+ GtkLabelLink *link = list->data;
+
+ attribute = pango_attr_underline_new (TRUE);
+ attribute->start_index = link->start;
+ attribute->end_index = link->end;
+ pango_attr_list_insert (attrs, attribute);
- gtk_label_rescan_links (label);
+ if (link->visited)
+ attribute = pango_attr_foreground_new (visited_color.red,
+ visited_color.green,
+ visited_color.blue);
+ else
+ attribute = pango_attr_foreground_new (link_color.red,
+ link_color.green,
+ link_color.blue);
+ attribute->start_index = link->start;
+ attribute->end_index = link->end;
+ pango_attr_list_insert (attrs, attribute);
+ }
+ }
+ else if (priv->markup_attrs || priv->attrs)
+ attrs = pango_attr_list_new ();
+ else
+ attrs = NULL;
+
+ if (priv->markup_attrs)
+ {
+ if (attrs)
+ my_pango_attr_list_merge (attrs, priv->markup_attrs);
+ else
+ attrs = pango_attr_list_ref (priv->markup_attrs);
+ }
+
+ if (priv->attrs)
+ {
+ if (attrs)
+ my_pango_attr_list_merge (attrs, priv->attrs);
+ else
+ attrs = pango_attr_list_ref (priv->attrs);
+ }
+
+ if (attrs)
+ {
+ pango_layout_set_attributes (priv->layout, attrs);
+ pango_attr_list_unref (attrs);
+ }
switch (priv->jtype)
{
pango_layout_set_alignment (priv->layout, align);
pango_layout_set_ellipsize (priv->layout, priv->ellipsize);
+ pango_layout_set_wrap (priv->layout, priv->wrap_mode);
pango_layout_set_single_paragraph_mode (priv->layout, priv->single_line_mode);
- gtk_widget_get_allocation (widget, &allocation);
-
- if (priv->ellipsize)
- pango_layout_set_width (priv->layout, allocation.width * PANGO_SCALE);
- else if (priv->wrap)
- {
- GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
- gint longest_paragraph;
- gint width, height;
- gint aux_width = 0;
-
- if ((angle == 90 || angle == 270) && aux_info && aux_info->height > 0)
- aux_width = aux_info->height;
- else if (aux_info && aux_info->width > 0)
- aux_width = aux_info->width;
-
- if (aux_width > 0)
- pango_layout_set_width (priv->layout, aux_width * PANGO_SCALE);
- else if (guess_wrap_width == FALSE && allocation.width > 1 && allocation.height > 1)
- {
- PangoRectangle rect;
- gint xpad, ypad, natural_width;
- gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
-
- if (angle == 90 || angle == 270)
- width = allocation.height - ypad * 2;
- else
- width = allocation.width - xpad * 2;
-
- /* dont set a wrap width wider than the label's natural width
- * incase we're allocated more space than needed */
- pango_layout_get_extents (priv->layout, NULL, &rect);
- natural_width = PANGO_PIXELS (rect.width);
- width = MIN (natural_width, width);
-
- pango_layout_set_wrap (priv->layout, priv->wrap_mode);
- pango_layout_set_width (priv->layout, MAX (width, 1) * PANGO_SCALE);
- }
- else
- {
- GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (label));
- gint wrap_width;
-
- pango_layout_set_width (priv->layout, -1);
- pango_layout_get_extents (priv->layout, NULL, &logical_rect);
-
- width = logical_rect.width;
- /* Try to guess a reasonable maximum width */
- longest_paragraph = width;
-
- wrap_width = get_label_wrap_width (label);
- width = MIN (width, wrap_width);
- width = MIN (width,
- PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
-
- pango_layout_set_width (priv->layout, width);
- pango_layout_get_extents (priv->layout, NULL, &logical_rect);
- width = logical_rect.width;
- height = logical_rect.height;
-
- /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
- * so we try short search for a narrower width that leaves us with the same height
- */
- if (longest_paragraph > 0)
- {
- gint nlines, perfect_width;
-
- nlines = pango_layout_get_line_count (priv->layout);
- perfect_width = (longest_paragraph + nlines - 1) / nlines;
-
- if (perfect_width < width)
- {
- pango_layout_set_width (priv->layout, perfect_width);
- pango_layout_get_extents (priv->layout, NULL, &logical_rect);
-
- if (logical_rect.height <= height)
- width = logical_rect.width;
- else
- {
- gint mid_width = (perfect_width + width) / 2;
-
- if (mid_width > perfect_width)
- {
- pango_layout_set_width (priv->layout, mid_width);
- pango_layout_get_extents (priv->layout, NULL, &logical_rect);
-
- if (logical_rect.height <= height)
- width = logical_rect.width;
- }
- }
- }
- }
- pango_layout_set_width (priv->layout, width);
- }
- }
- else /* !priv->wrap */
- pango_layout_set_width (priv->layout, -1);
+ gtk_label_update_layout_width (label);
}
}
-static gint
-get_single_line_height (GtkWidget *widget,
- PangoLayout *layout)
-{
- PangoContext *context;
- PangoFontMetrics *metrics;
- gint ascent, descent;
-
- context = pango_layout_get_context (layout);
- metrics = get_font_metrics (context, widget);
- ascent = pango_font_metrics_get_ascent (metrics);
- descent = pango_font_metrics_get_descent (metrics);
- pango_font_metrics_unref (metrics);
-
- return ascent + descent;
-}
-
static GtkSizeRequestMode
gtk_label_get_request_mode (GtkWidget *widget)
{
gint *natural_size)
{
PangoLayout *layout;
- GtkWidgetAuxInfo *aux_info =
- _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
- gint aux_size;
gint text_height;
- if (aux_info)
- {
- if (orientation == GTK_ORIENTATION_HORIZONTAL)
- aux_size = aux_info->width;
- else
- aux_size = aux_info->height;
- }
- else
- aux_size = 0;
-
- if (aux_size > 0)
- layout = gtk_label_get_measuring_layout (label, NULL, aux_size * PANGO_SCALE);
- else
- layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE);
+ layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE);
pango_layout_get_pixel_size (layout, NULL, &text_height);
g_object_unref (layout);
}
+static gint
+get_char_pixels (GtkWidget *label,
+ PangoLayout *layout)
+{
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ gint char_width, digit_width;
+
+ context = pango_layout_get_context (layout);
+ metrics = pango_context_get_metrics (context,
+ pango_context_get_font_description (context),
+ 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);
+ pango_font_metrics_unref (metrics);
+
+ return MAX (char_width, digit_width);;
+}
+
static void
-gtk_label_get_preferred_size (GtkWidget *widget,
- GtkOrientation orientation,
- gint *minimum_size,
- gint *natural_size)
+gtk_label_get_preferred_layout_size (GtkLabel *label,
+ PangoRectangle *smallest,
+ PangoRectangle *widest)
{
- GtkLabel *label = GTK_LABEL (widget);
- GtkLabelPrivate *priv = label->priv;
- PangoRectangle required_rect;
- PangoRectangle natural_rect;
- gint xpad, ypad;
+ GtkLabelPrivate *priv = label->priv;
PangoLayout *layout;
+ gint char_pixels;
/* "width-chars" Hard-coded minimum width:
* - minimum size should be MAX (width-chars, strlen ("..."));
* - minimum size should be MAX (width-chars, 0)
* - natural size should be MIN (max-width-chars, strlen (priv->text))
*
+ * For ellipsizing labels; if max-width-chars is specified: either it is used as
+ * a minimum size or the label text as a minimum size (natural size still overflows).
+ *
+ * For wrapping labels; A reasonable minimum size is useful to naturally layout
+ * interfaces automatically. In this case if no "width-chars" is specified, the minimum
+ * width will default to the wrap guess that gtk_label_ensure_layout() does.
*/
- /* When calculating ->wrap sometimes we need to invent a size; Ideally we should be doing
- * that stuff here instead of inside gtk_label_ensure_layout() */
- layout = gtk_label_get_layout_with_guessed_wrap_width (label);
+ /* Start off with the pixel extents of an as-wide-as-possible layout */
+ layout = gtk_label_get_measuring_layout (label, NULL, -1);
- /* Start off with the pixel extents of the rendered layout */
- pango_layout_get_extents (layout, NULL, &required_rect);
- required_rect.x = required_rect.y = 0;
+ if (priv->width_chars > -1 || priv->max_width_chars > -1)
+ char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
+ else
+ char_pixels = 0;
+
+ pango_layout_get_extents (layout, NULL, widest);
+ widest->width = MAX (widest->width, char_pixels * priv->width_chars);
+ widest->x = widest->y = 0;
- if (priv->single_line_mode || priv->wrap)
- required_rect.height = get_single_line_height (GTK_WIDGET (label), layout);
+ if (priv->ellipsize || priv->wrap)
+ {
+ /* a layout with width 0 will be as small as humanly possible */
+ layout = gtk_label_get_measuring_layout (label,
+ layout,
+ priv->width_chars > -1 ? char_pixels * priv->width_chars
+ : 0);
+
+ pango_layout_get_extents (layout, NULL, smallest);
+ smallest->width = MAX (smallest->width, char_pixels * priv->width_chars);
+ smallest->x = smallest->y = 0;
- natural_rect = required_rect;
+ if (priv->max_width_chars > -1 && widest->width > char_pixels * priv->max_width_chars)
+ {
+ layout = gtk_label_get_measuring_layout (label,
+ layout,
+ MAX (smallest->width, char_pixels * priv->max_width_chars));
+ pango_layout_get_extents (layout, NULL, widest);
+ widest->width = MAX (widest->width, char_pixels * priv->width_chars);
+ widest->x = widest->y = 0;
+ }
+ }
+ else
+ {
+ *smallest = *widest;
+ }
+
+ if (widest->width < smallest->width)
+ *smallest = *widest;
+
+ g_object_unref (layout);
+}
+
+static void
+gtk_label_get_preferred_size (GtkWidget *widget,
+ GtkOrientation orientation,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkLabel *label = GTK_LABEL (widget);
+ GtkLabelPrivate *priv = label->priv;
+ PangoRectangle widest_rect;
+ PangoRectangle smallest_rect;
+ GtkBorder border;
- /* Calculate text width itself based on GtkLabel property rules */
- get_label_width (label, layout, &required_rect.width, &natural_rect.width);
+ gtk_label_get_preferred_layout_size (label, &smallest_rect, &widest_rect);
/* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
if (priv->have_transform)
{
- PangoLayout *copy;
PangoContext *context;
const PangoMatrix *matrix;
- copy = pango_layout_copy (priv->layout);
- context = pango_layout_get_context (copy);
+ context = pango_layout_get_context (priv->layout);
matrix = pango_context_get_matrix (context);
- pango_layout_set_width (copy, -1);
- pango_layout_set_ellipsize (copy, PANGO_ELLIPSIZE_NONE);
+ pango_matrix_transform_rectangle (matrix, &widest_rect);
+ pango_matrix_transform_rectangle (matrix, &smallest_rect);
- pango_layout_get_extents (copy, NULL, &natural_rect);
- g_object_unref (copy);
-
- pango_matrix_transform_rectangle (matrix, &required_rect);
- pango_matrix_transform_rectangle (matrix, &natural_rect);
-
- /* Bump the natural size in case of ellipsize to ensure pango has
+ /* Bump the size in case of ellipsize to ensure pango has
* enough space in the angles (note, we could alternatively set the
* layout to not ellipsize when we know we have been allocated our
- * full natural size, or it may be that pango needs a fix here).
+ * full size, or it may be that pango needs a fix here).
*/
if (priv->ellipsize && priv->angle != 0 && priv->angle != 90 &&
priv->angle != 180 && priv->angle != 270 && priv->angle != 360)
/* For some reason we only need this at about 110 degrees, and only
* when gaining in height
*/
- natural_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
- natural_rect.width += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+ widest_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+ widest_rect.width += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+ smallest_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+ smallest_rect.width += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
}
}
- required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
- required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+ widest_rect.width = PANGO_PIXELS_CEIL (widest_rect.width);
+ widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
- natural_rect.width = PANGO_PIXELS_CEIL (natural_rect.width);
- natural_rect.height = PANGO_PIXELS_CEIL (natural_rect.height);
+ smallest_rect.width = PANGO_PIXELS_CEIL (smallest_rect.width);
+ smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
- gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+ _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
*/
get_size_for_allocation (label,
GTK_ORIENTATION_VERTICAL,
- required_rect.height,
+ smallest_rect.height,
minimum_size, natural_size);
}
else
{
/* Normal desired width */
- *minimum_size = required_rect.width;
- *natural_size = natural_rect.width;
+ *minimum_size = smallest_rect.width;
+ *natural_size = widest_rect.width;
}
- *minimum_size += xpad * 2;
- *natural_size += xpad * 2;
+ *minimum_size += border.left + border.right;
+ *natural_size += border.left + border.right;
}
else /* GTK_ORIENTATION_VERTICAL */
{
*/
get_size_for_allocation (label,
GTK_ORIENTATION_HORIZONTAL,
- required_rect.width,
+ widest_rect.width,
minimum_size, natural_size);
}
else
/* A vertically rotated label does w4h, so return the base
* desired height (text length)
*/
- *minimum_size = required_rect.height;
- *natural_size = natural_rect.height;
+ *minimum_size = MIN (smallest_rect.height, widest_rect.height);
+ *natural_size = MAX (smallest_rect.height, widest_rect.height);
}
- *minimum_size += ypad * 2;
- *natural_size += ypad * 2;
+ *minimum_size += border.top + border.bottom;
+ *natural_size += border.top + border.bottom;
}
-
- g_object_unref (layout);
}
-
static void
gtk_label_get_preferred_width (GtkWidget *widget,
gint *minimum_size,
if (priv->wrap && (priv->angle == 90 || priv->angle == 270))
{
- gint xpad, ypad;
+ GtkBorder border;
- gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+ _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
if (priv->wrap)
gtk_label_clear_layout (label);
get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL,
- MAX (1, height - (ypad * 2)),
+ MAX (1, height - border.top - border.bottom),
minimum_width, natural_width);
if (minimum_width)
- *minimum_width += xpad * 2;
+ *minimum_width += border.right + border.left;
if (natural_width)
- *natural_width += xpad * 2;
+ *natural_width += border.right + border.left;
}
else
GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
if (priv->wrap && (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
{
- gint xpad, ypad;
+ GtkBorder border;
- gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+ _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
if (priv->wrap)
gtk_label_clear_layout (label);
get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL,
- MAX (1, width - xpad * 2),
+ MAX (1, width - border.left - border.right),
minimum_height, natural_height);
if (minimum_height)
- *minimum_height += ypad * 2;
+ *minimum_height += border.top + border.bottom;
if (natural_height)
- *natural_height += ypad * 2;
+ *natural_height += border.top + border.bottom;
}
else
GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
- /* The layout may have been recently cleared in get_size_for_orientation(),
- * but the width at that point may not be the same as the allocated width
- */
- if (priv->wrap)
- gtk_label_clear_layout (label);
-
- gtk_label_ensure_layout (label, FALSE);
-
- if (priv->ellipsize)
- {
- if (priv->layout)
- {
- PangoRectangle logical;
- PangoRectangle bounds;
- gint xpad, ypad;
-
- gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
-
- bounds.x = bounds.y = 0;
- bounds.width = allocation->width - xpad * 2;
- bounds.height = allocation->height - ypad * 2;
-
- pango_layout_set_width (priv->layout, -1);
- pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
-
- if (priv->have_transform)
- {
- PangoContext *context = gtk_widget_get_pango_context (widget);
- const PangoMatrix *matrix = pango_context_get_matrix (context);
-
- const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
- const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
- if (fabs (dy) < 0.01)
- {
- if (logical.width > bounds.width)
- pango_layout_set_width (priv->layout, bounds.width * PANGO_SCALE);
- }
- else if (fabs (dx) < 0.01)
- {
- if (logical.width > bounds.height)
- pango_layout_set_width (priv->layout, bounds.height * PANGO_SCALE);
- }
- else
- {
- gdouble x0, y0, x1, y1, length;
- gboolean vertical;
- gint cy;
-
- x0 = bounds.width / 2;
- y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
- vertical = fabs (y0) > bounds.height / 2;
-
- if (vertical)
- {
- y0 = bounds.height/2;
- x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
- }
-
- length = 2 * sqrt (x0 * x0 + y0 * y0);
- pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
- pango_layout_get_pixel_size (priv->layout, NULL, &cy);
-
- x1 = +dy * cy/2;
- y1 = -dx * cy/2;
-
- if (vertical)
- {
- y0 = bounds.height/2 + y1 - y0;
- x0 = -y0 * dx/dy;
- }
- else
- {
- x0 = bounds.width/2 + x1 - x0;
- y0 = -x0 * dy/dx;
- }
-
- length = length - sqrt (x0 * x0 + y0 * y0) * 2;
- pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
- }
- }
- else if (logical.width > bounds.width)
- pango_layout_set_width (priv->layout, bounds.width * PANGO_SCALE);
- }
- }
+ if (priv->layout)
+ gtk_label_update_layout_width (label);
if (priv->select_info && priv->select_info->window)
{
}
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);
-}
-
-static void
-gtk_label_style_updated (GtkWidget *widget)
-{
- GtkLabel *label = GTK_LABEL (widget);
-
- GTK_WIDGET_CLASS (gtk_label_parent_class)->style_updated (widget);
-
- /* We have to clear the layout, fonts etc. may have changed */
- gtk_label_clear_layout (label);
- gtk_label_invalidate_wrap_width (label);
-}
-
-static void
-gtk_label_direction_changed (GtkWidget *widget,
- GtkTextDirection previous_dir)
-{
- GtkLabel *label = GTK_LABEL (widget);
- GtkLabelPrivate *priv = label->priv;
-
- if (priv->layout)
- pango_layout_context_changed (priv->layout);
-
- GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
+ 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
GtkMisc *misc;
GtkWidget *widget;
GtkLabelPrivate *priv;
+ GtkBorder border;
gint req_width, x, y;
gint req_height;
- gint xpad, ypad;
gfloat xalign, yalign;
PangoRectangle logical;
priv = label->priv;
gtk_misc_get_alignment (misc, &xalign, &yalign);
- gtk_misc_get_padding (misc, &xpad, &ypad);
+ _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
xalign = 1.0 - xalign;
req_width = logical.width;
req_height = logical.height;
- req_width += 2 * xpad;
- req_height += 2 * ypad;
+ req_width += border.left + border.right;
+ req_height += border.top + border.bottom;
gtk_widget_get_allocation (widget, &allocation);
- x = floor (allocation.x + xpad + xalign * (allocation.width - req_width));
-
- if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
- x = MAX (x, allocation.x + xpad);
- else
- x = MIN (x, allocation.x + allocation.width - xpad);
-
+ x = floor (allocation.x + border.left + xalign * (allocation.width - req_width) - logical.x);
/* bgo#315462 - For single-line labels, *do* align the requisition with
* respect to the allocation, even if we are under-allocated. For multi-line
* middle". You want to read the first line, at least, to get some context.
*/
if (pango_layout_get_line_count (priv->layout) == 1)
- y = floor (allocation.y + ypad + (allocation.height - req_height) * yalign);
+ y = floor (allocation.y + border.top + (allocation.height - req_height) * yalign) - logical.y;
else
- y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0));
+ y = floor (allocation.y + border.top + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y;
if (xp)
*xp = x;
*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)
{
g_assert (priv->select_info);
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
for (l = pango_layout_get_lines_readonly (priv->layout); l; l = l->next)
{
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, FALSE);
-
- 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)
{
GtkStateFlags state;
gint x, y;
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gtk_render_background (context, cr,
+ 0, 0,
+ allocation.width, allocation.height);
+ gtk_render_frame (context, cr,
+ 0, 0,
+ allocation.width, allocation.height);
if (priv->text && (*priv->text != '\0'))
{
- GdkRGBA *bg_color, *fg_color;
-
get_layout_location (label, &x, &y);
- context = gtk_widget_get_style_context (widget);
- gtk_widget_get_allocation (widget, &allocation);
-
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 (context, state,
- "background-color", &bg_color,
- "color", &fg_color,
- NULL);
+ gtk_style_context_get_color (context, state, &fg_color);
+ gtk_style_context_get_background_color (context, state, &bg_color);
- gdk_cairo_set_source_rgba (cr, bg_color);
+ gdk_cairo_set_source_rgba (cr, &bg_color);
cairo_paint (cr);
- gdk_cairo_set_source_rgba (cr, fg_color);
+ gdk_cairo_set_source_rgba (cr, &fg_color);
cairo_move_to (cr, x, y);
_gtk_pango_fill_layout (cr, priv->layout);
cairo_restore (cr);
cairo_region_destroy (clip);
-
- gdk_rgba_free (bg_color);
- gdk_rgba_free (fg_color);
}
else if (info)
{
cairo_region_t *clip;
GdkRectangle rect;
GdkColor *text_color;
- GdkColor *link_color;
- GdkColor *visited_link_color;
+ GdkColor link_color;
+ GdkColor visited_link_color;
+
+ if (info->selectable &&
+ gtk_widget_has_focus (widget) &&
+ gtk_widget_is_drawable (widget))
+ {
+ PangoDirection cursor_direction;
- if (info->selectable && gtk_widget_has_focus (widget))
- gtk_label_draw_cursor (label, cr, x, y);
+ 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;
+ GdkRGBA bg_color;
range[0] = active_link->start;
range[1] = active_link->end;
gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
if (active_link->visited)
- text_color = visited_link_color;
+ text_color = &visited_link_color;
else
- text_color = link_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 (context, state,
- "background-color", &bg_color,
- NULL);
+ gtk_style_context_get_background_color (context, state, &bg_color);
- gdk_cairo_set_source_rgba (cr, bg_color);
+ gdk_cairo_set_source_rgba (cr, &bg_color);
cairo_paint (cr);
- gdk_cairo_set_source_color (cr, text_color);
+ cairo_set_source_rgb (cr, text_color->red / 65535.,
+ text_color->green / 65535.,
+ text_color->blue / 65535.);
cairo_move_to (cr, x, y);
_gtk_pango_fill_layout (cr, priv->layout);
- gdk_color_free (link_color);
- gdk_color_free (visited_link_color);
- gdk_rgba_free (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);
*index = 0;
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
window_to_layout_coords (label, &x, &y);
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 == GDK_BUTTON_PRIMARY)
{
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 == GDK_BUTTON_PRIMARY)
{
if (!gtk_widget_has_focus (widget))
- {
- priv->in_click = TRUE;
- gtk_widget_grab_focus (widget);
- priv->in_click = FALSE;
- }
+ {
+ priv->in_click = TRUE;
+ gtk_widget_grab_focus (widget);
+ priv->in_click = FALSE;
+ }
if (event->type == GDK_3BUTTON_PRESS)
- {
- gtk_label_select_region_index (label, 0, strlen (priv->text));
- return TRUE;
- }
+ {
+ gtk_label_select_region_index (label, 0, strlen (priv->text));
+ return TRUE;
+ }
if (event->type == GDK_2BUTTON_PRESS)
- {
+ {
info->select_words = TRUE;
- gtk_label_select_word (label);
- return TRUE;
- }
+ gtk_label_select_word (label);
+ return TRUE;
+ }
get_layout_index (label, event->x, event->y, &index);
max = MAX (info->selection_anchor, info->selection_end);
if ((info->selection_anchor != info->selection_end) &&
- (event->state & GDK_SHIFT_MASK))
- {
- /* extend (same as motion) */
- min = MIN (min, index);
- max = MAX (max, index);
+ (event->state & GDK_SHIFT_MASK))
+ {
+ if (index > min && index < max)
+ {
+ /* truncate selection, but keep it as big as possible */
+ if (index - min > max - index)
+ max = index;
+ else
+ min = index;
+ }
+ else
+ {
+ /* extend (same as motion) */
+ min = MIN (min, index);
+ max = MAX (max, index);
+ }
- /* ensure the anchor is opposite index */
- if (index == min)
- {
- gint tmp = min;
- min = max;
- max = tmp;
- }
+ /* ensure the anchor is opposite index */
+ if (index == min)
+ {
+ gint tmp = min;
+ min = max;
+ max = tmp;
+ }
- gtk_label_select_region_index (label, min, max);
- }
+ gtk_label_select_region_index (label, min, max);
+ }
else
- {
- if (event->type == GDK_3BUTTON_PRESS)
- gtk_label_select_region_index (label, 0, strlen (priv->text));
- else if (event->type == GDK_2BUTTON_PRESS)
- gtk_label_select_word (label);
- else if (min < max && min <= index && index <= max)
- {
- info->in_drag = TRUE;
- info->drag_start_x = event->x;
- info->drag_start_y = event->y;
- }
- else
- /* start a replacement */
- gtk_label_select_region_index (label, index, index);
- }
+ {
+ if (event->type == GDK_3BUTTON_PRESS)
+ gtk_label_select_region_index (label, 0, strlen (priv->text));
+ else if (event->type == GDK_2BUTTON_PRESS)
+ gtk_label_select_word (label);
+ else if (min < max && min <= index && index <= max)
+ {
+ info->in_drag = TRUE;
+ info->drag_start_x = event->x;
+ info->drag_start_y = event->y;
+ }
+ else
+ /* start a replacement */
+ gtk_label_select_region_index (label, index, index);
+ }
return TRUE;
}
- else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
- {
- gtk_label_do_popup (label, event);
- return TRUE;
- }
return FALSE;
}
return FALSE;
}
- if (event->button != 1)
+ if (event->button != GDK_BUTTON_PRIMARY)
return FALSE;
if (info->active_link &&
priv->select_info->window = gdk_window_new (gtk_widget_get_window (widget),
&attributes, attributes_mask);
- gdk_window_set_user_data (priv->select_info->window, widget);
+ gtk_widget_register_window (widget, priv->select_info->window);
if (attributes_mask & GDK_WA_CURSOR)
g_object_unref (attributes.cursor);
if (priv->select_info->window == NULL)
return;
- gdk_window_set_user_data (priv->select_info->window, NULL);
+ gtk_widget_unregister_window (GTK_WIDGET (label), priv->select_info->window);
gdk_window_destroy (priv->select_info->window);
priv->select_info->window = NULL;
}
{
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));
}
}
priv = label->priv;
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
return priv->layout;
}
{
g_return_if_fail (GTK_IS_LABEL (label));
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
get_layout_location (label, x, y);
}
{
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));
}
/**
"gtk-split-cursor", &split_cursor,
NULL);
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
pango_layout_get_cursor_pos (priv->layout, index,
&strong_pos, &weak_pos);
gint n_attrs;
gint length;
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
length = g_utf8_strlen (priv->text, -1);
gboolean split_cursor;
gboolean strong;
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
"gtk-split-cursor", &split_cursor,
PangoLogAttr *log_attrs;
gint n_attrs;
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
PangoLogAttr *log_attrs;
gint n_attrs;
- gtk_label_ensure_layout (label, FALSE);
+ gtk_label_ensure_layout (label);
pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
static void
gtk_label_move_cursor (GtkLabel *label,
- GtkMovementStep step,
- gint count,
- gboolean extend_selection)
+ GtkMovementStep step,
+ gint count,
+ gboolean extend_selection)
{
GtkLabelPrivate *priv = label->priv;
gint old_pos;
* start/or end of the selection as appropriate
*/
switch (step)
- {
- case GTK_MOVEMENT_VISUAL_POSITIONS:
- {
- gint end_x, end_y;
- gint anchor_x, anchor_y;
- gboolean end_is_left;
-
- get_better_cursor (label, priv->select_info->selection_end, &end_x, &end_y);
- get_better_cursor (label, priv->select_info->selection_anchor, &anchor_x, &anchor_y);
-
- end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
-
- if (count < 0)
- new_pos = end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
- else
- new_pos = !end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
- break;
- }
- case GTK_MOVEMENT_LOGICAL_POSITIONS:
- case GTK_MOVEMENT_WORDS:
- if (count < 0)
- new_pos = MIN (priv->select_info->selection_end, priv->select_info->selection_anchor);
- else
- new_pos = MAX (priv->select_info->selection_end, priv->select_info->selection_anchor);
- 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 (priv->text);
- break;
- case GTK_MOVEMENT_DISPLAY_LINES:
- case GTK_MOVEMENT_PARAGRAPHS:
- case GTK_MOVEMENT_PAGES:
- case GTK_MOVEMENT_HORIZONTAL_PAGES:
- break;
- }
+ {
+ case GTK_MOVEMENT_VISUAL_POSITIONS:
+ {
+ gint end_x, end_y;
+ gint anchor_x, anchor_y;
+ gboolean end_is_left;
+
+ get_better_cursor (label, priv->select_info->selection_end, &end_x, &end_y);
+ get_better_cursor (label, priv->select_info->selection_anchor, &anchor_x, &anchor_y);
+
+ end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
+
+ if (count < 0)
+ new_pos = end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
+ else
+ new_pos = !end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
+ break;
+ }
+ case GTK_MOVEMENT_LOGICAL_POSITIONS:
+ case GTK_MOVEMENT_WORDS:
+ if (count < 0)
+ new_pos = MIN (priv->select_info->selection_end, priv->select_info->selection_anchor);
+ else
+ new_pos = MAX (priv->select_info->selection_end, priv->select_info->selection_anchor);
+ 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 (priv->text);
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINES:
+ case GTK_MOVEMENT_PARAGRAPHS:
+ case GTK_MOVEMENT_PAGES:
+ case GTK_MOVEMENT_HORIZONTAL_PAGES:
+ break;
+ }
}
else
{
switch (step)
- {
- case GTK_MOVEMENT_LOGICAL_POSITIONS:
- new_pos = gtk_label_move_logically (label, new_pos, count);
- break;
- case GTK_MOVEMENT_VISUAL_POSITIONS:
- new_pos = gtk_label_move_visually (label, new_pos, count);
+ {
+ case GTK_MOVEMENT_LOGICAL_POSITIONS:
+ new_pos = gtk_label_move_logically (label, new_pos, count);
+ break;
+ case GTK_MOVEMENT_VISUAL_POSITIONS:
+ new_pos = gtk_label_move_visually (label, new_pos, count);
if (new_pos == old_pos)
{
if (!extend_selection)
gtk_widget_error_bell (GTK_WIDGET (label));
}
}
- break;
- case GTK_MOVEMENT_WORDS:
- while (count > 0)
- {
- new_pos = gtk_label_move_forward_word (label, new_pos);
- count--;
- }
- while (count < 0)
- {
- new_pos = gtk_label_move_backward_word (label, new_pos);
- count++;
- }
+ break;
+ case GTK_MOVEMENT_WORDS:
+ while (count > 0)
+ {
+ new_pos = gtk_label_move_forward_word (label, new_pos);
+ count--;
+ }
+ 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 (priv->text);
+ 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 (priv->text);
if (new_pos == old_pos)
gtk_widget_error_bell (GTK_WIDGET (label));
- break;
- case GTK_MOVEMENT_DISPLAY_LINES:
- case GTK_MOVEMENT_PARAGRAPHS:
- case GTK_MOVEMENT_PAGES:
- case GTK_MOVEMENT_HORIZONTAL_PAGES:
- break;
- }
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINES:
+ case GTK_MOVEMENT_PARAGRAPHS:
+ case GTK_MOVEMENT_PAGES:
+ case GTK_MOVEMENT_HORIZONTAL_PAGES:
+ break;
+ }
}
if (extend_selection)
gtk_label_select_region_index (label,
- priv->select_info->selection_anchor,
- new_pos);
+ priv->select_info->selection_anchor,
+ new_pos);
else
gtk_label_select_region_index (label, new_pos, new_pos);
}
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)
{
link->visited = TRUE;
/* FIXME: shouldn't have to redo everything here */
- gtk_label_recalculate (label);
+ gtk_label_clear_layout (label);
}
}
*
* Since: 2.18
*/
-G_CONST_RETURN gchar *
+const gchar *
gtk_label_get_current_uri (GtkLabel *label)
{
GtkLabelLink *link;
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;
+}