]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktextview.c
Make sure anchored children get size allocated, even if the layout is
[~andy/gtk] / gtk / gtktextview.c
index c79c136a659986cb8f397647054304732cb0d4b7..50efb41bdb4989514a7128532b75cac743662eb4 100644 (file)
@@ -24,6 +24,7 @@
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
  */
 
+#include <config.h>
 #include <string.h>
 
 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
@@ -42,6 +43,7 @@
 #include "gtktextview.h"
 #include "gtkimmulticontext.h"
 #include "gdk/gdkkeysyms.h"
+#include "gtkprivate.h"
 #include "gtksizegroup.h"          /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=72258 */
 #include "gtktextutil.h"
 #include "gtkwindow.h"
@@ -140,6 +142,7 @@ enum
   PROP_CURSOR_VISIBLE,
   PROP_BUFFER,
   PROP_OVERWRITE,
+  PROP_ACCEPTS_TAB,
   LAST_PROP
 };
 
@@ -344,7 +347,7 @@ static void gtk_text_view_remove (GtkContainer *container,
                                   GtkWidget    *child);
 static void gtk_text_view_forall (GtkContainer *container,
                                   gboolean      include_internals,
-                                 GtkCallback   callback,
+                                  GtkCallback   callback,
                                   gpointer      callback_data);
 
 /* FIXME probably need the focus methods. */
@@ -551,8 +554,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_PIXELS_ABOVE_LINES,
                                    g_param_spec_int ("pixels_above_lines",
-                                                    _("Pixels Above Lines"),
-                                                    _("Pixels of blank space above paragraphs"),
+                                                    P_("Pixels Above Lines"),
+                                                    P_("Pixels of blank space above paragraphs"),
                                                     0,
                                                     G_MAXINT,
                                                     0,
@@ -561,8 +564,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_PIXELS_BELOW_LINES,
                                    g_param_spec_int ("pixels_below_lines",
-                                                    _("Pixels Below Lines"),
-                                                    _("Pixels of blank space below paragraphs"),
+                                                    P_("Pixels Below Lines"),
+                                                    P_("Pixels of blank space below paragraphs"),
                                                     0,
                                                     G_MAXINT,
                                                     0,
@@ -571,8 +574,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_PIXELS_INSIDE_WRAP,
                                    g_param_spec_int ("pixels_inside_wrap",
-                                                    _("Pixels Inside Wrap"),
-                                                    _("Pixels of blank space between wrapped lines in a paragraph"),
+                                                    P_("Pixels Inside Wrap"),
+                                                    P_("Pixels of blank space between wrapped lines in a paragraph"),
                                                     0,
                                                     G_MAXINT,
                                                     0,
@@ -581,16 +584,16 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_EDITABLE,
                                    g_param_spec_boolean ("editable",
-                                                        _("Editable"),
-                                                        _("Whether the text can be modified by the user"),
+                                                        P_("Editable"),
+                                                        P_("Whether the text can be modified by the user"),
                                                         TRUE,
                                                         G_PARAM_READWRITE));
 
   g_object_class_install_property (gobject_class,
                                    PROP_WRAP_MODE,
                                    g_param_spec_enum ("wrap_mode",
-                                                     _("Wrap Mode"),
-                                                     _("Whether to wrap lines never, at word boundaries, or at character boundaries"),
+                                                     P_("Wrap Mode"),
+                                                     P_("Whether to wrap lines never, at word boundaries, or at character boundaries"),
                                                      GTK_TYPE_WRAP_MODE,
                                                      GTK_WRAP_NONE,
                                                      G_PARAM_READWRITE));
@@ -598,8 +601,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_JUSTIFICATION,
                                    g_param_spec_enum ("justification",
-                                                     _("Justification"),
-                                                     _("Left, right, or center justification"),
+                                                     P_("Justification"),
+                                                     P_("Left, right, or center justification"),
                                                      GTK_TYPE_JUSTIFICATION,
                                                      GTK_JUSTIFY_LEFT,
                                                      G_PARAM_READWRITE));
@@ -607,8 +610,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_LEFT_MARGIN,
                                    g_param_spec_int ("left_margin",
-                                                    _("Left Margin"),
-                                                    _("Width of the left margin in pixels"),
+                                                    P_("Left Margin"),
+                                                    P_("Width of the left margin in pixels"),
                                                     0,
                                                     G_MAXINT,
                                                     0,
@@ -617,8 +620,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_RIGHT_MARGIN,
                                    g_param_spec_int ("right_margin",
-                                                    _("Right Margin"),
-                                                    _("Width of the right margin in pixels"),
+                                                    P_("Right Margin"),
+                                                    P_("Width of the right margin in pixels"),
                                                     0,
                                                     G_MAXINT,
                                                     0,
@@ -627,8 +630,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_INDENT,
                                    g_param_spec_int ("indent",
-                                                    _("Indent"),
-                                                    _("Amount to indent the paragraph, in pixels"),
+                                                    P_("Indent"),
+                                                    P_("Amount to indent the paragraph, in pixels"),
                                                     0,
                                                     G_MAXINT,
                                                     0,
@@ -637,35 +640,52 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   g_object_class_install_property (gobject_class,
                                    PROP_TABS,
                                    g_param_spec_boxed ("tabs",
-                                                       _("Tabs"),
-                                                       _("Custom tabs for this text"),
+                                                       P_("Tabs"),
+                                                       P_("Custom tabs for this text"),
                                                        PANGO_TYPE_TAB_ARRAY,
                                                       G_PARAM_READWRITE));
 
   g_object_class_install_property (gobject_class,
                                    PROP_CURSOR_VISIBLE,
                                    g_param_spec_boolean ("cursor_visible",
-                                                        _("Cursor Visible"),
-                                                        _("If the insertion cursor is shown"),
+                                                        P_("Cursor Visible"),
+                                                        P_("If the insertion cursor is shown"),
                                                         TRUE,
                                                         G_PARAM_READWRITE));
 
   g_object_class_install_property (gobject_class,
                                    PROP_BUFFER,
                                    g_param_spec_object ("buffer",
-                                                       _("Buffer"),
-                                                       _("The buffer which is displayed"),
+                                                       P_("Buffer"),
+                                                       P_("The buffer which is displayed"),
                                                        GTK_TYPE_TEXT_BUFFER,
                                                        G_PARAM_READWRITE));
 
   g_object_class_install_property (gobject_class,
                                    PROP_OVERWRITE,
                                    g_param_spec_boolean ("overwrite",
-                                                        _("Overwrite mode"),
-                                                        _("Whether entered text overwrites existing contents"),
+                                                        P_("Overwrite mode"),
+                                                        P_("Whether entered text overwrites existing contents"),
                                                         FALSE,
                                                         G_PARAM_READWRITE));
 
+  g_object_class_install_property (gobject_class,
+                                   PROP_ACCEPTS_TAB,
+                                   g_param_spec_boolean ("accepts_tab",
+                                                        P_("Accepts tab"),
+                                                        P_("Whether Tab will result in a tab character being entered"),
+                                                        TRUE,
+                                                        G_PARAM_READWRITE));
+
+  /*
+   * Style properties
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                          g_param_spec_boxed ("error-underline-color",
+                                                              P_("Error underline color"),
+                                                              P_("Color with which to draw error-indication underlines"),
+                                                              GDK_TYPE_COLOR,
+                                                              G_PARAM_READABLE));
   
   /*
    * Signals
@@ -1051,6 +1071,8 @@ gtk_text_view_init (GtkTextView *text_view)
 
   text_view->cursor_visible = TRUE;
 
+  text_view->accepts_tab = TRUE;
+
   text_view->text_window = text_window_new (GTK_TEXT_WINDOW_TEXT,
                                             widget, 200, 200);
 
@@ -2592,6 +2614,10 @@ gtk_text_view_set_property (GObject         *object,
       gtk_text_view_set_buffer (text_view, GTK_TEXT_BUFFER (g_value_get_object (value)));
       break;
 
+    case PROP_ACCEPTS_TAB:
+      gtk_text_view_set_accepts_tab (text_view, g_value_get_boolean (value));
+      break;
+      
     default:
       g_assert_not_reached ();
       break;
@@ -2661,6 +2687,10 @@ gtk_text_view_get_property (GObject         *object,
     case PROP_OVERWRITE:
       g_value_set_boolean (value, text_view->overwrite_mode);
       break;
+
+    case PROP_ACCEPTS_TAB:
+      g_value_set_boolean (value, text_view->accepts_tab);
+      break;
       
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2848,6 +2878,19 @@ gtk_text_view_allocate_children (GtkTextView *text_view)
                                                     &child_loc,
                                                     child->anchor);
 
+         /* Since anchored children are only ever allocated from
+           * gtk_text_layout_get_line_display() we have to make sure
+          * that the display line caching in the layout doesn't 
+           * get in the way. Invalidating the layout around the anchor
+           * achieves this.
+          */ 
+         if (GTK_WIDGET_ALLOC_NEEDED (child->widget))
+           {
+             GtkTextIter end = child_loc;
+             gtk_text_iter_forward_char (&end);
+             gtk_text_layout_invalidate (text_view->layout, &child_loc, &end);
+           }
+
           gtk_text_layout_validate_yrange (text_view->layout,
                                            &child_loc,
                                            0, 1);
@@ -3795,10 +3838,10 @@ gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
             event->keyval == GDK_KP_Tab) &&
            !(event->state & GDK_CONTROL_MASK))
     {
-      /* If the text widget isn't editable overall, move the focus
-       * instead
+      /* If the text widget isn't editable overall, or if the application
+       * has turned off "accepts_tab", move the focus instead
        */
-      if (text_view->editable)
+      if (text_view->accepts_tab && text_view->editable)
        {
          gtk_text_view_commit_text (text_view, "\t");
          obscure = TRUE;
@@ -4147,7 +4190,7 @@ gtk_text_view_paint (GtkWidget      *widget,
   while (tmp_list != NULL)
     {
       GtkWidget *child = tmp_list->data;
-      
+  
       gtk_container_propagate_expose (GTK_CONTAINER (text_view),
                                       child,
                                       event);
@@ -4589,8 +4632,11 @@ gtk_text_view_move_cursor_internal (GtkTextView     *text_view,
     case GTK_MOVEMENT_WORDS:
       if (count < 0)
         gtk_text_iter_backward_visible_word_starts (&newplace, -count);
-      else if (count > 0)
-        gtk_text_iter_forward_visible_word_ends (&newplace, count);
+      else if (count > 0) 
+       {
+         if (!gtk_text_iter_forward_visible_word_ends (&newplace, count))
+           gtk_text_iter_forward_to_end (&newplace);
+       }
       break;
 
     case GTK_MOVEMENT_DISPLAY_LINES:
@@ -5169,6 +5215,52 @@ gtk_text_view_set_overwrite (GtkTextView *text_view,
     }
 }
 
+/**
+ * gtk_text_view_set_accepts_tab:
+ * @text_view: A #GtkTextView
+ * @accepts_tab: %TRUE if pressing the Tab key should insert a tab character, %FALSE, if pressing the Tab key should move the keyboard focus.
+ * 
+ * Sets the behavior of the text widget when the Tab key is pressed. If @accepts_tab
+ * is %TRUE a tab character is inserted. If @accepts_tab is %FALSE the keyboard focus
+ * is moved to the next widget in the focus chain.
+ * 
+ * Since: 2.4
+ **/
+void
+gtk_text_view_set_accepts_tab (GtkTextView *text_view,
+                              gboolean     accepts_tab)
+{
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+
+  accepts_tab = accepts_tab != FALSE;
+
+  if (text_view->accepts_tab != accepts_tab)
+    {
+      text_view->accepts_tab = accepts_tab;
+
+      g_object_notify (G_OBJECT (text_view), "accepts_tab");
+    }
+}
+
+/**
+ * gtk_text_view_get_accepts_tab:
+ * @text_view: A #GtkTextView
+ * 
+ * Returns whether pressing the Tab key inserts a tab characters.
+ * gtk_text_view_set_accepts_tab().
+ * 
+ * Return value: %TRUE if pressing the Tab key inserts a tab character, %FALSE if pressing the Tab key moves the keyboard focus.
+ * 
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_view_get_accepts_tab (GtkTextView *text_view)
+{
+  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
+
+  return text_view->accepts_tab;
+}
+
 static void
 gtk_text_view_move_focus (GtkTextView     *text_view,
                           GtkDirectionType direction_type)
@@ -5324,7 +5416,10 @@ extend_selection (GtkTextView *text_view,
            gtk_text_iter_backward_visible_word_start (start);
          
          if (!gtk_text_iter_ends_word (end))
-           gtk_text_iter_forward_visible_word_end (end);
+           {
+             if (!gtk_text_iter_forward_visible_word_end (end))
+               gtk_text_iter_forward_to_end (end);
+           }
        }
       else
        extend = FALSE;
@@ -5546,26 +5641,28 @@ gtk_text_view_check_keymap_direction (GtkTextView *text_view)
 {
   if (text_view->layout)
     {
-      gboolean split_cursor;
-      GtkTextDirection new_dir;
       GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
-  
+      GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (text_view)));
+      GtkTextDirection new_cursor_dir;
+      GtkTextDirection new_keyboard_dir;
+      gboolean split_cursor;
+
       g_object_get (settings,
                    "gtk-split-cursor", &split_cursor,
                    NULL);
+      
+      if (gdk_keymap_get_direction (keymap) == PANGO_DIRECTION_LTR)
+       new_keyboard_dir = GTK_TEXT_DIR_LTR;
+      else
+       new_keyboard_dir  = GTK_TEXT_DIR_RTL;
+  
       if (split_cursor)
-       {
-         new_dir = GTK_TEXT_DIR_NONE;
-       }
+       new_cursor_dir = GTK_TEXT_DIR_NONE;
       else
-       {
-         GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (text_view)));
-         new_dir = (gdk_keymap_get_direction (keymap) == PANGO_DIRECTION_LTR) ?
-           GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
-       }
+       new_cursor_dir = new_keyboard_dir;
       
-      if (text_view->layout->cursor_direction != new_dir)
-       gtk_text_layout_set_cursor_direction (text_view->layout, new_dir);
+      gtk_text_layout_set_cursor_direction (text_view->layout, new_cursor_dir);
+      gtk_text_layout_set_keyboard_direction (text_view->layout, new_keyboard_dir);
     }
 }
 
@@ -6025,19 +6122,26 @@ gtk_text_view_drag_data_received (GtkWidget        *widget,
   GtkTextIter drop_point;
   GtkTextView *text_view;
   gboolean success = FALSE;
+  GtkTextBuffer *buffer = NULL;
 
   text_view = GTK_TEXT_VIEW (widget);
 
   if (!text_view->dnd_mark)
     goto done;
 
-  gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
+  buffer = get_buffer (text_view);
+
+  gtk_text_buffer_get_iter_at_mark (buffer,
                                     &drop_point,
                                     text_view->dnd_mark);
   
   if (!gtk_text_iter_can_insert (&drop_point, text_view->editable))
     goto done;
 
+  success = TRUE;
+
+  gtk_text_buffer_begin_user_action (buffer);
+
   if (selection_data->target == gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
     {
       GtkTextBuffer *src_buffer = NULL;
@@ -6055,7 +6159,7 @@ gtk_text_view_drag_data_received (GtkWidget        *widget,
       g_return_if_fail (GTK_IS_TEXT_BUFFER (src_buffer));
 
       if (gtk_text_buffer_get_tag_table (src_buffer) !=
-          gtk_text_buffer_get_tag_table (get_buffer (text_view)))
+          gtk_text_buffer_get_tag_table (buffer))
         copy_tags = FALSE;
 
       if (gtk_text_buffer_get_selection_bounds (src_buffer,
@@ -6063,7 +6167,7 @@ gtk_text_view_drag_data_received (GtkWidget        *widget,
                                                 &end))
         {
           if (copy_tags)
-            gtk_text_buffer_insert_range_interactive (get_buffer (text_view),
+            gtk_text_buffer_insert_range_interactive (buffer,
                                                       &drop_point,
                                                       &start,
                                                       &end,
@@ -6073,7 +6177,7 @@ gtk_text_view_drag_data_received (GtkWidget        *widget,
               gchar *str;
 
               str = gtk_text_iter_get_visible_text (&start, &end);
-              gtk_text_buffer_insert_interactive (get_buffer (text_view),
+              gtk_text_buffer_insert_interactive (buffer,
                                                   &drop_point, str, -1,
                                                   text_view->editable);
               g_free (str);
@@ -6082,15 +6186,21 @@ gtk_text_view_drag_data_received (GtkWidget        *widget,
     }
   else
     insert_text_data (text_view, &drop_point, selection_data);
-
-  gtk_text_buffer_place_cursor (get_buffer (text_view), &drop_point);
-
-  success = TRUE;
-
  done:
   gtk_drag_finish (context, success,
                   success && context->action == GDK_ACTION_MOVE,
                   time);
+
+  if (success)
+    {
+      gtk_text_buffer_get_iter_at_mark (buffer,
+                                        &drop_point,
+                                        text_view->dnd_mark);
+      gtk_text_buffer_place_cursor (buffer, &drop_point);
+
+      gtk_text_buffer_end_user_action (buffer);
+    }
 }
 
 static GtkAdjustment*