Back to index

im-sdk  12.3.91
imlabel.c
Go to the documentation of this file.
00001 /* GTK - The GIMP Toolkit
00002  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free
00016  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 /*
00020  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
00021  * file for a list of people on the GTK+ Team.  See the ChangeLog
00022  * files for a list of changes.  These files are distributed with
00023  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
00024  */
00025 
00026 /******************************************************
00027 *Imported from gtk+-2.0 source by phill.zhang@sun.com
00028 ******************************************************/
00029 
00030 #include <math.h>
00031 #include <string.h>
00032 #include <gtk/gtk.h>
00033 #include <gdk/gdkkeysyms.h>
00034 #include <pango/pango.h>
00035 
00036 #include <gtk/gtkmain.h>
00037 #include <gtk/gtkwindow.h>
00038 #include <gtk/gtkclipboard.h>
00039 #include <gtk/gtkimagemenuitem.h>
00040 #include <gtk/gtkseparatormenuitem.h>
00041 #include <gtk/gtkmenuitem.h>
00042 #include <gtk/gtknotebook.h>
00043 #include <gtk/gtkstock.h>
00044 #include <gtk/gtkbindings.h>
00045 
00046 //#include "gtkmarshalers.h"
00047 
00048 #include "imlabel.h"
00049 
00050 #define _(a)  (a)
00051 
00052 struct _ImLabelSelectionInfo
00053 {
00054   GdkWindow *window;
00055   gint selection_anchor;
00056   gint selection_end;
00057   GtkWidget *popup_menu;
00058 };
00059 
00060 enum {
00061   MOVE_CURSOR,
00062   COPY_CLIPBOARD,
00063   POPULATE_POPUP,
00064   LAST_SIGNAL
00065 };
00066 
00067 enum {
00068   PROP_0,
00069   PROP_LABEL,
00070   PROP_ATTRIBUTES,
00071   PROP_USE_MARKUP,
00072   PROP_USE_UNDERLINE,
00073   PROP_JUSTIFY,
00074   PROP_PATTERN,
00075   PROP_WRAP,
00076   PROP_SELECTABLE,
00077   PROP_MNEMONIC_KEYVAL,
00078   PROP_MNEMONIC_WIDGET,
00079   PROP_CURSOR_POSITION,
00080   PROP_SELECTION_BOUND
00081 };
00082 
00083 static guint signals[LAST_SIGNAL] = { 0 };
00084 
00085 static void im_label_class_init        (ImLabelClass    *klass);
00086 static void im_label_init              (ImLabel         *label);
00087 static void im_label_set_property      (GObject          *object, guint prop_id, const GValue *value, GParamSpec *pspec);
00088 static void im_label_get_property      (GObject          *object, guint prop_id, GValue *value, GParamSpec *pspec);
00089 static void im_label_destroy           (GtkObject        *object);
00090 static void im_label_finalize          (GObject          *object);
00091 static void im_label_size_request      (GtkWidget        *widget, GtkRequisition *requisition);
00092 static void im_label_size_allocate     (GtkWidget        *widget, GtkAllocation *allocation);
00093 static void im_label_state_changed     (GtkWidget        *widget, GtkStateType state);
00094 static void im_label_style_set         (GtkWidget        *widget, GtkStyle *previous_style);
00095 static void im_label_direction_changed (GtkWidget        *widget, GtkTextDirection  previous_dir);
00096 static gint im_label_expose            (GtkWidget        *widget,
00097                                          GdkEventExpose   *event);
00098 
00099 static void im_label_realize           (GtkWidget        *widget);
00100 static void im_label_unrealize         (GtkWidget        *widget);
00101 static void im_label_map               (GtkWidget        *widget);
00102 static void im_label_unmap             (GtkWidget        *widget);
00103 static gint im_label_button_press      (GtkWidget        *widget, GdkEventButton *event);
00104 static gint im_label_button_release    (GtkWidget        *widget, GdkEventButton *event);
00105 static gint im_label_motion            (GtkWidget        *widget, GdkEventMotion *event);
00106 
00107 
00108 static void im_label_set_text_internal          (ImLabel      *label, gchar *str);
00109 static void im_label_set_label_internal         (ImLabel      *label, gchar *str);
00110 static void im_label_set_attributes_internal    (ImLabel      *label, PangoAttrList *attrs);
00111 static void im_label_set_pattern_internal       (ImLabel      *label, const gchar *pattern);
00112 static void set_markup                          (ImLabel      *label, const gchar *str, gboolean with_uline);
00113 static void im_label_recalculate                (ImLabel      *label);
00114 
00115 static void im_label_hierarchy_changed          (GtkWidget     *widget, GtkWidget *old_toplevel);
00116 static void im_label_screen_changed             (GtkWidget     *widget, GdkScreen *old_screen);
00117 static gboolean im_label_focus                  (GtkWidget     *widget, GtkDirectionType direction);
00118 
00119 static void im_label_create_window       (ImLabel *label);
00120 static void im_label_destroy_window      (ImLabel *label);
00121 static void im_label_clear_layout        (ImLabel *label);
00122 static void im_label_ensure_layout       (ImLabel *label);
00123 static void im_label_select_region_index (ImLabel *label, gint anchor_index, gint end_index);
00124 
00125 
00126 
00127 /* For selectable lables: */
00128 static void im_label_move_cursor        (ImLabel        *label, GtkMovementStep  step,  gint count, gboolean extend_selection);
00129 static void im_label_copy_clipboard     (ImLabel        *label);
00130 static void im_label_select_all         (ImLabel        *label);
00131 static void im_label_do_popup           (ImLabel        *label, GdkEventButton  *event);
00132 static gint im_label_move_forward_word  (ImLabel        *label, gint start);
00133 static gint im_label_move_backward_word (ImLabel        *label, gint start);
00134 
00135 static GtkMiscClass *parent_class = NULL;
00136 
00137 GType
00138 im_label_get_type (void)
00139 {
00140   static GType label_type = 0;
00141 
00142   if (!label_type)
00143     {
00144       static const GTypeInfo label_info =
00145       {
00146         sizeof (ImLabelClass),
00147         NULL,           /* base_init */
00148         NULL,           /* base_finalize */
00149         (GClassInitFunc) im_label_class_init,
00150         NULL,           /* class_finalize */
00151         NULL,           /* class_data */
00152         sizeof (ImLabel),
00153         32,             /* n_preallocs */
00154         (GInstanceInitFunc) im_label_init,
00155       };
00156 
00157       label_type = g_type_register_static (GTK_TYPE_MISC, "ImLabel",
00158                                            &label_info, 0);
00159     }
00160 
00161   return label_type;
00162 }
00163 
00164 static void
00165 im_label_class_init (ImLabelClass *class)
00166 {
00167   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
00168   GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
00169   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
00170   GtkBindingSet *binding_set;
00171 
00172   parent_class = g_type_class_peek_parent (class);
00173 
00174   gobject_class->set_property = im_label_set_property;
00175   gobject_class->get_property = im_label_get_property;
00176   gobject_class->finalize = im_label_finalize;
00177   object_class->destroy = im_label_destroy;
00178 
00179   widget_class->size_request = im_label_size_request;
00180   widget_class->size_allocate = im_label_size_allocate;
00181   widget_class->state_changed = im_label_state_changed;
00182   widget_class->style_set = im_label_style_set;
00183   widget_class->direction_changed = im_label_direction_changed;
00184   widget_class->expose_event = im_label_expose;
00185   widget_class->realize = im_label_realize;
00186   widget_class->unrealize = im_label_unrealize;
00187   widget_class->map = im_label_map;
00188   widget_class->unmap = im_label_unmap;
00189   widget_class->button_press_event = im_label_button_press;
00190   widget_class->button_release_event = im_label_button_release;
00191   widget_class->motion_notify_event = im_label_motion;
00192   widget_class->screen_changed = im_label_screen_changed;
00193   widget_class->focus = im_label_focus;
00194   widget_class->hierarchy_changed = NULL;//im_label_hierarchy_changed;
00195   widget_class->mnemonic_activate = NULL;//im_label_mnemonic_activate;
00196 
00197   //class->move_cursor = im_label_move_cursor;
00198   //class->copy_clipboard = im_label_copy_clipboard;
00199 
00200 /*
00201   signals[MOVE_CURSOR] =
00202     g_signal_new ("move_cursor",
00203                   G_OBJECT_CLASS_TYPE (gobject_class),
00204                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
00205                   G_STRUCT_OFFSET (ImLabelClass, move_cursor),
00206                   NULL, NULL,
00207                   _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
00208                   G_TYPE_NONE, 3,
00209                   GTK_TYPE_MOVEMENT_STEP,
00210                   G_TYPE_INT,
00211                   G_TYPE_BOOLEAN);
00212 
00213 */
00214 
00215   g_object_class_install_property (gobject_class,
00216                                    PROP_LABEL,
00217                                    g_param_spec_string ("label",
00218                                                         _("Label"),
00219                                                         _("The text of the label"),
00220                                                         NULL,
00221                                                         G_PARAM_READWRITE));
00222   g_object_class_install_property (gobject_class,
00223                                    PROP_ATTRIBUTES,
00224                                    g_param_spec_boxed ("attributes",
00225                                                        _("Attributes"),
00226                                                        _("A list of style attributes to apply to the text of the label"),
00227                                                        PANGO_TYPE_ATTR_LIST,
00228                                                        G_PARAM_READWRITE));
00229   g_object_class_install_property (gobject_class,
00230                                    PROP_USE_MARKUP,
00231                                    g_param_spec_boolean ("use_markup",
00232                                                          _("Use markup"),
00233                                                          _("The text of the label includes XML markup. See pango_parse_markup()"),
00234                                                         FALSE,
00235                                                         G_PARAM_READWRITE));
00236   g_object_class_install_property (gobject_class,
00237                                    PROP_USE_UNDERLINE,
00238                                    g_param_spec_boolean ("use_underline",
00239                                                          _("Use underline"),
00240                                                          _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
00241                                                         FALSE,
00242                                                         G_PARAM_READWRITE));
00243 
00244   g_object_class_install_property (gobject_class,
00245                                    PROP_JUSTIFY,
00246                                    g_param_spec_enum ("justify",
00247                                                       _("Justification"),
00248                                                       _("The alignment of the lines in the text of the label relative to each other. This does NOT affect the alignment of the label within its allocation. See GtkMisc::xalign for that"),
00249                                                       GTK_TYPE_JUSTIFICATION,
00250                                                       GTK_JUSTIFY_LEFT,
00251                                                       G_PARAM_READWRITE));
00252 
00253   g_object_class_install_property (gobject_class,
00254                                    PROP_PATTERN,
00255                                    g_param_spec_string ("pattern",
00256                                                         _("Pattern"),
00257                                                         _("A string with _ characters in positions correspond to characters in the text to underline"),
00258                                                         NULL,
00259                                                         G_PARAM_WRITABLE));
00260 
00261   g_object_class_install_property (gobject_class,
00262                                    PROP_SELECTABLE,
00263                                    g_param_spec_boolean ("selectable",
00264                                                         _("Selectable"),
00265                                                         _("Whether the label text can be selected with the mouse"),
00266                                                         FALSE,
00267                                                         G_PARAM_READWRITE));
00268 
00269   g_object_class_install_property (gobject_class,
00270                                    PROP_CURSOR_POSITION,
00271                                    g_param_spec_int ("cursor_position",
00272                                                      _("Cursor Position"),
00273                                                      _("The current position of the insertion cursor in chars"),
00274                                                      0,
00275                                                      G_MAXINT,
00276                                                      0,
00277                                                      G_PARAM_READABLE));
00278 
00279   g_object_class_install_property (gobject_class,
00280                                    PROP_SELECTION_BOUND,
00281                                    g_param_spec_int ("selection_bound",
00282                                                      _("Selection Bound"),
00283                                                      _("The position of the opposite end of the selection from the cursor in chars"),
00284                                                      0,
00285                                                      G_MAXINT,
00286                                                      0,
00287                                                      G_PARAM_READABLE));
00288 
00289 }
00290 
00291 void
00292 im_label_set_caret_index(ImLabel *label, gint caret)
00293 {
00294     g_return_if_fail (IM_IS_LABEL (label));
00295     
00296     gint  len = im_label_get_text_length(label);
00297     if (caret < 0) caret = 0;
00298     if (caret > len) caret = len;
00299 
00300     if (caret >= 0)
00301         caret = g_utf8_offset_to_pointer (label->text, caret) - label->text;
00302 
00303     if (label->caret_index == caret)
00304         return;
00305 
00306     label->caret_index = caret;
00307     label->showing_caret = label->delay_hide_caret = 1;
00308     gtk_widget_queue_draw(GTK_WIDGET (label));
00309 }
00310 
00311 /*
00312 gint
00313 im_label_get_caret_index(ImLabel *label)
00314 {
00315     return label->caret_index;
00316 }
00317 */
00318 
00319 static void
00320 im_label_set_property (GObject      *object,
00321                         guint         prop_id,
00322                         const GValue *value,
00323                         GParamSpec   *pspec)
00324 {
00325   ImLabel *label;
00326 
00327   label = IM_LABEL (object);
00328 
00329   switch (prop_id)
00330     {
00331     case PROP_LABEL:
00332       im_label_set_label (label, g_value_get_string (value));
00333       break;
00334     case PROP_ATTRIBUTES:
00335       im_label_set_attributes (label, g_value_get_boxed (value));
00336       break;
00337     case PROP_JUSTIFY:
00338       im_label_set_justify (label, g_value_get_enum (value));
00339       break;
00340     case PROP_PATTERN:
00341       im_label_set_pattern (label, g_value_get_string (value));
00342       break;
00343     case PROP_SELECTABLE:
00344       im_label_set_selectable (label, g_value_get_boolean (value));
00345       break;
00346     default:
00347       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00348       break;
00349     }
00350 }
00351 
00352 static void
00353 im_label_get_property (GObject     *object,
00354                         guint        prop_id,
00355                         GValue      *value,
00356                         GParamSpec  *pspec)
00357 {
00358   ImLabel *label;
00359 
00360   label = IM_LABEL (object);
00361 
00362   switch (prop_id)
00363     {
00364     case PROP_LABEL:
00365       g_value_set_string (value, label->label);
00366       break;
00367     case PROP_ATTRIBUTES:
00368       g_value_set_boxed (value, label->attrs);
00369       break;
00370     case PROP_USE_MARKUP:
00371       g_value_set_boolean (value, label->use_markup);
00372       break;
00373     case PROP_USE_UNDERLINE:
00374       break;
00375     case PROP_JUSTIFY:
00376       g_value_set_enum (value, label->jtype);
00377       break;
00378     case PROP_SELECTABLE:
00379       g_value_set_boolean (value, im_label_get_selectable (label));
00380       break;
00381     case PROP_MNEMONIC_WIDGET:
00382       break;
00383     case PROP_CURSOR_POSITION:
00384       if (label->select_info)
00385         {
00386           gint offset = g_utf8_pointer_to_offset (label->text,
00387                                                   label->text + label->select_info->selection_end);
00388           g_value_set_int (value, offset);
00389         }
00390       else
00391         g_value_set_int (value, 0);
00392       break;
00393     case PROP_SELECTION_BOUND:
00394       if (label->select_info)
00395         {
00396           gint offset = g_utf8_pointer_to_offset (label->text,
00397                                                   label->text + label->select_info->selection_anchor);
00398           g_value_set_int (value, offset);
00399         }
00400       else
00401         g_value_set_int (value, 0);
00402       break;
00403 
00404     default:
00405       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00406       break;
00407     }
00408 }
00409 
00410 static void
00411 im_label_init (ImLabel *label)
00412 {
00413   GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
00414 
00415   label->label = NULL;
00416 
00417   label->jtype = GTK_JUSTIFY_LEFT;
00418 
00419   label->use_markup = TRUE;
00420 
00421   label->caret_index = -1;
00422   label->timer_id = -1;
00423   label->showing_caret = 1;
00424   label->delay_hide_caret = 0;
00425 
00426   label->layout = NULL;
00427   label->text = NULL;
00428   label->attrs = NULL;
00429 
00430   im_label_set_text (label, "");
00431 }
00432 
00433 static gint
00434 ImLabelCaretBlink(gpointer data)
00435 {
00436 
00437     g_return_if_fail (IM_IS_LABEL (data));
00438     ImLabel* label = IM_LABEL(data);
00439 
00440     if (label->showing_caret != 0) {
00441         if (label->delay_hide_caret == 0)
00442             label->showing_caret = 0;
00443         else {
00444             label->delay_hide_caret = 0;
00445             return;
00446         }
00447     } else {
00448         label->showing_caret = 1;
00449     }
00450     label->delay_hide_caret = 0;
00451     gtk_widget_queue_draw(GTK_WIDGET(label));
00452     return 1;
00453 }
00454 
00464 GtkWidget*
00465 im_label_new (const gchar *str)
00466 {
00467   ImLabel *label;
00468 
00469   label = g_object_new (IM_TYPE_LABEL, NULL);
00470 
00471   label->caret_index = -1;
00472   label->timer_id = -1;
00473   label->showing_caret = 1;
00474   label->delay_hide_caret = 0;
00475 
00476   if (str && *str)
00477       im_label_set_text (label, str);
00478 
00479   label->timer_id = gtk_timeout_add(600, (GtkFunction)ImLabelCaretBlink, label);
00480 
00481   return GTK_WIDGET (label);
00482 }
00483 
00484 static void
00485 im_label_screen_changed (GtkWidget *widget,
00486                           GdkScreen *old_screen)
00487 {
00488   im_label_clear_layout (IM_LABEL (widget));
00489 }
00490 
00491 static void
00492 im_label_set_text_internal (ImLabel *label,
00493                              gchar    *str)
00494 {
00495   g_free (label->text);
00496 
00497   label->text = str;
00498 
00499   im_label_select_region_index (label, 0, 0);
00500 }
00501 
00502 static void
00503 im_label_set_label_internal (ImLabel *label,
00504                               gchar    *str)
00505 {
00506   g_free (label->label);
00507 
00508   label->label = str;
00509 
00510   g_object_notify (G_OBJECT (label), "label");
00511 }
00512 
00513 static void
00514 im_label_set_attributes_internal (ImLabel      *label,
00515                                    PangoAttrList *attrs)
00516 {
00517   if (attrs)
00518     pango_attr_list_ref (attrs);
00519 
00520   if (label->attrs)
00521     pango_attr_list_unref (label->attrs);
00522 
00523   if (!label->use_markup)
00524     {
00525       pango_attr_list_ref (attrs);
00526       if (label->effective_attrs)
00527         pango_attr_list_unref (label->effective_attrs);
00528       label->effective_attrs = attrs;
00529     }
00530 
00531   label->attrs = attrs;
00532   g_object_notify (G_OBJECT (label), "attributes");
00533 }
00534 
00535 
00536 /* Calculates text, attrs and mnemonic_keyval from
00537  * label, use_underline and use_markup
00538  */
00539 static void
00540 im_label_recalculate (ImLabel *label)
00541 {
00542   if (label->use_markup)
00543     set_markup (label, label->label, 0);
00544   else {
00545     im_label_set_text_internal (label, g_strdup (label->label));
00546     if (label->attrs)
00547       pango_attr_list_ref (label->attrs);
00548     if (label->effective_attrs)
00549       pango_attr_list_unref (label->effective_attrs);
00550     label->effective_attrs = label->attrs;
00551   }
00552 
00553   im_label_clear_layout (label);
00554   gtk_widget_queue_resize (GTK_WIDGET (label));
00555 }
00556 
00567 void
00568 im_label_set_text (ImLabel    *label,
00569                     const gchar *str)
00570 {
00571   g_return_if_fail (IM_IS_LABEL (label));
00572 
00573   g_object_freeze_notify (G_OBJECT (label));
00574 
00575   im_label_set_label_internal (label, g_strdup (str ? str : ""));
00576 
00577   im_label_recalculate (label);
00578 
00579   g_object_thaw_notify (G_OBJECT (label));
00580 }
00581 
00592 void
00593 im_label_set_attributes (ImLabel         *label,
00594                           PangoAttrList    *attrs)
00595 {
00596   g_return_if_fail (IM_IS_LABEL (label));
00597 
00598   im_label_set_attributes_internal (label, attrs);
00599 
00600   im_label_clear_layout (label);
00601   gtk_widget_queue_resize (GTK_WIDGET (label));
00602 }
00603 
00617 PangoAttrList *
00618 im_label_get_attributes (ImLabel *label)
00619 {
00620   g_return_val_if_fail (IM_IS_LABEL (label), NULL);
00621 
00622   return label->attrs;
00623 }
00624 
00634 void
00635 im_label_set_label (ImLabel    *label,
00636                      const gchar *str)
00637 {
00638   g_return_if_fail (IM_IS_LABEL (label));
00639   g_return_if_fail (str != NULL);
00640 
00641   im_label_set_label_internal (label, g_strdup (str));
00642   im_label_recalculate (label);
00643 }
00644 
00656 G_CONST_RETURN gchar *
00657 im_label_get_label (ImLabel *label)
00658 {
00659   g_return_val_if_fail (IM_IS_LABEL (label), NULL);
00660 
00661   return label->label;
00662 }
00663 
00664 static void
00665 set_markup (ImLabel    *label,
00666             const gchar *str,
00667             gboolean     with_uline)
00668 {
00669   gchar *text = NULL;
00670   GError *error = NULL;
00671   PangoAttrList *attrs = NULL;
00672   gunichar accel_char = 0;
00673 
00674   if (!pango_parse_markup (str,
00675                            -1,
00676                            with_uline ? '_' : 0,
00677                            &attrs,
00678                            &text,
00679                            with_uline ? &accel_char : NULL,
00680                            &error))
00681     {
00682       g_warning ("Failed to set label from markup due to error parsing markup: %s",
00683                  error->message);
00684       g_error_free (error);
00685       return;
00686     }
00687 
00688   if (text)
00689     im_label_set_text_internal (label, text);
00690 
00691   if (attrs)
00692     {
00693       if (label->effective_attrs)
00694         pango_attr_list_unref (label->effective_attrs);
00695       label->effective_attrs = attrs;
00696     }
00697 }
00698 
00707 void
00708 im_label_set_markup (ImLabel *label, const gchar *str)
00709 {
00710   g_return_if_fail (IM_IS_LABEL (label));
00711 
00712   im_label_set_label_internal (label, g_strdup (str ? str : ""));
00713   im_label_recalculate (label);
00714 }
00715 
00727 G_CONST_RETURN gchar *
00728 im_label_get_text (ImLabel *label)
00729 {
00730   g_return_val_if_fail (IM_IS_LABEL (label), NULL);
00731 
00732   return label->text;
00733 }
00734 
00735 static PangoAttrList *
00736 im_label_pattern_to_attrs (ImLabel      *label,
00737                             const gchar   *pattern)
00738 {
00739   const char *start;
00740   const char *p = label->text;
00741   const char *q = pattern;
00742   PangoAttrList *attrs;
00743 
00744   attrs = pango_attr_list_new ();
00745 
00746   while (1)
00747     {
00748       while (*p && *q && *q != '_')
00749         {
00750           p = g_utf8_next_char (p);
00751           q++;
00752         }
00753       start = p;
00754       while (*p && *q && *q == '_')
00755         {
00756           p = g_utf8_next_char (p);
00757           q++;
00758         }
00759 
00760       if (p > start)
00761         {
00762           PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
00763           attr->start_index = start - label->text;
00764           attr->end_index = p - label->text;
00765 
00766           pango_attr_list_insert (attrs, attr);
00767         }
00768       else
00769         break;
00770     }
00771 
00772   return attrs;
00773 }
00774 
00775 static void
00776 im_label_set_pattern_internal (ImLabel    *label,
00777                                 const gchar *pattern)
00778 {
00779   PangoAttrList *attrs;
00780   g_return_if_fail (IM_IS_LABEL (label));
00781 
00782   attrs = im_label_pattern_to_attrs (label, pattern);
00783 
00784   if (label->effective_attrs)
00785     pango_attr_list_unref (label->effective_attrs);
00786   label->effective_attrs = attrs;
00787 }
00788 
00789 void
00790 im_label_set_pattern (ImLabel         *label,
00791                        const gchar *pattern)
00792 {
00793   g_return_if_fail (IM_IS_LABEL (label));
00794 
00795   im_label_set_pattern_internal (label, pattern);
00796 
00797   im_label_clear_layout (label);
00798   gtk_widget_queue_resize (GTK_WIDGET (label));
00799 }
00800 
00801 
00814 void
00815 im_label_set_justify (ImLabel        *label,
00816                        GtkJustification jtype)
00817 {
00818   g_return_if_fail (IM_IS_LABEL (label));
00819   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
00820 
00821   if ((GtkJustification) label->jtype != jtype)
00822     {
00823       label->jtype = jtype;
00824 
00825       /* No real need to be this drastic, but easier than duplicating the code */
00826       im_label_clear_layout (label);
00827 
00828       g_object_notify (G_OBJECT (label), "justify");
00829       gtk_widget_queue_resize (GTK_WIDGET (label));
00830     }
00831 }
00832 
00841 GtkJustification
00842 im_label_get_justify (ImLabel *label)
00843 {
00844   g_return_val_if_fail (IM_IS_LABEL (label), 0);
00845 
00846   return label->jtype;
00847 }
00848 
00849 static void
00850 im_label_destroy (GtkObject *object)
00851 {
00852   ImLabel *label = IM_LABEL (object);
00853   GTK_OBJECT_CLASS (parent_class)->destroy (object);
00854 }
00855 
00856 static void
00857 im_label_finalize (GObject *object)
00858 {
00859   ImLabel *label;
00860 
00861   g_return_if_fail (IM_IS_LABEL (object));
00862 
00863   label = IM_LABEL (object);
00864 
00865   if (label->timer_id > 0)
00866       gtk_timeout_remove(label->timer_id);
00867 
00868   g_free (label->label);
00869   g_free (label->text);
00870 
00871   if (label->layout)
00872     g_object_unref (label->layout);
00873 
00874   if (label->attrs)
00875     pango_attr_list_unref (label->attrs);
00876 
00877   if (label->effective_attrs)
00878     pango_attr_list_unref (label->effective_attrs);
00879 
00880   g_free (label->select_info);
00881 
00882   G_OBJECT_CLASS (parent_class)->finalize (object);
00883 }
00884 
00885 static void
00886 im_label_clear_layout (ImLabel *label)
00887 {
00888   if (label->layout)
00889     {
00890       g_object_unref (label->layout);
00891       label->layout = NULL;
00892     }
00893 }
00894 
00895 typedef struct _ImLabelWrapWidth ImLabelWrapWidth;
00896 struct _ImLabelWrapWidth
00897 {
00898   gint width;
00899   PangoFontDescription *font_desc;
00900 };
00901 
00902 static void
00903 label_wrap_width_free (gpointer data)
00904 {
00905   ImLabelWrapWidth *wrap_width = data;
00906   pango_font_description_free (wrap_width->font_desc);
00907   g_free (wrap_width);
00908 }
00909 
00910 static gint
00911 get_label_wrap_width (ImLabel *label)
00912 {
00913   PangoLayout *layout;
00914   GtkStyle *style = GTK_WIDGET (label)->style;
00915 
00916   ImLabelWrapWidth *wrap_width = g_object_get_data (G_OBJECT (style), "gtk-label-wrap-width");
00917   if (!wrap_width)
00918     {
00919       wrap_width = g_new0 (ImLabelWrapWidth, 1);
00920       g_object_set_data_full (G_OBJECT (style), "gtk-label-wrap-width",
00921                               wrap_width, label_wrap_width_free);
00922     }
00923 
00924   if (wrap_width->font_desc && pango_font_description_equal (wrap_width->font_desc, style->font_desc))
00925     return wrap_width->width;
00926 
00927   if (wrap_width->font_desc)
00928     pango_font_description_free (wrap_width->font_desc);
00929 
00930   wrap_width->font_desc = pango_font_description_copy (style->font_desc);
00931 
00932   layout = gtk_widget_create_pango_layout (GTK_WIDGET (label),
00933                                            "This long string gives a good enough length for any line to have.");
00934   pango_layout_get_size (layout, &wrap_width->width, NULL);
00935   g_object_unref (layout);
00936 
00937   return wrap_width->width;
00938 }
00939 
00940 static void
00941 im_label_ensure_layout (ImLabel *label)
00942 {
00943   GtkWidget *widget;
00944   PangoRectangle logical_rect;
00945   gint rwidth, rheight;
00946 
00947   widget = GTK_WIDGET (label);
00948 
00949   rwidth = label->misc.xpad * 2;
00950   rheight = label->misc.ypad * 2;
00951 
00952   if (!label->layout)
00953     {
00954       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
00955 
00956       label->layout = gtk_widget_create_pango_layout (widget, label->text);
00957 
00958       if (label->effective_attrs)
00959         pango_layout_set_attributes (label->layout, label->effective_attrs);
00960 
00961       switch (label->jtype)
00962         {
00963         case GTK_JUSTIFY_LEFT:
00964           align = PANGO_ALIGN_LEFT;
00965           break;
00966         case GTK_JUSTIFY_RIGHT:
00967           align = PANGO_ALIGN_RIGHT;
00968           break;
00969         case GTK_JUSTIFY_CENTER:
00970           align = PANGO_ALIGN_CENTER;
00971           break;
00972         case GTK_JUSTIFY_FILL:
00973           /* FIXME: This just doesn't work to do this */
00974           align = PANGO_ALIGN_LEFT;
00975           pango_layout_set_justify (label->layout, TRUE);
00976           break;
00977         default:
00978           g_assert_not_reached();
00979         }
00980 
00981       pango_layout_set_alignment (label->layout, align);
00982       pango_layout_set_width (label->layout, -1);
00983     }
00984 }
00985 
00986 static void
00987 im_label_size_request (GtkWidget      *widget,
00988                         GtkRequisition *requisition)
00989 {
00990   ImLabel *label;
00991   gint width, height;
00992   PangoRectangle logical_rect;
00993   GtkWidgetAuxInfo *aux_info;
00994 
00995   g_return_if_fail (IM_IS_LABEL (widget));
00996   g_return_if_fail (requisition != NULL);
00997 
00998   label = IM_LABEL (widget);
00999 
01000   im_label_ensure_layout (label);
01001 
01002   width = label->misc.xpad * 2;
01003   height = label->misc.ypad * 2;
01004 
01005   pango_layout_get_extents (label->layout, NULL, &logical_rect);
01006 
01007   //aux_info = _gtk_widget_get_aux_info (widget, FALSE);
01008   width += PANGO_PIXELS (logical_rect.width);
01009 
01010   height += PANGO_PIXELS (logical_rect.height);
01011 
01012   if (width < 100)
01013       width = 100;
01014   requisition->width = width;
01015   requisition->height = height;
01016 }
01017 
01018 static void
01019 im_label_size_allocate (GtkWidget     *widget,
01020                          GtkAllocation *allocation)
01021 {
01022   ImLabel *label;
01023 
01024   label = IM_LABEL (widget);
01025 
01026   (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
01027 
01028   if (label->select_info && label->select_info->window)
01029     {
01030       gdk_window_move_resize (label->select_info->window,
01031                               allocation->x,
01032                               allocation->y,
01033                               allocation->width,
01034                               allocation->height);
01035     }
01036 }
01037 
01038 static void
01039 im_label_state_changed (GtkWidget   *widget,
01040                          GtkStateType prev_state)
01041 {
01042   ImLabel *label;
01043 
01044   label = IM_LABEL (widget);
01045 
01046   if (label->select_info)
01047     im_label_select_region (label, 0, 0);
01048 
01049   if (GTK_WIDGET_CLASS (parent_class)->state_changed)
01050     GTK_WIDGET_CLASS (parent_class)->state_changed (widget, prev_state);
01051 }
01052 
01053 static void
01054 im_label_style_set (GtkWidget *widget,
01055                      GtkStyle  *previous_style)
01056 {
01057   ImLabel *label;
01058 
01059   g_return_if_fail (IM_IS_LABEL (widget));
01060 
01061   label = IM_LABEL (widget);
01062 
01063   /* We have to clear the layout, fonts etc. may have changed */
01064   im_label_clear_layout (label);
01065 }
01066 
01067 static void
01068 im_label_direction_changed (GtkWidget        *widget,
01069                              GtkTextDirection previous_dir)
01070 {
01071   ImLabel *label = IM_LABEL (widget);
01072 
01073   if (label->layout)
01074     pango_layout_context_changed (label->layout);
01075 
01076   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
01077 }
01078 
01079 #if 0
01080 static void
01081 im_label_paint_word (ImLabel     *label,
01082                       gint          x,
01083                       gint          y,
01084                       ImLabelWord *word,
01085                       GdkRectangle *area)
01086 {
01087   GtkWidget *widget = GTK_WIDGET (label);
01088   ImLabelULine *uline;
01089   gchar *tmp_str;
01090 
01091   tmp_str = gdk_wcstombs (word->beginning);
01092   if (tmp_str)
01093     {
01094       gtk_paint_string (widget->style, widget->window, widget->state,
01095                         area, widget, "label",
01096                         x + word->x,
01097                         y + word->y,
01098                         tmp_str);
01099       g_free (tmp_str);
01100     }
01101 
01102   for (uline = word->uline; uline; uline = uline->next)
01103     gtk_paint_hline (widget->style, widget->window,
01104                      widget->state, area,
01105                      widget, "label",
01106                      x + uline->x1, x + uline->x2, y + uline->y);
01107 }
01108 #endif
01109 
01110 static void
01111 get_layout_location (ImLabel  *label,
01112                      gint      *xp,
01113                      gint      *yp)
01114 {
01115   GtkMisc *misc;
01116   GtkWidget *widget;
01117   gfloat xalign;
01118   gint x, y;
01119 
01120   misc = GTK_MISC (label);
01121   widget = GTK_WIDGET (label);
01122 
01123   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
01124     xalign = misc->xalign;
01125   else
01126     xalign = 1.0 - misc->xalign;
01127 
01128   x = floor (widget->allocation.x + (gint)misc->xpad +
01129              xalign * (widget->allocation.width - widget->requisition.width)
01130              + 0.5);
01131 
01132   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
01133     x = MAX (x, widget->allocation.x + misc->xpad);
01134   else
01135     x = MIN (x,
01136              widget->allocation.x + widget->allocation.width -
01137              widget->requisition.width - misc->xpad);
01138 
01139   y = floor (widget->allocation.y + (gint)misc->ypad
01140              + MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign)
01141                     + 0.5, 0));
01142 
01143   if (xp)
01144     *xp = x;
01145 
01146   if (yp)
01147     *yp = y;
01148 }
01149 
01150 void
01151 im_label_get_caret_rect(ImLabel *label, int idx, int*px, int*py, int*pw, int*ph)
01152 {
01153     if (GTK_WIDGET_DRAWABLE (label)) {
01154         GtkWidget *widget = GTK_WIDGET (label);
01155 
01156         gint  len = im_label_get_text_length(label);
01157         if (idx < 0) idx = 0;
01158         if (idx > len) idx = len;
01159 
01160         if (idx >= 0)
01161             idx = g_utf8_offset_to_pointer (label->text, idx) - label->text;
01162 
01163         GtkTextDirection keymap_direction;
01164         GtkTextDirection widget_direction;
01165         PangoRectangle strong_pos, weak_pos;
01166         gboolean split_cursor;
01167         PangoRectangle *cursor1 = NULL;
01168         PangoRectangle *cursor2 = NULL;
01169         GdkRectangle cursor_location;
01170         GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
01171         GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
01172         GdkGC *gc;
01173 
01174 
01175         keymap_direction = GTK_TEXT_DIR_RTL;
01176         if (gdk_keymap_get_direction(gdk_keymap_get_for_display(gtk_widget_get_display (widget))) == PANGO_DIRECTION_LTR)
01177             keymap_direction = GTK_TEXT_DIR_LTR;
01178 
01179         widget_direction = gtk_widget_get_direction (widget);
01180         im_label_ensure_layout (label);
01181 
01182         //pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end, &strong_pos, &weak_pos);
01183         pango_layout_get_cursor_pos (label->layout, idx, &strong_pos, &weak_pos);
01184 
01185         g_object_get (gtk_widget_get_settings (widget), "gtk-split-cursor", &split_cursor, NULL);
01186 
01187         dir1 = widget_direction;
01188 
01189         if (split_cursor) {
01190             cursor1 = &strong_pos;
01191 
01192             if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y) {
01193                 dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
01194                 cursor2 = &weak_pos;
01195             }
01196 
01197         } else {
01198             if (keymap_direction == widget_direction)
01199               cursor1 = &strong_pos;
01200             else
01201               cursor1 = &weak_pos;
01202         }
01203 
01204         *px = PANGO_PIXELS (cursor1->x);
01205         *py = PANGO_PIXELS (cursor1->y);
01206         *pw = 2;
01207         *ph = PANGO_PIXELS (cursor1->height);
01208     }
01209 }
01210 
01211 static void
01212 im_label_draw_cursor (ImLabel *label, gint xoffset, gint yoffset)
01213 {
01214     //if (label->select_info == NULL) return;
01215 
01216     if (GTK_WIDGET_DRAWABLE (label)) {
01217         GtkWidget *widget = GTK_WIDGET (label);
01218 
01219         GtkTextDirection keymap_direction;
01220         GtkTextDirection widget_direction;
01221         PangoRectangle strong_pos, weak_pos;
01222         gboolean split_cursor;
01223         PangoRectangle *cursor1 = NULL;
01224         PangoRectangle *cursor2 = NULL;
01225         GdkRectangle cursor_location;
01226         GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
01227         GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
01228         GdkGC *gc;
01229 
01230 
01231         keymap_direction = GTK_TEXT_DIR_RTL;
01232         if (gdk_keymap_get_direction(gdk_keymap_get_for_display(gtk_widget_get_display (widget))) == PANGO_DIRECTION_LTR)
01233             keymap_direction = GTK_TEXT_DIR_LTR;
01234 
01235         widget_direction = gtk_widget_get_direction (widget);
01236         im_label_ensure_layout (label);
01237 
01238         //pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end, &strong_pos, &weak_pos);
01239         pango_layout_get_cursor_pos (label->layout, label->caret_index, &strong_pos, &weak_pos);
01240 
01241         g_object_get (gtk_widget_get_settings (widget), "gtk-split-cursor", &split_cursor, NULL);
01242 
01243         dir1 = widget_direction;
01244 
01245         if (split_cursor) {
01246             cursor1 = &strong_pos;
01247 
01248             if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y) {
01249                 dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
01250                 cursor2 = &weak_pos;
01251             }
01252 
01253         } else {
01254             if (keymap_direction == widget_direction)
01255               cursor1 = &strong_pos;
01256             else
01257               cursor1 = &weak_pos;
01258         }
01259 
01260         cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
01261         cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
01262         cursor_location.width = 0;
01263         cursor_location.height = PANGO_PIXELS (cursor1->height);
01264 
01265 #if (GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4))
01266             gtk_draw_insertion_cursor(widget, widget->window, NULL, &cursor_location, FALSE, dir1, dir2 != GTK_TEXT_DIR_NONE);
01267 #else
01268             gc = _gtk_get_insertion_cursor_gc (widget, TRUE);
01269             _gtk_draw_insertion_cursor (widget, widget->window, gc, &cursor_location, dir1, dir2 != GTK_TEXT_DIR_NONE);
01270             g_object_unref (gc);
01271 
01272 
01273             if (dir2 != GTK_TEXT_DIR_NONE) {
01274                 cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
01275                 cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
01276                 cursor_location.width = 0;
01277                 cursor_location.height = PANGO_PIXELS (cursor2->height);
01278 
01279                 gc = _gtk_get_insertion_cursor_gc (widget, FALSE);
01280                 _gtk_draw_insertion_cursor (widget, widget->window, gc,
01281                                             &cursor_location, dir2, TRUE);
01282                 g_object_unref (gc);
01283             }
01284 #endif
01285     }
01286 }
01287 
01288 
01289 static gint
01290 im_label_expose (GtkWidget *widget, GdkEventExpose *event)
01291 {
01292     ImLabel *label;
01293     gint x, y;
01294 
01295     g_return_val_if_fail (IM_IS_LABEL (widget), FALSE);
01296     g_return_val_if_fail (event != NULL, FALSE);
01297 
01298     label = IM_LABEL (widget);
01299 
01300     im_label_ensure_layout (label);
01301 
01302     if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) && label->text && (*label->text != '\0')) {
01303         get_layout_location (label, &x, &y);
01304         gtk_paint_layout (widget->style, widget->window, GTK_WIDGET_STATE (widget),
01305                           FALSE, &event->area, widget, "label", x, y, label->layout);
01306 
01307         if (label->select_info && (label->select_info->selection_anchor != label->select_info->selection_end)) {
01308             gint range[2];
01309             GdkRegion *clip;
01310             GtkStateType state;
01311 
01312             range[0] = label->select_info->selection_anchor;
01313             range[1] = label->select_info->selection_end;
01314 
01315             if (range[0] > range[1]) {
01316                 gint tmp = range[0];
01317                 range[0] = range[1];
01318                 range[1] = tmp;
01319             }
01320 
01321             clip = gdk_pango_layout_get_clip_region (label->layout, x, y, range, 1);
01322 
01323             /* FIXME should use gtk_paint, but it can't use a clip region */
01324             gdk_gc_set_clip_region (widget->style->black_gc, clip);
01325 
01326 
01327             state = GTK_STATE_SELECTED;
01328             if (!GTK_WIDGET_HAS_FOCUS (widget))
01329               state = GTK_STATE_ACTIVE;
01330 
01331             gdk_draw_layout_with_colors (widget->window, widget->style->black_gc, x, y, label->layout,
01332                                          &widget->style->text[state],&widget->style->base[state]);
01333 
01334             gdk_gc_set_clip_region (widget->style->black_gc, NULL);
01335             gdk_region_destroy (clip);
01336 
01337         } else if (label->caret_index >= 0 && label->showing_caret != 0) {
01338           im_label_draw_cursor(label, x, y);
01339         }
01340     }
01341 
01342     return FALSE;
01343 }
01344 
01345 
01346 static void
01347 im_label_realize (GtkWidget *widget)
01348 {
01349   ImLabel *label;
01350 
01351   label = IM_LABEL (widget);
01352 
01353   (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
01354 
01355   if (label->select_info)
01356     im_label_create_window (label);
01357 }
01358 
01359 static void
01360 im_label_unrealize (GtkWidget *widget)
01361 {
01362   ImLabel *label;
01363 
01364   label = IM_LABEL (widget);
01365 
01366   if (label->select_info)
01367     im_label_destroy_window (label);
01368 
01369   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
01370 }
01371 
01372 static void
01373 im_label_map (GtkWidget *widget)
01374 {
01375   ImLabel *label;
01376 
01377   label = IM_LABEL (widget);
01378 
01379   (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
01380 
01381   if (label->select_info)
01382     gdk_window_show (label->select_info->window);
01383 }
01384 
01385 static void
01386 im_label_unmap (GtkWidget *widget)
01387 {
01388   ImLabel *label;
01389 
01390   label = IM_LABEL (widget);
01391 
01392   if (label->select_info)
01393     gdk_window_hide (label->select_info->window);
01394 
01395   (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
01396 }
01397 
01398 static void
01399 window_to_layout_coords (ImLabel *label,
01400                          gint     *x,
01401                          gint     *y)
01402 {
01403   gint lx, ly;
01404   GtkWidget *widget;
01405 
01406   widget = GTK_WIDGET (label);
01407 
01408   /* get layout location in widget->window coords */
01409   get_layout_location (label, &lx, &ly);
01410 
01411   if (x)
01412     {
01413       *x += widget->allocation.x; /* go to widget->window */
01414       *x -= lx;                   /* go to layout */
01415     }
01416 
01417   if (y)
01418     {
01419       *y += widget->allocation.y; /* go to widget->window */
01420       *y -= ly;                   /* go to layout */
01421     }
01422 }
01423 
01424 #if 0
01425 static void
01426 layout_to_window_coords (ImLabel *label,
01427                          gint     *x,
01428                          gint     *y)
01429 {
01430   gint lx, ly;
01431   GtkWidget *widget;
01432 
01433   widget = GTK_WIDGET (label);
01434 
01435   /* get layout location in widget->window coords */
01436   get_layout_location (label, &lx, &ly);
01437 
01438   if (x)
01439     {
01440       *x += lx;                   /* go to widget->window */
01441       *x -= widget->allocation.x; /* go to selection window */
01442     }
01443 
01444   if (y)
01445     {
01446       *y += ly;                   /* go to widget->window */
01447       *y -= widget->allocation.y; /* go to selection window */
01448     }
01449 }
01450 #endif
01451 
01452 static void
01453 get_layout_index (ImLabel *label,
01454                   gint      x,
01455                   gint      y,
01456                   gint     *index)
01457 {
01458   gint trailing = 0;
01459   const gchar *cluster;
01460   const gchar *cluster_end;
01461 
01462   *index = 0;
01463 
01464   im_label_ensure_layout (label);
01465 
01466   window_to_layout_coords (label, &x, &y);
01467 
01468   x *= PANGO_SCALE;
01469   y *= PANGO_SCALE;
01470 
01471   pango_layout_xy_to_index (label->layout,
01472                             x, y,
01473                             index, &trailing);
01474 
01475 
01476   cluster = label->text + *index;
01477   cluster_end = cluster;
01478   while (trailing)
01479     {
01480       cluster_end = g_utf8_next_char (cluster_end);
01481       --trailing;
01482     }
01483 
01484   *index += (cluster_end - cluster);
01485 }
01486 
01487 static void
01488 im_label_select_word (ImLabel *label)
01489 {
01490   gint min, max;
01491 
01492   gint start_index = im_label_move_backward_word (label, label->select_info->selection_end);
01493   gint end_index = im_label_move_forward_word (label, label->select_info->selection_end);
01494 
01495   min = MIN (label->select_info->selection_anchor,
01496              label->select_info->selection_end);
01497   max = MAX (label->select_info->selection_anchor,
01498              label->select_info->selection_end);
01499 
01500   min = MIN (min, start_index);
01501   max = MAX (max, end_index);
01502 
01503   im_label_select_region_index (label, min, max);
01504 }
01505 
01506 static gint
01507 im_label_button_press (GtkWidget      *widget,
01508                         GdkEventButton *event)
01509 {
01510   ImLabel *label;
01511   gint index = 0;
01512 
01513   label = IM_LABEL (widget);
01514 
01515   if (label->select_info == NULL)
01516     return FALSE;
01517 
01518   if (event->button == 1)
01519     {
01520       if (!GTK_WIDGET_HAS_FOCUS (widget))
01521         gtk_widget_grab_focus (widget);
01522 
01523       if (event->type == GDK_3BUTTON_PRESS)
01524         {
01525           im_label_select_region_index (label, 0, strlen (label->text));
01526           return TRUE;
01527         }
01528 
01529       if (event->type == GDK_2BUTTON_PRESS)
01530         {
01531           im_label_select_word (label);
01532           return TRUE;
01533         }
01534 
01535       get_layout_index (label, event->x, event->y, &index);
01536 
01537       if ((label->select_info->selection_anchor !=
01538            label->select_info->selection_end) &&
01539           (event->state & GDK_SHIFT_MASK))
01540         {
01541           gint min, max;
01542 
01543           /* extend (same as motion) */
01544           min = MIN (label->select_info->selection_anchor,
01545                      label->select_info->selection_end);
01546           max = MAX (label->select_info->selection_anchor,
01547                      label->select_info->selection_end);
01548 
01549           min = MIN (min, index);
01550           max = MAX (max, index);
01551 
01552           /* ensure the anchor is opposite index */
01553           if (index == min)
01554             {
01555               gint tmp = min;
01556               min = max;
01557               max = tmp;
01558             }
01559 
01560           im_label_select_region_index (label, min, max);
01561         }
01562       else
01563         {
01564           if (event->type == GDK_3BUTTON_PRESS)
01565               im_label_select_region_index (label, 0, strlen (label->text));
01566           else if (event->type == GDK_2BUTTON_PRESS)
01567               im_label_select_word (label);
01568           else
01569             /* start a replacement */
01570             im_label_select_region_index (label, index, index);
01571         }
01572 
01573       return TRUE;
01574     }
01575   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
01576     {
01577       //im_label_do_popup (label, event);
01578 
01579       return TRUE;
01580 
01581     }
01582   return FALSE;
01583 }
01584 
01585 static gint
01586 im_label_button_release (GtkWidget      *widget,
01587                           GdkEventButton *event)
01588 
01589 {
01590   ImLabel *label;
01591 
01592   label = IM_LABEL (widget);
01593 
01594   if (label->select_info == NULL)
01595     return FALSE;
01596 
01597   if (event->button != 1)
01598     return FALSE;
01599 
01600   /* The goal here is to return TRUE iff we ate the
01601    * button press to start selecting.
01602    */
01603 
01604   return TRUE;
01605 }
01606 
01607 static gint
01608 im_label_motion (GtkWidget      *widget,
01609                   GdkEventMotion *event)
01610 {
01611   ImLabel *label;
01612   gint index;
01613   gint x, y;
01614 
01615   label = IM_LABEL (widget);
01616 
01617   if (label->select_info == NULL)
01618     return FALSE;
01619 
01620   if ((event->state & GDK_BUTTON1_MASK) == 0)
01621     return FALSE;
01622 
01623   gdk_window_get_pointer (label->select_info->window,
01624                           &x, &y, NULL);
01625 
01626   get_layout_index (label, x, y, &index);
01627 
01628   im_label_select_region_index (label,
01629                                  label->select_info->selection_anchor,
01630                                  index);
01631 
01632   return TRUE;
01633 }
01634 
01635 static void
01636 im_label_create_window (ImLabel *label)
01637 {
01638   GtkWidget *widget;
01639   GdkWindowAttr attributes;
01640   gint attributes_mask;
01641 
01642   g_assert (label->select_info);
01643   g_assert (GTK_WIDGET_REALIZED (label));
01644 
01645   if (label->select_info->window)
01646     return;
01647 
01648   widget = GTK_WIDGET (label);
01649 
01650   attributes.x = widget->allocation.x;
01651   attributes.y = widget->allocation.y;
01652   attributes.width = widget->allocation.width;
01653   attributes.height = widget->allocation.height;
01654   attributes.window_type = GDK_WINDOW_TEMP;
01655   attributes.wclass = GDK_INPUT_ONLY;
01656   attributes.override_redirect = TRUE;
01657   attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
01658                                                   GDK_XTERM);
01659   attributes.event_mask = gtk_widget_get_events (widget) |
01660     GDK_BUTTON_PRESS_MASK        |
01661     GDK_BUTTON_RELEASE_MASK      |
01662     GDK_BUTTON_MOTION_MASK;
01663 
01664   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR | GDK_WA_CURSOR;
01665 
01666   label->select_info->window = gdk_window_new (widget->window,
01667                                                &attributes, attributes_mask);
01668   gdk_window_set_user_data (label->select_info->window, widget);
01669 
01670   gdk_cursor_unref (attributes.cursor);
01671 }
01672 
01673 static void
01674 im_label_destroy_window (ImLabel *label)
01675 {
01676   g_assert (label->select_info);
01677 
01678   if (label->select_info->window == NULL)
01679     return;
01680 
01681   gdk_window_set_user_data (label->select_info->window, NULL);
01682   gdk_window_destroy (label->select_info->window);
01683   label->select_info->window = NULL;
01684 }
01685 
01695 void
01696 im_label_set_selectable (ImLabel *label,
01697                           gboolean  setting)
01698 {
01699   gboolean old_setting;
01700 
01701   g_return_if_fail (IM_IS_LABEL (label));
01702 
01703   setting = setting != FALSE;
01704   old_setting = label->select_info != NULL;
01705 
01706   if (setting)
01707     {
01708       if (label->select_info == NULL)
01709         {
01710           label->select_info = g_new0 (ImLabelSelectionInfo, 1);
01711 
01712           GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
01713 
01714           if (GTK_WIDGET_REALIZED (label))
01715             im_label_create_window (label);
01716 
01717           if (GTK_WIDGET_MAPPED (label))
01718             gdk_window_show (label->select_info->window);
01719         }
01720     }
01721   else
01722     {
01723       if (label->select_info)
01724         {
01725           /* unselect, to give up the selection */
01726           im_label_select_region (label, 0, 0);
01727 
01728           if (label->select_info->window)
01729             {
01730               im_label_destroy_window (label);
01731             }
01732 
01733           g_free (label->select_info);
01734 
01735           label->select_info = NULL;
01736 
01737           GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
01738         }
01739     }
01740   if (setting != old_setting)
01741     {
01742       g_object_freeze_notify (G_OBJECT (label));
01743       g_object_notify (G_OBJECT (label), "selectable");
01744       g_object_notify (G_OBJECT (label), "cursor_position");
01745       g_object_notify (G_OBJECT (label), "selection_bound");
01746       g_object_thaw_notify (G_OBJECT (label));
01747       gtk_widget_queue_draw (GTK_WIDGET (label));
01748     }
01749 }
01750 
01759 gboolean
01760 im_label_get_selectable (ImLabel *label)
01761 {
01762   g_return_val_if_fail (IM_IS_LABEL (label), FALSE);
01763 
01764   return label->select_info != NULL;
01765 }
01766 
01767 static void
01768 get_text_callback (GtkClipboard     *clipboard,
01769                    GtkSelectionData *selection_data,
01770                    guint             info,
01771                    gpointer          user_data_or_owner)
01772 {
01773   ImLabel *label;
01774 
01775   label = IM_LABEL (user_data_or_owner);
01776 
01777   if ((label->select_info->selection_anchor !=
01778        label->select_info->selection_end) &&
01779       label->text)
01780     {
01781       gint start, end;
01782       gint len;
01783 
01784       start = MIN (label->select_info->selection_anchor,
01785                    label->select_info->selection_end);
01786       end = MAX (label->select_info->selection_anchor,
01787                  label->select_info->selection_end);
01788 
01789       len = strlen (label->text);
01790 
01791       if (end > len)
01792         end = len;
01793 
01794       if (start > len)
01795         start = len;
01796 
01797       gtk_selection_data_set_text (selection_data,
01798                                    label->text + start,
01799                                    end - start);
01800     }
01801 }
01802 
01803 static void
01804 clear_text_callback (GtkClipboard     *clipboard,
01805                      gpointer          user_data_or_owner)
01806 {
01807   ImLabel *label;
01808 
01809   label = IM_LABEL (user_data_or_owner);
01810 
01811   if (label->select_info)
01812     {
01813       label->select_info->selection_anchor = label->select_info->selection_end;
01814 
01815       gtk_widget_queue_draw (GTK_WIDGET (label));
01816     }
01817 }
01818 
01819 static void
01820 im_label_select_region_index (ImLabel *label,
01821                                gint      anchor_index,
01822                                gint      end_index)
01823 {
01824   static const GtkTargetEntry targets[] = {
01825     { "STRING", 0, 0 },
01826     { "TEXT",   0, 0 },
01827     { "COMPOUND_TEXT", 0, 0 },
01828     { "UTF8_STRING", 0, 0 }
01829   };
01830 
01831   g_return_if_fail (IM_IS_LABEL (label));
01832 
01833   if (label->select_info)
01834     {
01835       GtkClipboard *clipboard;
01836 
01837       if (label->select_info->selection_anchor == anchor_index &&
01838           label->select_info->selection_end == end_index)
01839         return;
01840 
01841       label->select_info->selection_anchor = anchor_index;
01842       label->select_info->selection_end = end_index;
01843 
01844       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
01845                                             GDK_SELECTION_PRIMARY);
01846 
01847       if (anchor_index != end_index)
01848         {
01849           gtk_clipboard_set_with_owner (clipboard,
01850                                         targets,
01851                                         G_N_ELEMENTS (targets),
01852                                         get_text_callback,
01853                                         clear_text_callback,
01854                                         G_OBJECT (label));
01855         }
01856       else
01857         {
01858           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
01859             gtk_clipboard_clear (clipboard);
01860         }
01861 
01862       gtk_widget_queue_draw (GTK_WIDGET (label));
01863 
01864       g_object_freeze_notify (G_OBJECT (label));
01865       g_object_notify (G_OBJECT (label), "cursor_position");
01866       g_object_notify (G_OBJECT (label), "selection_bound");
01867       g_object_thaw_notify (G_OBJECT (label));
01868     }
01869 }
01870 
01883 void
01884 im_label_select_region  (ImLabel *label,
01885                           gint      start_offset,
01886                           gint      end_offset)
01887 {
01888   g_return_if_fail (IM_IS_LABEL (label));
01889 
01890   if (label->text && label->select_info)
01891     {
01892       if (start_offset < 0)
01893         start_offset = g_utf8_strlen (label->text, -1);
01894 
01895       if (end_offset < 0)
01896         end_offset = g_utf8_strlen (label->text, -1);
01897 
01898       im_label_select_region_index (label,
01899                                      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
01900                                      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
01901     }
01902 }
01903 
01915 gboolean
01916 im_label_get_selection_bounds (ImLabel  *label,
01917                                 gint      *start,
01918                                 gint      *end)
01919 {
01920   g_return_val_if_fail (IM_IS_LABEL (label), FALSE);
01921 
01922   if (label->select_info == NULL)
01923     {
01924       /* not a selectable label */
01925       if (start)
01926         *start = 0;
01927       if (end)
01928         *end = 0;
01929 
01930       return FALSE;
01931     }
01932   else
01933     {
01934       gint start_index, end_index;
01935       gint start_offset, end_offset;
01936       gint len;
01937 
01938       start_index = MIN (label->select_info->selection_anchor,
01939                    label->select_info->selection_end);
01940       end_index = MAX (label->select_info->selection_anchor,
01941                  label->select_info->selection_end);
01942 
01943       len = strlen (label->text);
01944 
01945       if (end_index > len)
01946         end_index = len;
01947 
01948       if (start_index > len)
01949         start_index = len;
01950 
01951       start_offset = g_utf8_strlen (label->text, start_index);
01952       end_offset = g_utf8_strlen (label->text, end_index);
01953 
01954       if (start_offset > end_offset)
01955         {
01956           gint tmp = start_offset;
01957           start_offset = end_offset;
01958           end_offset = tmp;
01959         }
01960 
01961       if (start)
01962         *start = start_offset;
01963 
01964       if (end)
01965         *end = end_offset;
01966 
01967       return start_offset != end_offset;
01968     }
01969 }
01970 
01971 
01984 PangoLayout*
01985 im_label_get_layout (ImLabel *label)
01986 {
01987   g_return_val_if_fail (IM_IS_LABEL (label), NULL);
01988 
01989   im_label_ensure_layout (label);
01990 
01991   return label->layout;
01992 }
01993 
02010 void
02011 im_label_get_layout_offsets (ImLabel *label, gint *x, gint *y)
02012 {
02013   g_return_if_fail (IM_IS_LABEL (label));
02014   get_layout_location (label, x, y);
02015 }
02016 
02017 static gboolean
02018 im_label_focus (GtkWidget *widget,  GtkDirectionType   direction)
02019 {
02020   /* We never want to be in the tab chain */
02021   return FALSE;
02022 }
02023 
02024 /* Compute the X position for an offset that corresponds to the "more important
02025  * cursor position for that offset. We use this when trying to guess to which
02026  * end of the selection we should go to when the user hits the left or
02027  * right arrow key.
02028  */
02029 static void
02030 get_better_cursor (ImLabel *label, gint index, gint *x, gint *y)
02031 {
02032   GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
02033   GtkTextDirection keymap_direction =
02034     (gdk_keymap_get_direction (keymap) == PANGO_DIRECTION_LTR) ?
02035     GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
02036   GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (label));
02037   gboolean split_cursor;
02038   PangoRectangle strong_pos, weak_pos;
02039 
02040   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
02041                 "gtk-split-cursor", &split_cursor,
02042                 NULL);
02043 
02044   im_label_ensure_layout (label);
02045 
02046   pango_layout_get_cursor_pos (label->layout, index,
02047                                &strong_pos, &weak_pos);
02048 
02049   if (split_cursor)
02050     {
02051       *x = strong_pos.x / PANGO_SCALE;
02052       *y = strong_pos.y / PANGO_SCALE;
02053     }
02054   else
02055     {
02056       if (keymap_direction == widget_direction)
02057         {
02058           *x = strong_pos.x / PANGO_SCALE;
02059           *y = strong_pos.y / PANGO_SCALE;
02060         }
02061       else
02062         {
02063           *x = weak_pos.x / PANGO_SCALE;
02064           *y = weak_pos.y / PANGO_SCALE;
02065         }
02066     }
02067 }
02068 
02069 
02070 static gint
02071 im_label_move_logically (ImLabel *label,
02072                           gint      start,
02073                           gint      count)
02074 {
02075   gint offset = g_utf8_pointer_to_offset (label->text,
02076                                           label->text + start);
02077 
02078   if (label->text)
02079     {
02080       PangoLogAttr *log_attrs;
02081       gint n_attrs;
02082       gint length;
02083 
02084       im_label_ensure_layout (label);
02085 
02086       length = g_utf8_strlen (label->text, -1);
02087 
02088       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
02089 
02090       while (count > 0 && offset < length)
02091         {
02092           do
02093             offset++;
02094           while (offset < length && !log_attrs[offset].is_cursor_position);
02095 
02096           count--;
02097         }
02098       while (count < 0 && offset > 0)
02099         {
02100           do
02101             offset--;
02102           while (offset > 0 && !log_attrs[offset].is_cursor_position);
02103 
02104           count++;
02105         }
02106 
02107       g_free (log_attrs);
02108     }
02109 
02110   return g_utf8_offset_to_pointer (label->text, offset) - label->text;
02111 }
02112 
02113 static gint
02114 im_label_move_visually (ImLabel *label,
02115                          gint      start,
02116                          gint      count)
02117 {
02118   gint index;
02119 
02120   index = start;
02121 
02122   while (count != 0)
02123     {
02124       int new_index, new_trailing;
02125       gboolean split_cursor;
02126       gboolean strong;
02127 
02128       im_label_ensure_layout (label);
02129 
02130       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
02131                     "gtk-split-cursor", &split_cursor,
02132                     NULL);
02133 
02134       if (split_cursor)
02135         strong = TRUE;
02136       else
02137         {
02138           GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
02139           GtkTextDirection keymap_direction =
02140             (gdk_keymap_get_direction (keymap) == PANGO_DIRECTION_LTR) ?
02141             GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
02142 
02143           strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (label));
02144         }
02145 
02146       if (count > 0)
02147         {
02148           pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
02149           count--;
02150         }
02151       else
02152         {
02153           pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
02154           count++;
02155         }
02156 
02157       if (new_index < 0 || new_index == G_MAXINT)
02158         break;
02159 
02160       index = new_index;
02161 
02162       while (new_trailing--)
02163         index = g_utf8_next_char (label->text + new_index) - label->text;
02164     }
02165 
02166   return index;
02167 }
02168 
02169 static gint
02170 im_label_move_forward_word (ImLabel *label,
02171                              gint      start)
02172 {
02173   gint new_pos = g_utf8_pointer_to_offset (label->text,
02174                                            label->text + start);
02175   gint length;
02176 
02177   length = g_utf8_strlen (label->text, -1);
02178   if (new_pos < length)
02179     {
02180       PangoLogAttr *log_attrs;
02181       gint n_attrs;
02182 
02183       im_label_ensure_layout (label);
02184 
02185       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
02186 
02187       /* Find the next word end */
02188       new_pos++;
02189       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
02190         new_pos++;
02191 
02192       g_free (log_attrs);
02193     }
02194 
02195   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
02196 }
02197 
02198 gint
02199 im_label_get_text_length(ImLabel *label)
02200 {
02201     if (label->text) {
02202         return g_utf8_strlen (label->text, -1);
02203     }
02204     return 0;
02205 }
02206 
02207 static gint
02208 im_label_move_backward_word (ImLabel *label,
02209                               gint      start)
02210 {
02211   gint new_pos = g_utf8_pointer_to_offset (label->text,
02212                                            label->text + start);
02213   gint length;
02214 
02215   length = g_utf8_strlen (label->text, -1);
02216 
02217   if (new_pos > 0)
02218     {
02219       PangoLogAttr *log_attrs;
02220       gint n_attrs;
02221 
02222       im_label_ensure_layout (label);
02223 
02224       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
02225 
02226       new_pos -= 1;
02227 
02228       /* Find the previous word beginning */
02229       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
02230         new_pos--;
02231 
02232       g_free (log_attrs);
02233     }
02234 
02235   return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
02236 }
02237 
02238 static void
02239 im_label_move_cursor (ImLabel       *label,
02240                        GtkMovementStep step,
02241                        gint            count,
02242                        gboolean        extend_selection)
02243 {
02244   gint new_pos;
02245 
02246   if (label->select_info == NULL)
02247     return;
02248 
02249   new_pos = label->select_info->selection_end;
02250 
02251   if (label->select_info->selection_end != label->select_info->selection_anchor &&
02252       !extend_selection)
02253     {
02254       /* If we have a current selection and aren't extending it, move to the
02255        * start/or end of the selection as appropriate
02256        */
02257       switch (step)
02258         {
02259         case GTK_MOVEMENT_VISUAL_POSITIONS:
02260           {
02261             gint end_x, end_y;
02262             gint anchor_x, anchor_y;
02263             gboolean end_is_left;
02264 
02265             get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y);
02266             get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y);
02267 
02268             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
02269 
02270             if (count < 0)
02271               new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
02272             else
02273               new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
02274 
02275             break;
02276           }
02277         case GTK_MOVEMENT_LOGICAL_POSITIONS:
02278         case GTK_MOVEMENT_WORDS:
02279           if (count < 0)
02280             new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor);
02281           else
02282             new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor);
02283           break;
02284         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
02285         case GTK_MOVEMENT_PARAGRAPH_ENDS:
02286         case GTK_MOVEMENT_BUFFER_ENDS:
02287           /* FIXME: Can do better here */
02288           new_pos = count < 0 ? 0 : strlen (label->text);
02289           break;
02290         case GTK_MOVEMENT_DISPLAY_LINES:
02291         case GTK_MOVEMENT_PARAGRAPHS:
02292         case GTK_MOVEMENT_PAGES:
02293           break;
02294         }
02295     }
02296   else
02297     {
02298       switch (step)
02299         {
02300         case GTK_MOVEMENT_LOGICAL_POSITIONS:
02301           new_pos = im_label_move_logically (label, new_pos, count);
02302           break;
02303         case GTK_MOVEMENT_VISUAL_POSITIONS:
02304           new_pos = im_label_move_visually (label, new_pos, count);
02305           break;
02306         case GTK_MOVEMENT_WORDS:
02307           while (count > 0)
02308             {
02309               new_pos = im_label_move_forward_word (label, new_pos);
02310               count--;
02311             }
02312           while (count < 0)
02313             {
02314               new_pos = im_label_move_backward_word (label, new_pos);
02315               count++;
02316             }
02317           break;
02318         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
02319         case GTK_MOVEMENT_PARAGRAPH_ENDS:
02320         case GTK_MOVEMENT_BUFFER_ENDS:
02321           /* FIXME: Can do better here */
02322           new_pos = count < 0 ? 0 : strlen (label->text);
02323           break;
02324         case GTK_MOVEMENT_DISPLAY_LINES:
02325         case GTK_MOVEMENT_PARAGRAPHS:
02326         case GTK_MOVEMENT_PAGES:
02327           break;
02328         }
02329     }
02330 
02331   if (extend_selection)
02332     im_label_select_region_index (label,
02333                                    label->select_info->selection_anchor,
02334                                    new_pos);
02335   else
02336     im_label_select_region_index (label, new_pos, new_pos);
02337 }
02338 
02339 static void
02340 im_label_copy_clipboard (ImLabel *label)
02341 {
02342   if (label->text && label->select_info)
02343     {
02344       gint start, end;
02345       gint len;
02346 
02347       start = MIN (label->select_info->selection_anchor,
02348                    label->select_info->selection_end);
02349       end = MAX (label->select_info->selection_anchor,
02350                  label->select_info->selection_end);
02351 
02352       len = strlen (label->text);
02353 
02354       if (end > len)
02355         end = len;
02356 
02357       if (start > len)
02358         start = len;
02359 
02360       if (start != end)
02361         gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (label),
02362                                                           GDK_SELECTION_CLIPBOARD),
02363                                 label->text + start, end - start);
02364     }
02365 }
02366 
02367 static void
02368 im_label_select_all (ImLabel *label)
02369 {
02370   im_label_select_region_index (label, 0, strlen (label->text));
02371 }
02372