[anjuta/git-shell: 11/12] git: Make the status command automatically refresh
- From: James Liggett <jrliggett src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [anjuta/git-shell: 11/12] git: Make the status command automatically refresh
- Date: Mon, 14 Jun 2010 23:53:52 +0000 (UTC)
commit 1a3be0bdb8d319454d2480c7a36b07a16997f6ff
Author: James Liggett <jrliggett cox net>
Date: Mon Jun 14 14:43:36 2010 -0700
git: Make the status command automatically refresh
It can now at least detect commits and index changes. File modifications aren't
detected though.
plugins/git/git-status-command.c | 132 +++++++++++++++++++-
plugins/git/git-status-pane.c | 255 +++++++++++++++++++-------------------
plugins/git/plugin.c | 45 +++++--
plugins/git/plugin.h | 7 +-
4 files changed, 294 insertions(+), 145 deletions(-)
---
diff --git a/plugins/git/git-status-command.c b/plugins/git/git-status-command.c
index 67268c4..f0188d9 100644
--- a/plugins/git/git-status-command.c
+++ b/plugins/git/git-status-command.c
@@ -42,6 +42,8 @@ struct _GitStatusCommandPriv
GRegex *section_commit_regex;
GRegex *section_not_updated_regex;
GRegex *section_untracked_regex;
+ GFileMonitor *head_monitor;
+ GFileMonitor *index_monitor;
};
G_DEFINE_TYPE (GitStatusCommand, git_status_command, GIT_TYPE_COMMAND);
@@ -146,6 +148,125 @@ git_status_command_init (GitStatusCommand *self)
}
static void
+on_file_monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other_file,
+ GFileMonitorEvent event, AnjutaCommand *command)
+{
+ /* Handle created and modified events just to cover all possible cases.
+ * Sometimes git does some odd things... */
+ if (event == G_FILE_MONITOR_EVENT_CHANGED ||
+ event == G_FILE_MONITOR_EVENT_CREATED)
+ {
+ anjuta_command_start (command);
+ }
+}
+
+static gboolean
+git_status_command_start_automatic_monitor (AnjutaCommand *command)
+{
+ GitStatusCommand *self;
+ gchar *working_directory;
+ gchar *git_head_path;
+ gchar *git_index_path;
+ GFile *git_head_file;
+ GFile *git_index_file;
+
+ self = GIT_STATUS_COMMAND (command);
+
+ g_object_get (self, "working-directory", &working_directory, NULL);
+
+ /* Watch for changes to the HEAD file and the index file, so that we can
+ * at least detect commits and index changes. */
+ git_head_path = g_strjoin (G_DIR_SEPARATOR_S,
+ working_directory,
+ ".git",
+ "HEAD",
+ NULL);
+ git_index_path = g_strjoin (G_DIR_SEPARATOR_S,
+ working_directory,
+ ".git",
+ "index",
+ NULL);
+ git_head_file = g_file_new_for_path (git_head_path);
+ git_index_file = g_file_new_for_path (git_index_path);
+ self->priv->head_monitor = g_file_monitor_file (git_head_file, 0, NULL,
+ NULL);
+ self->priv->index_monitor = g_file_monitor_file (git_index_file, 0, NULL,
+ NULL);
+
+ g_signal_connect (G_OBJECT (self->priv->head_monitor), "changed",
+ G_CALLBACK (on_file_monitor_changed),
+ command);
+
+ g_signal_connect (G_OBJECT (self->priv->index_monitor), "changed",
+ G_CALLBACK (on_file_monitor_changed),
+ command);
+
+ g_free (git_head_path);
+ g_free (git_index_path);
+ g_object_unref (git_head_file);
+ g_object_unref (git_index_file);
+
+ return TRUE;
+}
+
+static void
+git_status_command_stop_automatic_monitor (AnjutaCommand *command)
+{
+ GitStatusCommand *self;
+
+ self = GIT_STATUS_COMMAND (command);
+
+ if (self->priv->head_monitor)
+ {
+ g_file_monitor_cancel (self->priv->head_monitor);
+ g_object_unref (self->priv->head_monitor);
+ self->priv->head_monitor = NULL;
+ }
+
+ if (self->priv->index_monitor)
+ {
+ g_file_monitor_cancel (self->priv->index_monitor);
+ g_object_unref (self->priv->index_monitor);
+ self->priv->index_monitor = NULL;
+ }
+}
+
+static void
+git_status_command_clear_output (GitStatusCommand *self)
+{
+ GList *current_output;
+
+ current_output = self->priv->status_queue->head;
+
+ while (current_output)
+ {
+ g_object_unref (current_output->data);
+ current_output = g_list_next (current_output);
+ }
+
+ g_queue_clear (self->priv->status_queue);
+}
+
+static void
+git_status_command_data_arrived (AnjutaCommand *command)
+{
+ git_status_command_clear_output (GIT_STATUS_COMMAND (command));
+}
+
+static void
+git_status_command_finished (AnjutaCommand *command, guint return_code)
+{
+ GitStatusCommand *self;
+
+ self = GIT_STATUS_COMMAND (command);
+
+ g_hash_table_remove_all (self->priv->path_lookup_table);
+
+ ANJUTA_COMMAND_CLASS (git_status_command_parent_class)->command_finished (command,
+ return_code);
+}
+
+static void
git_status_command_finalize (GObject *object)
{
GitStatusCommand *self;
@@ -154,11 +275,8 @@ git_status_command_finalize (GObject *object)
self = GIT_STATUS_COMMAND (object);
current_status = self->priv->status_queue->head;
- while (current_status)
- {
- g_object_unref (current_status->data);
- current_status = g_list_next (current_status);
- }
+ git_status_command_clear_output (self);
+ git_status_command_stop_automatic_monitor (ANJUTA_COMMAND (self));
g_queue_free (self->priv->status_queue);
g_hash_table_destroy (self->priv->path_lookup_table);
@@ -183,6 +301,10 @@ git_status_command_class_init (GitStatusCommandClass *klass)
object_class->finalize = git_status_command_finalize;
parent_class->output_handler = git_status_command_handle_output;
command_class->run = git_status_command_run;
+ command_class->data_arrived = git_status_command_data_arrived;
+ command_class->command_finished = git_status_command_finished;
+ command_class->start_automatic_monitor = git_status_command_start_automatic_monitor;
+ command_class->stop_automatic_monitor = git_status_command_stop_automatic_monitor;
}
diff --git a/plugins/git/git-status-pane.c b/plugins/git/git-status-pane.c
index c1d68b9..f08f245 100644
--- a/plugins/git/git-status-pane.c
+++ b/plugins/git/git-status-pane.c
@@ -261,92 +261,6 @@ on_selected_renderer_toggled (GtkCellRendererToggle *renderer, gchar *tree_path,
}
static void
-git_status_pane_init (GitStatusPane *self)
-{
- gchar *objects[] = {"status_pane",
- "status_model",
- NULL};
- GError *error = NULL;
- GtkTreeViewColumn *status_column;
- GtkCellRenderer *selected_renderer;
- GtkCellRenderer *status_icon_renderer;
- GtkCellRenderer *status_name_renderer;
- GtkCellRenderer *path_renderer;
- GtkWidget *refresh_button;
-
- self->priv = g_new0 (GitStatusPanePriv, 1);
- self->priv->builder = gtk_builder_new ();
- self->priv->selected_commit_items = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- NULL);
- self->priv->selected_not_updated_items = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- NULL);
-
- if (!gtk_builder_add_objects_from_file (self->priv->builder, BUILDER_FILE,
- objects,
- &error))
- {
- g_warning ("Couldn't load builder file: %s", error->message);
- g_error_free (error);
- }
-
- status_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
- "status_column"));
- selected_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
- "selected_renderer"));
- status_icon_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
- "status_icon_renderer"));
- status_name_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
- "status_name_renderer"));
- path_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
- "path_renderer"));
- refresh_button = GTK_WIDGET (gtk_builder_get_object (self->priv->builder,
- "refresh_button"));
-
- gtk_tree_view_column_set_cell_data_func (status_column, selected_renderer,
- (GtkTreeCellDataFunc) selected_renderer_data_func,
- NULL, NULL);
-
- gtk_tree_view_column_set_cell_data_func (status_column, status_icon_renderer,
- (GtkTreeCellDataFunc) status_icon_renderer_data_func,
- NULL, NULL);
-
- gtk_tree_view_column_set_cell_data_func (status_column, status_name_renderer,
- (GtkTreeCellDataFunc) status_name_renderer_data_func,
- NULL, NULL);
-
- gtk_tree_view_column_set_cell_data_func (status_column, path_renderer,
- (GtkTreeCellDataFunc) path_renderer_data_func,
- NULL, NULL);
-
- g_signal_connect (G_OBJECT (selected_renderer), "toggled",
- G_CALLBACK (on_selected_renderer_toggled),
- self);
-
- g_signal_connect_swapped (G_OBJECT (refresh_button), "clicked",
- G_CALLBACK (anjuta_dock_pane_refresh),
- self);
-}
-
-static void
-git_status_pane_finalize (GObject *object)
-{
- GitStatusPane *self;
-
- self = GIT_STATUS_PANE (object);
-
- g_object_unref (self->priv->builder);
- g_hash_table_destroy (self->priv->selected_commit_items);
- g_hash_table_destroy (self->priv->selected_not_updated_items);
- g_free (self->priv);
-
- G_OBJECT_CLASS (git_status_pane_parent_class)->finalize (object);
-}
-
-static void
add_status_items (GQueue *output, GtkTreeStore *status_model,
GtkTreeIter *parent_iter, gint type)
{
@@ -406,24 +320,12 @@ on_not_updated_status_data_arrived (AnjutaCommand *command,
}
static void
-git_status_pane_refresh (AnjutaDockPane *pane)
+git_status_pane_clear (GitStatusPane *self)
{
- GitStatusPane *self;
- Git *plugin;
- GitStatusCommand *commit_status_command;
- GitStatusCommand *not_updated_status_command;
- AnjutaCommandQueue *command_queue;
GtkTreeStore *status_model;
GtkTreeView *status_view;
- self = GIT_STATUS_PANE (pane);
- plugin = ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (pane));
- commit_status_command = git_status_command_new (plugin->project_root_directory,
- GIT_STATUS_SECTION_COMMIT);
- not_updated_status_command = git_status_command_new (plugin->project_root_directory,
- GIT_STATUS_SECTION_NOT_UPDATED);
- command_queue = anjuta_command_queue_new (ANJUTA_COMMAND_QUEUE_EXECUTE_MANUAL);
- status_model = GTK_TREE_STORE (gtk_builder_get_object (self->priv->builder,
+ status_model = GTK_TREE_STORE (gtk_builder_get_object (self->priv->builder,
"status_model"));
status_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
"status_view"));
@@ -439,9 +341,6 @@ git_status_pane_refresh (AnjutaDockPane *pane)
COL_TYPE, STATUS_TYPE_NONE,
-1);
- g_hash_table_remove_all (self->priv->selected_commit_items);
- g_hash_table_remove_all (self->priv->selected_not_updated_items);
-
gtk_tree_store_append (status_model, &(self->priv->not_updated_iter),
NULL);
gtk_tree_store_set (status_model, &(self->priv->not_updated_iter),
@@ -451,37 +350,107 @@ git_status_pane_refresh (AnjutaDockPane *pane)
COL_TYPE, STATUS_TYPE_NONE,
-1);
- g_signal_connect (G_OBJECT (commit_status_command), "data-arrived",
- G_CALLBACK (on_commit_status_data_arrived),
- self);
+ g_hash_table_remove_all (self->priv->selected_commit_items);
+ g_hash_table_remove_all (self->priv->selected_not_updated_items);
+}
- g_signal_connect (G_OBJECT (not_updated_status_command), "data-arrived",
- G_CALLBACK (on_not_updated_status_data_arrived),
+static void
+git_status_pane_init (GitStatusPane *self)
+{
+ gchar *objects[] = {"status_pane",
+ "status_model",
+ NULL};
+ GError *error = NULL;
+ GtkTreeViewColumn *status_column;
+ GtkCellRenderer *selected_renderer;
+ GtkCellRenderer *status_icon_renderer;
+ GtkCellRenderer *status_name_renderer;
+ GtkCellRenderer *path_renderer;
+ GtkWidget *refresh_button;
+
+ self->priv = g_new0 (GitStatusPanePriv, 1);
+ self->priv->builder = gtk_builder_new ();
+ self->priv->selected_commit_items = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ self->priv->selected_not_updated_items = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ if (!gtk_builder_add_objects_from_file (self->priv->builder, BUILDER_FILE,
+ objects,
+ &error))
+ {
+ g_warning ("Couldn't load builder file: %s", error->message);
+ g_error_free (error);
+ }
+
+ status_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
+ "status_column"));
+ selected_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
+ "selected_renderer"));
+ status_icon_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
+ "status_icon_renderer"));
+ status_name_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
+ "status_name_renderer"));
+ path_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
+ "path_renderer"));
+ refresh_button = GTK_WIDGET (gtk_builder_get_object (self->priv->builder,
+ "refresh_button"));
+
+ gtk_tree_view_column_set_cell_data_func (status_column, selected_renderer,
+ (GtkTreeCellDataFunc) selected_renderer_data_func,
+ NULL, NULL);
+
+ gtk_tree_view_column_set_cell_data_func (status_column, status_icon_renderer,
+ (GtkTreeCellDataFunc) status_icon_renderer_data_func,
+ NULL, NULL);
+
+ gtk_tree_view_column_set_cell_data_func (status_column, status_name_renderer,
+ (GtkTreeCellDataFunc) status_name_renderer_data_func,
+ NULL, NULL);
+
+ gtk_tree_view_column_set_cell_data_func (status_column, path_renderer,
+ (GtkTreeCellDataFunc) path_renderer_data_func,
+ NULL, NULL);
+
+ g_signal_connect (G_OBJECT (selected_renderer), "toggled",
+ G_CALLBACK (on_selected_renderer_toggled),
self);
- g_signal_connect (G_OBJECT (commit_status_command), "command-finished",
- G_CALLBACK (g_object_unref),
- NULL);
+ g_signal_connect_swapped (G_OBJECT (refresh_button), "clicked",
+ G_CALLBACK (anjuta_dock_pane_refresh),
+ self);
+}
+
+static void
+git_status_pane_finalize (GObject *object)
+{
+ GitStatusPane *self;
+
+ self = GIT_STATUS_PANE (object);
- g_signal_connect (G_OBJECT (not_updated_status_command), "command-finished",
- G_CALLBACK (g_object_unref),
- NULL);
+ g_object_unref (self->priv->builder);
+ g_hash_table_destroy (self->priv->selected_commit_items);
+ g_hash_table_destroy (self->priv->selected_not_updated_items);
+ g_free (self->priv);
- /* Expand all of the rows so everything is visible */
- g_signal_connect_swapped (G_OBJECT (command_queue), "finished",
- G_CALLBACK (gtk_tree_view_expand_all),
- status_view);
+ G_OBJECT_CLASS (git_status_pane_parent_class)->finalize (object);
+}
- g_signal_connect (G_OBJECT (command_queue), "finished",
- G_CALLBACK (g_object_unref),
- NULL);
+static void
+git_status_pane_refresh (AnjutaDockPane *pane)
+{
+ GitStatusPane *self;
+ Git *plugin;
- anjuta_command_queue_push (command_queue,
- ANJUTA_COMMAND (commit_status_command));
- anjuta_command_queue_push (command_queue,
- ANJUTA_COMMAND (not_updated_status_command));
+ self = GIT_STATUS_PANE (pane);
+ plugin = ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (pane));
- anjuta_command_queue_start (command_queue);
+ anjuta_command_start (ANJUTA_COMMAND (plugin->commit_status_command));
+
}
static GtkWidget *
@@ -506,11 +475,39 @@ git_status_pane_class_init (GitStatusPaneClass *klass)
pane_class->get_widget = git_status_pane_get_widget;
}
-
AnjutaDockPane *
git_status_pane_new (Git *plugin)
{
- return g_object_new (GIT_TYPE_STATUS_PANE, "plugin", plugin, NULL);
+ GitStatusPane *self;
+ GtkTreeView *status_view;
+
+ self = g_object_new (GIT_TYPE_STATUS_PANE, "plugin", plugin, NULL);
+ status_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
+ "status_view"));
+
+ g_signal_connect_swapped (G_OBJECT (plugin->commit_status_command),
+ "command-started",
+ G_CALLBACK (git_status_pane_clear),
+ self);
+
+ /* Expand the placeholders so something is visible to the user after
+ * refreshing */
+ g_signal_connect_swapped (G_OBJECT (plugin->not_updated_status_command),
+ "command-finished",
+ G_CALLBACK (gtk_tree_view_expand_all),
+ status_view);
+
+ g_signal_connect (G_OBJECT (plugin->commit_status_command),
+ "data-arrived",
+ G_CALLBACK (on_commit_status_data_arrived),
+ self);
+
+ g_signal_connect (G_OBJECT (plugin->not_updated_status_command),
+ "data-arrived",
+ G_CALLBACK (on_not_updated_status_data_arrived),
+ self);
+
+ return ANJUTA_DOCK_PANE (self);
}
static void
@@ -563,4 +560,4 @@ git_status_pane_get_all_selected_items (GitStatusPane *self)
&list);
return list;
-}
\ No newline at end of file
+}
diff --git a/plugins/git/plugin.c b/plugins/git/plugin.c
index 929990f..71d96d3 100644
--- a/plugins/git/plugin.c
+++ b/plugins/git/plugin.c
@@ -118,13 +118,18 @@ on_project_root_added (AnjutaPlugin *plugin, const gchar *name,
g_object_set (G_OBJECT (git_plugin->remote_branch_list_command),
"working-directory", git_plugin->project_root_directory,
NULL);
+ g_object_set (G_OBJECT (git_plugin->commit_status_command),
+ "working-directory", git_plugin->project_root_directory,
+ NULL);
+ g_object_set (G_OBJECT (git_plugin->not_updated_status_command),
+ "working-directory", git_plugin->project_root_directory,
+ NULL);
anjuta_command_start_automatic_monitor (ANJUTA_COMMAND (git_plugin->local_branch_list_command));
+ anjuta_command_start_automatic_monitor (ANJUTA_COMMAND (git_plugin->commit_status_command));
anjuta_command_start (ANJUTA_COMMAND (git_plugin->local_branch_list_command));
-
- anjuta_dock_pane_refresh (git_plugin->status_pane);
+ anjuta_command_start (ANJUTA_COMMAND (git_plugin->commit_status_command));
-
gtk_widget_set_sensitive (git_plugin->dock, TRUE);
gtk_widget_set_sensitive (git_plugin->command_bar, TRUE);
}
@@ -140,7 +145,7 @@ on_project_root_removed (AnjutaPlugin *plugin, const gchar *name,
status = anjuta_shell_get_status (plugin->shell, NULL);
anjuta_command_stop_automatic_monitor (ANJUTA_COMMAND (git_plugin->local_branch_list_command));
- anjuta_command_stop_automatic_monitor (ANJUTA_COMMAND (git_plugin->remote_branch_list_command));
+ anjuta_command_stop_automatic_monitor (ANJUTA_COMMAND (git_plugin->commit_status_command));
g_free (git_plugin->project_root_directory);
git_plugin->project_root_directory = NULL;
@@ -189,13 +194,17 @@ on_editor_removed (AnjutaPlugin *plugin, const gchar *name, gpointer user_data)
git_plugin->current_editor_filename = NULL;
}
+/* This function is used to run the next command in a series of commands,
+ * usually used in cases where different types of data need to be treated
+ * differently, like local vs. remote branches or staged or unstaged status
+ * items. */
static void
-on_local_branch_list_command_finished (AnjutaCommand *command,
- guint return_code, Git *plugin)
+run_next_command (AnjutaCommand *command,
+ guint return_code,
+ AnjutaCommand *next_command)
{
- /* Always run the remote list command after the local command finishes */
if (return_code == 0)
- anjuta_command_start (ANJUTA_COMMAND (plugin->remote_branch_list_command));
+ anjuta_command_start (ANJUTA_COMMAND (next_command));
}
static void
@@ -285,8 +294,8 @@ git_activate_plugin (AnjutaPlugin *plugin)
g_signal_connect (G_OBJECT (git_plugin->local_branch_list_command),
"command-finished",
- G_CALLBACK (on_local_branch_list_command_finished),
- git_plugin);
+ G_CALLBACK (run_next_command),
+ git_plugin->remote_branch_list_command);
/* Detect the active branch and indicate it in the status bar. The active
* branch should never be a remote branch in practice, but it is possible to
@@ -301,6 +310,20 @@ git_activate_plugin (AnjutaPlugin *plugin)
G_CALLBACK (on_branch_list_command_data_arrived),
plugin);
+ /* Create the status list commands. The different commands correspond to
+ * the two different sections in status output: Changes to be committed
+ * (staged) and Changed but not updated (unstaged.) */
+ git_plugin->commit_status_command = git_status_command_new (NULL,
+ GIT_STATUS_SECTION_COMMIT);
+ git_plugin->not_updated_status_command = git_status_command_new (NULL,
+ GIT_STATUS_SECTION_NOT_UPDATED);
+
+ /* Always run the not updated commmand after the commmit command. */
+ g_signal_connect (G_OBJECT (git_plugin->commit_status_command),
+ "command-finished",
+ G_CALLBACK (run_next_command),
+ git_plugin->not_updated_status_command);
+
/* Add the panes to the dock */
git_plugin->status_pane = git_status_pane_new (git_plugin);
anjuta_dock_add_pane (ANJUTA_DOCK (git_plugin->dock), "Status",
@@ -360,6 +383,8 @@ git_deactivate_plugin (AnjutaPlugin *plugin)
g_object_unref (git_plugin->local_branch_list_command);
g_object_unref (git_plugin->remote_branch_list_command);
+ g_object_unref (git_plugin->commit_status_command);
+ g_object_unref (git_plugin->not_updated_status_command);
g_free (git_plugin->project_root_directory);
g_free (git_plugin->current_editor_filename);
diff --git a/plugins/git/plugin.h b/plugins/git/plugin.h
index a30e493..42c98ac 100644
--- a/plugins/git/plugin.h
+++ b/plugins/git/plugin.h
@@ -36,6 +36,7 @@
#include <libanjuta/anjuta-debug.h>
#include <libanjuta/anjuta-command-queue.h>
#include "git-branch-list-command.h"
+#include "git-status-command.h"
extern GType git_get_type (GTypeModule *module);
#define ANJUTA_TYPE_PLUGIN_GIT (git_get_type (NULL))
@@ -67,9 +68,13 @@ struct _Git
AnjutaDockPane *status_pane;
AnjutaDockPane *branches_pane;
- /* Branch list commands */
+ /* List commands for various panes.
+ * Keep them in the plugin so that the commands have the most direct
+ * way of handling project (working directory) changes */
GitBranchListCommand *local_branch_list_command;
GitBranchListCommand *remote_branch_list_command;
+ GitStatusCommand *commit_status_command;
+ GitStatusCommand *not_updated_status_command;
IAnjutaMessageView *message_view;
AnjutaCommandQueue *command_queue;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]