[evolution/webkit-composer: 73/150] Fix ESpellEntry crash
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/webkit-composer: 73/150] Fix ESpellEntry crash
- Date: Wed, 6 Feb 2013 00:17:04 +0000 (UTC)
commit fe3012253fbd2380dc113599e2f9577ffa32b35f
Author: Dan VrÃtil <dvratil redhat com>
Date: Wed Aug 29 15:43:02 2012 +0200
Fix ESpellEntry crash
composer/e-composer-private.c | 17 ++-
e-util/e-editor-widget.c | 23 ++--
e-util/e-editor-window.c | 4 +-
e-util/e-spell-dictionary.c | 3 +
e-util/e-spell-entry.c | 63 ++++-----
modules/mail/e-mail-shell-backend.c | 2 +-
widgets/editor/selection-style.c | 288 +++++++++++++++++++++++++++++++++++
7 files changed, 345 insertions(+), 55 deletions(-)
---
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index e1ae146..2ebad5c 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -131,13 +131,18 @@ composer_update_gallery_visibility (EMsgComposer *composer)
static void
composer_spell_languages_changed (EMsgComposer *composer,
- GList *languages)
+ GParamSpec *pspec,
+ EEditorWidget *editor_widget)
{
+ GList *languages;
EComposerHeader *header;
EComposerHeaderTable *table = e_msg_composer_get_header_table (composer);
+ languages = e_editor_widget_get_spell_languages (editor_widget);
header = e_composer_header_table_get_header (table, E_COMPOSER_HEADER_SUBJECT);
e_composer_spell_header_set_languages (E_COMPOSER_SPELL_HEADER (header), languages);
+
+ g_list_free (languages);
}
void
@@ -288,17 +293,15 @@ e_composer_private_constructed (EMsgComposer *composer)
priv->header_table = g_object_ref (widget);
gtk_widget_show (widget);
- header = e_composer_header_table_get_header (
- E_COMPOSER_HEADER_TABLE (widget),
+ header = e_composer_header_table_get_header (E_COMPOSER_HEADER_TABLE (widget),
E_COMPOSER_HEADER_SUBJECT);
g_object_bind_property (
shell_settings, "composer-inline-spelling",
header->input_widget, "checking-enabled",
G_BINDING_SYNC_CREATE);
- g_signal_connect (
- G_OBJECT (composer), "spell-languages-changed",
- G_CALLBACK (composer_spell_languages_changed), NULL);
+ g_signal_connect_swapped (editor_widget, "notify::spell-languages",
+ G_CALLBACK (composer_spell_languages_changed), composer);
/* Construct the attachment paned. */
@@ -1018,7 +1021,7 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
"<!--+GtkHTML:<DATA class=\"ClueFlow\" "
" key=\"signature\" value=\"1\">-->"
"<!--+GtkHTML:<DATA class=\"ClueFlow\" "
- " key=\"signature_name\" value=\"uid:%s\">-->",
+ " key=\"signature_name\" value=\"uid:%s\"-->",
(encoded_uid != NULL) ? encoded_uid : "");
g_string_append (
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index 23f8143..83f43a3 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -806,6 +806,15 @@ e_editor_widget_class_init (EEditorWidgetClass *klass)
FALSE,
G_PARAM_READABLE));
+ g_object_class_install_property (
+ object_class,
+ PROP_SPELL_LANGUAGES,
+ g_param_spec_pointer (
+ "spell-languages",
+ "Active spell checking languages",
+ NULL,
+ G_PARAM_READWRITE));
+
signals[POPUP_EVENT] = g_signal_new (
"popup-event",
G_TYPE_FROM_CLASS (klass),
@@ -1064,20 +1073,12 @@ void
e_editor_widget_set_spell_languages (EEditorWidget *widget,
GList *spell_languages)
{
- GList *iter;
-
g_return_if_fail (E_IS_EDITOR_WIDGET (widget));
g_return_if_fail (spell_languages);
- g_list_free_full (widget->priv->spelling_langs, g_free);
-
- widget->priv->spelling_langs = NULL;
- for (iter = spell_languages; iter; iter = g_list_next (iter)) {
- widget->priv->spelling_langs =
- g_list_append (
- widget->priv->spelling_langs,
- g_strdup (iter->data));
- }
+ g_list_free_full (widget->priv->spelling_langs, g_object_unref);
+ widget->priv->spelling_langs = g_list_copy (spell_languages);
+ g_list_foreach (widget->priv->spelling_langs, (GFunc) g_object_ref, NULL);
g_object_notify (G_OBJECT (widget), "spell-languages");
}
diff --git a/e-util/e-editor-window.c b/e-util/e-editor-window.c
index fd55563..30a4286 100644
--- a/e-util/e-editor-window.c
+++ b/e-util/e-editor-window.c
@@ -147,8 +147,10 @@ e_editor_window_pack_above (EEditorWindow *window,
g_return_if_fail (E_IS_EDITOR_WINDOW (window));
g_return_if_fail (GTK_IS_WIDGET (child));
- gtk_grid_insert_row (window->priv->main_layout, window->priv->editor_row);
+ gtk_grid_insert_row (
+ window->priv->main_layout, window->priv->editor_row);
window->priv->editor_row++;
+
gtk_grid_attach_next_to (
window->priv->main_layout, child,
GTK_WIDGET (window->priv->editor),
diff --git a/e-util/e-spell-dictionary.c b/e-util/e-spell-dictionary.c
index e5ed02a..002a5ac 100644
--- a/e-util/e-spell-dictionary.c
+++ b/e-util/e-spell-dictionary.c
@@ -647,5 +647,8 @@ gint
e_spell_dictionary_compare (ESpellDictionary *dict1,
ESpellDictionary *dict2)
{
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dict1), 0);
+ g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dict2), 0);
+
return strcmp (dict1->priv->collate_key, dict2->priv->collate_key);
}
diff --git a/e-util/e-spell-entry.c b/e-util/e-spell-entry.c
index a1c00ca..dd29a1d 100644
--- a/e-util/e-spell-entry.c
+++ b/e-util/e-spell-entry.c
@@ -42,7 +42,7 @@ struct _ESpellEntryPrivate
GSettings *settings;
gboolean custom_checkers;
gboolean checking_enabled;
- GSList *dictionaries;
+ GList *dictionaries;
gchar **words;
gint *word_starts;
gint *word_ends;
@@ -74,12 +74,12 @@ word_misspelled (ESpellEntry *entry,
g_strlcpy (word, text + start, end - start + 1);
if (g_unichar_isalpha (*word)) {
- GSList *li;
+ GList *li;
gssize wlen = strlen (word);
- for (li = entry->priv->dictionaries; li; li = g_slist_next (li)) {
- EnchantDict *dict = li->data;
- if (enchant_dict_check (dict, word, wlen)) {
+ for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
+ ESpellDictionary *dict = li->data;
+ if (e_spell_dictionary_check (dict, word, wlen)) {
result = FALSE;
break;
}
@@ -253,14 +253,14 @@ add_to_dictionary (GtkWidget *menuitem,
{
gchar *word;
gint start, end;
- EnchantDict *dict;
+ ESpellDictionary *dict;
get_word_extents_from_position (entry, &start, &end, entry->priv->mark_character);
word = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
dict = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
if (dict) {
- enchant_dict_add_to_personal (dict, word, -1);
+ e_spell_dictionary_learn_word (dict, word, -1);
}
g_free (word);
@@ -281,14 +281,14 @@ ignore_all (GtkWidget *menuitem,
{
gchar *word;
gint start, end;
- GSList *li;
+ GList *li;
get_word_extents_from_position (entry, &start, &end, entry->priv->mark_character);
word = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
- for (li = entry->priv->dictionaries; li; li = g_slist_next (li)) {
- EnchantDict *dict = li->data;
- enchant_dict_add_to_session (dict, word, -1);
+ for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
+ ESpellDictionary *dict = li->data;
+ e_spell_dictionary_ignore_word (dict, word, -1);
}
g_free (word);
@@ -310,7 +310,7 @@ replace_word (GtkWidget *menuitem,
const gchar *newword;
gint start, end;
gint cursor;
- EnchantDict *dict;
+ ESpellDictionary *dict;
get_word_extents_from_position (entry, &start, &end, entry->priv->mark_character);
oldword = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
@@ -332,7 +332,7 @@ replace_word (GtkWidget *menuitem,
dict = g_object_get_data (G_OBJECT (menuitem), "spell-entry-checker");
if (dict != NULL)
- enchant_dict_store_replacement (dict, oldword, -1, newword, -1);
+ e_spell_dictionary_store_correction (dict, oldword, -1, newword, -1);
g_free (oldword);
}
@@ -414,11 +414,11 @@ build_spelling_menu (ESpellEntry *entry,
dict = entry->priv->dictionaries->data;
build_suggestion_menu (entry, topmenu, dict, word);
} else {
- GSList *li;
+ GList *li;
GtkWidget *menu;
const gchar *lang_name;
- for (li = entry->priv->dictionaries; li; li = g_slist_next (li)) {
+ for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
dict = li->data;
lang_name = e_spell_dictionary_get_name (dict);
@@ -452,14 +452,14 @@ build_spelling_menu (ESpellEntry *entry,
g_object_set_data (G_OBJECT (mi), "spell-entry-checker", dict);
g_signal_connect (G_OBJECT (mi), "activate", G_CALLBACK (add_to_dictionary), entry);
} else {
- GSList *li;
+ GList *li;
GtkWidget *menu, *submi;
const gchar *lang_name;
menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
- for (li = entry->priv->dictionaries; li; li = g_slist_next (li)) {
+ for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
dict = li->data;
lang_name = e_spell_dictionary_get_name (dict);
@@ -570,7 +570,7 @@ spell_entry_changed (GtkEditable *editable)
static void
spell_entry_notify_scroll_offset (ESpellEntry *spell_entry)
{
- g_return_if_fail (spell_entry != NULL);
+ g_return_if_fail (E_IS_SPELL_ENTRY (spell_entry));
g_object_get (G_OBJECT (spell_entry), "scroll-offset", &spell_entry->priv->entry_scroll_offset, NULL);
}
@@ -762,7 +762,7 @@ e_spell_entry_finalize (GObject *object)
g_clear_object (&entry->priv->settings);
g_clear_object (&entry->priv->spell_checker);
- g_slist_free_full (entry->priv->dictionaries, g_object_unref);
+ g_list_free_full (entry->priv->dictionaries, g_object_unref);
if (entry->priv->attr_list)
pango_attr_list_unref (entry->priv->attr_list);
@@ -810,30 +810,23 @@ e_spell_entry_new (void)
void
e_spell_entry_set_languages (ESpellEntry *spell_entry,
- GList *languages)
+ GList *dictionaries)
{
- GList *iter;
-
- g_return_if_fail (spell_entry != NULL);
+ g_return_if_fail (E_IS_SPELL_ENTRY (spell_entry));
spell_entry->priv->custom_checkers = TRUE;
- if (spell_entry->priv->dictionaries)
- g_slist_free_full (spell_entry->priv->dictionaries, g_object_unref);
- spell_entry->priv->dictionaries = NULL;
-
- for (iter = languages; iter; iter = g_list_next (iter)) {
- ESpellDictionary *dict = iter->data;
-
- if (dict)
- spell_entry->priv->dictionaries =
- g_slist_prepend (spell_entry->priv->dictionaries, dict);
+ if (spell_entry->priv->dictionaries) {
+ g_list_free_full (spell_entry->priv->dictionaries, g_object_unref);
}
+ spell_entry->priv->dictionaries = NULL;
- spell_entry->priv->dictionaries = g_slist_reverse (spell_entry->priv->dictionaries);
+ spell_entry->priv->dictionaries = g_list_copy (dictionaries);
+ g_list_foreach (spell_entry->priv->dictionaries, (GFunc) g_object_ref, NULL);
- if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
+ if (gtk_widget_get_realized (GTK_WIDGET (spell_entry))) {
spell_entry_recheck_all (spell_entry);
+ }
}
gboolean
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index 2f7cee6..9a745a6 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -383,7 +383,7 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
editor_widget));
e_editor_widget_set_spell_languages (editor_widget, spell_languages);
- g_list_free (spell_languages);
+ g_list_free_full (spell_languages, g_object_unref);
shell_settings = e_shell_get_shell_settings (shell);
diff --git a/widgets/editor/selection-style.c b/widgets/editor/selection-style.c
new file mode 100644
index 0000000..d75f1f0
--- /dev/null
+++ b/widgets/editor/selection-style.c
@@ -0,0 +1,288 @@
+
+
+/*
+ * DO NOT replace type casting of WebKit types by GLib macros unless
+ * you know what you are doing (I do).
+ *
+ * Probably due to bugs in WebKitGtk+ DOM bindings these macros will
+ * produce runtime warnings, but the objects and class hierarchy ARE VALID.
+ *
+ * This mostly affects only WebKitDOMText, which is subclass
+ * of WebKitDOMNode, but the text nodes are rarely created as instances of
+ * WebKitDOMText. To make sure that you really can cast WebKitDOMNode to
+ * WebKitDOMText, check whether webkit_dom_node_get_node_type() == 3
+ * (3 is "text" node type). WebKitDOMNode is just a thin wrapper around
+ * WebKit's internal WebCore objects. Using get_node_type() is evaluated
+ * against properties of these internal object.
+ */
+
+
+
+static void
+normalize (WebKitDOMNode *node)
+{
+ WebKitDOMNodeList *children;
+ gulong ii;
+
+ /* Standard normalization */
+ webkit_dom_node_normalize (node);
+
+ children = webkit_dom_node_get_child_nodes (node);
+
+ ii = 0;
+ while (ii < webkit_dom_node_list_get_length (children)) {
+ WebKitDOMNode *child, *sibling;
+ gchar *tag_name, *sibling_tag_name;
+
+ child = webkit_dom_node_list_item (children, ii);
+
+ /* We are interested only in nodes representing HTML
+ * elements */
+ if (webkit_dom_node_get_node_type (child) != 1) {
+ ii++;
+ continue;
+ }
+
+ sibling = webkit_dom_node_get_next_sibling (child);
+
+ /* If sibling node is not an element, then skip the current
+ * element and the sibling node as well */
+ if (webkit_dom_node_get_node_type (sibling) != 1) {
+ ii += 2;
+ continue;
+ }
+
+ /* Recursively normalize the child element */
+ normalize (child);
+
+ tag_name = webkit_dom_element_get_tag_name (
+ WEBKIT_DOM_ELEMENT (child));
+ sibling_tag_name = webkit_dom_element_get_tag_name (
+ WEBKIT_DOM_ELEMENT (sibling));
+
+ if (g_strcmp0 (tag_name, sibling_tag_name) == 0) {
+ gchar *str1, *str2, *inner_html;
+
+ str1 = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (child));
+ str2 = webkit_dom_html_element_get_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (sibling));
+ inner_html = g_strconcat (str1, str2, NULL);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (child), inner_html, NULL);
+
+ g_free (str1);
+ g_free (str2);
+ g_free (inner_html);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (sibling),
+ sibling, NULL);
+ }
+
+ ii++;
+ }
+}
+
+static void
+remove_format (EEditorSelection *selection,
+ const gchar *format_tag)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMRange *range;
+ WebKitDOMNode *start_node, *end_node;
+ WebKitDOMElement *common_ancestor;
+
+ document = webkit_web_view_get_dom_document (selection->priv->webview);
+ range = editor_selection_get_current_range (selection);
+
+ start_node = webkit_dom_range_get_start_container (range, NULL);
+ end_node = webkit_dom_range_get_end_container (range, NULL);
+
+ common_ancestor = webkit_dom_node_get_parent_element (
+ webkit_dom_node_get_parent_node (
+ webkit_dom_range_get_common_ancestor_container (
+ range, NULL)));
+
+ /* Cool! The selection is all within one node */
+ if (start_node == end_node) {
+ WebKitDOMElement *element;
+ WebKitDOMNode *node = start_node;
+ WebKitDOMNodeList *children;
+ gchar *wrapper_tag_name;
+
+ if (webkit_dom_node_get_node_type (start_node) != 3) {
+ /* XXX Is it possible for selection to start somewhere
+ * else then in a text node? If yes, what should we
+ * do about it? */
+ return;
+ }
+
+ /* Split <b>|blabla SELECTED TEXT bla|</b> to
+ * <b>|blabla |SELECTED TEXT| bla|</b> (| indicates node) */
+ node = (WebKitDOMNode *) webkit_dom_text_split_text (
+ (WebKitDOMText *) (node),
+ webkit_dom_range_get_start_offset (range, NULL), NULL);
+ webkit_dom_text_split_text (
+ (WebKitDOMText *) node,
+ webkit_dom_range_get_end_offset (range, NULL),
+ NULL);
+
+ element = webkit_dom_node_get_parent_element (node);
+ children = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (element));
+ wrapper_tag_name = webkit_dom_element_get_tag_name (element);
+
+ while (webkit_dom_node_list_get_length (children) > 0) {
+ WebKitDOMNode *child;
+
+ child = webkit_dom_node_list_item (children, 0);
+
+ if (child != node) {
+ WebKitDOMElement *wrapper;
+ wrapper = webkit_dom_document_create_element (
+ document, wrapper_tag_name, NULL);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (wrapper), child, NULL);
+
+ child = WEBKIT_DOM_NODE (wrapper);
+ }
+
+ webkit_dom_node_insert_before (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (element)),
+ child, WEBKIT_DOM_NODE (element), NULL);
+ }
+
+ /* Remove the now empty container */
+ /*
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (
+ WEBKIT_DOM_NODE (element)),
+ WEBKIT_DOM_NODE (element), NULL);
+ */
+
+ g_free (wrapper_tag_name);
+ }
+
+ normalize (WEBKIT_DOM_NODE (common_ancestor));
+}
+
+static void
+apply_format (EEditorSelection *editor_selection,
+ const gchar *format_tag)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMRange *range;
+ WebKitDOMNode *selection, *node;
+ WebKitDOMNode *new_parent;
+ gint format_tag_len = strlen (format_tag);
+ gboolean prev_sibling_match, next_sibling_match;
+
+ prev_sibling_match = FALSE;
+ next_sibling_match = FALSE;
+
+ document = webkit_web_view_get_dom_document (editor_selection->priv->webview);
+ range = editor_selection_get_current_range (editor_selection);
+
+ if (webkit_dom_range_get_start_offset (range, NULL) != 0) {
+ node = webkit_dom_range_get_start_container (range, NULL);
+ selection = (WebKitDOMNode*) webkit_dom_text_split_text (
+ (WebKitDOMText *) node,
+ webkit_dom_range_get_start_offset (range, NULL), NULL);
+ } else {
+ selection = webkit_dom_range_get_start_container (range, NULL);
+ }
+
+ webkit_dom_text_split_text ((WebKitDOMText *) selection,
+ webkit_dom_range_get_end_offset (range, NULL), NULL);
+
+ /* The split above might have produced an empty text node
+ * (for example splitting "TEXT" on offset 4 will produce
+ * "TEXT" and "" nodes), so remove it */
+ node = webkit_dom_node_get_next_sibling (selection);
+ if (webkit_dom_node_get_node_type (node) == 3) {
+ gchar *content;
+
+ content = webkit_dom_node_get_text_content (node);
+ if (!content || (strlen (content) == 0)) {
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (node),
+ node, NULL);
+ }
+
+ g_free (content);
+ }
+
+ /* Check whether previous sibling is an element and whether it is <format_tag> */
+ node = webkit_dom_node_get_previous_sibling (selection);
+ if (node && (webkit_dom_node_get_node_type (node) == 1)) {
+ gchar *tag_name;
+
+ tag_name = webkit_dom_element_get_tag_name (
+ (WebKitDOMElement *) node);
+ prev_sibling_match = ((format_tag_len == strlen (tag_name)) &&
+ (g_ascii_strncasecmp (
+ format_tag, tag_name,
+ format_tag_len) == 0));
+ g_free (tag_name);
+ }
+
+ /* Check whether next sibling is an element and whether it is <format_tag> */
+ node = webkit_dom_node_get_next_sibling (selection);
+ if (node && (webkit_dom_node_get_node_type (node) == 1)) {
+ gchar *tag_name;
+
+ tag_name = webkit_dom_element_get_tag_name (
+ (WebKitDOMElement *) node);
+ next_sibling_match = ((format_tag_len == strlen (tag_name)) &&
+ (g_ascii_strncasecmp (
+ format_tag, tag_name,
+ format_tag_len) == 0));
+ g_free (tag_name);
+ }
+
+ /* Merge selection and next sibling to the orevious sibling */
+ if (prev_sibling_match && next_sibling_match) {
+ WebKitDOMNode *next_sibling, *child;
+
+ new_parent = webkit_dom_node_get_previous_sibling (selection);
+ next_sibling = webkit_dom_node_get_next_sibling (selection);
+
+ /* Append selection to the new parent */
+ webkit_dom_node_append_child (new_parent, selection, NULL);
+
+ /* Append all children of next sibling to the new parent */
+ while ((child = webkit_dom_node_get_first_child (next_sibling)) != NULL) {
+ webkit_dom_node_append_child (new_parent, child, NULL);
+ }
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (next_sibling),
+ next_sibling, NULL);
+
+ /* Merge selection to the previous sibling */
+ } else if (prev_sibling_match && !next_sibling_match) {
+ new_parent = webkit_dom_node_get_previous_sibling (selection);
+ webkit_dom_node_append_child (new_parent, selection, NULL);
+
+ /* Merge selection to the next sibling */
+ } else if (!prev_sibling_match && next_sibling_match) {
+ new_parent = webkit_dom_node_get_next_sibling (selection);
+ webkit_dom_node_insert_before (
+ new_parent, selection,
+ webkit_dom_node_get_first_child (new_parent), NULL);
+
+ /* Just wrap the selection to <tag_name> */
+ } else {
+ new_parent = (WebKitDOMNode *)
+ webkit_dom_document_create_element (
+ document, format_tag, NULL);
+ webkit_dom_range_surround_contents (range, new_parent, NULL);
+ }
+
+ webkit_dom_node_normalize (
+ (WebKitDOMNode *) webkit_dom_node_get_parent_element (new_parent));
+}
+
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]