Unfocused selection color patch (and more)
- From: Alex Larsson <alexl redhat com>
- To: <gtk-devel-list gnome org>
- Subject: Unfocused selection color patch (and more)
- Date: Fri, 7 Sep 2001 12:57:18 -0400 (EDT)
My patch to make unfocused selections show as gray sort of went overboard.
I fixed it in GtkEntry, GtkTextView and GtkLabel. But then i noticed that
selections in selectable GtkLabels were always grayed out, since labels
couldn't get focus.
So I had to make selectable labels focusable (by gtk_widget_grab() or by
direct mouse click. They're not in the tab order), which led to the labels
needing support for keynav, bidi-cursors, context menu and all that
rocks. I also added "Select All" to the context menu of GtkLabel and
GtkEntry for good measure.
This patch "depends" on the Pango bugfix patch I just sent.
/ Alex
Index: gtk/gtkentry.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkentry.c,v
retrieving revision 1.146
diff -u -p -r1.146 gtkentry.c
--- gtk/gtkentry.c 2001/07/19 18:47:03 1.146
+++ gtk/gtkentry.c 2001/09/07 16:09:15
@@ -67,6 +67,7 @@ enum {
COPY_CLIPBOARD,
PASTE_CLIPBOARD,
TOGGLE_OVERWRITE,
+ SELECT_ALL,
LAST_SIGNAL
};
@@ -210,6 +211,7 @@ static void gtk_entry_cut_clipboard
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);
@@ -378,6 +380,7 @@ gtk_entry_class_init (GtkEntryClass *cla
class->copy_clipboard = gtk_entry_copy_clipboard;
class->paste_clipboard = gtk_entry_paste_clipboard;
class->toggle_overwrite = gtk_entry_toggle_overwrite;
+ class->select_all = gtk_entry_select_all;
class->activate = gtk_entry_real_activate;
g_object_class_install_property (gobject_class,
@@ -564,6 +567,14 @@ gtk_entry_class_init (GtkEntryClass *cla
gtk_marshal_VOID__VOID,
GTK_TYPE_NONE, 0);
+ signals[SELECT_ALL] =
+ gtk_signal_new ("select_all",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, select_all),
+ gtk_marshal_VOID__VOID,
+ GTK_TYPE_NONE, 0);
+
/*
* Key bindings
*/
@@ -2106,6 +2117,12 @@ gtk_entry_toggle_overwrite (GtkEntry *en
}
static void
+gtk_entry_select_all (GtkEntry *entry)
+{
+ gtk_entry_select_line (entry);
+}
+
+static void
gtk_entry_real_activate (GtkEntry *entry)
{
GtkWindow *window;
@@ -2500,6 +2517,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;
@@ -2507,6 +2526,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->bg_gc [GTK_STATE_ACTIVE];
+ text_gc = widget->style->fg_gc [GTK_STATE_ACTIVE];
+ }
+
for (i=0; i < n_ranges; i++)
{
GdkRectangle rect;
@@ -2515,18 +2545,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);
@@ -3644,6 +3674,8 @@ gtk_entry_do_popup (GtkEntry *entr
append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard",
have_selection);
append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard",
+ TRUE);
+ append_action_signal (entry, entry->popup_menu, _("Select All"), "select_all",
TRUE);
menuitem = gtk_separator_menu_item_new ();
Index: gtk/gtkentry.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkentry.h,v
retrieving revision 1.48
diff -u -p -r1.48 gtkentry.h
--- gtk/gtkentry.h 2001/07/19 18:47:03 1.48
+++ gtk/gtkentry.h 2001/09/07 16:09:15
@@ -145,6 +145,7 @@ struct _GtkEntryClass
void (* copy_clipboard) (GtkEntry *entry);
void (* paste_clipboard) (GtkEntry *entry);
void (* toggle_overwrite) (GtkEntry *entry);
+ void (* select_all) (GtkEntry *entry);
};
GtkType gtk_entry_get_type (void) G_GNUC_CONST;
Index: gtk/gtklabel.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtklabel.c,v
retrieving revision 1.98
diff -u -p -r1.98 gtklabel.c
--- gtk/gtklabel.c 2001/08/27 01:05:07 1.98
+++ gtk/gtklabel.c 2001/09/07 16:09:15
@@ -26,6 +26,7 @@
#include <math.h>
#include <string.h>
#include "gtklabel.h"
+#include "gtkmain.h"
#include "gtksignal.h"
#include "gtkwindow.h"
#include "gdk/gdkkeysyms.h"
@@ -33,16 +34,27 @@
#include "gdk/gdki18n.h"
#include <pango/pango.h>
#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,
+ SELECT_ALL,
+ POPULATE_POPUP,
+ LAST_SIGNAL
+};
enum {
PROP_0,
@@ -58,6 +70,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,12 +140,28 @@ static void gtk_label_select_region_inde
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;
@@ -162,11 +192,35 @@ gtk_label_get_type (void)
}
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 +245,43 @@ gtk_label_class_init (GtkLabelClass *cla
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;
+ class->select_all = gtk_label_select_all;
+
+ 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[SELECT_ALL] =
+ gtk_signal_new ("select_all",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkLabelClass, select_all),
+ 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 +360,90 @@ gtk_label_class_init (GtkLabelClass *cla
"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 +1596,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->text_gc[GTK_STATE_NORMAL];
+ 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 +1699,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;
@@ -1477,17 +1721,24 @@ gtk_label_expose (GtkWidget *widget
*/
gdk_gc_set_clip_region (widget->style->white_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,
x, y,
label->layout,
- &widget->style->fg[GTK_STATE_SELECTED],
- &widget->style->bg[GTK_STATE_SELECTED]);
+ &widget->style->fg[state],
+ &widget->style->bg[state]);
gdk_gc_set_clip_region (widget->style->white_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,7 +1888,27 @@ gtk_label_set_text_with_mnemonic (GtkLab
gtk_label_setup_mnemonic (label, last_keyval);
}
+static void
+gtk_label_realize_cursor_gc (GtkLabel *label)
+{
+ GdkColor *cursor_color;
+ 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);
+ if (cursor_color)
+ {
+ label->select_info->cursor_gc = gdk_gc_new (GTK_WIDGET (label)->window);
+ gdk_gc_set_rgb_fg_color (label->select_info->cursor_gc, cursor_color);
+ }
+ else
+ label->select_info->cursor_gc = gdk_gc_ref (GTK_WIDGET (label)->style->base_gc[GTK_STATE_SELECTED]);
+}
+
static void
gtk_label_realize (GtkWidget *widget)
{
@@ -1648,7 +1919,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 +1946,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 +2069,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 +2100,73 @@ gtk_label_button_press (GtkWidget *
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);
- else
- gtk_label_select_region_index (label,
- label->select_info->selection_anchor,
- index);
+ if (!GTK_WIDGET_HAS_FOCUS (widget))
+ gtk_widget_grab_focus (widget);
- /* 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;
- }
+ 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
+ {
+ 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;
}
- else
+ else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
{
- /* start a replacement */
- gtk_label_select_region_index (label, index, index);
+ gtk_label_do_popup (label, event);
+
+ return TRUE;
+
}
-
- return TRUE;
+ return FALSE;
}
static gint
@@ -1948,14 +2290,15 @@ gtk_label_set_selectable (GtkLabel *labe
{
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 +2311,18 @@ gtk_label_set_selectable (GtkLabel *labe
/* 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 +2399,7 @@ clear_text_callback (GtkClipboard *c
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 +2417,7 @@ gtk_label_select_region_index (GtkLabel
{ "COMPOUND_TEXT", 0, 0 },
{ "UTF8_STRING", 0, 0 }
};
-
+
g_return_if_fail (GTK_IS_LABEL (label));
if (label->select_info)
@@ -2321,4 +2669,480 @@ gtk_label_get_use_underline (GtkLabel *l
g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
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);
+ append_action_signal (label, label->select_info->popup_menu, _("Select All"), "select_all",
+ TRUE);
+
+ 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 ());
}
Index: gtk/gtklabel.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtklabel.h,v
retrieving revision 1.34
diff -u -p -r1.34 gtklabel.h
--- gtk/gtklabel.h 2001/08/23 15:26:47 1.34
+++ gtk/gtklabel.h 2001/09/07 16:09:15
@@ -30,8 +30,8 @@
#include <gdk/gdk.h>
#include <gtk/gtkmisc.h>
#include <gtk/gtkwindow.h>
+#include <gtk/gtkmenu.h>
-
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -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;
Index: gtk/gtktextdisplay.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextdisplay.c,v
retrieving revision 1.28
diff -u -p -r1.28 gtktextdisplay.c
--- gtk/gtktextdisplay.c 2001/08/30 19:09:09 1.28
+++ gtk/gtktextdisplay.c 2001/09/07 16:09:15
@@ -250,7 +250,10 @@ render_layout_line (GdkDrawable *
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->fg_gc [GTK_STATE_ACTIVE];
}
else
{
@@ -494,6 +497,7 @@ render_para (GdkDrawable *drawabl
PangoLayoutIter *iter;
PangoRectangle layout_logical;
int screen_width;
+ GdkGC *fg_gc, *bg_gc;
gboolean first = TRUE;
@@ -508,6 +512,17 @@ render_para (GdkDrawable *drawabl
screen_width = line_display->total_width;
+ if (GTK_WIDGET_HAS_FOCUS (render_state->widget))
+ {
+ fg_gc = render_state->widget->style->text_gc [GTK_STATE_SELECTED];
+ bg_gc = render_state->widget->style->base_gc [GTK_STATE_SELECTED];
+ }
+ else
+ {
+ fg_gc = render_state->widget->style->fg_gc [GTK_STATE_ACTIVE];
+ bg_gc = render_state->widget->style->bg_gc [GTK_STATE_ACTIVE];
+ }
+
do
{
PangoLayoutLine *line = pango_layout_iter_get_line (iter);
@@ -547,7 +562,7 @@ render_para (GdkDrawable *drawabl
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 +592,11 @@ render_para (GdkDrawable *drawabl
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 +608,8 @@ render_para (GdkDrawable *drawabl
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 +619,7 @@ render_para (GdkDrawable *drawabl
(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 +639,7 @@ render_para (GdkDrawable *drawabl
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,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]