cpw.nader.asynclogging-3: f3f2ce13: Work on gobjectifying PidginLogViewer, s...

morshed.nader at gmail.com morshed.nader at gmail.com
Thu Jan 13 21:05:55 EST 2011


----------------------------------------------------------------------
Revision: f3f2ce131743f7b3cb698e6204e35d1bff343d3c
Parent:   c5a14fed2c83244f9bb6a70a1ad5ade6dbbc2861
Author:   morshed.nader at gmail.com
Date:     01/13/11 17:00:00
Branch:   im.pidgin.cpw.nader.asynclogging-3
URL: http://d.pidgin.im/viewmtn/revision/info/f3f2ce131743f7b3cb698e6204e35d1bff343d3c

Changelog: 

Work on gobjectifying PidginLogViewer, still needs all the properties/getters/setters

Changes against parent c5a14fed2c83244f9bb6a70a1ad5ade6dbbc2861

  patched  libpurple/log.c
  patched  libpurple/log.h
  patched  pidgin/gtklog.c
  patched  pidgin/gtklog.h

-------------- next part --------------
============================================================
--- libpurple/log.c	47bcfc2d0cb70cad28c6d701d2d1895cbabcf152
+++ libpurple/log.c	e3600bffa8ead07dc65de588d879d09d731fd779
@@ -39,6 +39,7 @@
 #include "imgstore.h"
 #include "time.h"
 
+
 #if ! GLIB_CHECK_VERSION(2, 19, 8)
 //Fixes strict-aliasing warning
 #undef g_static_mutex_get_mutex_impl_shortcut
@@ -80,6 +81,7 @@ struct _PurpleLogPrivate {
 	struct tm *tm;                     /**< The time this conversation started */
 };
 
+
 typedef struct {
 	PurpleAccount *account;
 	gchar *name;
============================================================
--- libpurple/log.h	78d3847d34d9645be44819ad6df5a01f4e460dec
+++ libpurple/log.h	b04587886b1503cbef63701a37e8fec56a46fd82
@@ -218,6 +218,11 @@ struct _PurpleLogSet {
 
 	/* IMPORTANT: Some code in log.c allocates these without zeroing them.
 	 * IMPORTANT: Update that code if you add members here. */
+
+	void (*_purple_reserved1)(void);
+	void (*_purple_reserved2)(void);
+	void (*_purple_reserved3)(void);
+	void (*_purple_reserved4)(void);
 };
 
 /***************************************/
============================================================
--- pidgin/gtklog.c	950222a881f7915ca37ce3f65d20d5e202a49b02
+++ pidgin/gtklog.c	cd450e7397fc939752f81d9dac07149c8398c6f1
@@ -40,24 +40,57 @@
 #include "gtkutils.h"
 
 
-// PidginLogViewer members are stored here to avoid an ABI break, will be moved back in 3.x
-typedef struct {
-	PidginLogViewer    *lv;            /**< The rest of the struct */
+#if ! GLIB_CHECK_VERSION(2, 19, 8)
+//Fixes strict-aliasing warning
+#undef g_static_mutex_get_mutex_impl_shortcut
+
+#define g_static_mutex_get_mutex_impl_shortcut(mutex) \
+	(g_atomic_pointer_get (mutex) ? *(mutex) : \
+	g_static_mutex_get_mutex_impl (mutex))
+
+#endif
+
+
+G_DEFINE_TYPE (PidginLogViewer, pidgin_log_viewer, GTK_TYPE_DIALOG)
+static void pidgin_log_viewer_get_property(GObject *, guint, GValue *, GParamSpec *);
+static void pidgin_log_viewer_set_property(GObject *, guint, const GValue *, GParamSpec *);
+static void pidgin_log_viewer_finalize(GObject *);
+
+enum {
+	PROP_0,
+	LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { 0 };
+
+#define PIDGIN_LOG_VIEWER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), PIDGIN_TYPE_LOG_VIEWER, PidginLogViewerPrivate))
+typedef struct _PidginLogViewerPrivate PidginLogViewerPrivate;
+
+struct _PidginLogViewerPrivate {
+	GList              *logs;       /**< The list of logs viewed in this viewer */
+	GtkTreeStore       *treestore;  /**< The treestore containing said logs */
+	GtkWidget          *treeview;   /**< The treeview representing said treestore */
+	GtkWidget          *imhtml;     /**< The imhtml to display said logs */
+	GtkWidget          *entry;      /**< The search entry, in which search terms are entered */
+	gchar              *search;     /**< The string currently being searched for */
+	GtkWidget          *label;      /**< The label at the top of the log viewer */
+	GtkWidget          *size_label; /**< The label with total log size */
 	gsize              size;           /**< The actual value of the total log size */
 	GtkWidget          *list_bar;      /**< The progress bar */
 	GtkWidget          *search_bar;    /**< The progess bar for searches */
-	GCancellable       *read_cancel;   /**< A GCancellable to stop any reads */
 	GCancellable       *search_cancel; /**< A GCancellable to stop any searches */
 	GCancellable       *list_cancel;   /**< A GCancellable to stop any folder listings */
+	GCancellable       *read_cancel;   /**< A GCancellable to stop any reads */
 	gboolean           selected;       /**< A gboolean to indicate the user has already selected a log */
-	PurpleLogChatType  type;           /**< The log type of the window */
+	PurpleLogChatType  chat_type;      /**< The log type of the window */
 #if GTK_CHECK_VERSION(2, 20, 0)
 	GtkWidget          *spinner;       /**< A spinner to indicate a read is in progress */
 #endif
-} PidginLogViewerEx;
+};
 
+
 typedef struct {
-	PidginLogViewerEx *log_viewer;
+	PidginLogViewer *log_viewer;
 	PurpleLog *log;
 
 	gboolean is_window_open;
@@ -69,11 +102,6 @@ typedef struct {
 
 typedef struct {
 	_pidgin_log_data *log_data;
-	PurpleLog *log;
-} _pidgin_search_callback_data;
-
-typedef struct {
-	_pidgin_log_data *log_data;
 	GtkMenuPositionFunc func;
 	GtkTreeIter *iter;
 } log_menu_callback_data;
@@ -87,7 +115,7 @@ typedef struct {
 typedef struct {
 	PurpleAccount *account;
 	PurpleContact *contact;
-	PurpleLogChatType type;
+	PurpleLogChatType chat_type;
 	gchar *buddyname;
 } log_viewer_hash_t;
 
@@ -96,12 +124,12 @@ static gboolean log_viewer_equal(gconstp
 static void pidgin_window_destroy_cb(_pidgin_log_data *);
 static guint log_viewer_hash(gconstpointer);
 static gboolean log_viewer_equal(gconstpointer, gconstpointer);
-static void select_first_log(PidginLogViewerEx *);
+static void select_first_log(PidginLogViewer *);
 static const gchar *log_get_date(PurpleLog *);
 static void pidgin_log_search_done_cb(_pidgin_log_data *);
 static void pidgin_log_search_cb(GObject *, GAsyncResult *, gpointer);
-static void search_cb(GtkWidget *, PidginLogViewerEx *);
-static void destroy_cb(GtkWidget *, gint, log_viewer_hash_t *);
+static void search_cb(GtkWidget *, PidginLogViewer *);
+static void destroy_cb(PidginLogViewer *, gint, log_viewer_hash_t *);
 static void log_row_activated_cb(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *);
 static void delete_log_cleanup_cb(log_delete_callback_data *);
 static void pidgin_log_delete_log_cb(GObject *, GAsyncResult *, gpointer);
@@ -109,17 +137,16 @@ static void log_menu_callback_data_free(
 static void remove_delete_log_window(void *);
 static void log_delete_log_cb(GtkWidget *, log_menu_callback_data *);
 static void log_menu_callback_data_free(log_menu_callback_data *);
-static void log_show_popup_menu(GtkWidget *, GdkEventButton *, log_menu_callback_data *);
-static gboolean log_button_press_cb(GtkWidget *, GdkEventButton *, PidginLogViewerEx *);
-static gboolean log_popup_menu_cb(GtkWidget *, PidginLogViewerEx *);
+static void log_show_popup_menu(GdkEventButton *, log_menu_callback_data *);
+static gboolean log_button_press_cb(GtkTreeView *, GdkEventButton *, PidginLogViewer *);
+static gboolean log_popup_menu_cb(GtkTreeView *, PidginLogViewer *);
 static gboolean search_find_cb(gpointer);
 static void pidgin_log_read_cb(GObject *, GAsyncResult *, gpointer);
-static void log_select_cb(GtkTreeSelection *, PidginLogViewerEx *);
-static void populate_log_tree(PidginLogViewerEx *);
-static PidginLogViewerEx *display_log_viewer_nonblocking(log_viewer_hash_t *,
+static void log_select_cb(GtkTreeSelection *, PidginLogViewer *);
+static void populate_log_tree(PidginLogViewer *);
+static PidginLogViewer *pidgin_log_viewer_new(log_viewer_hash_t *,
 	const gchar *, GtkWidget *, gboolean);
-static void insert_log_viewer_log(PidginLogViewerEx *, PurpleLog *);
-static void append_log_viewer_logs(PidginLogViewerEx *, GList *);
+static void insert_log_viewer_log(PidginLogViewer *, PurpleLog *);
 static void pidgin_log_done_cb(_pidgin_log_data *);
 static void pidgin_log_viewer_update_list_bar(_pidgin_log_data *);
 static void pidgin_log_viewer_update_search_bar(_pidgin_log_data *);
@@ -128,14 +155,19 @@ static void pidgin_log_system_list_cb(GO
 static void pidgin_log_system_list_cb(GObject *, GAsyncResult *, gpointer);
 
 
+G_LOCK_DEFINE(log_viewers);
+G_LOCK_DEFINE(syslog_viewer);
+
+
 static GHashTable *log_viewers = NULL;
-static PidginLogViewerEx *syslog_viewer = NULL;
+static PidginLogViewer *syslog_viewer = NULL;
 
+
 static void
 pidgin_log_data_free(_pidgin_log_data *data)
 {
 	if (data->destroy_handler_id > 0)
-		g_signal_handler_disconnect(data->log_viewer->lv->window,
+		g_signal_handler_disconnect(GTK_WINDOW(data->log_viewer),
 			data->destroy_handler_id);
 
 	g_free(data);
@@ -148,46 +180,67 @@ pidgin_window_destroy_cb(_pidgin_log_dat
 	data->is_window_open = FALSE;
 }
 
-static void
-pidgin_log_viewer_ex_set_read_cancel(PidginLogViewerEx *lv_ex, GCancellable *cancel)
+void
+pidgin_log_viewer_set_read_cancel(PidginLogViewer *lv, GCancellable *cancel)
 {
-	if (lv_ex->read_cancel != NULL) {
-		g_cancellable_cancel(lv_ex->read_cancel);
-		g_object_unref(lv_ex->read_cancel);
+	PidginLogViewerPrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_LOG_VIEWER(lv));
+	g_return_if_fail(cancel == NULL || G_IS_CANCELLABLE(cancel));
+
+	priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+
+	if (priv->read_cancel != NULL) {
+		g_cancellable_cancel(priv->read_cancel);
+		g_object_unref(priv->read_cancel);
 	}
 
 	if (cancel != NULL)
 		g_object_ref(cancel);
 
-	lv_ex->read_cancel = cancel;
+	priv->read_cancel = cancel;
 }
 
-static void
-pidgin_log_viewer_ex_set_search_cancel(PidginLogViewerEx *lv_ex, GCancellable *cancel)
+void
+pidgin_log_viewer_set_search_cancel(PidginLogViewer *lv, GCancellable *cancel)
 {
-	if (lv_ex->search_cancel != NULL) {
-		g_cancellable_cancel(lv_ex->search_cancel);
-		g_object_unref(lv_ex->search_cancel);
+	PidginLogViewerPrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_LOG_VIEWER(lv));
+	g_return_if_fail(cancel == NULL || G_IS_CANCELLABLE(cancel));
+
+	priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+
+	if (priv->search_cancel != NULL) {
+		g_cancellable_cancel(priv->search_cancel);
+		g_object_unref(priv->search_cancel);
 	}
 
 	if (cancel != NULL)
 		g_object_ref(cancel);
 
-	lv_ex->search_cancel = cancel;
+	priv->search_cancel = cancel;
 }
 
-static void
-pidgin_log_viewer_ex_set_list_cancel(PidginLogViewerEx *lv_ex, GCancellable *cancel)
+void
+pidgin_log_viewer_set_list_cancel(PidginLogViewer *lv, GCancellable *cancel)
 {
-	if (lv_ex->list_cancel != NULL) {
-		g_cancellable_cancel(lv_ex->list_cancel);
-		g_object_unref(lv_ex->list_cancel);
+	PidginLogViewerPrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_LOG_VIEWER(lv));
+	g_return_if_fail(cancel == NULL || G_IS_CANCELLABLE(cancel));
+
+	priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+
+	if (priv->list_cancel != NULL) {
+		g_cancellable_cancel(priv->list_cancel);
+		g_object_unref(priv->list_cancel);
 	}
 
 	if (cancel != NULL)
 		g_object_ref(cancel);
 
-	lv_ex->list_cancel = cancel;
+	priv->list_cancel = cancel;
 }
 
 static guint
@@ -211,7 +264,7 @@ log_viewer_equal(gconstpointer y, gconst
 
 	if (a->contact != NULL) {
 		if (b->contact != NULL)
-			return (a->contact == b->contact);
+			return a->contact == b->contact;
 		else
 			return FALSE;
 	} else if (b->contact != NULL)
@@ -226,10 +279,10 @@ static void
 }
 
 static void
-select_first_log(PidginLogViewerEx *lv_ex)
+select_first_log(PidginLogViewer *lv)
 {
-	PidginLogViewer *lv = lv_ex->lv;
-	GtkTreeModel *model = GTK_TREE_MODEL(lv->treestore);
+	PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+	GtkTreeModel *model = GTK_TREE_MODEL(priv->treestore);
 	GtkTreeIter iter, it;
 	GtkTreePath *path;
 
@@ -239,11 +292,11 @@ select_first_log(PidginLogViewerEx *lv_e
 	path = gtk_tree_model_get_path(model, &iter);
 
 	if (gtk_tree_model_iter_children(model, &it, &iter)) {
-		gtk_tree_view_expand_row(GTK_TREE_VIEW(lv->treeview), path, TRUE);
+		gtk_tree_view_expand_row(GTK_TREE_VIEW(priv->treeview), path, TRUE);
 		path = gtk_tree_model_get_path(model, &it);
 	}
 
-	gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(lv->treeview)), path);
+	gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview)), path);
 	gtk_tree_path_free(path);
 }
 
@@ -264,12 +317,13 @@ pidgin_log_search_done_cb(_pidgin_log_da
 pidgin_log_search_done_cb(_pidgin_log_data *pidgin_log_data)
 {
 	if (pidgin_log_data->is_window_open) {
-		PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+		PidginLogViewer *lv = pidgin_log_data->log_viewer;
+		PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
 
-		if (!lv_ex->selected)
-			select_first_log(lv_ex);
+		if (!priv->selected)
+			select_first_log(lv);
 
-		gtk_widget_hide(lv_ex->search_bar);
+		gtk_widget_hide(priv->search_bar);
 	}
 
 	pidgin_log_data_free(pidgin_log_data);
@@ -278,136 +332,123 @@ pidgin_log_search_cb(GObject *object, GA
 static void
 pidgin_log_search_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_pidgin_search_callback_data *callback_data = userdata;
-	_pidgin_log_data *pidgin_log_data = callback_data->log_data;
-	PurpleLog *log = callback_data->log;
-	GError *err = NULL;
+	_pidgin_log_data *pidgin_log_data = userdata;
+	PurpleLog *log = PURPLE_LOG(object);
+	GError *error = NULL;
 	gchar *text;
 
-	text = purple_log_read_finish(log, res, NULL, &err);
+	text = purple_log_read_finish(log, res, NULL, &error);
 	pidgin_log_data->count--;
 
 	if (text == NULL) {
-		if (err->code != G_IO_ERROR_CANCELLED)
-			purple_debug_error("gtklog", "Error reading file during search: %s\n", err->message);
+		if (error->code != G_IO_ERROR_CANCELLED)
+			purple_debug_error("gtklog", "Error reading file during search: %s\n",
+				error->message);
 	} else if (pidgin_log_data->is_window_open) {
-		PidginLogViewer *lv = pidgin_log_data->log_viewer->lv;
+		PidginLogViewer *lv = pidgin_log_data->log_viewer;
+		GtkTreeStore *treestore = pidgin_log_viewer_get_tree_store(lv);
 		GtkTreeIter iter;
+		const gchar *search = pidgin_log_viewer_get_search_string(lv);
 
-		if (*text && purple_strcasestr(text, lv->search)) {
-			gtk_tree_store_append(lv->treestore, &iter, NULL);
-			gtk_tree_store_set(lv->treestore, &iter, 0, log_get_date(log), 1, log, -1);
+		if (*text && purple_strcasestr(text, search)) {
+			gtk_tree_store_append(treestore, &iter, NULL);
+			gtk_tree_store_set(treestore, &iter, 0, log_get_date(log), 1, log, -1);
 		}
 	}
 
-	g_clear_error(&err);
+	g_clear_error(&error);
 	pidgin_log_viewer_update_search_bar(pidgin_log_data);
 
 	if (pidgin_log_data->count < 1)
 		pidgin_log_search_done_cb(pidgin_log_data);
-
-	g_free(callback_data);
 }
 
 static void
-search_cb(GtkWidget *button, PidginLogViewerEx *lv_ex)
+search_cb(GtkWidget *button, PidginLogViewer *lv)
 {
 	_pidgin_log_data *pidgin_log_data;
-	PidginLogViewer *lv = lv_ex->lv;
-	GtkIMHtml *imhtml;
+	GtkIMHtml *imhtml = GTK_IMHTML(pidgin_log_viewer_get_text_area(lv));
+	GtkTreeStore *treestore = pidgin_log_viewer_get_tree_store(lv);
+	GtkWidget *bar, *entry;
 	GCancellable *cancel;
 	GList *logs;
-	const gchar *search_term;
+	const gchar *search_term, *old_search;
 	guint length;
 
-	search_term = gtk_entry_get_text(GTK_ENTRY(lv->entry));
-	imhtml = GTK_IMHTML(lv->imhtml);
+	entry = pidgin_log_viewer_get_entry(lv);
+	search_term = gtk_entry_get_text(GTK_ENTRY(entry));
 
 	if (!(*search_term)) {
 		/* reset the tree */
-		pidgin_log_viewer_ex_set_search_cancel(lv_ex, NULL);
+		pidgin_log_viewer_set_search_cancel(lv, NULL);
 
-		gtk_tree_store_clear(lv->treestore);
-		populate_log_tree(lv_ex);
+		gtk_tree_store_clear(treestore);
+		populate_log_tree(lv);
 
-		g_free(lv->search);
-		lv->search = NULL;
+		pidgin_log_viewer_set_search_string(lv, NULL);
 
 		gtk_imhtml_search_clear(imhtml);
-		select_first_log(lv_ex);
+		select_first_log(lv);
 
 		return;
 	}
 
-	if (lv->search != NULL && !strcmp(lv->search, search_term)) {
+	old_search = pidgin_log_viewer_get_search_string(lv);
+
+	if (old_search != NULL && !strcmp(old_search, search_term)) {
 		/* Searching for the same term acts as "Find Next" */
-		gtk_imhtml_search_find(imhtml, lv->search);
+		gtk_imhtml_search_find(imhtml, old_search);
 
 		return;
 	}
 
-	length = g_list_length(lv->logs);
+	logs = pidgin_log_viewer_get_logs(lv);
+	length = g_list_length(logs);
 
 	if (length < 1)
 		return;
 
-	g_free(lv->search);
-	lv->search = g_strdup(search_term);
-
-	gtk_tree_store_clear(lv->treestore);
+	pidgin_log_viewer_set_search_string(lv, search_term);
+	gtk_tree_store_clear(treestore);
 	gtk_imhtml_clear(imhtml);
-	lv_ex->selected = FALSE;
+	pidgin_log_viewer_set_selected(lv, FALSE);
 
 	pidgin_log_data = g_new0(_pidgin_log_data, 1);
 	pidgin_log_data->is_window_open = TRUE;
-	pidgin_log_data->log_viewer = lv_ex;
+	pidgin_log_data->log_viewer = lv;
 	pidgin_log_data->count = pidgin_log_data->total = length;
 
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv->window,
+	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(GTK_WINDOW(lv),
 		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
 
-	gtk_widget_show(lv_ex->search_bar);
-	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lv_ex->search_bar), 0.0);
+	bar = pidgin_log_viewer_get_search_bar(lv);
+	gtk_widget_show(bar);
+	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(bar), 0.0);
 
 	cancel = g_cancellable_new();
-	pidgin_log_viewer_ex_set_search_cancel(lv_ex, cancel);
+	pidgin_log_viewer_set_search_cancel(lv, cancel);
 
-	for (logs = lv->logs; logs != NULL; logs = g_list_next(logs)) {
-		_pidgin_search_callback_data *callback_data;
-		PurpleLog *log = logs->data;
+	for ( ; logs != NULL; logs = g_list_next(logs))
+		purple_log_read_async(logs->data, G_PRIORITY_DEFAULT_IDLE, cancel,
+			pidgin_log_search_cb, pidgin_log_data);
 
-		callback_data = g_new0(_pidgin_search_callback_data, 1);
-		callback_data->log_data = pidgin_log_data;
-		callback_data->log = log;
-
-		purple_log_read_async(log, G_PRIORITY_DEFAULT_IDLE, cancel,
-			pidgin_log_search_cb, callback_data);
-	}
-
 	g_object_unref(cancel);
 }
 
 static void
-destroy_cb(GtkWidget *w, gint resp, log_viewer_hash_t *ht)
+destroy_cb(PidginLogViewer *lv, gint resp, log_viewer_hash_t *ht)
 {
-	PidginLogViewerEx *lv_ex = syslog_viewer;
-	PidginLogViewer *lv;
-
 #ifdef G_OS_WIN32
 	if (resp == GTK_RESPONSE_HELP) {
 		GtkTreeSelection *sel;
 		GtkTreeIter iter;
-		GtkTreeModel *model;
+		GtkTreeModel *model = GTK_TREE_MODEL(pidgin_log_viewer_get_tree_store(lv));
+		GtkWidget *view = pidgin_log_viewer_get_tree_view(lv);
 		PurpleLog *log = NULL;
 		gchar *logdir;
 
-		if (ht != NULL)
-			lv_ex = g_hash_table_lookup(log_viewers, ht);
+		sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
 
-		lv = lv_ex->lv;
-		model = GTK_TREE_MODEL(lv->treestore);
-		sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(lv->treeview));
-
 		if (gtk_tree_selection_get_selected(sel, &model, &iter))
 			gtk_tree_model_get(model, &iter, 1, &log, -1);
 
@@ -425,40 +466,18 @@ destroy_cb(GtkWidget *w, gint resp, log_
 #endif
 
 	if (ht != NULL) {
-		lv_ex = g_hash_table_lookup(log_viewers, ht);
+		G_LOCK(log_viewers);
+		lv = g_hash_table_lookup(log_viewers, ht);
 		g_hash_table_remove(log_viewers, ht);
+		G_UNLOCK(log_viewers);
 
 		g_free(ht->buddyname);
 		g_free(ht);
-	} else
+	} else {
+		G_LOCK(syslog_viewer);
 		syslog_viewer = NULL;
-
-	lv = lv_ex->lv;
-
-	purple_request_close_with_handle(lv);
-
-	g_list_foreach(lv->logs, (GFunc) g_object_unref, NULL);
-	g_list_free(lv->logs);
-	g_free(lv->search);
-	g_free(lv);
-
-	if (lv_ex->read_cancel != NULL) {
-		g_cancellable_cancel(lv_ex->read_cancel);
-		g_object_unref(lv_ex->read_cancel);
+		G_UNLOCK(syslog_viewer);
 	}
-
-	if (lv_ex->search_cancel != NULL) {
-		g_cancellable_cancel(lv_ex->search_cancel);
-		g_object_unref(lv_ex->search_cancel);
-	}
-
-	if (lv_ex->list_cancel != NULL) {
-		g_cancellable_cancel(lv_ex->list_cancel);
-		g_object_unref(lv_ex->list_cancel);
-	}
-
-	g_free(lv_ex);
-	gtk_widget_destroy(w);
 }
 
 static void
@@ -474,7 +493,7 @@ delete_log_cleanup_cb(log_delete_callbac
 delete_log_cleanup_cb(log_delete_callback_data *data)
 {
 	if (data->destroy_handler_id > 0)
-		g_signal_handler_disconnect(data->log_data->log_viewer->lv->window,
+		g_signal_handler_disconnect(GTK_WINDOW(data->log_data->log_viewer),
 			data->destroy_handler_id);
 
 	pidgin_log_data_free(data->log_data);
@@ -497,7 +516,8 @@ pidgin_log_delete_log_cb(GObject *object
 		purple_notify_error(NULL, NULL, "Log Deletion Failed",
 			err->message);
 	else {
-		GtkTreeStore *treestore = temp->log_data->log_viewer->lv->treestore;
+		PidginLogViewer *lv = temp->log_data->log_viewer;
+		GtkTreeStore *treestore = pidgin_log_viewer_get_tree_store(lv);
 		GtkTreeIter *iter = temp->iter;
 		GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(treestore), iter);
 		gboolean first = !gtk_tree_path_prev(path);
@@ -535,7 +555,7 @@ log_delete_log_cb(GtkWidget *menuitem, l
 log_delete_log_cb(GtkWidget *menuitem, log_menu_callback_data *data)
 {
 	log_delete_callback_data *data2;
-	PidginLogViewerEx *lv_ex = data->log_data->log_viewer;
+	PidginLogViewer *lv = data->log_data->log_viewer;
 	PurpleBuddy *buddy;
 	PurpleChat *chat;
 	PurpleLog *log = data->log_data->log;
@@ -590,7 +610,7 @@ log_delete_log_cb(GtkWidget *menuitem, l
 	*(data2->iter) = *(data->iter);
 	data2->log_data->count++;
 
-	handle = purple_request_action(lv_ex, NULL, _("Delete Log?"), str, 0,
+	handle = purple_request_action(lv, NULL, _("Delete Log?"), str, 0,
 		NULL, NULL, NULL, data2, 2,
 		_("Delete"), delete_log_cb,
 		_("Cancel"), delete_log_cleanup_cb);
@@ -598,7 +618,7 @@ log_delete_log_cb(GtkWidget *menuitem, l
 	g_free(str);
 
 	/* Close the dialog window when the log viewer is closed */
-	data2->destroy_handler_id = g_signal_connect_swapped(lv_ex->lv->window,
+	data2->destroy_handler_id = g_signal_connect_swapped(lv,
 		"destroy", G_CALLBACK(remove_delete_log_window), handle);
 }
 
@@ -617,10 +637,10 @@ static void
 }
 
 static void
-log_show_popup_menu(GtkWidget *treeview, GdkEventButton *event, log_menu_callback_data *data)
+log_show_popup_menu(GdkEventButton *event, log_menu_callback_data *data)
 {
 	_pidgin_log_data *pidgin_log_data = data->log_data;
-	PidginLogViewer *lv = pidgin_log_data->log_viewer->lv;
+	PidginLogViewer *lv = pidgin_log_data->log_viewer;
 	GtkWidget *menu, *menuitem;
 
 	menu = gtk_menu_new();
@@ -637,7 +657,7 @@ log_show_popup_menu(GtkWidget *treeview,
 
 	pidgin_log_data->count = 1;
 	pidgin_log_data->is_window_open = TRUE;
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv->window,
+	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
 		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
 
 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, data->func, NULL,
@@ -646,23 +666,23 @@ static gboolean
 }
 
 static gboolean
-log_button_press_cb(GtkWidget *treeview, GdkEventButton *event, PidginLogViewerEx *lv_ex)
+log_button_press_cb(GtkTreeView *treeview, GdkEventButton *event, PidginLogViewer *lv)
 {
 	if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
 		log_menu_callback_data *data;
-		GtkTreeModel *model = GTK_TREE_MODEL(lv_ex->lv->treestore);
+		GtkTreeModel *model = GTK_TREE_MODEL(pidgin_log_viewer_get_tree_store(lv));
 		GtkTreeSelection *selection;
 		GtkTreePath *path;
 		GtkTreeIter *iter;
 		PurpleLog *log;
 
-		if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), event->x, event->y,
+		if (!gtk_tree_view_get_path_at_pos(treeview, event->x, event->y,
 			&path, NULL, NULL, NULL))
 			return FALSE;
 
 		iter = g_new(GtkTreeIter, 1);
 		gtk_tree_model_get_iter(model, iter, path);
-		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+		selection = gtk_tree_view_get_selection(treeview);
 		gtk_tree_selection_select_iter(selection, iter);
 		gtk_tree_model_get(model, iter, 1, &log, -1);
 		gtk_tree_path_free(path);
@@ -674,12 +694,12 @@ log_button_press_cb(GtkWidget *treeview,
 
 		data = g_new(log_menu_callback_data, 1);
 		data->log_data = g_new0(_pidgin_log_data, 1);
-		data->log_data->log_viewer = lv_ex;
+		data->log_data->log_viewer = lv;
 		data->log_data->log = log;
 		data->func = NULL;
 		data->iter = iter;
 
-		log_show_popup_menu(treeview, event, data);
+		log_show_popup_menu(event, data);
 
 		return TRUE;
 	}
@@ -688,23 +708,22 @@ static gboolean
 }
 
 static gboolean
-log_popup_menu_cb(GtkWidget *treeview, PidginLogViewerEx *lv_ex)
+log_popup_menu_cb(GtkTreeView *treeview, PidginLogViewer *lv)
 {
 	log_menu_callback_data *data;
-	PidginLogViewer *lv = lv_ex->lv;
 	GtkTreeSelection *sel;
 	GtkTreeIter *iter;
 	PurpleLog *log;
 
 	iter = g_new(GtkTreeIter, 1);
-	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(lv->treeview));
+	sel = gtk_tree_view_get_selection(treeview);
 
 	if (!gtk_tree_selection_get_selected(sel, NULL, iter)) {
 		g_free(iter);
 		return FALSE;
 	}
 
-	gtk_tree_model_get(GTK_TREE_MODEL(lv->treestore), iter, NODE_COLUMN, &log, -1);
+	gtk_tree_model_get(GTK_TREE_MODEL(pidgin_log_viewer_get_tree_store(lv)), iter, NODE_COLUMN, &log, -1);
 
 	if (log == NULL) {
 		g_free(iter);
@@ -713,12 +732,12 @@ log_popup_menu_cb(GtkWidget *treeview, P
 
 	data = g_new(log_menu_callback_data, 1);
 	data->log_data = g_new0(_pidgin_log_data, 1);
-	data->log_data->log_viewer = lv_ex;
+	data->log_data->log_viewer = lv;
 	data->log_data->log = log;
 	data->func = pidgin_treeview_popup_menu_position_func;
 	data->iter = iter;
 
-	log_show_popup_menu(treeview, NULL, data);
+	log_show_popup_menu(NULL, data);
 
 	return TRUE;
 }
@@ -729,9 +748,11 @@ search_find_cb(gpointer userdata)
 	_pidgin_log_data *pidgin_log_data = userdata;
 
 	if (pidgin_log_data->is_window_open) {
-		PidginLogViewer *lv = pidgin_log_data->log_viewer->lv;
+		PidginLogViewer *lv = pidgin_log_data->log_viewer;
+		GtkIMHtml *imhtml = GTK_IMHTML(pidgin_log_viewer_get_text_area(lv));
+		const gchar *search = pidgin_log_viewer_get_search_string(lv);
 
-		gtk_imhtml_search_find(GTK_IMHTML(lv->imhtml), lv->search);
+		gtk_imhtml_search_find(imhtml, search);
 	}
 
 	pidgin_log_data_free(pidgin_log_data);
@@ -746,28 +767,32 @@ pidgin_log_read_cb(GObject *object, GAsy
 
 	if (pidgin_log_data->is_window_open) {
 		PurpleLog *log = pidgin_log_data->log;
-		PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
-		PidginLogViewer *lv = lv_ex->lv;
-		GtkIMHtml *imhtml = GTK_IMHTML(lv->imhtml);
-		GError *err = NULL;
+		PurpleLogReadFlags flags;
+		PidginLogViewer *lv = pidgin_log_data->log_viewer;
+		GtkIMHtml *imhtml = GTK_IMHTML(pidgin_log_viewer_get_text_area(lv));
+		GError *error = NULL;
+		const gchar *search = pidgin_log_viewer_get_search_string(lv);
 		gchar *text;
 
-		text = purple_log_read_finish(log, res, &lv->flags, &err);
+		text = purple_log_read_finish(log, res, &flags, &error);
 
 		if (text == NULL) {
-			if (err->code == G_IO_ERROR_CANCELLED) {
+			if (error->code == G_IO_ERROR_CANCELLED) {
 				pidgin_log_data_free(pidgin_log_data);
-				g_clear_error(&err);
+				g_clear_error(&error);
 
 				return;
 			}
 
-			text = err->message;
+			text = error->message;
 		}
 
 #if GTK_CHECK_VERSION(2, 20, 0)
-		gtk_widget_show(lv->imhtml);
-		gtk_widget_hide(lv_ex->spinner);
+		{
+			PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+			gtk_widget_show(priv->imhtml);
+			gtk_widget_hide(priv->spinner);
+		}
 #endif
 
 		gtk_imhtml_clear(imhtml);
@@ -779,35 +804,34 @@ pidgin_log_read_cb(GObject *object, GAsy
 		//gtk_imhtml_append_text is a time killer in loading logs, annoyingly
 		gtk_imhtml_append_text(imhtml, text,
 			GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_TITLE | GTK_IMHTML_NO_SCROLL |
-			(lv->flags & PURPLE_LOG_READ_NO_NEWLINE ? GTK_IMHTML_NO_NEWLINE : 0));
+			(flags & PURPLE_LOG_READ_NO_NEWLINE ? GTK_IMHTML_NO_NEWLINE : 0));
 
 		/* If we have something in the search bar, scroll to it in the current log */
-		if (lv->search != NULL) {
+		if (search != NULL) {
 			gtk_imhtml_search_clear(imhtml);
 			g_idle_add(search_find_cb, pidgin_log_data);
 		} else
 			pidgin_log_data_free(pidgin_log_data);
 
-		g_clear_error(&err);
+		g_clear_error(&error);
 	} else
 		pidgin_log_data_free(pidgin_log_data);
 }
 
 static void
-log_select_cb(GtkTreeSelection *sel, PidginLogViewerEx *lv_ex)
+log_select_cb(GtkTreeSelection *sel, PidginLogViewer *lv)
 {
 	_pidgin_log_data *pidgin_log_data;
-	PidginLogViewer *lv = lv_ex->lv;
 	PurpleLog *log = NULL;
 	PurpleLogChatType chat_type;
-	GtkTreeModel *model = GTK_TREE_MODEL(lv->treestore);
+	GtkTreeModel *model = GTK_TREE_MODEL(pidgin_log_viewer_get_tree_store(lv));
 	GtkTreeIter iter;
 	GCancellable *cancel;
 
 	if (!gtk_tree_selection_get_selected(sel, &model, &iter))
 		return;
 
-	lv_ex->selected = TRUE;
+	pidgin_log_viewer_set_selected(lv, TRUE);
 	gtk_tree_model_get(model, &iter, 1, &log, -1);
 
 	if (log == NULL)
@@ -816,6 +840,7 @@ log_select_cb(GtkTreeSelection *sel, Pid
 	chat_type = purple_log_get_chat_type(log);
 
 	if (chat_type != PURPLE_LOG_SYSTEM) {
+		GtkWidget *label = pidgin_log_viewer_get_label(lv);
 		const gchar *log_name = purple_log_get_name(log);
 		gchar *title;
 
@@ -826,24 +851,27 @@ log_select_cb(GtkTreeSelection *sel, Pid
 			title = g_strdup_printf(_("<span size='larger' weight='bold'>Conversation with %s on %s</span>"),
 				log_name, log_get_date(log));
 
-		gtk_label_set_markup(GTK_LABEL(lv->label), title);
+		gtk_label_set_markup(GTK_LABEL(label), title);
 		g_free(title);
 	}
 
 	pidgin_log_data = g_new0(_pidgin_log_data, 1);
 	pidgin_log_data->is_window_open = TRUE;
 	pidgin_log_data->count = 1;
-	pidgin_log_data->log_viewer = lv_ex;
+	pidgin_log_data->log_viewer = lv;
 	pidgin_log_data->log = log;
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv->window,
+	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
 		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
 
 	cancel = g_cancellable_new();
-	pidgin_log_viewer_ex_set_read_cancel(lv_ex, cancel);
+	pidgin_log_viewer_set_read_cancel(lv, cancel);
 
 #if GTK_CHECK_VERSION(2, 20, 0)
-	gtk_widget_hide(lv->imhtml);
-	gtk_widget_show(lv_ex->spinner);
+		{
+			PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+			gtk_widget_hide(priv->imhtml);
+			gtk_widget_show(priv->spinner);
+		}
 #endif
 
 	purple_log_read_async(log, G_PRIORITY_DEFAULT, cancel,
@@ -859,19 +887,19 @@ static void
  * For now, I'll just make it a flat list.
  */
 static void
-populate_log_tree(PidginLogViewerEx *lv_ex)
+populate_log_tree(PidginLogViewer *lv)
      /* Logs are made from trees in real life.
         This is a tree made from logs */
 {
-	PidginLogViewer *lv = lv_ex->lv;
 	GtkTreeIter toplevel, child;
-	GList *logs;
+	GtkTreeStore *treestore = pidgin_log_viewer_get_tree_store(lv);
+	GList *logs = pidgin_log_viewer_get_logs(lv);
 	const gchar *month;
 	gchar prev_top_month[30] = "";
 
-	gtk_tree_store_clear(lv->treestore);
+	gtk_tree_store_clear(treestore);
 
-	for (logs = lv->logs; logs != NULL; logs = g_list_next(logs)) {
+	for ( ; logs != NULL; logs = g_list_next(logs)) {
 		PurpleLog *log = logs->data;
 		const struct tm *log_tm;
 		time_t log_time;
@@ -882,176 +910,46 @@ populate_log_tree(PidginLogViewerEx *lv_
 
 		if (strcmp(month, prev_top_month) != 0) {
 			/* top level */
-			gtk_tree_store_append(lv->treestore, &toplevel, NULL);
-			gtk_tree_store_set(lv->treestore, &toplevel, 0, month, 1, NULL, 2, log_tm, -1);
+			gtk_tree_store_append(treestore, &toplevel, NULL);
+			gtk_tree_store_set(treestore, &toplevel, 0, month, 1, NULL, 2, log_tm, -1);
 
 			strncpy(prev_top_month, month, sizeof(prev_top_month));
 		}
 
 		/* sub */
-		gtk_tree_store_append(lv->treestore, &child, &toplevel);
-		gtk_tree_store_set(lv->treestore, &child,
+		gtk_tree_store_append(treestore, &child, &toplevel);
+		gtk_tree_store_set(treestore, &child,
 			0, log_get_date(log), 1, log, -1);
 	}
 }
 
-static PidginLogViewerEx *
-display_log_viewer_nonblocking(log_viewer_hash_t *ht,
-	const gchar *title, GtkWidget *icon, gboolean need_log_size)
+PidginLogViewer *
+pidgin_log_viewer_new(log_viewer_hash_t *ht, const gchar *title,
+	GtkWidget *icon, gboolean need_log_size)
 {
-	PidginLogViewerEx *lv_ex;
 	PidginLogViewer *lv;
-	GtkCellRenderer *rend;
-	GtkTreeViewColumn *col;
-	GtkTreeSelection *sel;
-	GtkWidget *title_box, *pane, *sw, *frame, *find_button, *content_area, *vbox, *hbox;
-	gchar *text;
 
-	lv_ex = g_new0(PidginLogViewerEx, 1);
-	lv = g_new0(PidginLogViewer, 1);
-	lv->logs = NULL;
-	lv_ex->lv = lv;
+	lv = g_object_new(PIDGIN_TYPE_LOG_VIEWER,
+		"title", title,
+		"viewer-type", ht != NULL ? ht->chat_type : PURPLE_LOG_SYSTEM,
+		"icon", icon,
+		"need-log-size", need_log_size,
+		NULL);
 
+	/* Store the information to later prevent duplicate windows from popping up */
 	if (ht != NULL) {
-		lv_ex->type = ht->type;
-		g_hash_table_insert(log_viewers, ht, lv_ex);
-	} else
-		lv_ex->type = PURPLE_LOG_SYSTEM;
-
-	/* Window ***********/
-	lv->window = gtk_dialog_new_with_buttons(title, NULL, 0,
-		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
-	content_area = gtk_dialog_get_content_area(GTK_DIALOG(lv->window));
-
-#ifdef G_OS_WIN32
-	/* Steal the "HELP" response and use it to trigger browsing to the logs folder */
-	gtk_dialog_add_button(GTK_DIALOG(lv->window), _("_Browse logs folder"), GTK_RESPONSE_HELP);
-#endif
-	gtk_container_set_border_width (GTK_CONTAINER(lv->window), PIDGIN_HIG_BOX_SPACE);
-	gtk_dialog_set_has_separator(GTK_DIALOG(lv->window), FALSE);
-	gtk_box_set_spacing(GTK_BOX(content_area), 0);
-	g_signal_connect(lv->window, "response", G_CALLBACK(destroy_cb), ht);
-	gtk_window_set_role(GTK_WINDOW(lv->window), "log_viewer");
-
-	/* Icon *************/
-	if (icon != NULL) {
-		title_box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-		gtk_container_set_border_width(GTK_CONTAINER(title_box), PIDGIN_HIG_BOX_SPACE);
-		gtk_box_pack_start(GTK_BOX(content_area), title_box, FALSE, FALSE, 0);
-
-		gtk_box_pack_start(GTK_BOX(title_box), icon, FALSE, FALSE, 0);
-	} else
-		title_box = content_area;
-
-	/* Label ************/
-	lv->label = gtk_label_new(NULL);
-
-	text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>", title);
-
-	gtk_label_set_markup(GTK_LABEL(lv->label), text);
-	gtk_misc_set_alignment(GTK_MISC(lv->label), 0, 0);
-	gtk_box_pack_start(GTK_BOX(title_box), lv->label, FALSE, FALSE, 0);
-	g_free(text);
-
-	/* Pane *************/
-	pane = gtk_hpaned_new();
-	gtk_container_set_border_width(GTK_CONTAINER(pane), PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(content_area), pane, TRUE, TRUE, 0);
-
-	/* List *************/
-	sw = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
-	gtk_paned_add1(GTK_PANED(pane), sw);
-
-	lv->treestore = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, PURPLE_TYPE_STRUCT_TM);
-	lv->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(lv->treestore));
-	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(lv->treeview), FALSE);
-	pidgin_set_accessible_label(lv->treeview, lv->label);
-	g_object_unref(lv->treestore);
-
-	rend = gtk_cell_renderer_text_new();
-	col = gtk_tree_view_column_new_with_attributes ("time", rend, "markup", 0, NULL);
-	gtk_tree_view_append_column (GTK_TREE_VIEW(lv->treeview), col);
-	gtk_container_add (GTK_CONTAINER (sw), lv->treeview);
-
-	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(lv->treeview));
-
-	g_signal_connect(sel, "changed", G_CALLBACK (log_select_cb), lv_ex);
-	g_signal_connect(lv->treeview, "row-activated", G_CALLBACK(log_row_activated_cb), NULL);
-	g_signal_connect(lv->treeview, "button-press-event", G_CALLBACK(log_button_press_cb), lv_ex);
-	g_signal_connect(lv->treeview, "popup-menu", G_CALLBACK(log_popup_menu_cb), lv_ex);
-
-	/* Log size ************/
-	if (need_log_size) {
-		lv->size_label = gtk_label_new(NULL);
-		lv_ex->size = 0;
-
-		text = g_strdup_printf("<span weight='bold'>%s</span> %s", _("Total log size:"), _("calculating..."));
-		gtk_label_set_markup(GTK_LABEL(lv->size_label), text);
-		g_free(text);
-
-		gtk_misc_set_alignment(GTK_MISC(lv->size_label), 0, 0);
-		gtk_box_pack_end(GTK_BOX(content_area), lv->size_label, FALSE, FALSE, 0);
+		G_LOCK(log_viewers);
+		g_hash_table_insert(log_viewers, ht, g_object_ref(lv));
+		G_UNLOCK(log_viewers);
 	}
 
-	/* A fancy little box ************/
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_add2(GTK_PANED(pane), vbox);
+	g_signal_connect(lv, "response", G_CALLBACK(destroy_cb), ht);
 
-	/* Viewer ************/
-	frame = pidgin_create_imhtml(FALSE, &lv->imhtml, NULL, NULL);
-	gtk_widget_set_name(lv->imhtml, "pidgin_log_imhtml");
-	gtk_widget_set_size_request(lv->imhtml, 320, 200);
-	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
-	gtk_widget_show(frame);
-
-#if GTK_CHECK_VERSION(2, 20, 0)
-	lv_ex->spinner = gtk_spinner_new();
-	gtk_spinner_start(GTK_SPINNER(lv_ex->spinner));
-	gtk_box_pack_start(GTK_BOX(vbox), lv_ex->spinner, TRUE, FALSE, 0);
-#endif
-
-	/* Search box **********/
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	lv->entry = gtk_entry_new();
-	find_button = gtk_button_new_from_stock(GTK_STOCK_FIND);
-
-	gtk_box_pack_start(GTK_BOX(hbox), lv->entry, TRUE, TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(hbox), find_button, FALSE, FALSE, 0);
-
-	g_signal_connect(lv->entry, "activate", G_CALLBACK(search_cb), lv_ex);
-	g_signal_connect(find_button, "clicked", G_CALLBACK(search_cb), lv_ex);
-
-	/* Progress bars **********/
-	lv_ex->list_bar = gtk_progress_bar_new();
-	lv_ex->search_bar = gtk_progress_bar_new();
-
-	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lv_ex->list_bar), _("Waiting for logs ..."));
-	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lv_ex->search_bar), _("Searching logs ..."));
-
-	gtk_box_pack_start(GTK_BOX(vbox), lv_ex->list_bar, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), lv_ex->search_bar, FALSE, FALSE, 0);
-
-	gtk_widget_show_all(lv->window);
-	gtk_widget_hide(lv_ex->search_bar);
-#if GTK_CHECK_VERSION(2, 20, 0)
-	gtk_widget_hide(lv_ex->spinner);
-#endif
-
-	lv_ex->read_cancel = NULL;
-	lv_ex->search_cancel = NULL;
-	lv_ex->list_cancel = NULL;
-
-	lv_ex->selected = FALSE;
-
-	return lv_ex;
+	return lv;
 }
 
 static void
-insert_log_viewer_log(PidginLogViewerEx *lv_ex, PurpleLog *log)
+insert_log_viewer_log(PidginLogViewer *lv, PurpleLog *log)
 {
 	/* It runs about twice as quick to insert the logs, rather than re-populate the entire tree
 
@@ -1059,18 +957,14 @@ insert_log_viewer_log(PidginLogViewerEx 
 	   populate_log_tree if the tree format is to ever be changed
 	 */
 	PurpleLog *child_log;
-	GtkTreeModel *model;
-	GtkTreeStore *store;
+	GtkTreeStore *store = pidgin_log_viewer_get_tree_store(lv);
+	GtkTreeModel *model = GTK_TREE_MODEL(store);
 	GtkTreeIter insertion, inserted_month, month_iter, child;
 	const gchar *month;
-	const struct tm *log_tm;
+	const struct tm *log_tm = purple_log_get_tm(log);
 	struct tm *tm;
-	time_t log_time;
+	time_t log_time = purple_log_get_time(log);
 
-	store = lv_ex->lv->treestore;
-	model = GTK_TREE_MODEL(store);
-	log_tm = purple_log_get_tm(log);
-	log_time = purple_log_get_time(log);
 	month = purple_utf8_strftime(_("%B %Y"), log_tm != NULL ? log_tm : localtime(&log_time));
 
 	if (!gtk_tree_model_get_iter_first(model, &month_iter)) {
@@ -1137,55 +1031,57 @@ insert_log_viewer_log(PidginLogViewerEx 
 	gtk_tree_store_set(store, &insertion, 0, log_get_date(log), 1, log, -1);
 }
 
-static void
-append_log_viewer_logs(PidginLogViewerEx *lv_ex, GList *logs)
+void
+pidgin_log_viewer_add_logs(PidginLogViewer *lv, GList *logs)
 {
-	PidginLogViewer *lv = lv_ex->lv;
+	PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
 	GList *list;
 
 	logs = g_list_sort(logs, purple_log_compare);
 
 	for (list = logs; list != NULL; list = g_list_next(list))
-		insert_log_viewer_log(lv_ex, list->data);
+		insert_log_viewer_log(lv, list->data);
 
-	lv->logs = g_list_concat(lv->logs, logs);
-	lv->logs = g_list_sort(lv->logs, purple_log_compare);
+	priv->logs = g_list_concat(priv->logs, logs);
+	priv->logs = g_list_sort(priv->logs, purple_log_compare);
 }
 
 static void
 pidgin_log_done_cb(_pidgin_log_data *pidgin_log_data)
 {
 	if (pidgin_log_data->is_window_open) {
-		PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
-		PidginLogViewer *lv = lv_ex->lv;
+		PidginLogViewer *lv = pidgin_log_data->log_viewer;
+		GtkWidget *bar = pidgin_log_viewer_get_list_bar(lv);
+		GList *logs = pidgin_log_viewer_get_logs(lv);
 
-		gtk_widget_hide(lv_ex->list_bar);
+		gtk_widget_hide(bar);
 
-		if (lv->logs == NULL) {
+		if (logs == NULL) {
 			/* No logs were found. */
+			PurpleLogChatType type = pidgin_log_viewer_get_viewer_type(lv);
 			const gchar *log_preferences = NULL;
 
-			if (lv_ex->type == PURPLE_LOG_SYSTEM) {
+			if (type == PURPLE_LOG_SYSTEM) {
 				if (!purple_prefs_get_bool("/purple/logging/log_system"))
 					log_preferences = _("System events will only be logged if "
 						"the \"Log all status changes to system log\" "
 						"preference is enabled.");
-			} else if (lv_ex->type == PURPLE_LOG_IM) {
+			} else if (type == PURPLE_LOG_IM) {
 				if (!purple_prefs_get_bool("/purple/logging/log_ims"))
 					log_preferences = _("Instant messages will only be logged "
 					"if the \"Log all instant messages\" "
 					"preference is enabled.");
-			} else if (lv_ex->type == PURPLE_LOG_CHAT) {
+			} else if (type == PURPLE_LOG_CHAT) {
 				if (!purple_prefs_get_bool("/purple/logging/log_chats"))
 					log_preferences = _("Chats will only be logged if the "
 						"\"Log all chats\" preference is enabled.");
 			}
 
-			purple_notify_info(NULL, gtk_window_get_title(GTK_WINDOW(lv->window)),
+			purple_notify_info(NULL, gtk_window_get_title(GTK_WINDOW(lv)),
 				_("No logs were found"), log_preferences);
-			gtk_dialog_response(GTK_DIALOG(lv->window), GTK_RESPONSE_CLOSE);
-		} else if (!lv_ex->selected)
-			select_first_log(lv_ex);
+			gtk_dialog_response(GTK_DIALOG(lv), GTK_RESPONSE_CLOSE);
+		} else if (!pidgin_log_viewer_is_selected(lv))
+			select_first_log(lv);
 	}
 
 	pidgin_log_data_free(pidgin_log_data);
@@ -1195,12 +1091,13 @@ pidgin_log_viewer_update_list_bar(_pidgi
 pidgin_log_viewer_update_list_bar(_pidgin_log_data *pidgin_log_data)
 {
 	if (pidgin_log_data->is_window_open) {
-		PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+		PidginLogViewer *lv = pidgin_log_data->log_viewer;
+		GtkWidget *bar = pidgin_log_viewer_get_list_bar(lv);
 		gdouble fraction;
 
 		fraction = (gdouble) pidgin_log_data->count /
 			(gdouble) pidgin_log_data->total;
-		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lv_ex->list_bar), 1.0 - fraction);
+		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(bar), 1.0 - fraction);
 	}
 }
 
@@ -1208,12 +1105,13 @@ pidgin_log_viewer_update_search_bar(_pid
 pidgin_log_viewer_update_search_bar(_pidgin_log_data *pidgin_log_data)
 {
 	if (pidgin_log_data->is_window_open) {
-		PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+		PidginLogViewer *lv = pidgin_log_data->log_viewer;
+		GtkWidget *bar = pidgin_log_viewer_get_search_bar(lv);
 		gdouble fraction;
 
 		fraction = (gdouble) pidgin_log_data->count /
 			(gdouble) pidgin_log_data->total;
-		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lv_ex->search_bar), 1.0 - fraction);
+		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(bar), 1.0 - fraction);
 	}
 }
 
@@ -1232,23 +1130,22 @@ pidgin_log_size_cb(GObject *object, GAsy
 			purple_debug_error("gtklog", "Error getting total log size: %s\n",
 				error->message);
 	} else if (pidgin_log_data->is_window_open) {
-		PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+		PidginLogViewer *lv = pidgin_log_data->log_viewer;
 		gchar *sz_txt, *text;
 		gsize total;
 
-		total = lv_ex->size;
+		total = pidgin_log_viewer_get_total_size(lv);
 		total += log_size;
+		pidgin_log_viewer_set_total_size(lv, total);
 
-		lv_ex->size = total;
+		// sz_txt = purple_str_size_to_units(total);
+		// text = g_strdup_printf("<span weight='bold'>%s</span> %s",
+			// _("Total log size:"), sz_txt);
 
-		sz_txt = purple_str_size_to_units(total);
-		text = g_strdup_printf("<span weight='bold'>%s</span> %s",
-			_("Total log size:"), sz_txt);
+		// gtk_label_set_markup(GTK_LABEL(lv_ex->lv->size_label), text);
 
-		gtk_label_set_markup(GTK_LABEL(lv_ex->lv->size_label), text);
-
-		g_free(sz_txt);
-		g_free(text);
+		// g_free(sz_txt);
+		// g_free(text);
 	}
 
 	g_clear_error(&error);
@@ -1272,7 +1169,7 @@ pidgin_log_list_cb(GObject *object, GAsy
 		if (error != NULL && error->code != G_IO_ERROR_CANCELLED)
 			purple_debug_error("gtklog", "Error getting logs: %s\n", error->message);
 	} else if (pidgin_log_data->is_window_open)
-		append_log_viewer_logs(pidgin_log_data->log_viewer, list);
+		pidgin_log_viewer_add_logs(pidgin_log_data->log_viewer, list);
 
 	g_clear_error(&error);
 	pidgin_log_viewer_update_search_bar(pidgin_log_data);
@@ -1295,7 +1192,7 @@ pidgin_log_system_list_cb(GObject *objec
 		if (error != NULL && error->code != G_IO_ERROR_CANCELLED)
 			purple_debug_error("gtklog", "Error getting system logs: %s\n", error->message);
 	} else if (pidgin_log_data->is_window_open)
-		append_log_viewer_logs(pidgin_log_data->log_viewer, list);
+		pidgin_log_viewer_add_logs(pidgin_log_data->log_viewer, list);
 
 	g_clear_error(&error);
 	pidgin_log_viewer_update_search_bar(pidgin_log_data);
@@ -1305,18 +1202,18 @@ void
 }
 
 void
-pidgin_log_show(PurpleLogChatType type, const gchar *buddyname, PurpleAccount *account)
+pidgin_log_show(PurpleLogChatType chat_type, const gchar *buddyname, PurpleAccount *account)
 {
 	log_viewer_hash_t *ht;
 	_pidgin_log_data *pidgin_log_data;
-	PidginLogViewerEx *lv_ex;
+	PidginLogViewer *lv;
 	GdkPixbuf *prpl_icon;
 	GtkWidget *image;
 	GCancellable *cancel;
 	const gchar *name;
 	gchar *title;
 
-	if (type != PURPLE_LOG_IM) {
+	if (chat_type != PURPLE_LOG_IM) {
 		g_return_if_fail(account != NULL);
 		g_return_if_fail(buddyname != NULL);
 	}
@@ -1324,22 +1221,24 @@ pidgin_log_show(PurpleLogChatType type, 
 	name = buddyname;
 	ht = g_new0(log_viewer_hash_t, 1);
 
-	ht->type = type;
+	ht->chat_type = chat_type;
 	ht->buddyname = g_strdup(buddyname);
 	ht->account = account;
 
-	if (log_viewers == NULL)
-		log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
-	else if ((lv_ex = g_hash_table_lookup(log_viewers, ht)) != NULL) {
-		gtk_window_present(GTK_WINDOW(lv_ex->lv->window));
+	G_LOCK(log_viewers);
+	lv = g_hash_table_lookup(log_viewers, ht);
+	G_UNLOCK(log_viewers);
 
+	if (lv != NULL) {
+		gtk_window_present(GTK_WINDOW(lv));
+
 		g_free(ht->buddyname);
 		g_free(ht);
 
 		return;
 	}
 
-	if (type == PURPLE_LOG_CHAT) {
+	if (chat_type == PURPLE_LOG_CHAT) {
 		PurpleChat *chat = purple_blist_find_chat(account, buddyname);
 
 		if (chat != NULL)
@@ -1361,23 +1260,22 @@ pidgin_log_show(PurpleLogChatType type, 
 
 	prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 	image = gtk_image_new_from_pixbuf(prpl_icon);
-	lv_ex = pidgin_log_data->log_viewer =
-		display_log_viewer_nonblocking(ht, title, image, TRUE);
-
+	lv = pidgin_log_data->log_viewer = pidgin_log_viewer_new(ht, title, image, TRUE);
 	g_free(title);
+	gtk_widget_show(GTK_WIDGET(lv));
 
 	if (prpl_icon != NULL)
 		g_object_unref(prpl_icon);
 
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv_ex->lv->window,
+	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(GTK_WINDOW(lv),
 		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
 
 	cancel = g_cancellable_new();
-	pidgin_log_viewer_ex_set_list_cancel(lv_ex, cancel);
+	pidgin_log_viewer_set_list_cancel(lv, cancel);
 
-	purple_logs_get_logs_async(type, buddyname, account, G_PRIORITY_DEFAULT,
+	purple_logs_get_logs_async(chat_type, buddyname, account, G_PRIORITY_DEFAULT,
 		cancel, pidgin_log_list_cb, pidgin_log_data);
-	purple_logs_get_total_size_async(type, buddyname, account, G_PRIORITY_DEFAULT,
+	purple_logs_get_total_size_async(chat_type, buddyname, account, G_PRIORITY_DEFAULT,
 		cancel, pidgin_log_size_cb, pidgin_log_data);
 
 	g_object_unref(cancel);
@@ -1389,7 +1287,7 @@ pidgin_log_show_contact(PurpleContact *c
 	log_viewer_hash_t *ht;
 	_pidgin_log_data *pidgin_log_data;
 	PurpleBlistNode *child;
-	PidginLogViewerEx *lv_ex;
+	PidginLogViewer *lv;
 	GCancellable *cancel;
 	GdkPixbuf *pixbuf;
 	GtkWidget *image;
@@ -1399,14 +1297,16 @@ pidgin_log_show_contact(PurpleContact *c
 	g_return_if_fail(contact != NULL);
 
 	ht = g_new0(log_viewer_hash_t, 1);
-	ht->type = PURPLE_LOG_IM;
+	ht->chat_type = PURPLE_LOG_IM;
 	ht->contact = contact;
 
-	if (log_viewers == NULL) {
-		log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
-	} else if ((lv_ex = g_hash_table_lookup(log_viewers, ht)) != NULL) {
-		gtk_window_present(GTK_WINDOW(lv_ex->lv->window));
+	G_LOCK(log_viewers);
+	lv = g_hash_table_lookup(log_viewers, ht);
+	G_UNLOCK(log_viewers);
 
+	if (lv != NULL) {
+		gtk_window_present(GTK_WINDOW(lv));
+
 		g_free(ht);
 
 		return;
@@ -1443,16 +1343,16 @@ pidgin_log_show_contact(PurpleContact *c
 	pidgin_log_data->is_window_open = TRUE;
 
 	title = g_strdup_printf(_("Conversations with %s"), name);
-	lv_ex = pidgin_log_data->log_viewer = display_log_viewer_nonblocking(ht, title, image, TRUE);
-
+	lv = pidgin_log_data->log_viewer = pidgin_log_viewer_new(ht, title, image, TRUE);
 	g_free(title);
+	gtk_widget_show(GTK_WIDGET(lv));
 
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv_ex->lv->window,
+	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(GTK_WINDOW(lv),
 		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
 
 	pidgin_log_data->count = pidgin_log_data->total = 0;
 	cancel = g_cancellable_new();
-	pidgin_log_viewer_ex_set_list_cancel(lv_ex, cancel);
+	pidgin_log_viewer_set_list_cancel(lv, cancel);
 
 	for (child = contact->node.child; child != NULL; child = child->next) {
 		if (PURPLE_BLIST_NODE_IS_BUDDY(child)) {
@@ -1475,25 +1375,31 @@ pidgin_syslog_show(void)
 {
 	_pidgin_log_data *pidgin_log_data;
 	PurpleAccount *account;
+	PidginLogViewer *lv;
 	GCancellable *cancel;
 	GList *accounts;
 
-	if (syslog_viewer != NULL) {
-		gtk_window_present(GTK_WINDOW(syslog_viewer->lv->window));
+	G_LOCK(syslog_viewer);
+	lv = syslog_viewer;
+	G_UNLOCK(syslog_viewer);
+
+	if (lv != NULL) {
+		gtk_window_present(GTK_WINDOW(lv));
 		return;
 	}
 
 	pidgin_log_data = g_new0(_pidgin_log_data, 1);
 	pidgin_log_data->is_window_open = TRUE;
 	
-	syslog_viewer = pidgin_log_data->log_viewer = display_log_viewer_nonblocking(NULL,
+	lv = pidgin_log_data->log_viewer = pidgin_log_viewer_new(NULL,
 		_("System Log"), NULL, FALSE);
+	gtk_widget_show(GTK_WIDGET(lv));
 
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(syslog_viewer->lv->window,
+	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(GTK_WINDOW(lv),
 		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
 
 	cancel = g_cancellable_new();
-	pidgin_log_viewer_ex_set_list_cancel(syslog_viewer, cancel);
+	pidgin_log_viewer_set_list_cancel(lv, cancel);
 	pidgin_log_data->count = pidgin_log_data->total = 0;
 
 	for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = g_list_next(accounts)) {
@@ -1509,8 +1415,211 @@ pidgin_syslog_show(void)
 	}
 
 	g_object_unref(cancel);
+
+	G_LOCK(syslog_viewer);
+	syslog_viewer = lv;
+	G_UNLOCK(syslog_viewer);
 }
 
+static void
+pidgin_log_viewer_finalize(GObject *object)
+{
+	PidginLogViewer *lv = PIDGIN_LOG_VIEWER(object);
+	// PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+
+	purple_request_close_with_handle(lv);
+
+	// Move these into a finalize function
+	pidgin_log_viewer_clear_logs(lv);
+	pidgin_log_viewer_set_search_string(lv, NULL);
+	pidgin_log_viewer_set_read_cancel(lv, NULL);
+	pidgin_log_viewer_set_search_cancel(lv, NULL);
+	pidgin_log_viewer_set_list_cancel(lv, NULL);
+	g_object_unref(lv);
+
+	// gtk_widget_destroy(w);
+}
+
+static void
+pidgin_log_viewer_class_init(PidginLogViewerClass *class)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+
+	gobject_class->set_property = pidgin_log_viewer_set_property;
+	gobject_class->get_property = pidgin_log_viewer_get_property;
+	gobject_class->finalize = pidgin_log_viewer_finalize;
+
+	// properties[PROP_LOG_CHAT_TYPE] =
+		// g_param_spec_enum("chat-type",
+			// "Chat Type",
+			// "The chat type of the log",
+			// PURPLE_TYPE_LOG_CHAT_TYPE,
+			// PURPLE_LOG_SYSTEM,
+			// G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+	// Icon : Constructor only
+
+	// g_object_class_install_property(gobject_class,
+		// PROP_LOG_CHAT_TYPE,
+		// properties[PROP_LOG_CHAT_TYPE]);
+
+	g_type_class_add_private(gobject_class, sizeof(PidginLogViewerPrivate));
+}
+
+static void
+pidgin_log_viewer_init(PidginLogViewer *lv)
+{
+	PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+
+	priv->logs = NULL;
+	priv->size = 0;
+	priv->read_cancel = NULL;
+	priv->search_cancel = NULL;
+	priv->list_cancel = NULL;
+	priv->selected = FALSE;
+}
+
+static void
+pidgin_log_viewer_constructed(PidginLogViewer *lv)
+{
+	PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+	GtkCellRenderer *rend;
+	GtkTreeViewColumn *col;
+	GtkTreeSelection *sel;
+	GtkDialog *dialog = GTK_DIALOG(lv);
+	GtkWindow *window = GTK_WINDOW(lv);
+	GtkWidget *icon, *title_box, *pane, *sw, *frame;
+	GtkWidget *find_button, *content_area, *vbox, *hbox;
+	gchar *text;
+	gboolean need_log_size;
+
+	/* Window ***********/
+	gtk_dialog_add_buttons(dialog, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
+#ifdef G_OS_WIN32
+	/* Steal the "HELP" response and use it to trigger browsing to the logs folder */
+	gtk_dialog_add_button(dialog, _("_Browse logs folder"), GTK_RESPONSE_HELP);
+#endif
+
+	gtk_container_set_border_width (GTK_CONTAINER(lv), PIDGIN_HIG_BOX_SPACE);
+	gtk_dialog_set_has_separator(dialog, FALSE);
+	content_area = gtk_dialog_get_content_area(dialog);
+	gtk_box_set_spacing(GTK_BOX(content_area), 0);
+	gtk_window_set_role(window, "log_viewer");
+
+	/* Icon *************/
+	icon = pidgin_log_viewer_get_icon(lv);
+
+	if (icon != NULL) {
+		title_box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+		gtk_container_set_border_width(GTK_CONTAINER(title_box), PIDGIN_HIG_BOX_SPACE);
+		gtk_box_pack_start(GTK_BOX(content_area), title_box, FALSE, FALSE, 0);
+		gtk_box_pack_start(GTK_BOX(title_box), icon, FALSE, FALSE, 0);
+	} else
+		title_box = content_area;
+
+	/* Label ************/
+	priv->label = gtk_label_new(NULL);
+
+	text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>",
+		gtk_window_get_title(window));
+
+	gtk_label_set_markup(GTK_LABEL(priv->label), text);
+	gtk_misc_set_alignment(GTK_MISC(priv->label), 0, 0);
+	gtk_box_pack_start(GTK_BOX(title_box), priv->label, FALSE, FALSE, 0);
+	g_free(text);
+
+	/* Pane *************/
+	pane = gtk_hpaned_new();
+	gtk_container_set_border_width(GTK_CONTAINER(pane), PIDGIN_HIG_BOX_SPACE);
+	gtk_box_pack_start(GTK_BOX(content_area), pane, TRUE, TRUE, 0);
+
+	/* List *************/
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+	gtk_paned_add1(GTK_PANED(pane), sw);
+
+	priv->treestore = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, PURPLE_TYPE_STRUCT_TM);
+	priv->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(priv->treestore));
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(priv->treeview), FALSE);
+	pidgin_set_accessible_label(priv->treeview, priv->label);
+	// g_object_unref(priv->treestore);
+
+	rend = gtk_cell_renderer_text_new();
+	col = gtk_tree_view_column_new_with_attributes ("time", rend, "markup", 0, NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW(priv->treeview), col);
+	gtk_container_add (GTK_CONTAINER (sw), priv->treeview);
+
+	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview));
+
+	g_signal_connect(sel, "changed", G_CALLBACK (log_select_cb), lv);
+	g_signal_connect(priv->treeview, "row-activated", G_CALLBACK(log_row_activated_cb), NULL);
+	g_signal_connect(priv->treeview, "button-press-event", G_CALLBACK(log_button_press_cb), lv);
+	g_signal_connect(priv->treeview, "popup-menu", G_CALLBACK(log_popup_menu_cb), lv);
+
+	/* Log size ************/
+	need_log_size = pidgin_log_viewer_get_need_log_size(lv);
+
+	if (need_log_size) {
+		priv->size_label = gtk_label_new(NULL);
+
+		text = g_strdup_printf("<span weight='bold'>%s</span> %s",
+			_("Total log size:"), _("calculating..."));
+		gtk_label_set_markup(GTK_LABEL(priv->size_label), text);
+		g_free(text);
+
+		gtk_misc_set_alignment(GTK_MISC(priv->size_label), 0, 0);
+		gtk_box_pack_end(GTK_BOX(content_area), priv->size_label, FALSE, FALSE, 0);
+	}
+
+	/* A fancy little box ************/
+	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_paned_add2(GTK_PANED(pane), vbox);
+
+	/* Viewer ************/
+	frame = pidgin_create_imhtml(FALSE, &priv->imhtml, NULL, NULL);
+	gtk_widget_set_name(priv->imhtml, "pidgin_log_imhtml");
+	gtk_widget_set_size_request(priv->imhtml, 320, 200);
+	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+	gtk_widget_show(frame);
+
+#if GTK_CHECK_VERSION(2, 20, 0)
+	priv->spinner = gtk_spinner_new();
+	gtk_spinner_start(GTK_SPINNER(priv->spinner));
+	gtk_box_pack_start(GTK_BOX(vbox), priv->spinner, TRUE, FALSE, 0);
+#endif
+
+	/* Search box **********/
+	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+	priv->entry = gtk_entry_new();
+	find_button = gtk_button_new_from_stock(GTK_STOCK_FIND);
+
+	gtk_box_pack_start(GTK_BOX(hbox), priv->entry, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), find_button, FALSE, FALSE, 0);
+
+	g_signal_connect(priv->entry, "activate", G_CALLBACK(search_cb), lv);
+	g_signal_connect(find_button, "clicked", G_CALLBACK(search_cb), lv);
+
+	/* Progress bars **********/
+	priv->list_bar = gtk_progress_bar_new();
+	priv->search_bar = gtk_progress_bar_new();
+
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->list_bar), _("Waiting for logs ..."));
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->search_bar), _("Searching logs ..."));
+
+	gtk_box_pack_start(GTK_BOX(vbox), priv->list_bar, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), priv->search_bar, FALSE, FALSE, 0);
+
+	gtk_widget_show_all(GTK_WIDGET(lv));
+	gtk_widget_hide(GTK_WIDGET(lv));
+	gtk_widget_hide(priv->search_bar);
+#if GTK_CHECK_VERSION(2, 20, 0)
+	gtk_widget_hide(priv->spinner);
+#endif
+}
+
 /****************************************************************************
  * GTK+ LOG SUBSYSTEM *******************************************************
  ****************************************************************************/
@@ -1528,6 +1637,11 @@ pidgin_log_init(void)
 {
 	void *handle = pidgin_log_get_handle();
 
+	G_LOCK(log_viewers);
+	// Is the ht stuff leaking?
+	log_viewers = g_hash_table_new_full(log_viewer_hash, log_viewer_equal, NULL, g_object_unref);
+	G_UNLOCK(log_viewers);
+
 	purple_signal_register(handle, "log-displaying",
 		purple_marshal_VOID__POINTER_POINTER,
 		NULL, 2,
@@ -1539,4 +1653,14 @@ pidgin_log_uninit(void)
 pidgin_log_uninit(void)
 {
 	purple_signals_unregister_by_instance(pidgin_log_get_handle());
+
+	if (log_viewers != NULL) {
+		g_hash_table_destroy(log_viewers);
+		log_viewers = NULL;
+	}
+
+	if (syslog_viewer != NULL) {
+		g_object_unref(syslog_viewer);
+		syslog_viewer = NULL;
+	}
 }
============================================================
--- pidgin/gtklog.h	908b652405ccb703cfc5985446da0478f087fdab
+++ pidgin/gtklog.h	8a817ee7c6fc35869d32dbf88ee7887ad33fcc1e
@@ -27,13 +27,25 @@
 #ifndef _PIDGINLOG_H_
 #define _PIDGINLOG_H_
 
+#include <glib-object.h>
+
 #include "pidgin.h"
 #include "log.h"
+#include "account.h"
 
-#include "account.h"
+G_BEGIN_DECLS
 
+#define PIDGIN_TYPE_LOG_VIEWER          (pidgin_log_viewer_get_type())
+#define PIDGIN_LOG_VIEWER(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), PIDGIN_TYPE_LOG_VIEWER, PidginLogViewer))
+#define PIDGIN_LOG_VIEWER_CLASS(k)      (G_TYPE_CHECK_CLASS_CAST((k), PIDGIN_TYPE_LOG_VIEWER, PidginLogViewerClass))
+#define PIDGIN_IS_LOG_VIEWER(o)	        (G_TYPE_CHECK_INSTANCE_TYPE ((o), PIDGIN_TYPE_LOG_VIEWER))
+#define PIDGIN_IS_LOG_VIEWER_CLASS(k)   (G_TYPE_CHECK_CLASS_TYPE((k), PIDGIN_TYPE_LOG_VIEWER))
+#define PIDGIN_LOG_VIEWER_GET_CLASS(o)  (G_TYPE_INSTANCE_GET_CLASS((o), PIDGIN_TYPE_LOG_VIEWER, PidginLogViewerClass))
+
 /** @copydoc _PidginLogViewer */
 typedef struct _PidginLogViewer PidginLogViewer;
+/** @copydoc _PidginLogViewerClass */
+typedef struct _PidginLogViewerClass PidginLogViewerClass;
 
 /********************************************************
  * DATA STRUCTURES **************************************
@@ -43,26 +55,79 @@ struct _PidginLogViewer {
  * A GTK+ Log Viewer.  You can look at logs with it.
  */
 struct _PidginLogViewer {
-	GList              *logs;       /**< The list of logs viewed in this viewer */
-	GtkWidget          *window;     /**< The viewer's window */
-	GtkTreeStore       *treestore;  /**< The treestore containing said logs */
-	GtkWidget          *treeview;   /**< The treeview representing said treestore */
-	GtkWidget          *imhtml;     /**< The imhtml to display said logs */
-	GtkWidget          *entry;      /**< The search entry, in which search terms are entered */
-	PurpleLogReadFlags flags;       /**< The most recently used log read flags */
-	gchar              *search;     /**< The string currently being searched for */
-	GtkWidget          *label;      /**< The label at the top of the log viewer */
-	GtkWidget          *size_label; /**< The label with total log size */
+	GtkDialog parent_instance;
 };
 
-G_BEGIN_DECLS
+// Should this extend something else? Like GtkObject or GtkDialog?
+struct _PidginLogViewerClass {
+	GtkDialogClass parent_class;
 
+	void (*_purple_reserved1)(void);
+	void (*_purple_reserved2)(void);
+	void (*_purple_reserved3)(void);
+	void (*_purple_reserved4)(void);
+};
+
+
+//
+GList *pidgin_log_viewer_get_logs(PidginLogViewer *lv);
+//
+GtkTreeStore *pidgin_log_viewer_get_tree_store(PidginLogViewer *lv);
+//
+GtkWidget *pidgin_log_viewer_get_tree_view(PidginLogViewer *lv);
+//
+GtkWidget *pidgin_log_viewer_get_text_area(PidginLogViewer *lv);
+//
+GtkWidget *pidgin_log_viewer_get_entry(PidginLogViewer *lv);
+//
+G_CONST_RETURN gchar *pidgin_log_viewer_get_search_string(PidginLogViewer *lv);
+// Better name?
+GtkWidget *pidgin_log_viewer_get_label(PidginLogViewer *lv);
+// Better name?
+GtkWidget *pidgin_log_viewer_get_size_label(PidginLogViewer *lv);
+//
+gssize pidgin_log_viewer_get_total_size(PidginLogViewer *lv);
+//
+GtkWidget *pidgin_log_viewer_get_list_bar(PidginLogViewer *lv);
+//
+GtkWidget *pidgin_log_viewer_get_search_bar(PidginLogViewer *lv);
+//
+GCancellable *pidgin_log_viewer_get_search_cancel(PidginLogViewer *lv);
+//
+GCancellable *pidgin_log_viewer_get_list_cancel(PidginLogViewer *lv);
+//
+GCancellable *pidgin_log_viewer_get_read_cancel(PidginLogViewer *lv);
+//
+gboolean pidgin_log_viewer_is_selected(PidginLogViewer *lv);
+//
+PurpleLogChatType pidgin_log_viewer_get_viewer_type(PidginLogViewer *lv);
+
+//
+void pidgin_log_viewer_clear_logs(PidginLogViewer *lv);
+//
+void pidgin_log_viewer_add_logs(PidginLogViewer *lv, GList *logs);
+//
+void pidgin_log_viewer_set_search_string(PidginLogViewer *lv, const gchar *string);
+//
+void pidgin_log_viewer_set_total_size(PidginLogViewer *lv, gssize total);
+//
+void pidgin_log_viewer_set_search_cancel(PidginLogViewer *lv, GCancellable *cancellable);
+//
+void pidgin_log_viewer_set_list_cancel(PidginLogViewer *lv, GCancellable *cancellable);
+//
+void pidgin_log_viewer_set_read_cancel(PidginLogViewer *lv, GCancellable *cancellable);
+//
+void pidgin_log_viewer_set_selected(PidginLogViewer *lv, gboolean selected);
+
+
 /**************************************************************************/
 /** @name Log Viewer Creators                                             */
 /**************************************************************************/
 
 /*@{*/
 
+GType pidgin_log_viewer_get_type(void);
+
 /**
  * Displays the logs of a certain type for a buddy or chat on an account
  *


More information about the Commits mailing list