summaryrefslogtreecommitdiff
path: root/logview/logview-loglist.c
diff options
context:
space:
mode:
Diffstat (limited to 'logview/logview-loglist.c')
-rw-r--r--logview/logview-loglist.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/logview/logview-loglist.c b/logview/logview-loglist.c
new file mode 100644
index 00000000..5f879027
--- /dev/null
+++ b/logview/logview-loglist.c
@@ -0,0 +1,493 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* logview-loglist.c - displays a list of the opened logs
+ *
+ * Copyright (C) 2005 Vincent Noel <[email protected]>
+ * Copyright (C) 2008 Cosimo Cecchi <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "logview-manager.h"
+#include "logview-log.h"
+#include "logview-utils.h"
+
+#include "logview-loglist.h"
+
+struct _LogviewLoglistPrivate {
+ GtkTreeStore *model;
+ LogviewManager *manager;
+ GtkTreePath *selection;
+ gboolean has_day_selection;
+};
+
+G_DEFINE_TYPE (LogviewLoglist, logview_loglist, GTK_TYPE_TREE_VIEW);
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), LOGVIEW_TYPE_LOGLIST, LogviewLoglistPrivate))
+
+enum {
+ LOG_OBJECT = 0,
+ LOG_NAME,
+ LOG_WEIGHT,
+ LOG_WEIGHT_SET,
+ LOG_DAY
+};
+
+enum {
+ DAY_SELECTED,
+ DAY_CLEARED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+save_day_selection (LogviewLoglist *loglist, GtkTreeIter *iter)
+{
+ if (loglist->priv->selection) {
+ gtk_tree_path_free (loglist->priv->selection);
+ }
+
+ loglist->priv->selection = gtk_tree_model_get_path
+ (GTK_TREE_MODEL (loglist->priv->model), iter);
+}
+
+static void
+update_days_and_lines_for_log (LogviewLoglist *loglist,
+ GtkTreeIter *log, GSList *days)
+{
+ gboolean res;
+ GtkTreeIter iter, dummy;
+ GSList *l;
+ int i;
+ char date[200];
+ Day *day;
+
+ /* if we have some days, we can't remove all the items immediately, otherwise,
+ * if the row is expanded, it will be collapsed because there are no items,
+ * so we create a dummy entry, remove all the others and then remove the
+ * dummy one.
+ */
+ res = gtk_tree_model_iter_children (GTK_TREE_MODEL (loglist->priv->model),
+ &iter, log);
+ if (res) {
+ gtk_tree_store_insert_before (loglist->priv->model, &dummy, log, &iter);
+ gtk_tree_store_set (loglist->priv->model, &dummy,
+ LOG_NAME, "", -1);
+ do {
+ gtk_tree_store_remove (loglist->priv->model, &iter);
+ } while (gtk_tree_store_iter_is_valid (loglist->priv->model, &iter));
+ }
+
+ for (i = 1, l = days; l; l = l->next) {
+ /* now insert all the days */
+ day = l->data;
+
+ g_date_strftime (date, 200, "%A, %e %b", day->date);
+
+ gtk_tree_store_insert (GTK_TREE_STORE (loglist->priv->model),
+ &iter, log, i);
+ gtk_tree_store_set (GTK_TREE_STORE (loglist->priv->model),
+ &iter, LOG_NAME, date, LOG_DAY, day, -1);
+ i++;
+ }
+
+ if (res) {
+ gtk_tree_store_remove (loglist->priv->model, &dummy);
+ }
+}
+
+static GtkTreeIter *
+logview_loglist_find_log (LogviewLoglist *list, LogviewLog *log)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeIter *retval = NULL;
+ LogviewLog *current;
+
+ model = GTK_TREE_MODEL (list->priv->model);
+
+ if (!gtk_tree_model_get_iter_first (model, &iter)) {
+ return NULL;
+ }
+
+ do {
+ gtk_tree_model_get (model, &iter, LOG_OBJECT, &current, -1);
+ if (current == log) {
+ retval = gtk_tree_iter_copy (&iter);
+ }
+ g_object_unref (current);
+ } while (gtk_tree_model_iter_next (model, &iter) != FALSE && retval == NULL);
+
+ return retval;
+}
+
+static void
+log_changed_cb (LogviewLog *log,
+ gpointer user_data)
+{
+ LogviewLoglist *list = user_data;
+ LogviewLog *active;
+ GtkTreeIter *iter;
+
+ active = logview_manager_get_active_log (list->priv->manager);
+
+ if (log == active) {
+ g_object_unref (active);
+ return;
+ }
+
+ iter = logview_loglist_find_log (list, log);
+
+ if (!iter) {
+ return;
+ }
+
+ /* make the log bold in the list */
+ gtk_tree_store_set (list->priv->model, iter,
+ LOG_WEIGHT, PANGO_WEIGHT_BOLD,
+ LOG_WEIGHT_SET, TRUE, -1);
+
+ gtk_tree_iter_free (iter);
+}
+
+
+static void
+tree_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ LogviewLoglist *list = user_data;
+ GtkTreeModel *model;
+ GtkTreeIter iter, parent;
+ LogviewLog *log;
+ gboolean is_bold, is_active;
+ Day *day;
+
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ return;
+ }
+
+ gtk_tree_model_get (model, &iter, LOG_OBJECT, &log,
+ LOG_WEIGHT_SET, &is_bold,
+ LOG_DAY, &day, -1);
+ if (log) {
+ is_active = logview_manager_log_is_active (list->priv->manager, log);
+
+ if (is_active && list->priv->has_day_selection) {
+ list->priv->has_day_selection = FALSE;
+ g_signal_emit (list, signals[DAY_CLEARED], 0, NULL);
+ } else if (!is_active) {
+ logview_manager_set_active_log (list->priv->manager, log);
+ }
+ } else if (day) {
+ list->priv->has_day_selection = TRUE;
+ gtk_tree_model_iter_parent (model, &parent, &iter);
+ gtk_tree_model_get (model, &parent, LOG_OBJECT, &log, -1);
+
+ if (!logview_manager_log_is_active (list->priv->manager, log)) {
+ save_day_selection (list, &iter);
+ logview_manager_set_active_log (list->priv->manager, log);
+ } else {
+ g_signal_emit (list, signals[DAY_SELECTED], 0, day, NULL);
+ }
+ }
+
+ if (is_bold) {
+ gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
+ LOG_WEIGHT_SET, FALSE, -1);
+ }
+
+ if (log) {
+ g_object_unref (log);
+ }
+}
+
+static void
+manager_active_changed_cb (LogviewManager *manager,
+ LogviewLog *log,
+ LogviewLog *old_log,
+ gpointer user_data)
+{
+ LogviewLoglist *list = user_data;
+ GtkTreeIter * iter, sel_iter;
+ GtkTreeSelection * selection;
+
+ if (list->priv->selection &&
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (list->priv->model),
+ &sel_iter, list->priv->selection))
+ {
+ Day *day;
+
+ iter = gtk_tree_iter_copy (&sel_iter);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (list->priv->model), iter,
+ LOG_DAY, &day, -1);
+
+ if (day) {
+ g_signal_emit (list, signals[DAY_SELECTED], 0, day, NULL);
+ }
+
+ gtk_tree_path_free (list->priv->selection);
+ list->priv->selection = NULL;
+ } else {
+ iter = logview_loglist_find_log (list, log);
+ }
+
+ if (!iter) {
+ return;
+ }
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+ g_signal_handlers_block_by_func (selection, tree_selection_changed_cb, list);
+
+ gtk_tree_selection_select_iter (selection, iter);
+
+ g_signal_handlers_unblock_by_func (selection, tree_selection_changed_cb, list);
+ gtk_tree_iter_free (iter);
+}
+
+static void
+manager_log_closed_cb (LogviewManager *manager,
+ LogviewLog *log,
+ gpointer user_data)
+{
+ LogviewLoglist *list = user_data;
+ GtkTreeIter *iter;
+ gboolean res;
+
+ iter = logview_loglist_find_log (list, log);
+
+ if (!iter) {
+ return;
+ }
+
+ g_signal_handlers_disconnect_by_func (log, log_changed_cb, list);
+
+ res = gtk_tree_store_remove (list->priv->model, iter);
+ if (res) {
+ GtkTreeSelection *selection;
+
+ /* iter now points to the next valid row */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+ gtk_tree_selection_select_iter (selection, iter);
+ } else {
+ /* FIXME: what shall we do here? */
+ }
+
+ gtk_tree_iter_free (iter);
+}
+
+static void
+manager_log_added_cb (LogviewManager *manager,
+ LogviewLog *log,
+ gpointer user_data)
+{
+ LogviewLoglist *list = user_data;
+ GtkTreeIter iter, child;
+
+ gtk_tree_store_append (list->priv->model, &iter, NULL);
+ gtk_tree_store_set (list->priv->model, &iter,
+ LOG_OBJECT, g_object_ref (log),
+ LOG_NAME, logview_log_get_display_name (log), -1);
+ if (logview_log_get_has_days (log)) {
+ gtk_tree_store_insert (list->priv->model,
+ &child, &iter, 0);
+ gtk_tree_store_set (list->priv->model, &child,
+ LOG_NAME, _("Loading..."), -1);
+ }
+
+ g_signal_connect (log, "log-changed",
+ G_CALLBACK (log_changed_cb), list);
+}
+
+static void
+row_expanded_cb (GtkTreeView *view,
+ GtkTreeIter *iter,
+ GtkTreePath *path,
+ gpointer user_data)
+{
+ LogviewLoglist *list = user_data;
+ LogviewLog *log;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (list->priv->model), iter,
+ LOG_OBJECT, &log, -1);
+ if (!logview_manager_log_is_active (list->priv->manager, log)) {
+ logview_manager_set_active_log (list->priv->manager, log);
+ }
+
+ g_object_unref (log);
+}
+
+static int
+loglist_sort_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ char *name_a, *name_b;
+ Day *day_a, *day_b;
+ int retval = 0;
+
+ switch (gtk_tree_store_iter_depth (GTK_TREE_STORE (model), a)) {
+ case 0:
+ gtk_tree_model_get (model, a, LOG_NAME, &name_a, -1);
+ gtk_tree_model_get (model, b, LOG_NAME, &name_b, -1);
+ retval = g_strcmp0 (name_a, name_b);
+ g_free (name_a);
+ g_free (name_b);
+
+ break;
+ case 1:
+ gtk_tree_model_get (model, a, LOG_DAY, &day_a, -1);
+ gtk_tree_model_get (model, b, LOG_DAY, &day_b, -1);
+ if (day_a && day_b) {
+ retval = days_compare (day_a, day_b);
+ } else {
+ retval = 0;
+ }
+
+ break;
+ default:
+ g_assert_not_reached ();
+
+ break;
+ }
+
+ return retval;
+}
+
+static void
+do_finalize (GObject *obj)
+{
+ LogviewLoglist *list = LOGVIEW_LOGLIST (obj);
+
+ g_object_unref (list->priv->model);
+ list->priv->model = NULL;
+
+ if (list->priv->selection) {
+ gtk_tree_path_free (list->priv->selection);
+ list->priv->selection = NULL;
+ }
+
+ G_OBJECT_CLASS (logview_loglist_parent_class)->finalize (obj);
+}
+
+static void
+logview_loglist_init (LogviewLoglist *list)
+{
+ GtkTreeStore *model;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *cell;
+
+ list->priv = GET_PRIVATE (list);
+ list->priv->has_day_selection = FALSE;
+ list->priv->selection = NULL;
+
+ model = gtk_tree_store_new (5, LOGVIEW_TYPE_LOG, G_TYPE_STRING, G_TYPE_INT,
+ G_TYPE_BOOLEAN, G_TYPE_POINTER);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (model));
+ list->priv->model = model;
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (tree_selection_changed_cb), list);
+
+ cell = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", LOG_NAME,
+ "weight-set", LOG_WEIGHT_SET,
+ "weight", LOG_WEIGHT,
+ NULL);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list->priv->model), LOG_NAME, GTK_SORT_ASCENDING);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (list->priv->model),
+ LOG_NAME,
+ (GtkTreeIterCompareFunc) loglist_sort_func,
+ list, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (list), -1);
+
+ list->priv->manager = logview_manager_get ();
+
+ g_signal_connect (list->priv->manager, "log-added",
+ G_CALLBACK (manager_log_added_cb), list);
+ g_signal_connect (list->priv->manager, "log-closed",
+ G_CALLBACK (manager_log_closed_cb), list);
+ g_signal_connect_after (list->priv->manager, "active-changed",
+ G_CALLBACK (manager_active_changed_cb), list);
+ g_signal_connect (list, "row-expanded",
+ G_CALLBACK (row_expanded_cb), list);
+}
+
+static void
+logview_loglist_class_init (LogviewLoglistClass *klass)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (klass);
+ oclass->finalize = do_finalize;
+
+ signals[DAY_SELECTED] = g_signal_new ("day-selected",
+ G_OBJECT_CLASS_TYPE (oclass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LogviewLoglistClass, day_selected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+ signals[DAY_CLEARED] = g_signal_new ("day-cleared",
+ G_OBJECT_CLASS_TYPE (oclass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LogviewLoglistClass, day_cleared),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (klass, sizeof (LogviewLoglistPrivate));
+}
+
+/* public methods */
+
+GtkWidget *
+logview_loglist_new (void)
+{
+ GtkWidget *widget;
+ widget = g_object_new (LOGVIEW_TYPE_LOGLIST, NULL);
+ return widget;
+}
+
+void
+logview_loglist_update_lines (LogviewLoglist *loglist, LogviewLog *log)
+{
+ GSList *days;
+ GtkTreeIter *parent;
+
+ g_assert (LOGVIEW_IS_LOGLIST (loglist));
+ g_assert (LOGVIEW_IS_LOG (log));
+
+ days = logview_log_get_days_for_cached_lines (log);
+ parent = logview_loglist_find_log (loglist, log);
+ update_days_and_lines_for_log (loglist, parent, days);
+
+ gtk_tree_iter_free (parent);
+}
+