]> Pileus Git - ~andy/gtk/blobdiff - tests/testtext.c
Replace a lot of idle and timeout calls by the new gdk_threads api.
[~andy/gtk] / tests / testtext.c
index e27ce80be9500614d1c192b857277f4031bc20b7..b1f6f532a037f47d616b972c5b6639329b6f740b 100644 (file)
@@ -1,9 +1,32 @@
+/* testtext.c
+ * Copyright (C) 2000 Red Hat, Inc
+ * Author: Havoc Pennington
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 
+#undef GTK_DISABLE_DEPRECATED
+
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 
@@ -25,6 +48,10 @@ struct _Buffer
   GtkTextTag *invisible_tag;
   GtkTextTag *not_editable_tag;
   GtkTextTag *found_text_tag;
+  GtkTextTag *rise_tag;
+  GtkTextTag *large_tag;
+  GtkTextTag *indent_tag;
+  GtkTextTag *margin_tag;
   GtkTextTag *custom_tabs_tag;
   GSList *color_tags;
   guint color_cycle_timeout;
@@ -500,10 +527,12 @@ fill_example_buffer (GtkTextBuffer *buffer)
                 "underline", PANGO_UNDERLINE_SINGLE,
                 NULL);
 
+  tag = gtk_text_buffer_create_tag (buffer, "underline_error", NULL);
+
   setup_tag (tag);
       
   g_object_set (tag,
-                "underline", PANGO_UNDERLINE_SINGLE,
+                "underline", PANGO_UNDERLINE_ERROR,
                 NULL);
 
   tag = gtk_text_buffer_create_tag (buffer, "centered", NULL);
@@ -578,6 +607,11 @@ fill_example_buffer (GtkTextBuffer *buffer)
 
       gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
 
+      gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 4);
+      gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 7);
+
+      gtk_text_buffer_apply_tag_by_name (buffer, "underline_error", &iter, &iter2);
+
       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
 
@@ -1023,6 +1057,114 @@ do_apply_invisible (gpointer callback_data,
     }
 }
 
+static void
+do_apply_rise (gpointer callback_data,
+              guint callback_action,
+              GtkWidget *widget)
+{
+  View *view = view_from_widget (widget);
+  GtkTextIter start;
+  GtkTextIter end;
+  
+  if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
+                                            &start, &end))
+    {
+      if (callback_action)
+        {
+          gtk_text_buffer_remove_tag (view->buffer->buffer,
+                                      view->buffer->rise_tag,
+                                      &start, &end);
+        }
+      else
+        {
+          gtk_text_buffer_apply_tag (view->buffer->buffer,
+                                     view->buffer->rise_tag,
+                                     &start, &end);
+        }
+    }
+}
+
+static void
+do_apply_large (gpointer callback_data,
+               guint callback_action,
+               GtkWidget *widget)
+{
+  View *view = view_from_widget (widget);
+  GtkTextIter start;
+  GtkTextIter end;
+  
+  if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
+                                            &start, &end))
+    {
+      if (callback_action)
+        {
+          gtk_text_buffer_remove_tag (view->buffer->buffer,
+                                      view->buffer->large_tag,
+                                      &start, &end);
+        }
+      else
+        {
+          gtk_text_buffer_apply_tag (view->buffer->buffer,
+                                     view->buffer->large_tag,
+                                     &start, &end);
+        }
+    }
+}
+
+static void
+do_apply_indent (gpointer callback_data,
+                guint callback_action,
+                GtkWidget *widget)
+{
+  View *view = view_from_widget (widget);
+  GtkTextIter start;
+  GtkTextIter end;
+  
+  if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
+                                            &start, &end))
+    {
+      if (callback_action)
+        {
+          gtk_text_buffer_remove_tag (view->buffer->buffer,
+                                      view->buffer->indent_tag,
+                                      &start, &end);
+        }
+      else
+        {
+          gtk_text_buffer_apply_tag (view->buffer->buffer,
+                                     view->buffer->indent_tag,
+                                     &start, &end);
+        }
+    }
+}
+
+static void
+do_apply_margin (gpointer callback_data,
+                guint callback_action,
+                GtkWidget *widget)
+{
+  View *view = view_from_widget (widget);
+  GtkTextIter start;
+  GtkTextIter end;
+  
+  if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
+                                            &start, &end))
+    {
+      if (callback_action)
+        {
+          gtk_text_buffer_remove_tag (view->buffer->buffer,
+                                      view->buffer->margin_tag,
+                                      &start, &end);
+        }
+      else
+        {
+          gtk_text_buffer_apply_tag (view->buffer->buffer,
+                                     view->buffer->margin_tag,
+                                     &start, &end);
+        }
+    }
+}
+
 static void
 do_apply_tabs (gpointer callback_data,
                guint callback_action,
@@ -1140,6 +1282,267 @@ do_properties (gpointer callback_data,
   create_prop_editor (G_OBJECT (view->text_view), 0);
 }
 
+static void
+rich_text_store_populate (GtkListStore  *store,
+                          GtkTextBuffer *buffer,
+                          gboolean       deserialize)
+{
+  GdkAtom *formats;
+  gint     n_formats;
+  gint     i;
+
+  gtk_list_store_clear (store);
+
+  if (deserialize)
+    formats = gtk_text_buffer_get_deserialize_formats (buffer, &n_formats);
+  else
+    formats = gtk_text_buffer_get_serialize_formats (buffer, &n_formats);
+
+  for (i = 0; i < n_formats; i++)
+    {
+      GtkTreeIter  iter;
+      gchar       *mime_type;
+      gboolean     can_create_tags = FALSE;
+
+      mime_type = gdk_atom_name (formats[i]);
+
+      if (deserialize)
+        can_create_tags =
+          gtk_text_buffer_deserialize_get_can_create_tags (buffer, formats[i]);
+
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter,
+                          0, formats[i],
+                          1, mime_type,
+                          2, can_create_tags,
+                          -1);
+
+      g_free (mime_type);
+    }
+
+  g_free (formats);
+}
+
+static void
+rich_text_paste_target_list_notify (GtkTextBuffer    *buffer,
+                                    const GParamSpec *pspec,
+                                    GtkListStore     *store)
+{
+  rich_text_store_populate (store, buffer, TRUE);
+}
+
+static void
+rich_text_copy_target_list_notify (GtkTextBuffer    *buffer,
+                                   const GParamSpec *pspec,
+                                   GtkListStore     *store)
+{
+  rich_text_store_populate (store, buffer, FALSE);
+}
+
+static void
+rich_text_can_create_tags_toggled (GtkCellRendererToggle *toggle,
+                                   const gchar           *path,
+                                   GtkTreeModel          *model)
+{
+  GtkTreeIter iter;
+
+  if (gtk_tree_model_get_iter_from_string (model, &iter, path))
+    {
+      GtkTextBuffer *buffer;
+      GdkAtom        format;
+      gboolean       can_create_tags;
+
+      buffer = g_object_get_data (G_OBJECT (model), "buffer");
+
+      gtk_tree_model_get (model, &iter,
+                          0, &format,
+                          2, &can_create_tags,
+                          -1);
+
+      gtk_text_buffer_deserialize_set_can_create_tags (buffer, format,
+                                                       !can_create_tags);
+
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                          2, !can_create_tags,
+                          -1);
+    }
+}
+
+static void
+rich_text_unregister_clicked (GtkWidget   *button,
+                              GtkTreeView *tv)
+{
+  GtkTreeSelection *sel = gtk_tree_view_get_selection (tv);
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+
+  if (gtk_tree_selection_get_selected (sel, &model, &iter))
+    {
+      GtkTextBuffer *buffer;
+      gboolean       deserialize;
+      GdkAtom        format;
+
+      buffer = g_object_get_data (G_OBJECT (model), "buffer");
+      deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
+                                                        "deserialize"));
+
+      gtk_tree_model_get (model, &iter,
+                          0, &format,
+                          -1);
+
+      if (deserialize)
+        gtk_text_buffer_unregister_deserialize_format (buffer, format);
+      else
+        gtk_text_buffer_unregister_serialize_format (buffer, format);
+    }
+}
+
+static void
+rich_text_register_clicked (GtkWidget   *button,
+                            GtkTreeView *tv)
+{
+  GtkWidget *dialog;
+  GtkWidget *label;
+  GtkWidget *entry;
+
+  dialog = gtk_dialog_new_with_buttons ("Register new Tagset",
+                                        GTK_WINDOW (gtk_widget_get_toplevel (button)),
+                                        GTK_DIALOG_DESTROY_WITH_PARENT,
+                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                        GTK_STOCK_OK,     GTK_RESPONSE_OK,
+                                        NULL);
+  label = gtk_label_new ("Enter tagset name or leave blank for "
+                         "unrestricted internal format:");
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label,
+                      FALSE, FALSE, 0);
+
+  entry = gtk_entry_new ();
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), entry,
+                      FALSE, FALSE, 0);
+
+  gtk_widget_show_all (dialog);
+
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
+    {
+      GtkTreeModel  *model  = gtk_tree_view_get_model (tv);
+      GtkTextBuffer *buffer = g_object_get_data (G_OBJECT (model), "buffer");
+      const gchar   *tagset = gtk_entry_get_text (GTK_ENTRY (entry));
+      gboolean       deserialize;
+
+      deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
+                                                        "deserialize"));
+
+      if (tagset && ! strlen (tagset))
+        tagset = NULL;
+
+      if (deserialize)
+        gtk_text_buffer_register_deserialize_tagset (buffer, tagset);
+      else
+        gtk_text_buffer_register_serialize_tagset (buffer, tagset);
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+static void
+do_rich_text (gpointer callback_data,
+              guint deserialize,
+              GtkWidget *widget)
+{
+  View *view = view_from_widget (widget);
+  GtkTextBuffer *buffer;
+  GtkWidget *dialog;
+  GtkWidget *tv;
+  GtkWidget *sw;
+  GtkWidget *hbox;
+  GtkWidget *button;
+  GtkListStore *store;
+
+  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view->text_view));
+
+  dialog = gtk_dialog_new_with_buttons (deserialize ?
+                                        "Rich Text Paste & Drop" :
+                                        "Rich Text Copy & Drag",
+                                        GTK_WINDOW (view->window),
+                                        GTK_DIALOG_DESTROY_WITH_PARENT,
+                                        GTK_STOCK_CLOSE, 0,
+                                        NULL);
+  g_signal_connect (dialog, "response",
+                    G_CALLBACK (gtk_widget_destroy),
+                    NULL);
+
+  store = gtk_list_store_new (3,
+                              G_TYPE_POINTER,
+                              G_TYPE_STRING,
+                              G_TYPE_BOOLEAN);
+
+  g_object_set_data (G_OBJECT (store), "buffer", buffer);
+  g_object_set_data (G_OBJECT (store), "deserialize",
+                     GUINT_TO_POINTER (deserialize));
+
+  tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+  g_object_unref (store);
+
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
+                                               0, "Rich Text Format",
+                                               gtk_cell_renderer_text_new (),
+                                               "text", 1,
+                                               NULL);
+
+  if (deserialize)
+    {
+      GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new ();
+
+      gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
+                                                   1, "Can Create Tags",
+                                                   renderer,
+                                                   "active", 2,
+                                                   NULL);
+
+      g_signal_connect (renderer, "toggled",
+                        G_CALLBACK (rich_text_can_create_tags_toggled),
+                        store);
+    }
+
+  sw = gtk_scrolled_window_new (NULL, NULL);
+  gtk_widget_set_size_request (sw, 300, 100);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), sw);
+
+  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), tv);
+
+  hbox = gtk_hbox_new (FALSE, 6);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
+                      FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label ("Unregister Selected Format");
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+  g_signal_connect (button, "clicked",
+                    G_CALLBACK (rich_text_unregister_clicked),
+                    tv);
+
+  button = gtk_button_new_with_label ("Register New Tagset\n"
+                                      "for the Internal Format");
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+  g_signal_connect (button, "clicked",
+                    G_CALLBACK (rich_text_register_clicked),
+                    tv);
+
+  if (deserialize)
+    g_signal_connect_object (buffer, "notify::paste-target-list",
+                             G_CALLBACK (rich_text_paste_target_list_notify),
+                             G_OBJECT (store), 0);
+  else
+    g_signal_connect_object (buffer, "notify::copy-target-list",
+                             G_CALLBACK (rich_text_copy_target_list_notify),
+                             G_OBJECT (store), 0);
+
+  rich_text_store_populate (store, buffer, deserialize);
+
+  gtk_widget_show_all (dialog);
+}
+
 enum
 {
   RESPONSE_FORWARD,
@@ -1179,6 +1582,20 @@ dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
   gtk_widget_destroy (dialog);
 }
 
+static void
+do_copy  (gpointer callback_data,
+          guint callback_action,
+          GtkWidget *widget)
+{
+  View *view = view_from_widget (widget);
+  GtkTextBuffer *buffer;
+
+  buffer = view->buffer->buffer;
+
+  gtk_text_buffer_copy_clipboard (buffer,
+                                  gtk_clipboard_get (GDK_NONE));
+}
+
 static void
 do_search (gpointer callback_data,
            guint callback_action,
@@ -1222,6 +1639,21 @@ do_search (gpointer callback_data,
   gtk_widget_show_all (dialog);
 }
 
+static void
+do_select_all (gpointer callback_data,
+               guint callback_action,
+               GtkWidget *widget)
+{
+  View *view = view_from_widget (widget);
+  GtkTextBuffer *buffer;
+  GtkTextIter start, end;
+
+  buffer = view->buffer->buffer;
+
+  gtk_text_buffer_get_bounds (buffer, &start, &end);
+  gtk_text_buffer_select_range (buffer, &start, &end);
+}
+
 typedef struct
 {
   /* position is in coordinate system of text_view_move_child */
@@ -1489,53 +1921,62 @@ view_init_menus (View *view)
 
 static GtkItemFactoryEntry menu_items[] =
 {
-  { "/_File",            NULL,         0,           0, "<Branch>" },
+  { "/_File",            NULL,         NULL,        0, "<Branch>" },
   { "/File/_New",        "<control>N", do_new,      0, NULL },
   { "/File/New _View",   NULL,         do_new_view, 0, NULL },
   { "/File/_Open",       "<control>O", do_open,     0, NULL },
   { "/File/_Save",       "<control>S", do_save,     0, NULL },
   { "/File/Save _As...", NULL,         do_save_as,  0, NULL },
-  { "/File/sep1",        NULL,         0,           0, "<Separator>" },
+  { "/File/sep1",        NULL,         NULL,        0, "<Separator>" },
   { "/File/_Close",     "<control>W" , do_close,    0, NULL },
   { "/File/E_xit",      "<control>Q" , do_exit,     0, NULL },
 
   { "/_Edit", NULL, 0, 0, "<Branch>" },
+  { "/Edit/Copy", NULL, do_copy, 0, NULL },
+  { "/Edit/sep1", NULL, NULL, 0, "<Separator>" },
   { "/Edit/Find...", NULL, do_search, 0, NULL },
+  { "/Edit/Select All", "<control>A", do_select_all, 0, NULL }, 
 
-  { "/_Settings",        NULL,         0,                0, "<Branch>" },
+  { "/_Settings",        NULL,         NULL,             0, "<Branch>" },
   { "/Settings/Wrap _Off",   NULL,      do_wrap_changed,  GTK_WRAP_NONE, "<RadioItem>" },
   { "/Settings/Wrap _Words", NULL,      do_wrap_changed,  GTK_WRAP_WORD, "/Settings/Wrap Off" },
   { "/Settings/Wrap _Chars", NULL,      do_wrap_changed,  GTK_WRAP_CHAR, "/Settings/Wrap Off" },
-  { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
+  { "/Settings/sep1",        NULL,      NULL,             0, "<Separator>" },
   { "/Settings/Editable", NULL,      do_editable_changed,  TRUE, "<RadioItem>" },
   { "/Settings/Not editable",    NULL,      do_editable_changed,  FALSE, "/Settings/Editable" },
-  { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
+  { "/Settings/sep1",        NULL,      NULL,             0, "<Separator>" },
 
   { "/Settings/Cursor visible",    NULL,      do_cursor_visible_changed,  TRUE, "<RadioItem>" },
   { "/Settings/Cursor not visible", NULL,      do_cursor_visible_changed,  FALSE, "/Settings/Cursor visible" },
-  { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
+  { "/Settings/sep1",        NULL,      NULL,          0, "<Separator>" },
   
   { "/Settings/Left-to-Right", NULL,    do_direction_changed,  GTK_TEXT_DIR_LTR, "<RadioItem>" },
   { "/Settings/Right-to-Left", NULL,    do_direction_changed,  GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
 
-  { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
+  { "/Settings/sep1",        NULL,      NULL,                0, "<Separator>" },
   { "/Settings/Sane spacing", NULL,    do_spacing_changed,  FALSE, "<RadioItem>" },
   { "/Settings/Funky spacing", NULL,    do_spacing_changed,  TRUE, "/Settings/Sane spacing" },
-  { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
+  { "/Settings/sep1",        NULL,      NULL,                0, "<Separator>" },
   { "/Settings/Don't cycle color tags", NULL,    do_color_cycle_changed,  FALSE, "<RadioItem>" },
   { "/Settings/Cycle colors", NULL,    do_color_cycle_changed,  TRUE, "/Settings/Don't cycle color tags" },
-  { "/_Attributes",      NULL,         0,                0, "<Branch>" },
+  { "/_Attributes",      NULL,         NULL,                0, "<Branch>" },
   { "/Attributes/Editable",      NULL,         do_apply_editable, TRUE, NULL },
   { "/Attributes/Not editable",          NULL,         do_apply_editable, FALSE, NULL },
   { "/Attributes/Invisible",             NULL,         do_apply_invisible, FALSE, NULL },
   { "/Attributes/Visible",       NULL,         do_apply_invisible, TRUE, NULL },
+  { "/Attributes/Rise",          NULL,         do_apply_rise, FALSE, NULL },
+  { "/Attributes/Large",         NULL,         do_apply_large, FALSE, NULL },
+  { "/Attributes/Indent",        NULL,         do_apply_indent, FALSE, NULL },
+  { "/Attributes/Margins",       NULL,         do_apply_margin, FALSE, NULL },
   { "/Attributes/Custom tabs",           NULL,         do_apply_tabs, FALSE, NULL },
   { "/Attributes/Default tabs",          NULL,         do_apply_tabs, TRUE, NULL },
   { "/Attributes/Color cycles",          NULL,         do_apply_colors, TRUE, NULL },
   { "/Attributes/No colors",                     NULL,         do_apply_colors, FALSE, NULL },
-  { "/Attributes/Remove all tags",       NULL, do_remove_tags, 0, NULL },
-  { "/Attributes/Properties",       NULL, do_properties, 0, NULL },
-  { "/_Test",           NULL,         0,           0, "<Branch>" },
+  { "/Attributes/Remove all tags",        NULL, do_remove_tags, 0, NULL },
+  { "/Attributes/Properties",             NULL, do_properties, 0, NULL },
+  { "/Attributes/Rich Text copy & drag",  NULL, do_rich_text, 0, NULL },
+  { "/Attributes/Rich Text paste & drop", NULL, do_rich_text, 1, NULL },
+  { "/_Test",           NULL,         NULL,           0, "<Branch>" },
   { "/Test/_Example",           NULL,         do_example,  0, NULL },
   { "/Test/_Insert and scroll", NULL,         do_insert_and_scroll,  0, NULL },
   { "/Test/_Add fixed children", NULL,         do_add_children,  0, NULL },
@@ -1720,7 +2161,7 @@ create_buffer (void)
       ++i;
     }
 
-#if 0  
+#if 1  
   buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
                                                       "invisible", TRUE, NULL);
 #endif  
@@ -1733,6 +2174,18 @@ create_buffer (void)
   buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
                                                        "foreground", "red", NULL);
 
+  buffer->rise_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
+                                                "rise", 10 * PANGO_SCALE, NULL);
+
+  buffer->large_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
+                                                "scale", PANGO_SCALE_X_LARGE, NULL);
+
+  buffer->indent_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
+                                                  "indent", 20, NULL);
+
+  buffer->margin_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
+                                                  "left_margin", 20, "right_margin", 20, NULL);
+
   tabs = pango_tab_array_new_with_positions (4,
                                              TRUE,
                                              PANGO_TAB_LEFT, 10,
@@ -2007,7 +2460,7 @@ buffer_set_colors (Buffer  *buffer,
   gdouble hue = 0.0;
 
   if (enabled && buffer->color_cycle_timeout == 0)
-    buffer->color_cycle_timeout = g_timeout_add (200, color_cycle_timeout, buffer);
+    buffer->color_cycle_timeout = gdk_threads_add_timeout (200, color_cycle_timeout, buffer);
   else if (!enabled && buffer->color_cycle_timeout != 0)
     {
       g_source_remove (buffer->color_cycle_timeout);
@@ -2406,11 +2859,19 @@ line_numbers_expose (GtkWidget      *widget,
   return FALSE;
 }
 
+static void
+selection_changed (GtkTextBuffer *buffer,
+                  GParamSpec    *pspec,
+                  GtkWidget     *copy_menu)
+{
+  gtk_widget_set_sensitive (copy_menu, gtk_text_buffer_get_has_selection (buffer));
+}
+
 static View *
 create_view (Buffer *buffer)
 {
   View *view;
-  
+  GtkWidget *copy_menu;
   GtkWidget *sw;
   GtkWidget *vbox;
   
@@ -2432,6 +2893,14 @@ create_view (Buffer *buffer)
   
   gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
 
+  /* make the Copy menu item sensitivity update according to the selection */
+  copy_menu = gtk_item_factory_get_item (view->item_factory, "<main>/Edit/Copy");
+  gtk_widget_set_sensitive (copy_menu, gtk_text_buffer_get_has_selection (view->buffer->buffer));
+  g_signal_connect (view->buffer->buffer,
+                   "notify::has-selection",
+                   G_CALLBACK (selection_changed),
+                   copy_menu);
+
   gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
 
   vbox = gtk_vbox_new (FALSE, 0);
@@ -2532,7 +3001,7 @@ view_add_example_widgets (View *view)
 }
 
 void
-test_init ()
+test_init (void)
 {
   if (g_file_test ("../gdk-pixbuf/libpixbufloader-pnm.la",
                   G_FILE_TEST_EXISTS))