[ghex/gtk4-port: 52/91] Sane copy/paste; allow opening files from cmdline
- From: Logan Rathbone <larathbone src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ghex/gtk4-port: 52/91] Sane copy/paste; allow opening files from cmdline
- Date: Thu, 12 Aug 2021 23:35:10 +0000 (UTC)
commit 87d004d9938b77bda7dadb52f38550f59a54d0f3
Author: Logan Rathbone <poprocks gmail com>
Date: Mon Jan 25 00:22:43 2021 -0500
Sane copy/paste; allow opening files from cmdline
src/Makefile | 2 +-
src/ghex-application-window.c | 32 +++-
src/ghex-application-window.h | 2 +
src/gtkhex.c | 393 +++++++++++++++++++++++++++++++++++-------
src/hex-document.c | 14 +-
src/hex-document.h | 4 +-
src/main.c | 48 +++++-
7 files changed, 413 insertions(+), 82 deletions(-)
---
diff --git a/src/Makefile b/src/Makefile
index 24dc351f..4fe665bd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,6 +1,6 @@
# TEMPORARY MAKEFILE FOR TESTING PURPOSES ONLY
-DEBUG=-g -DENABLE_DEBUG
+DEBUG=-g
CC=gcc
GTK_CFLAGS=$(shell pkg-config --libs --cflags gtk4)
CFLAGS=-Wall -Wextra -Werror=implicit -std=c11 -pedantic \
diff --git a/src/ghex-application-window.c b/src/ghex-application-window.c
index 23d4173f..118bbd68 100644
--- a/src/ghex-application-window.c
+++ b/src/ghex-application-window.c
@@ -1258,14 +1258,16 @@ do_print (GtkWidget *widget,
static GtkHex *
new_gh_from_gfile (GFile *file)
{
+ GFile *my_file;
char *path;
GFileInfo *info;
GError *error = NULL;
HexDocument *doc;
GtkHex *gh;
- path = g_file_get_path (file);
- info = g_file_query_info (file,
+ my_file = g_object_ref (file);
+ path = g_file_get_path (my_file);
+ info = g_file_query_info (my_file,
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
G_FILE_QUERY_INFO_NONE, // GFileQueryInfoFlags flags
NULL, // GCancellable
*cancellable
@@ -1279,7 +1281,7 @@ new_gh_from_gfile (GFile *file)
if (error) g_error_free (error);
g_clear_object (&info);
- g_clear_object (&file);
+ g_object_unref (my_file);
return gh;
}
@@ -1292,16 +1294,12 @@ open_response_cb (GtkNativeDialog *dialog,
GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
GFile *file;
- GtkHex *gh;
if (resp == GTK_RESPONSE_ACCEPT)
{
file = gtk_file_chooser_get_file (chooser);
- gh = new_gh_from_gfile (file);
- ghex_application_window_add_hex (self, gh);
- ghex_application_window_set_hex (self, gh);
- ghex_application_window_activate_tab (self, gh);
+ ghex_application_window_open_file (self, file);
}
else
{
@@ -2190,3 +2188,21 @@ ghex_application_window_get_list (GHexApplicationWindow *self)
return self->gh_list;
}
+
+void
+ghex_application_window_open_file (GHexApplicationWindow *self, GFile *file)
+{
+ GtkHex *gh;
+ GFile *my_file;
+
+ g_return_if_fail (GHEX_IS_APPLICATION_WINDOW(self));
+
+ my_file = g_object_ref (file);
+ gh = new_gh_from_gfile (my_file);
+
+ ghex_application_window_add_hex (self, gh);
+ ghex_application_window_set_hex (self, gh);
+ ghex_application_window_activate_tab (self, gh);
+
+ g_object_unref (my_file);
+}
diff --git a/src/ghex-application-window.h b/src/ghex-application-window.h
index c0a50771..0079116a 100644
--- a/src/ghex-application-window.h
+++ b/src/ghex-application-window.h
@@ -29,5 +29,7 @@ void ghex_application_window_set_hex (GHexApplicationWindow *self,
void ghex_application_window_activate_tab (GHexApplicationWindow *self,
GtkHex *gh);
GList * ghex_application_window_get_list (GHexApplicationWindow *self);
+void ghex_application_window_open_file (GHexApplicationWindow *self,
+ GFile *file);
#endif
diff --git a/src/gtkhex.c b/src/gtkhex.c
index c5ef6253..7ddc250c 100644
--- a/src/gtkhex.c
+++ b/src/gtkhex.c
@@ -135,6 +135,35 @@ struct _GtkHex_AutoHighlight
GtkHex_AutoHighlight *next, *prev;
};
+/* GtkHexPasteData - allow us to get around the issue of C-strings being
+ * nul-teminated, so we can paste `0x00`'s cleanly.
+ */
+#define GTK_TYPE_HEX_PASTE_DATA (gtk_hex_paste_data_get_type ())
+G_DECLARE_FINAL_TYPE (GtkHexPasteData, gtk_hex_paste_data, GTK, HEX_PASTE_DATA,
+ GObject)
+
+#define GTK_HEX_PASTE_DATA_MAGIC 256
+
+/* GtkHexPasteData - Method Declarations */
+
+static GtkHexPasteData * gtk_hex_paste_data_new (guchar *doc_data,
+ guint elems);
+
+/* GtkHexPasteData - GObject Definition */
+
+struct _GtkHexPasteData
+{
+ GObject parent_instance;
+
+ guchar *doc_data;
+ int *paste_data;
+ guint elems;
+};
+
+G_DEFINE_TYPE (GtkHexPasteData, gtk_hex_paste_data, G_TYPE_OBJECT)
+
+/* </GtkHexPasteData Decls> */
+
/* TODO / NOTE - 'GtkHexClass' previously had these members:
* GtkClipboard *clipboard, *primary;
* so when you see ->clipboard and ->primary, these are related to
@@ -227,8 +256,151 @@ static void gtk_hex_update_auto_highlight(GtkHex *gh, GtkHex_AutoHighlight *ahl,
gboolean delete, gboolean add);
static void recalc_displays(GtkHex *gh);
+static char *doc_data_to_string (const guchar *data, guint len);
+
+/* GtkHexPasteData - Helper Functions */
+
+/* Helper function for the copy and paste stuff, since the data returned by
+ * hex_document_get_data is NOT null-temrinated.
+ *
+ * String returned should be freed with g_free.
+ */
+static char *
+doc_data_to_string (const guchar *data, guint len)
+{
+ char *str;
+
+ str = g_malloc (len + 1);
+ memcpy (str, data, len);
+ str[len] = '\0';
+
+ return str;
+}
+
+/* Creates a magic_int_array for GtkHexPasteData. Should be freed with g_free.
+ */
+static int *
+doc_data_to_magic_int_array (const guchar *data, guint len)
+{
+ int *arr = 0;
+ guint i;
+
+ arr = g_malloc (len * (sizeof *arr));
+
+ for (i = 0; i < len; ++i)
+ {
+ arr[i] = data[i];
+
+ if (arr[i] == 0)
+ arr[i] = GTK_HEX_PASTE_DATA_MAGIC;
+ }
+ return arr;
+}
+
+static guchar *
+magic_int_array_to_data (int *arr, guint len)
+{
+ guchar *data;
+ guint i;
+
+ data = g_malloc (len);
+
+ for (i = 0;
+ i < len;
+ ++i, ++arr, ++data)
+ {
+ g_assert (arr);
+
+ if (*arr == GTK_HEX_PASTE_DATA_MAGIC) {
+ *data = 0;
+ } else if (*arr < GTK_HEX_PASTE_DATA_MAGIC) {
+ *data = *arr;
+ } else {
+ g_error ("%s: Programmer error. Nothing in a magic_int_array "
+ "shall be greater than %d",
+ __func__,
+ GTK_HEX_PASTE_DATA_MAGIC);
+ }
+ }
+ return data;
+}
+
+// TEST - this transforms certain problematic characters for copy/paste
+// to a '?'. Maybe find a home for this guy at some point.
+#if 0
+{
+ char *cp;
+ for (cp = text; *cp != '\0'; ++cp)
+ {
+ if (! is_copyable(*cp))
+ *cp = '?';
+ }
+}
+#endif
+
+/* GtkHexPasteData - Constructors and Destructors */
+static void
+gtk_hex_paste_data_init (GtkHexPasteData *self)
+{
+ g_debug ("%s: doc_data: %p - elems: %u",
+ __func__, (void *)self->doc_data, self->elems);
+}
+
+static void
+gtk_hex_paste_data_class_init (GtkHexPasteDataClass *klass)
+{
+ /* <boilerplate> */
+// GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+// object_class->dispose = demo_widget_dispose;
+// object_class->finalize = demo_widget_finalize;
+ /* </boilerplate> */
+}
+
+
+/* GtkHexPasteData - Method Definitions (all private) */
+
+static GtkHexPasteData *
+gtk_hex_paste_data_new (guchar *doc_data, guint elems)
+{
+ GtkHexPasteData *self;
+
+ g_return_val_if_fail (doc_data, NULL);
+ g_return_val_if_fail (elems, NULL);
+
+ self = g_object_new (GTK_TYPE_HEX_PASTE_DATA, NULL);
+
+ self->doc_data = doc_data;
+ self->elems = elems;
+
+ self->paste_data = doc_data_to_magic_int_array (self->doc_data,
+ self->elems);
+
+ g_debug ("%s: doc_data: %p - elems: %u",
+ __func__, (void *)self->doc_data, self->elems);
+
+ g_assert (self->paste_data);
+
+ return self;
+}
+
+/* String returned should be freed with g_free. */
+static char *
+gtk_hex_paste_data_get_string (GtkHexPasteData *self)
+{
+ char *string;
+
+ g_return_val_if_fail (self->doc_data, NULL);
+ g_return_val_if_fail (self->elems, NULL);
+
+ string = doc_data_to_string (self->doc_data, self->elems);
+
+ return string;
+}
+
+/* GtkHex - Method Definitions */
static void
popup_context_menu(GtkWidget *widget, double x, double y)
@@ -271,6 +443,34 @@ copy_action (GtkWidget *widget,
gtk_hex_copy_to_clipboard (gh);
}
+static void
+cut_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter)
+{
+ GtkHex *gh = GTK_HEX(widget);
+
+ g_return_if_fail (GTK_IS_HEX(gh));
+
+ (void)action_name, (void)parameter;
+
+ gtk_hex_cut_to_clipboard (gh);
+}
+
+static void
+paste_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter)
+{
+ GtkHex *gh = GTK_HEX(widget);
+
+ g_return_if_fail (GTK_IS_HEX(gh));
+
+ (void)action_name, (void)parameter;
+
+ gtk_hex_paste_from_clipboard (gh);
+}
+
static void
redo_action (GtkWidget *widget,
const char *action_name,
@@ -1341,15 +1541,9 @@ display_scrolled (GtkAdjustment *adj, GtkHex *gh)
gint dx;
gint dy;
- TEST_DEBUG_FUNCTION_START
-
g_return_if_fail (gtk_widget_is_drawable (gh->xdisp) &&
gtk_widget_is_drawable (gh->adisp));
- g_debug("%s: ADJ - VALUE: %f",
- __func__,
- gtk_adjustment_get_value (gh->adj));
-
gh->top_line = gtk_adjustment_get_value (adj);
gtk_hex_update_all_auto_highlights(gh, TRUE, TRUE);
@@ -1363,8 +1557,6 @@ display_scrolled (GtkAdjustment *adj, GtkHex *gh)
gtk_widget_queue_draw (GTK_WIDGET(gh->xdisp));
gtk_widget_queue_draw (GTK_WIDGET(gh->offsets));
gtk_widget_queue_draw (GTK_WIDGET(gh));
-
- TEST_DEBUG_FUNCTION_END
}
/*
@@ -1956,6 +2148,8 @@ key_release_cb (GtkEventControllerKey *controller,
if (state & GDK_SHIFT_MASK) {
gh->selecting = FALSE;
}
+
+ return ret;
}
@@ -2382,85 +2576,159 @@ gtk_hex_real_copy_to_clipboard (GtkHex *gh)
{
GtkWidget *widget = GTK_WIDGET(gh);
GdkClipboard *clipboard;
- int start_pos, end_pos;
+ GtkHexPasteData *paste;
+ GdkContentProvider *provider_union;
+ GdkContentProvider *provider_array[2];
+ guint start_pos, end_pos, len;
+ guchar *doc_data;
+ char *string;
TEST_DEBUG_FUNCTION_START
- start_pos = MIN(gh->selection.start, gh->selection.end);
- end_pos = MAX(gh->selection.start, gh->selection.end);
-
clipboard = gtk_widget_get_clipboard (widget);
- if(start_pos != end_pos) {
- char *text;
- char *cp;
-
- text = hex_document_get_data(gh->document,
- start_pos,
- end_pos - start_pos);
+ start_pos = MIN(gh->selection.start, gh->selection.end);
+ end_pos = MAX(gh->selection.start, gh->selection.end);
- // TEST
+ /* +1 because we're counting the number of characters to grab here.
+ * You have to actually include the first character in the range.
+ */
+ len = end_pos - start_pos + 1;
+ g_return_if_fail (len);
- for (cp = text; *cp != '\0'; ++cp)
- {
-// if (! is_displayable(*cp))
- if (! is_copyable(*cp))
- *cp = '?';
- }
+ /* Grab the raw data from the HexDocument. */
+ doc_data = hex_document_get_data (gh->document, start_pos, len);
- printf("%s\n", text);
+ /* Setup a union of HexPasteData and a plain C string */
+ paste = gtk_hex_paste_data_new (doc_data, len);
+ g_return_if_fail (GTK_IS_HEX_PASTE_DATA(paste));
+ string = gtk_hex_paste_data_get_string (paste);
- gdk_clipboard_set_text (clipboard, text);
+ provider_array[0] =
+ gdk_content_provider_new_typed (GTK_TYPE_HEX_PASTE_DATA, paste);
+ provider_array[1] =
+ gdk_content_provider_new_typed (G_TYPE_STRING, string);
- g_free(text);
- }
+ provider_union = gdk_content_provider_new_union (provider_array, 2);
-#if 0
- gint start_pos;
- gint end_pos;
- GtkHexClass *klass = GTK_HEX_CLASS(GTK_WIDGET_GET_CLASS(gh));
+ /* Finally, set our content to our newly created union. */
+ gdk_clipboard_set_content (clipboard, provider_union);
- start_pos = MIN(gh->selection.start, gh->selection.end);
- end_pos = MAX(gh->selection.start, gh->selection.end);
-
- if(start_pos != end_pos) {
- guchar *text = hex_document_get_data(gh->document, start_pos,
- end_pos - start_pos);
- gtk_clipboard_set_text(klass->clipboard, text, end_pos - start_pos);
- g_free(text);
- }
-#endif
+ TEST_DEBUG_FUNCTION_END
}
-static void gtk_hex_real_cut_to_clipboard(GtkHex *gh,
+static void
+gtk_hex_real_cut_to_clipboard(GtkHex *gh,
gpointer user_data)
{
- if(gh->selection.start != -1 && gh->selection.end != -1) {
+ (void)user_data;
+
+ if (gh->selection.start != -1 && gh->selection.end != -1) {
gtk_hex_real_copy_to_clipboard(gh);
gtk_hex_delete_selection(gh);
}
}
-static void gtk_hex_real_paste_from_clipboard(GtkHex *gh,
+static void
+plaintext_paste_received_cb (GObject *source_object,
+ GAsyncResult *result,
gpointer user_data)
{
- g_debug("%s: NOT IMPLEMENTED", __func__);
+ GtkHex *gh = GTK_HEX(user_data);
+ GdkClipboard *clipboard;
+ char *text;
+ GError *error = NULL;
- // API CHANGES - clipboard stuff
-#if 0
- GtkHexClass *klass = GTK_HEX_CLASS(GTK_WIDGET_GET_CLASS(gh));
- gchar *text;
+ g_debug ("%s: We DON'T have our special HexPasteData. Falling back "
+ "to plaintext paste.",
+ __func__);
+
+ clipboard = GDK_CLIPBOARD (source_object);
+
+ /* Get the resulting text of the read operation */
+ text = gdk_clipboard_read_text_finish (clipboard, result, &error);
+
+ if (text) {
+ hex_document_set_data (gh->document,
+ gh->cursor_pos,
+ strlen(text),
+ 0, /* rep_len (0 to insert w/o replacing; what we want) */
+ (guchar *)text,
+ TRUE);
- text = gtk_clipboard_wait_for_text(klass->clipboard);
- if(text) {
- hex_document_set_data(gh->document, gh->cursor_pos,
- strlen(text), 0, text, TRUE);
gtk_hex_set_cursor(gh, gh->cursor_pos + strlen(text));
+
g_free(text);
}
-#endif
+ else {
+ g_critical ("Error pasting text: %s",
+ error->message);
+ g_error_free (error);
+ }
}
+
+static void
+gtk_hex_real_paste_from_clipboard (GtkHex *gh,
+ gpointer user_data)
+{
+ GtkWidget *widget = GTK_WIDGET(gh);
+ GdkClipboard *clipboard;
+ GdkContentProvider *content;
+ GValue value = G_VALUE_INIT;
+ GtkHexPasteData *paste;
+ gboolean have_hex_paste_data = FALSE;
+
+ TEST_DEBUG_FUNCTION_START
+
+ (void)user_data;
+
+ clipboard = gtk_widget_get_clipboard (widget);
+ content = gdk_clipboard_get_content (clipboard);
+ g_value_init (&value, GTK_TYPE_HEX_PASTE_DATA);
+
+ /* If the clipboard contains our special HexPasteData, we'll use it.
+ * If not, just fall back to plaintext.
+ *
+ * Note the double test here; it seems the test is semi-superfluous for
+ * *this* purpose because _get_content will itself return NULL if the
+ * clipboard data we're getting is not owned by the process; that will
+ * pretty much *always* be the case when we're falling back to plaintext,
+ * ie, when pasting from external apps. Oh well.
+ */
+ have_hex_paste_data =
+ GDK_IS_CONTENT_PROVIDER (content) &&
+ gdk_content_provider_get_value (content,
+ &value,
+ NULL); /* GError - NULL to ignore */
+
+ if (have_hex_paste_data)
+ {
+ g_debug("%s: We HAVE our special HexPasteData.",
+ __func__);
+
+ paste = GTK_HEX_PASTE_DATA(g_value_get_object (&value));
+
+ hex_document_set_data (gh->document,
+ gh->cursor_pos,
+ paste->elems,
+ 0, /* rep_len (0 to insert w/o replacing; what we want) */
+ paste->doc_data,
+ TRUE);
+
+ gtk_hex_set_cursor(gh, gh->cursor_pos + paste->elems);
+ }
+ else {
+ gdk_clipboard_read_text_async (clipboard,
+ NULL, /* GCancellable *cancellable */
+ plaintext_paste_received_cb,
+ gh);
+ }
+
+ TEST_DEBUG_FUNCTION_END
+}
+
+
static void
gtk_hex_real_draw_complete (GtkHex *gh,
gpointer user_data)
@@ -2473,7 +2741,8 @@ gtk_hex_real_draw_complete (GtkHex *gh,
TEST_DEBUG_FUNCTION_END
}
-static void gtk_hex_finalize(GObject *gobject) {
+static void
+gtk_hex_finalize (GObject *gobject) {
GtkHex *gh = GTK_HEX(gobject);
if (gh->disp_buffer)
@@ -2643,6 +2912,14 @@ gtk_hex_class_init (GtkHexClass *klass)
NULL, // GVariant string param_type
copy_action);
+ gtk_widget_class_install_action (widget_class, "gtkhex.cut",
+ NULL, // GVariant string param_type
+ cut_action);
+
+ gtk_widget_class_install_action (widget_class, "gtkhex.paste",
+ NULL, // GVariant string param_type
+ paste_action);
+
gtk_widget_class_install_action (widget_class, "gtkhex.undo",
NULL, // GVariant string param_type
undo_action);
diff --git a/src/hex-document.c b/src/hex-document.c
index 97524ea7..1da6dcd9 100644
--- a/src/hex-document.c
+++ b/src/hex-document.c
@@ -509,15 +509,17 @@ hex_document_get_data(HexDocument *doc, guint offset, guint len)
guint i;
ptr = doc->buffer + offset;
- if(ptr >= doc->gap_pos)
+
+ if (ptr >= doc->gap_pos)
ptr += doc->gap_size;
- dptr = data = g_malloc(sizeof(guchar)*len);
- i = 0;
- while(i < len) {
- if(ptr >= doc->gap_pos && ptr < doc->gap_pos + doc->gap_size)
+
+ dptr = data = g_malloc(len * sizeof(guchar));
+
+ for (i = 0; i < len; ++i) {
+ if (ptr >= doc->gap_pos && ptr < doc->gap_pos + doc->gap_size)
ptr += doc->gap_size;
+
*dptr++ = *ptr++;
- i++;
}
return data;
diff --git a/src/hex-document.h b/src/hex-document.h
index bb27c2c2..9067cbec 100644
--- a/src/hex-document.h
+++ b/src/hex-document.h
@@ -47,7 +47,9 @@ typedef enum {
struct _HexChangeData
{
- guint start, end, rep_len;
+ guint start, end;
+ /* length to replace (overwrite); (0 to insert without overwriting) */
+ guint rep_len;
gboolean lower_nibble;
gboolean insert;
HexChangeType type;
diff --git a/src/main.c b/src/main.c
index b6314940..a071ff01 100644
--- a/src/main.c
+++ b/src/main.c
@@ -29,6 +29,8 @@
#include "ghex-application-window.h"
+static GtkWindow *window = NULL;
+
/* FIXME - TEST ON WIN32.
* This is one of the few functions in this file that has been ripped verbatim
* from the old main.c. It might work. Maybe.
@@ -44,7 +46,8 @@ ghex_win32_locale_dir (void)
install_dir = g_win32_get_package_installation_directory_of_module (NULL);
if (install_dir) {
- utf8_locale_dir = g_build_filename (install_dir, "share", "locale", NULL);
+ utf8_locale_dir = g_build_filename (install_dir,
+ "share", "locale", NULL);
locale_dir = g_win32_locale_filename_from_utf8 (utf8_locale_dir);
g_free (install_dir);
@@ -65,18 +68,43 @@ ghex_locale_dir (void)
#endif
}
+static void
+do_app_window (GtkApplication *app)
+{
+ if (! window)
+ window = GTK_WINDOW(ghex_application_window_new (app));
+ else
+ g_return_if_fail (GHEX_IS_APPLICATION_WINDOW
+ (GHEX_APPLICATION_WINDOW(window)));
+}
+
static void
activate (GtkApplication *app,
gpointer user_data)
{
- GtkWidget *window;
-
(void)user_data; /* unused */
- window = ghex_application_window_new (app);
+ do_app_window (app);
+
+ gtk_window_set_application (window, app);
+ gtk_window_present (window);
+}
+
+static void
+open (GApplication *application,
+ GFile **files,
+ int n_files,
+ const char *hint,
+ gpointer user_data)
+{
+ GHexApplicationWindow *app_win;
- gtk_window_set_application (GTK_WINDOW(window), app);
- gtk_window_present (GTK_WINDOW(window));
+ if (n_files > 1)
+ g_warning ("Can only open a single file");
+
+ activate (GTK_APPLICATION(application), NULL);
+ app_win = GHEX_APPLICATION_WINDOW(window);
+ ghex_application_window_open_file (app_win, files[0]);
}
int
@@ -98,9 +126,13 @@ main (int argc, char *argv[])
ghex_init_configuration ();
- /* FIXME - don't know if NON_UNIQUE is correct for this context. */
- app = gtk_application_new("org.gnome.GHex", G_APPLICATION_NON_UNIQUE);
+ /* FIXME - not 100% decided on NON_UNIQUE for this as yet. */
+ app = gtk_application_new ("org.gnome.GHex",
+ G_APPLICATION_NON_UNIQUE | G_APPLICATION_HANDLES_OPEN);
+
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
+ g_signal_connect (app, "open", G_CALLBACK(open), NULL);
+
g_application_register (G_APPLICATION (app), NULL, NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]