From 1a46d3d534698081b67e56e5510f521e45bfcbd1 Mon Sep 17 00:00:00 2001 From: Alex Larsson Date: Wed, 19 Sep 2001 00:49:52 +0000 Subject: [PATCH] Don't draw with GTK_STATE_ACTIVE. 2001-09-18 Alex Larsson * gtk/gtkcheckbutton.c: * gtk/gtkradiobutton.c: Don't draw with GTK_STATE_ACTIVE. * gtk/gtkclist.c: * gtk/gtkctree.c: Draw lines between rows with base_gc[GTK_STATE_NORMAL]. * gtk/gtktextdisplay.c: Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and unfocused with base_gc [GTK_STATE_ACTIVE]. * gtk/gtkentry.c: Add select all menu-item. Default cursor color is red. Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and unfocused with base_gc [GTK_STATE_ACTIVE]. * gtk/gtklabel.[ch]: Add keynav + menu to selectable lables. Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and unfocused with base_gc [GTK_STATE_ACTIVE]. * gtk/gtkfilesel.c: Add drag and drop support. * gtk/gtkstyle.c: (This was checked in earlier) New default values for text/base SELECTED and ACTIVE --- ChangeLog | 32 ++ ChangeLog.pre-2-0 | 32 ++ ChangeLog.pre-2-10 | 32 ++ ChangeLog.pre-2-2 | 32 ++ ChangeLog.pre-2-4 | 32 ++ ChangeLog.pre-2-6 | 32 ++ ChangeLog.pre-2-8 | 32 ++ gtk/gtkcheckbutton.c | 16 +- gtk/gtkclist.c | 8 +- gtk/gtkctree.c | 6 +- gtk/gtkentry.c | 45 ++- gtk/gtkfilesel.c | 230 ++++++++++- gtk/gtklabel.c | 925 ++++++++++++++++++++++++++++++++++++++++--- gtk/gtklabel.h | 13 +- gtk/gtkradiobutton.c | 2 +- gtk/gtktextdisplay.c | 32 +- 16 files changed, 1404 insertions(+), 97 deletions(-) diff --git a/ChangeLog b/ChangeLog index be9123b672..c2e8297ba1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,35 @@ +2001-09-18 Alex Larsson + + * gtk/gtkcheckbutton.c: + * gtk/gtkradiobutton.c: + Don't draw with GTK_STATE_ACTIVE. + + * gtk/gtkclist.c: + * gtk/gtkctree.c: + Draw lines between rows with base_gc[GTK_STATE_NORMAL]. + + * gtk/gtktextdisplay.c: + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkentry.c: + Add select all menu-item. + Default cursor color is red. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtklabel.[ch]: + Add keynav + menu to selectable lables. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkfilesel.c: + Add drag and drop support. + + * gtk/gtkstyle.c: + (This was checked in earlier) + New default values for text/base SELECTED and ACTIVE + Tue Sep 18 23:51:49 2001 Tim Janik * configure.in: up version to 1.3.8, interface age 0, diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index be9123b672..c2e8297ba1 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,35 @@ +2001-09-18 Alex Larsson + + * gtk/gtkcheckbutton.c: + * gtk/gtkradiobutton.c: + Don't draw with GTK_STATE_ACTIVE. + + * gtk/gtkclist.c: + * gtk/gtkctree.c: + Draw lines between rows with base_gc[GTK_STATE_NORMAL]. + + * gtk/gtktextdisplay.c: + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkentry.c: + Add select all menu-item. + Default cursor color is red. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtklabel.[ch]: + Add keynav + menu to selectable lables. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkfilesel.c: + Add drag and drop support. + + * gtk/gtkstyle.c: + (This was checked in earlier) + New default values for text/base SELECTED and ACTIVE + Tue Sep 18 23:51:49 2001 Tim Janik * configure.in: up version to 1.3.8, interface age 0, diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index be9123b672..c2e8297ba1 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,35 @@ +2001-09-18 Alex Larsson + + * gtk/gtkcheckbutton.c: + * gtk/gtkradiobutton.c: + Don't draw with GTK_STATE_ACTIVE. + + * gtk/gtkclist.c: + * gtk/gtkctree.c: + Draw lines between rows with base_gc[GTK_STATE_NORMAL]. + + * gtk/gtktextdisplay.c: + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkentry.c: + Add select all menu-item. + Default cursor color is red. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtklabel.[ch]: + Add keynav + menu to selectable lables. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkfilesel.c: + Add drag and drop support. + + * gtk/gtkstyle.c: + (This was checked in earlier) + New default values for text/base SELECTED and ACTIVE + Tue Sep 18 23:51:49 2001 Tim Janik * configure.in: up version to 1.3.8, interface age 0, diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index be9123b672..c2e8297ba1 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,35 @@ +2001-09-18 Alex Larsson + + * gtk/gtkcheckbutton.c: + * gtk/gtkradiobutton.c: + Don't draw with GTK_STATE_ACTIVE. + + * gtk/gtkclist.c: + * gtk/gtkctree.c: + Draw lines between rows with base_gc[GTK_STATE_NORMAL]. + + * gtk/gtktextdisplay.c: + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkentry.c: + Add select all menu-item. + Default cursor color is red. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtklabel.[ch]: + Add keynav + menu to selectable lables. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkfilesel.c: + Add drag and drop support. + + * gtk/gtkstyle.c: + (This was checked in earlier) + New default values for text/base SELECTED and ACTIVE + Tue Sep 18 23:51:49 2001 Tim Janik * configure.in: up version to 1.3.8, interface age 0, diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index be9123b672..c2e8297ba1 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,35 @@ +2001-09-18 Alex Larsson + + * gtk/gtkcheckbutton.c: + * gtk/gtkradiobutton.c: + Don't draw with GTK_STATE_ACTIVE. + + * gtk/gtkclist.c: + * gtk/gtkctree.c: + Draw lines between rows with base_gc[GTK_STATE_NORMAL]. + + * gtk/gtktextdisplay.c: + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkentry.c: + Add select all menu-item. + Default cursor color is red. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtklabel.[ch]: + Add keynav + menu to selectable lables. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkfilesel.c: + Add drag and drop support. + + * gtk/gtkstyle.c: + (This was checked in earlier) + New default values for text/base SELECTED and ACTIVE + Tue Sep 18 23:51:49 2001 Tim Janik * configure.in: up version to 1.3.8, interface age 0, diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index be9123b672..c2e8297ba1 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,35 @@ +2001-09-18 Alex Larsson + + * gtk/gtkcheckbutton.c: + * gtk/gtkradiobutton.c: + Don't draw with GTK_STATE_ACTIVE. + + * gtk/gtkclist.c: + * gtk/gtkctree.c: + Draw lines between rows with base_gc[GTK_STATE_NORMAL]. + + * gtk/gtktextdisplay.c: + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkentry.c: + Add select all menu-item. + Default cursor color is red. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtklabel.[ch]: + Add keynav + menu to selectable lables. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkfilesel.c: + Add drag and drop support. + + * gtk/gtkstyle.c: + (This was checked in earlier) + New default values for text/base SELECTED and ACTIVE + Tue Sep 18 23:51:49 2001 Tim Janik * configure.in: up version to 1.3.8, interface age 0, diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index be9123b672..c2e8297ba1 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,35 @@ +2001-09-18 Alex Larsson + + * gtk/gtkcheckbutton.c: + * gtk/gtkradiobutton.c: + Don't draw with GTK_STATE_ACTIVE. + + * gtk/gtkclist.c: + * gtk/gtkctree.c: + Draw lines between rows with base_gc[GTK_STATE_NORMAL]. + + * gtk/gtktextdisplay.c: + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkentry.c: + Add select all menu-item. + Default cursor color is red. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtklabel.[ch]: + Add keynav + menu to selectable lables. + Focused selection is drawn with base_gc [GTK_STATE_SELECTED] and + unfocused with base_gc [GTK_STATE_ACTIVE]. + + * gtk/gtkfilesel.c: + Add drag and drop support. + + * gtk/gtkstyle.c: + (This was checked in earlier) + New default values for text/base SELECTED and ACTIVE + Tue Sep 18 23:51:49 2001 Tim Janik * configure.in: up version to 1.3.8, interface age 0, diff --git a/gtk/gtkcheckbutton.c b/gtk/gtkcheckbutton.c index b9671e0d69..d574877023 100644 --- a/gtk/gtkcheckbutton.c +++ b/gtk/gtkcheckbutton.c @@ -404,21 +404,13 @@ gtk_real_check_button_draw_indicator (GtkCheckButton *check_button, width = indicator_size; height = indicator_size; + state_type = GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE ? GTK_STATE_NORMAL : GTK_WIDGET_STATE (widget); if (GTK_TOGGLE_BUTTON (widget)->inconsistent) - { - state_type = GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE ? GTK_STATE_NORMAL : GTK_WIDGET_STATE (widget); - shadow_type = GTK_SHADOW_ETCHED_IN; - } + shadow_type = GTK_SHADOW_ETCHED_IN; else if (GTK_TOGGLE_BUTTON (widget)->active) - { - state_type = GTK_STATE_ACTIVE; - shadow_type = GTK_SHADOW_IN; - } + shadow_type = GTK_SHADOW_IN; else - { - shadow_type = GTK_SHADOW_OUT; - state_type = GTK_WIDGET_STATE (widget); - } + shadow_type = GTK_SHADOW_OUT; if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) x = widget->allocation.x + widget->allocation.width - (width + x - widget->allocation.x); diff --git a/gtk/gtkclist.c b/gtk/gtkclist.c index 2ea8aa7e25..f83a02b5e0 100644 --- a/gtk/gtkclist.c +++ b/gtk/gtkclist.c @@ -5661,7 +5661,7 @@ draw_row (GtkCList *clist, if (gdk_rectangle_intersect (area, &cell_rectangle, &intersect_rectangle)) gdk_draw_rectangle (clist->clist_window, - widget->style->base_gc[GTK_STATE_ACTIVE], + widget->style->base_gc[GTK_STATE_NORMAL], TRUE, intersect_rectangle.x, intersect_rectangle.y, @@ -5676,7 +5676,7 @@ draw_row (GtkCList *clist, if (gdk_rectangle_intersect (area, &cell_rectangle, &intersect_rectangle)) gdk_draw_rectangle (clist->clist_window, - widget->style->base_gc[GTK_STATE_ACTIVE], + widget->style->base_gc[GTK_STATE_NORMAL], TRUE, intersect_rectangle.x, intersect_rectangle.y, @@ -5692,7 +5692,7 @@ draw_row (GtkCList *clist, { rect = &clip_rectangle; gdk_draw_rectangle (clist->clist_window, - widget->style->base_gc[GTK_STATE_ACTIVE], + widget->style->base_gc[GTK_STATE_NORMAL], TRUE, cell_rectangle.x, cell_rectangle.y, @@ -5705,7 +5705,7 @@ draw_row (GtkCList *clist, cell_rectangle.y += clist->row_height + CELL_SPACING; gdk_draw_rectangle (clist->clist_window, - widget->style->base_gc[GTK_STATE_ACTIVE], + widget->style->base_gc[GTK_STATE_NORMAL], TRUE, cell_rectangle.x, cell_rectangle.y, diff --git a/gtk/gtkctree.c b/gtk/gtkctree.c index fc637bbf97..c250eeaba5 100644 --- a/gtk/gtkctree.c +++ b/gtk/gtkctree.c @@ -1705,7 +1705,7 @@ draw_row (GtkCList *clist, if (gdk_rectangle_intersect (area, &cell_rectangle, crect)) gdk_draw_rectangle (clist->clist_window, - widget->style->base_gc[GTK_STATE_ACTIVE], TRUE, + widget->style->base_gc[GTK_STATE_NORMAL], TRUE, crect->x, crect->y, crect->width, crect->height); } else @@ -1714,7 +1714,7 @@ draw_row (GtkCList *clist, crect = &cell_rectangle; gdk_draw_rectangle (clist->clist_window, - widget->style->base_gc[GTK_STATE_ACTIVE], TRUE, + widget->style->base_gc[GTK_STATE_NORMAL], TRUE, crect->x, crect->y, crect->width, crect->height); } @@ -1761,7 +1761,7 @@ draw_row (GtkCList *clist, if (!area || gdk_rectangle_intersect (area, &cell_rectangle, crect)) { gdk_draw_rectangle (clist->clist_window, - widget->style->base_gc[GTK_STATE_ACTIVE], TRUE, + widget->style->base_gc[GTK_STATE_NORMAL], TRUE, crect->x, crect->y, crect->width, crect->height); /* horizontal black lines */ diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index 2162beabcf..0715097f2b 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -211,6 +211,7 @@ static void gtk_entry_cut_clipboard (GtkEntry *entry); static void gtk_entry_copy_clipboard (GtkEntry *entry); static void gtk_entry_paste_clipboard (GtkEntry *entry); static void gtk_entry_toggle_overwrite (GtkEntry *entry); +static void gtk_entry_select_all (GtkEntry *entry); static void gtk_entry_real_activate (GtkEntry *entry); static void gtk_entry_popup_menu (GtkWidget *widget); @@ -932,18 +933,17 @@ static void gtk_entry_realize_cursor_gc (GtkEntry *entry) { GdkColor *cursor_color; + GdkColor red = {0, 0xffff, 0x0000, 0x0000}; if (entry->cursor_gc) gdk_gc_unref (entry->cursor_gc); gtk_widget_style_get (GTK_WIDGET (entry), "cursor_color", &cursor_color, NULL); - if (cursor_color) - { entry->cursor_gc = gdk_gc_new (entry->text_area); - gdk_gc_set_rgb_fg_color (entry->cursor_gc, cursor_color); - } + if (cursor_color) + gdk_gc_set_rgb_fg_color (entry->cursor_gc, cursor_color); else - entry->cursor_gc = gdk_gc_ref (GTK_WIDGET (entry)->style->base_gc[GTK_STATE_SELECTED]); + gdk_gc_set_rgb_fg_color (entry->cursor_gc, &red); } static void @@ -2121,6 +2121,12 @@ gtk_entry_toggle_overwrite (GtkEntry *entry) entry->overwrite_mode = !entry->overwrite_mode; } +static void +gtk_entry_select_all (GtkEntry *entry) +{ + gtk_entry_select_line (entry); +} + static void gtk_entry_real_activate (GtkEntry *entry) { @@ -2516,6 +2522,8 @@ gtk_entry_draw_text (GtkEntry *entry) gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text; gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text; GdkRegion *clip_region = gdk_region_new (); + GdkGC *text_gc; + GdkGC *selection_gc; line = pango_layout_get_lines (layout)->data; @@ -2523,6 +2531,17 @@ gtk_entry_draw_text (GtkEntry *entry) pango_layout_get_extents (layout, NULL, &logical_rect); + if (GTK_WIDGET_HAS_FOCUS (entry)) + { + selection_gc = widget->style->base_gc [GTK_STATE_SELECTED]; + text_gc = widget->style->text_gc [GTK_STATE_SELECTED]; + } + else + { + selection_gc = widget->style->base_gc [GTK_STATE_ACTIVE]; + text_gc = widget->style->text_gc [GTK_STATE_ACTIVE]; + } + for (i=0; i < n_ranges; i++) { GdkRectangle rect; @@ -2531,18 +2550,18 @@ gtk_entry_draw_text (GtkEntry *entry) rect.y = y; rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE; rect.height = logical_rect.height / PANGO_SCALE; - - gdk_draw_rectangle (entry->text_area, widget->style->base_gc [GTK_STATE_SELECTED], TRUE, + + gdk_draw_rectangle (entry->text_area, selection_gc, TRUE, rect.x, rect.y, rect.width, rect.height); gdk_region_union_with_rect (clip_region, &rect); } - gdk_gc_set_clip_region (widget->style->text_gc [GTK_STATE_SELECTED], clip_region); - gdk_draw_layout (entry->text_area, widget->style->text_gc [GTK_STATE_SELECTED], + gdk_gc_set_clip_region (text_gc, clip_region); + gdk_draw_layout (entry->text_area, text_gc, x, y, layout); - gdk_gc_set_clip_region (widget->style->text_gc [GTK_STATE_SELECTED], NULL); + gdk_gc_set_clip_region (text_gc, NULL); gdk_region_destroy (clip_region); g_free (ranges); @@ -3664,6 +3683,12 @@ gtk_entry_do_popup (GtkEntry *entry, append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard", TRUE); + menuitem = gtk_menu_item_new_with_label (_("Select All")); + gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gtk_entry_select_all), entry); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem); + menuitem = gtk_separator_menu_item_new (); gtk_widget_show (menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem); diff --git a/gtk/gtkfilesel.c b/gtk/gtkfilesel.c index 0119428ce4..4326af41d3 100644 --- a/gtk/gtkfilesel.c +++ b/gtk/gtkfilesel.c @@ -68,6 +68,8 @@ #include "gtkdialog.h" #include "gtkmessagedialog.h" #include "gtkintl.h" +#include "gtkdnd.h" +#include "gtkeventbox.h" #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN) #define STRICT @@ -588,6 +590,7 @@ gtk_file_selection_init (GtkFileSelection *filesel) GtkWidget *confirm_area; GtkWidget *pulldown_hbox; GtkWidget *scrolled_win; + GtkWidget *eventbox; GtkDialog *dialog; char *dir_title [2]; @@ -692,11 +695,14 @@ gtk_file_selection_init (GtkFileSelection *filesel) entry_vbox = gtk_vbox_new (FALSE, 2); gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2); gtk_widget_show (entry_vbox); - + + eventbox = gtk_event_box_new (); filesel->selection_text = label = gtk_label_new (""); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0); + gtk_container_add (GTK_CONTAINER (eventbox), label); + gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0); gtk_widget_show (label); + gtk_widget_show (eventbox); filesel->selection_entry = gtk_entry_new (); gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event", @@ -728,6 +734,224 @@ gtk_file_selection_init (GtkFileSelection *filesel) gtk_widget_grab_focus (filesel->selection_entry); } +static gchar * +uri_list_extract_first_uri (const gchar* uri_list) +{ + const gchar *p, *q; + + g_return_val_if_fail (uri_list != NULL, NULL); + + p = uri_list; + /* We don't actually try to validate the URI according to RFC + * 2396, or even check for allowed characters - we just ignore + * comments and trim whitespace off the ends. We also + * allow LF delimination as well as the specified CRLF. + * + * We do allow comments like specified in RFC 2483. + */ + while (p) + { + if (*p != '#') + { + while (g_ascii_isspace(*p)) + p++; + + q = p; + while (*q && (*q != '\n') && (*q != '\r')) + q++; + + if (q > p) + { + q--; + while (q > p && g_ascii_isspace (*q)) + q--; + + if (q > p) + return g_strndup (p, q - p + 1); + } + } + p = strchr (p, '\n'); + if (p) + p++; + } + return NULL; +} + +static void +dnd_really_drop (GtkWidget *dialog, gint response_id, GtkFileSelection *fs) +{ + gchar *filename; + + if (response_id == GTK_RESPONSE_YES) + { + filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename"); + + gtk_file_selection_set_filename (fs, filename); + } + + gtk_widget_destroy (dialog); +} + + +static void +filenames_dropped (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + char *uri = NULL; + char *filename = NULL; + char *hostname; + char this_hostname[257]; + int res; + GError *error = NULL; + + if (!selection_data->data) + return; + + uri = uri_list_extract_first_uri ((char *)selection_data->data); + + if (!uri) + return; + + filename = g_filename_from_uri (uri, &hostname, &error); + g_free (uri); + + if (!filename) + { + g_warning ("Error getting dropped filename: %s\n", + error->message); + g_error_free (error); + return; + } + + res = gethostname (this_hostname, 256); + this_hostname[256] = 0; + + if ((hostname == NULL) || + (res == 0 && strcmp (hostname, this_hostname) == 0) || + (strcmp (hostname, "localhost") == 0)) + gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget), + filename); + else + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (widget), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + _("The file \"%s\" resides on another machine (called %s) and may not be availible to this program.\n" + "Are you sure that you want to select it?"), filename, hostname); + + g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free); + + g_signal_connect_data (dialog, "response", + (GCallback) dnd_really_drop, + widget, NULL, 0); + + gtk_widget_show (dialog); + } + + g_free (hostname); + g_free (filename); +} + +enum +{ + TARGET_URILIST, + TARGET_UTF8_STRING, + TARGET_STRING, + TARGET_TEXT, + TARGET_COMPOUND_TEXT +}; + + +static void +filenames_drag_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + GtkFileSelection *filesel) +{ + gchar *file; + gchar *uri_list; + char hostname[256]; + int res; + GError *error; + + file = gtk_file_selection_get_filename (filesel); + + if (file) + { + if (info == TARGET_URILIST) + { + res = gethostname (hostname, 256); + + error = NULL; + uri_list = g_filename_to_uri (file, (!res)?hostname:NULL, &error); + if (!uri_list) + { + g_warning ("Error getting filename: %s\n", + error->message); + g_error_free (error); + return; + } + + gtk_selection_data_set (selection_data, + selection_data->target, 8, + (void *)uri_list, strlen((char *)uri_list)); + g_free (uri_list); + } + else + { + g_print ("Setting text: '%s'\n", file); + gtk_selection_data_set_text (selection_data, file); + } + } +} + +static void +file_selection_setup_dnd (GtkFileSelection *filesel) +{ + GtkWidget *eventbox; + static GtkTargetEntry drop_types[] = { + { "text/uri-list", 0, TARGET_URILIST} + }; + static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]); + static GtkTargetEntry drag_types[] = { + { "text/uri-list", 0, TARGET_URILIST}, + { "UTF8_STRING", 0, TARGET_UTF8_STRING }, + { "STRING", 0, 0 }, + { "TEXT", 0, 0 }, + { "COMPOUND_TEXT", 0, 0 } + }; + static gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]); + + gtk_drag_dest_set (GTK_WIDGET (filesel), + GTK_DEST_DEFAULT_ALL, + drop_types, n_drop_types, + GDK_ACTION_COPY); + + gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received", + GTK_SIGNAL_FUNC(filenames_dropped), NULL); + + eventbox = gtk_widget_get_parent (filesel->selection_text); + gtk_drag_source_set (eventbox, + GDK_BUTTON1_MASK, + drag_types, n_drag_types, + GDK_ACTION_COPY); + + gtk_signal_connect (GTK_OBJECT (eventbox), + "drag_data_get", + GTK_SIGNAL_FUNC (filenames_drag_get), + filesel); +} + GtkWidget* gtk_file_selection_new (const gchar *title) { @@ -737,6 +961,8 @@ gtk_file_selection_new (const gchar *title) gtk_window_set_title (GTK_WINDOW (filesel), title); gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE); + file_selection_setup_dnd (filesel); + return GTK_WIDGET (filesel); } diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 7100a567d9..89d74efca6 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -26,6 +26,7 @@ #include #include #include "gtklabel.h" +#include "gtkmain.h" #include "gtksignal.h" #include "gtkwindow.h" #include "gdk/gdkkeysyms.h" @@ -33,16 +34,26 @@ #include "gdk/gdki18n.h" #include #include "gtkintl.h" +#include "gtkseparatormenuitem.h" #include "gtkmenuitem.h" #include "gtknotebook.h" +#include "gtkbindings.h" struct _GtkLabelSelectionInfo { GdkWindow *window; gint selection_anchor; gint selection_end; + GdkGC *cursor_gc; + GtkWidget *popup_menu; }; +enum { + MOVE_CURSOR, + COPY_CLIPBOARD, + POPULATE_POPUP, + LAST_SIGNAL +}; enum { PROP_0, @@ -58,6 +69,8 @@ enum { PROP_MNEMONIC_WIDGET }; +static guint signals[LAST_SIGNAL] = { 0 }; + static void gtk_label_class_init (GtkLabelClass *klass); static void gtk_label_init (GtkLabel *label); static void gtk_label_set_property (GObject *object, @@ -126,11 +139,27 @@ static void gtk_label_select_region_index (GtkLabel *label, gint anchor_index, gint end_index); -static gboolean gtk_label_mnemonic_activate (GtkWidget *widget, - gboolean group_cycling); -static void gtk_label_setup_mnemonic (GtkLabel *label, - guint last_key); +static gboolean gtk_label_mnemonic_activate (GtkWidget *widget, + gboolean group_cycling); +static void gtk_label_setup_mnemonic (GtkLabel *label, + guint last_key); +static gboolean gtk_label_focus (GtkWidget *widget, + GtkDirectionType direction); +/* For selectable lables: */ +static void gtk_label_move_cursor (GtkLabel *label, + GtkMovementStep step, + gint count, + gboolean extend_selection); +static void gtk_label_copy_clipboard (GtkLabel *label); +static void gtk_label_select_all (GtkLabel *label); +static void gtk_label_do_popup (GtkLabel *label, + GdkEventButton *event); + +static gint gtk_label_move_forward_word (GtkLabel *label, + gint start); +static gint gtk_label_move_backward_word (GtkLabel *label, + gint start); static GtkMiscClass *parent_class = NULL; @@ -161,12 +190,36 @@ gtk_label_get_type (void) return label_type; } +static void +add_move_binding (GtkBindingSet *binding_set, + guint keyval, + guint modmask, + GtkMovementStep step, + gint count) +{ + g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0); + + gtk_binding_entry_add_signal (binding_set, keyval, modmask, + "move_cursor", 3, + GTK_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, FALSE); + + /* Selection-extending version */ + gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK, + "move_cursor", 3, + GTK_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, TRUE); +} + static void gtk_label_class_init (GtkLabelClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkObjectClass *object_class = GTK_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GtkBindingSet *binding_set; parent_class = gtk_type_class (GTK_TYPE_MISC); @@ -191,6 +244,34 @@ gtk_label_class_init (GtkLabelClass *class) widget_class->motion_notify_event = gtk_label_motion; widget_class->hierarchy_changed = gtk_label_hierarchy_changed; widget_class->mnemonic_activate = gtk_label_mnemonic_activate; + widget_class->focus = gtk_label_focus; + + class->move_cursor = gtk_label_move_cursor; + class->copy_clipboard = gtk_label_copy_clipboard; + + signals[MOVE_CURSOR] = + gtk_signal_new ("move_cursor", + GTK_RUN_LAST | GTK_RUN_ACTION, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GtkLabelClass, move_cursor), + gtk_marshal_VOID__ENUM_INT_BOOLEAN, + GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL); + + signals[COPY_CLIPBOARD] = + gtk_signal_new ("copy_clipboard", + GTK_RUN_LAST | GTK_RUN_ACTION, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GtkLabelClass, copy_clipboard), + gtk_marshal_VOID__VOID, + GTK_TYPE_NONE, 0); + + signals[POPULATE_POPUP] = + gtk_signal_new ("populate_popup", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GtkLabelClass, populate_popup), + gtk_marshal_VOID__OBJECT, + GTK_TYPE_NONE, 1, GTK_TYPE_MENU); g_object_class_install_property (G_OBJECT_CLASS(object_class), PROP_LABEL, @@ -269,6 +350,90 @@ gtk_label_class_init (GtkLabelClass *class) "key is pressed."), GTK_TYPE_WIDGET, G_PARAM_READWRITE)); + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boxed ("cursor_color", + _("Cursor color"), + _("Color with which to draw insertion cursor"), + GDK_TYPE_COLOR, + G_PARAM_READABLE)); + + /* + * Key bindings + */ + + binding_set = gtk_binding_set_by_class (class); + + /* Moving the insertion point */ + add_move_binding (binding_set, GDK_Right, 0, + GTK_MOVEMENT_VISUAL_POSITIONS, 1); + + add_move_binding (binding_set, GDK_Left, 0, + GTK_MOVEMENT_VISUAL_POSITIONS, -1); + + add_move_binding (binding_set, GDK_KP_Right, 0, + GTK_MOVEMENT_VISUAL_POSITIONS, 1); + + add_move_binding (binding_set, GDK_KP_Left, 0, + GTK_MOVEMENT_VISUAL_POSITIONS, -1); + + add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK, + GTK_MOVEMENT_LOGICAL_POSITIONS, 1); + + add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK, + GTK_MOVEMENT_LOGICAL_POSITIONS, -1); + + add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, + GTK_MOVEMENT_WORDS, 1); + + add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, + GTK_MOVEMENT_WORDS, -1); + + add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, + GTK_MOVEMENT_WORDS, 1); + + add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, + GTK_MOVEMENT_WORDS, -1); + + add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK, + GTK_MOVEMENT_PARAGRAPH_ENDS, -1); + + add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK, + GTK_MOVEMENT_PARAGRAPH_ENDS, 1); + + add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK, + GTK_MOVEMENT_WORDS, 1); + + add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK, + GTK_MOVEMENT_WORDS, -1); + + add_move_binding (binding_set, GDK_Home, 0, + GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); + + add_move_binding (binding_set, GDK_End, 0, + GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); + + add_move_binding (binding_set, GDK_KP_Home, 0, + GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); + + add_move_binding (binding_set, GDK_KP_End, 0, + GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); + + add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK, + GTK_MOVEMENT_BUFFER_ENDS, -1); + + add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK, + GTK_MOVEMENT_BUFFER_ENDS, 1); + + add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK, + GTK_MOVEMENT_BUFFER_ENDS, -1); + + add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK, + GTK_MOVEMENT_BUFFER_ENDS, 1); + + /* copy */ + gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK, + "copy_clipboard", 0); } static void @@ -1421,6 +1586,74 @@ get_layout_location (GtkLabel *label, *yp = y; } +static void +gtk_label_draw_cursor (GtkLabel *label, gint xoffset, gint yoffset) +{ + if (label->select_info == NULL) + return; + + if (GTK_WIDGET_DRAWABLE (label)) + { + GtkWidget *widget = GTK_WIDGET (label); + + GtkTextDirection keymap_direction; + GtkTextDirection widget_direction; + PangoRectangle strong_pos, weak_pos; + gboolean split_cursor; + PangoRectangle *cursor1 = NULL; + PangoRectangle *cursor2 = NULL; + GdkGC *gc1 = NULL; + GdkGC *gc2 = NULL; + + keymap_direction = + (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? + GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; + + widget_direction = gtk_widget_get_direction (widget); + + gtk_label_ensure_layout (label, NULL, NULL); + + pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end, + &strong_pos, &weak_pos); + + g_object_get (gtk_widget_get_settings (widget), + "gtk-split-cursor", &split_cursor, + NULL); + + if (split_cursor) + { + gc1 = label->select_info->cursor_gc; + cursor1 = &strong_pos; + + if (strong_pos.x != weak_pos.x || + strong_pos.y != weak_pos.y) + { + gc2 = widget->style->black_gc; + cursor2 = &weak_pos; + } + } + else + { + gc1 = label->select_info->cursor_gc; + + if (keymap_direction == widget_direction) + cursor1 = &strong_pos; + else + cursor1 = &weak_pos; + } + + gdk_draw_line (widget->window, gc1, + xoffset + PANGO_PIXELS (cursor1->x), yoffset + PANGO_PIXELS (cursor1->y), + xoffset + PANGO_PIXELS (cursor1->x), yoffset + PANGO_PIXELS (cursor1->y + cursor1->height)); + + if (gc2) + gdk_draw_line (widget->window, gc2, + xoffset + PANGO_PIXELS (cursor2->x), yoffset + PANGO_PIXELS (cursor2->y), + xoffset + PANGO_PIXELS (cursor2->x), yoffset + PANGO_PIXELS (cursor2->y + cursor2->height)); + } +} + + static gint gtk_label_expose (GtkWidget *widget, GdkEventExpose *event) @@ -1456,7 +1689,8 @@ gtk_label_expose (GtkWidget *widget, { gint range[2]; GdkRegion *clip; - + GtkStateType state; + range[0] = label->select_info->selection_anchor; range[1] = label->select_info->selection_end; @@ -1476,18 +1710,25 @@ gtk_label_expose (GtkWidget *widget, * region */ - gdk_gc_set_clip_region (widget->style->white_gc, clip); - + gdk_gc_set_clip_region (widget->style->black_gc, clip); + + + state = GTK_STATE_SELECTED; + if (!GTK_WIDGET_HAS_FOCUS (widget)) + state = GTK_STATE_ACTIVE; + gdk_draw_layout_with_colors (widget->window, - widget->style->white_gc, + widget->style->black_gc, x, y, label->layout, - &widget->style->fg[GTK_STATE_SELECTED], - &widget->style->bg[GTK_STATE_SELECTED]); + &widget->style->text[state], + &widget->style->base[state]); - gdk_gc_set_clip_region (widget->style->white_gc, NULL); + gdk_gc_set_clip_region (widget->style->black_gc, NULL); gdk_region_destroy (clip); } + else if (label->select_info && GTK_WIDGET_HAS_FOCUS (widget)) + gtk_label_draw_cursor (label, x, y); } return TRUE; @@ -1637,6 +1878,25 @@ gtk_label_set_text_with_mnemonic (GtkLabel *label, gtk_label_setup_mnemonic (label, last_keyval); } +static void +gtk_label_realize_cursor_gc (GtkLabel *label) +{ + GdkColor *cursor_color; + GdkColor red = {0, 0xffff, 0x0000, 0x0000}; + + if (label->select_info == NULL) + return; + + if (label->select_info->cursor_gc) + gdk_gc_unref (label->select_info->cursor_gc); + + gtk_widget_style_get (GTK_WIDGET (label), "cursor_color", &cursor_color, NULL); + label->select_info->cursor_gc = gdk_gc_new (GTK_WIDGET (label)->window); + if (cursor_color) + gdk_gc_set_rgb_fg_color (label->select_info->cursor_gc, cursor_color); + else + gdk_gc_set_rgb_fg_color (label->select_info->cursor_gc, &red); +} static void gtk_label_realize (GtkWidget *widget) @@ -1648,7 +1908,23 @@ gtk_label_realize (GtkWidget *widget) (* GTK_WIDGET_CLASS (parent_class)->realize) (widget); if (label->select_info) - gtk_label_create_window (label); + { + gtk_label_create_window (label); + gtk_label_realize_cursor_gc (label); + } +} + +static void +gtk_label_unrealize_cursor_gc (GtkLabel *label) +{ + if (label->select_info == NULL) + return; + + if (label->select_info->cursor_gc) + { + gdk_gc_unref (label->select_info->cursor_gc); + label->select_info->cursor_gc = NULL; + } } static void @@ -1659,7 +1935,10 @@ gtk_label_unrealize (GtkWidget *widget) label = GTK_LABEL (widget); if (label->select_info) - gtk_label_destroy_window (label); + { + gtk_label_unrealize_cursor_gc (label); + gtk_label_destroy_window (label); + } (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); } @@ -1779,6 +2058,25 @@ get_layout_index (GtkLabel *label, *index += (cluster_end - cluster); } +static void +gtk_label_select_word (GtkLabel *label) +{ + gint min, max; + + gint start_index = gtk_label_move_backward_word (label, label->select_info->selection_end); + gint end_index = gtk_label_move_forward_word (label, label->select_info->selection_end); + + min = MIN (label->select_info->selection_anchor, + label->select_info->selection_end); + max = MAX (label->select_info->selection_anchor, + label->select_info->selection_end); + + min = MIN (min, start_index); + max = MAX (max, end_index); + + gtk_label_select_region_index (label, min, max); +} + static gint gtk_label_button_press (GtkWidget *widget, GdkEventButton *event) @@ -1791,40 +2089,73 @@ gtk_label_button_press (GtkWidget *widget, if (label->select_info == NULL) return FALSE; - if (event->button != 1) - return FALSE; - - get_layout_index (label, event->x, event->y, &index); - - if ((label->select_info->selection_anchor != - label->select_info->selection_end) && - (event->state & GDK_SHIFT_MASK)) + if (event->button == 1) { - /* extend (same as motion) */ - if (index < label->select_info->selection_end) - gtk_label_select_region_index (label, - index, - label->select_info->selection_end); + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + + if (event->type == GDK_3BUTTON_PRESS) + { + gtk_label_select_region_index (label, 0, strlen (label->label)); + return TRUE; + } + + if (event->type == GDK_2BUTTON_PRESS) + { + gtk_label_select_word (label); + return TRUE; + } + + get_layout_index (label, event->x, event->y, &index); + + if ((label->select_info->selection_anchor != + label->select_info->selection_end) && + (event->state & GDK_SHIFT_MASK)) + { + gint min, max; + + /* extend (same as motion) */ + min = MIN (label->select_info->selection_anchor, + label->select_info->selection_end); + max = MAX (label->select_info->selection_anchor, + label->select_info->selection_end); + + min = MIN (min, index); + max = MAX (max, index); + + gtk_label_select_region_index (label, + min, + max); + + /* ensure the anchor is opposite index */ + if (index == label->select_info->selection_anchor) + { + gint tmp = label->select_info->selection_end; + label->select_info->selection_end = label->select_info->selection_anchor; + label->select_info->selection_anchor = tmp; + } + } else - gtk_label_select_region_index (label, - label->select_info->selection_anchor, - index); - - /* ensure the anchor is opposite index */ - if (index == label->select_info->selection_anchor) - { - gint tmp = label->select_info->selection_end; - label->select_info->selection_end = label->select_info->selection_anchor; - label->select_info->selection_anchor = tmp; - } - } - else - { - /* start a replacement */ - gtk_label_select_region_index (label, index, index); - } + { + if (event->type == GDK_3BUTTON_PRESS) + gtk_label_select_region_index (label, 0, strlen (label->label)); + else if (event->type == GDK_2BUTTON_PRESS) + gtk_label_select_word (label); + else + /* start a replacement */ + gtk_label_select_region_index (label, index, index); + } - return TRUE; + return TRUE; + } + else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) + { + gtk_label_do_popup (label, event); + + return TRUE; + + } + return FALSE; } static gint @@ -1948,14 +2279,15 @@ gtk_label_set_selectable (GtkLabel *label, { if (label->select_info == NULL) { - label->select_info = g_new (GtkLabelSelectionInfo, 1); - - label->select_info->window = NULL; - label->select_info->selection_anchor = 0; - label->select_info->selection_end = 0; + label->select_info = g_new0 (GtkLabelSelectionInfo, 1); + GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS); + if (GTK_WIDGET_REALIZED (label)) - gtk_label_create_window (label); + { + gtk_label_create_window (label); + gtk_label_realize_cursor_gc (label); + } if (GTK_WIDGET_MAPPED (label)) gdk_window_show (label->select_info->window); @@ -1968,12 +2300,18 @@ gtk_label_set_selectable (GtkLabel *label, /* unselect, to give up the selection */ gtk_label_select_region (label, 0, 0); + gtk_label_unrealize_cursor_gc (label); + if (label->select_info->window) - gtk_label_destroy_window (label); + { + gtk_label_destroy_window (label); + } g_free (label->select_info); label->select_info = NULL; + + GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS); } } if (setting != old_setting) @@ -2050,8 +2388,7 @@ clear_text_callback (GtkClipboard *clipboard, if (label->select_info) { - label->select_info->selection_anchor = 0; - label->select_info->selection_end = 0; + label->select_info->selection_anchor = label->select_info->selection_end; gtk_label_clear_layout (label); gtk_widget_queue_draw (GTK_WIDGET (label)); @@ -2069,7 +2406,7 @@ gtk_label_select_region_index (GtkLabel *label, { "COMPOUND_TEXT", 0, 0 }, { "UTF8_STRING", 0, 0 } }; - + g_return_if_fail (GTK_IS_LABEL (label)); if (label->select_info) @@ -2322,3 +2659,483 @@ gtk_label_get_use_underline (GtkLabel *label) return label->use_underline; } + +static gboolean +gtk_label_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + /* We never want to be in the tab chain */ + return FALSE; +} + +/* Compute the X position for an offset that corresponds to the "more important + * cursor position for that offset. We use this when trying to guess to which + * end of the selection we should go to when the user hits the left or + * right arrow key. + */ +static void +get_better_cursor (GtkLabel *label, + gint index, + gint *x, + gint *y) +{ + GtkTextDirection keymap_direction = + (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? + GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; + GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (label)); + gboolean split_cursor; + PangoRectangle strong_pos, weak_pos; + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)), + "gtk-split-cursor", &split_cursor, + NULL); + + gtk_label_ensure_layout (label, NULL, NULL); + + pango_layout_get_cursor_pos (label->layout, index, + &strong_pos, &weak_pos); + + if (split_cursor) + { + *x = strong_pos.x / PANGO_SCALE; + *y = strong_pos.y / PANGO_SCALE; + } + else + { + if (keymap_direction == widget_direction) + { + *x = strong_pos.x / PANGO_SCALE; + *y = strong_pos.y / PANGO_SCALE; + } + else + { + *x = weak_pos.x / PANGO_SCALE; + *y = weak_pos.y / PANGO_SCALE; + } + } +} + + +static gint +gtk_label_move_logically (GtkLabel *label, + gint start, + gint count) +{ + gint offset = g_utf8_pointer_to_offset (label->label, + label->label + start); + + if (label->label) + { + PangoLogAttr *log_attrs; + gint n_attrs; + gint length; + + gtk_label_ensure_layout (label, NULL, NULL); + + length = g_utf8_strlen (label->label, -1); + + pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs); + + while (count > 0 && offset < length) + { + do + offset++; + while (offset < length && !log_attrs[offset].is_cursor_position); + + count--; + } + while (count < 0 && offset > 0) + { + do + offset--; + while (offset > 0 && !log_attrs[offset].is_cursor_position); + + count++; + } + + g_free (log_attrs); + } + + return g_utf8_offset_to_pointer (label->label, offset) - label->label; +} + +static gint +gtk_label_move_visually (GtkLabel *label, + gint start, + gint count) +{ + gint index; + + index = start; + + while (count != 0) + { + int new_index, new_trailing; + gboolean split_cursor; + gboolean strong; + + gtk_label_ensure_layout (label, NULL, NULL); + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)), + "gtk-split-cursor", &split_cursor, + NULL); + + if (split_cursor) + strong = TRUE; + else + { + GtkTextDirection keymap_direction = + (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? + GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; + + strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (label)); + } + + if (count > 0) + { + pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing); + count--; + } + else + { + pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing); + count++; + } + + if (new_index < 0 || new_index == G_MAXINT) + break; + + index = new_index; + + while (new_trailing--) + index = g_utf8_next_char (label->label + new_index) - label->label; + } + + return index; +} + +static gint +gtk_label_move_forward_word (GtkLabel *label, + gint start) +{ + gint new_pos = g_utf8_pointer_to_offset (label->label, + label->label + start); + gint length; + + length = g_utf8_strlen (label->label, -1); + if (new_pos < length) + { + PangoLogAttr *log_attrs; + gint n_attrs; + + gtk_label_ensure_layout (label, NULL, NULL); + + pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs); + + /* Find the next word end */ + new_pos++; + while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end) + new_pos++; + + g_free (log_attrs); + } + + return g_utf8_offset_to_pointer (label->label, new_pos) - label->label; +} + + +static gint +gtk_label_move_backward_word (GtkLabel *label, + gint start) +{ + gint new_pos = g_utf8_pointer_to_offset (label->label, + label->label + start); + gint length; + + length = g_utf8_strlen (label->label, -1); + + if (new_pos > 0) + { + PangoLogAttr *log_attrs; + gint n_attrs; + + gtk_label_ensure_layout (label, NULL, NULL); + + pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs); + + new_pos -= 1; + + /* Find the previous word beginning */ + while (new_pos > 0 && !log_attrs[new_pos].is_word_start) + new_pos--; + + g_free (log_attrs); + } + + return g_utf8_offset_to_pointer (label->label, new_pos) - label->label; +} + +static void +gtk_label_move_cursor (GtkLabel *label, + GtkMovementStep step, + gint count, + gboolean extend_selection) +{ + gint new_pos; + + if (label->select_info == NULL) + return; + + new_pos = label->select_info->selection_end; + + if (label->select_info->selection_end != label->select_info->selection_anchor && + !extend_selection) + { + /* If we have a current selection and aren't extending it, move to the + * start/or end of the selection as appropriate + */ + switch (step) + { + case GTK_MOVEMENT_VISUAL_POSITIONS: + { + gint end_x, end_y; + gint anchor_x, anchor_y; + gboolean end_is_left; + + get_better_cursor (label, label->select_info->selection_end, &end_x, &end_y); + get_better_cursor (label, label->select_info->selection_anchor, &anchor_x, &anchor_y); + + end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x); + + if (count < 0) + new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor; + else + new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor; + + break; + } + case GTK_MOVEMENT_LOGICAL_POSITIONS: + case GTK_MOVEMENT_WORDS: + if (count < 0) + new_pos = MIN (label->select_info->selection_end, label->select_info->selection_anchor); + else + new_pos = MAX (label->select_info->selection_end, label->select_info->selection_anchor); + break; + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + case GTK_MOVEMENT_PARAGRAPH_ENDS: + case GTK_MOVEMENT_BUFFER_ENDS: + /* FIXME: Can do better here */ + new_pos = count < 0 ? 0 : strlen (label->label); + break; + case GTK_MOVEMENT_DISPLAY_LINES: + case GTK_MOVEMENT_PARAGRAPHS: + case GTK_MOVEMENT_PAGES: + break; + } + } + else + { + switch (step) + { + case GTK_MOVEMENT_LOGICAL_POSITIONS: + new_pos = gtk_label_move_logically (label, new_pos, count); + break; + case GTK_MOVEMENT_VISUAL_POSITIONS: + new_pos = gtk_label_move_visually (label, new_pos, count); + break; + case GTK_MOVEMENT_WORDS: + while (count > 0) + { + new_pos = gtk_label_move_forward_word (label, new_pos); + count--; + } + while (count < 0) + { + new_pos = gtk_label_move_backward_word (label, new_pos); + count++; + } + break; + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + case GTK_MOVEMENT_PARAGRAPH_ENDS: + case GTK_MOVEMENT_BUFFER_ENDS: + /* FIXME: Can do better here */ + new_pos = count < 0 ? 0 : strlen (label->label); + break; + case GTK_MOVEMENT_DISPLAY_LINES: + case GTK_MOVEMENT_PARAGRAPHS: + case GTK_MOVEMENT_PAGES: + break; + } + } + + if (extend_selection) + gtk_label_select_region_index (label, + label->select_info->selection_anchor, + new_pos); + else + gtk_label_select_region_index (label, new_pos, new_pos); +} + +static void +gtk_label_copy_clipboard (GtkLabel *label) +{ + if (label->text && label->select_info) + { + gint start, end; + gint len; + + start = MIN (label->select_info->selection_anchor, + label->select_info->selection_end); + end = MAX (label->select_info->selection_anchor, + label->select_info->selection_end); + + len = strlen (label->text); + + if (end > len) + end = len; + + if (start > len) + start = len; + + if (start != end) + gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), + label->text + start, end - start); + } +} + +static void +gtk_label_select_all (GtkLabel *label) +{ + gtk_label_select_region_index (label, 0, strlen (label->label)); +} + +/* Quick hack of a popup menu + */ +static void +activate_cb (GtkWidget *menuitem, + GtkLabel *label) +{ + const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal"); + gtk_signal_emit_by_name (GTK_OBJECT (label), signal); +} + +static void +append_action_signal (GtkLabel *label, + GtkWidget *menu, + const gchar *label_text, + const gchar *signal, + gboolean sensitive) +{ + GtkWidget *menuitem = gtk_menu_item_new_with_label (label_text); + + gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-signal", (char *)signal); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (activate_cb), label); + + gtk_widget_set_sensitive (menuitem, sensitive); + + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); +} + +static void +popup_menu_detach (GtkWidget *attach_widget, + GtkMenu *menu) +{ + GtkLabel *label; + label = GTK_LABEL (attach_widget); + + if (label->select_info) + label->select_info->popup_menu = NULL; +} + +static void +popup_position_func (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data) +{ + GtkLabel *label; + GtkWidget *widget; + GtkRequisition req; + + label = GTK_LABEL (user_data); + widget = GTK_WIDGET (label); + + if (label->select_info == NULL) + return; + + g_return_if_fail (GTK_WIDGET_REALIZED (label)); + + gdk_window_get_origin (widget->window, x, y); + + gtk_widget_size_request (label->select_info->popup_menu, &req); + + *x += widget->allocation.width / 2; + *y += widget->allocation.height; + + *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width)); + *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height)); +} + + +static void +gtk_label_do_popup (GtkLabel *label, + GdkEventButton *event) +{ + GtkWidget *menuitem; + gboolean have_selection; + + if (label->select_info == NULL) + return; + + if (label->select_info->popup_menu) + gtk_widget_destroy (label->select_info->popup_menu); + + label->select_info->popup_menu = gtk_menu_new (); + + gtk_menu_attach_to_widget (GTK_MENU (label->select_info->popup_menu), + GTK_WIDGET (label), + popup_menu_detach); + + have_selection = + label->select_info->selection_anchor != label->select_info->selection_end; + + + append_action_signal (label, label->select_info->popup_menu, _("Cut"), "cut_clipboard", + FALSE); + append_action_signal (label, label->select_info->popup_menu, _("Copy"), "copy_clipboard", + have_selection); + append_action_signal (label, label->select_info->popup_menu, _("Paste"), "paste_clipboard", + FALSE); + + menuitem = gtk_menu_item_new_with_label (_("Select All")); + gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gtk_label_select_all), label); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem); + + menuitem = gtk_separator_menu_item_new (); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem); + + menuitem = gtk_menu_item_new_with_label (_("Input Methods")); + gtk_widget_show (menuitem); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), gtk_menu_new ()); + gtk_widget_set_sensitive (menuitem, FALSE); + gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem); + + gtk_signal_emit (GTK_OBJECT (label), + signals[POPULATE_POPUP], + label->select_info->popup_menu); + + if (event) + gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL, + NULL, NULL, + event->button, event->time); + else + gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL, + popup_position_func, label, + 0, gtk_get_current_event_time ()); +} diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h index 03524a3b28..149a3cbffb 100644 --- a/gtk/gtklabel.h +++ b/gtk/gtklabel.h @@ -30,7 +30,7 @@ #include #include #include - +#include #ifdef __cplusplus extern "C" { @@ -78,6 +78,17 @@ struct _GtkLabel struct _GtkLabelClass { GtkMiscClass parent_class; + + void (* move_cursor) (GtkLabel *label, + GtkMovementStep step, + gint count, + gboolean extend_selection); + void (* copy_clipboard) (GtkLabel *label); + void (* select_all) (GtkLabel *label); + + /* Hook to customize right-click popup for selectable labels */ + void (* populate_popup) (GtkLabel *label, + GtkMenu *menu); }; GtkType gtk_label_get_type (void) G_GNUC_CONST; diff --git a/gtk/gtkradiobutton.c b/gtk/gtkradiobutton.c index cc9b573e2f..521b0bf12b 100644 --- a/gtk/gtkradiobutton.c +++ b/gtk/gtkradiobutton.c @@ -463,7 +463,7 @@ gtk_radio_button_draw_indicator (GtkCheckButton *check_button, x = widget->allocation.x + widget->allocation.width - (indicator_size + x - widget->allocation.x); gtk_paint_option (widget->style, widget->window, - GTK_WIDGET_STATE (widget), shadow_type, + state_type, shadow_type, area, widget, "radiobutton", x, y, indicator_size, indicator_size); } diff --git a/gtk/gtktextdisplay.c b/gtk/gtktextdisplay.c index d5b5f50185..d605a1ba1d 100644 --- a/gtk/gtktextdisplay.c +++ b/gtk/gtktextdisplay.c @@ -250,7 +250,10 @@ render_layout_line (GdkDrawable *drawable, if (selected) { - fg_gc = render_state->widget->style->text_gc[GTK_STATE_SELECTED]; + if (GTK_WIDGET_HAS_FOCUS (render_state->widget)) + fg_gc = render_state->widget->style->text_gc[GTK_STATE_SELECTED]; + else + fg_gc = render_state->widget->style->text_gc [GTK_STATE_ACTIVE]; } else { @@ -494,6 +497,8 @@ render_para (GdkDrawable *drawable, PangoLayoutIter *iter; PangoRectangle layout_logical; int screen_width; + GdkGC *fg_gc, *bg_gc; + gint state; gboolean first = TRUE; @@ -508,6 +513,14 @@ render_para (GdkDrawable *drawable, screen_width = line_display->total_width; + if (GTK_WIDGET_HAS_FOCUS (render_state->widget)) + state = GTK_STATE_SELECTED; + else + state = GTK_STATE_ACTIVE; + + fg_gc = render_state->widget->style->text_gc [state]; + bg_gc = render_state->widget->style->base_gc [state]; + do { PangoLayoutLine *line = pango_layout_iter_get_line (iter); @@ -547,7 +560,7 @@ render_para (GdkDrawable *drawable, selection_end_index > line->length + byte_offset) /* All selected */ { gdk_draw_rectangle (drawable, - render_state->widget->style->base_gc[GTK_STATE_SELECTED], + bg_gc, TRUE, x + line_display->left_margin, selection_y, @@ -577,12 +590,11 @@ render_para (GdkDrawable *drawable, selection_y, selection_height, selection_start_index, selection_end_index); - - gdk_gc_set_clip_region (render_state->widget->style->text_gc [GTK_STATE_SELECTED], clip_region); - gdk_gc_set_clip_region (render_state->widget->style->base_gc [GTK_STATE_SELECTED], clip_region); + gdk_gc_set_clip_region (fg_gc, clip_region); + gdk_gc_set_clip_region (bg_gc, clip_region); gdk_draw_rectangle (drawable, - render_state->widget->style->base_gc[GTK_STATE_SELECTED], + bg_gc, TRUE, x + PANGO_PIXELS (line_rect.x), selection_y, @@ -594,8 +606,8 @@ render_para (GdkDrawable *drawable, y + PANGO_PIXELS (baseline), TRUE); - gdk_gc_set_clip_region (render_state->widget->style->text_gc [GTK_STATE_SELECTED], NULL); - gdk_gc_set_clip_region (render_state->widget->style->base_gc [GTK_STATE_SELECTED], NULL); + gdk_gc_set_clip_region (fg_gc, NULL); + gdk_gc_set_clip_region (bg_gc, NULL); gdk_region_destroy (clip_region); @@ -605,7 +617,7 @@ render_para (GdkDrawable *drawable, (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length))) { gdk_draw_rectangle (drawable, - render_state->widget->style->base_gc[GTK_STATE_SELECTED], + bg_gc, TRUE, x + line_display->left_margin, selection_y, @@ -625,7 +637,7 @@ render_para (GdkDrawable *drawable, PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width); gdk_draw_rectangle (drawable, - render_state->widget->style->base_gc[GTK_STATE_SELECTED], + bg_gc, TRUE, x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width), selection_y,