]> git.0d.be Git - empathy.git/blob - src/empathy-debug-dialog.c
Add Debug interface to TpProxy's known interfaces.
[empathy.git] / src / empathy-debug-dialog.c
1 /*
2 *  Copyright (C) 2009 Collabora Ltd.
3 *
4 *  This library is free software; you can redistribute it and/or
5 *  modify it under the terms of the GNU Lesser General Public
6 *  License as published by the Free Software Foundation; either
7 *  version 2.1 of the License, or (at your option) any later version.
8 *
9 *  This library is distributed in the hope that it will be useful,
10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 *  Lesser General Public License for more details.
13 *
14 *  You should have received a copy of the GNU Lesser General Public
15 *  License along with this library; if not, write to the Free Software
16 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 *
18 *  Authors: Jonny Lamb <jonny.lamb@collabora.co.uk>
19 */
20
21 #include "config.h"
22
23 #include <glib/gi18n.h>
24 #include <gtk/gtk.h>
25
26 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
27 #include <libempathy/empathy-debug.h>
28 #include <libempathy/empathy-utils.h>
29
30 #include <libempathy-gtk/empathy-account-chooser.h>
31
32 #include <telepathy-glib/dbus.h>
33 #include <telepathy-glib/util.h>
34 #include <telepathy-glib/proxy-subclass.h>
35
36 #include "extensions/extensions.h"
37
38 #include "empathy-debug-dialog.h"
39
40 G_DEFINE_TYPE (EmpathyDebugDialog, empathy_debug_dialog,
41     GTK_TYPE_DIALOG)
42
43 enum
44 {
45   PROP_0,
46   PROP_PARENT
47 };
48
49 enum
50 {
51   COL_TIMESTAMP = 0,
52   COL_DOMAIN,
53   COL_CATEGORY,
54   COL_LEVEL,
55   COL_MESSAGE,
56   NUM_COLS
57 };
58
59 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyDebugDialog)
60 typedef struct
61 {
62   GtkWidget *filter;
63   GtkWindow *parent;
64   GtkWidget *view;
65   GtkWidget *cm_chooser;
66   GtkListStore *store;
67   TpProxy *proxy;
68   TpProxySignalConnection *signal_connection;
69   gboolean paused;
70   gboolean dispose_run;
71 } EmpathyDebugDialogPriv;
72
73 static const gchar *
74 log_level_to_string (guint level)
75 {
76   switch (level)
77     {
78     case EMP_DEBUG_LEVEL_ERROR:
79       return _("Error");
80       break;
81     case EMP_DEBUG_LEVEL_CRITICAL:
82       return _("Critical");
83       break;
84     case EMP_DEBUG_LEVEL_WARNING:
85       return _("Warning");
86       break;
87     case EMP_DEBUG_LEVEL_MESSAGE:
88       return _("Message");
89       break;
90     case EMP_DEBUG_LEVEL_INFO:
91       return _("Info");
92       break;
93     case EMP_DEBUG_LEVEL_DEBUG:
94       return _("Debug");
95       break;
96     default:
97       g_assert_not_reached ();
98       break;
99     }
100 }
101
102 static void
103 debug_dialog_add_message (EmpathyDebugDialog *debug_dialog,
104                           gdouble timestamp,
105                           const gchar *domain_category,
106                           guint level,
107                           const gchar *message)
108 {
109   EmpathyDebugDialogPriv *priv = GET_PRIV (debug_dialog);
110   gchar *domain, *category;
111   GtkTreeIter iter;
112
113   if (g_strrstr (domain_category, "/"))
114     {
115       gchar **parts = g_strsplit (domain_category, "/", 2);
116       domain = g_strdup (parts[0]);
117       category = g_strdup (parts[1]);
118       g_strfreev (parts);
119     }
120   else
121     {
122       domain = g_strdup (domain_category);
123       category = "";
124     }
125
126   gtk_list_store_append (priv->store, &iter);
127   gtk_list_store_set (priv->store, &iter,
128                       COL_TIMESTAMP, timestamp,
129                       COL_DOMAIN, domain,
130                       COL_CATEGORY, category,
131                       COL_LEVEL, log_level_to_string (level),
132                       COL_MESSAGE, message,
133                       -1);
134
135   g_free (domain);
136   g_free (category);
137 }
138
139 static void
140 debug_dialog_new_debug_message_cb (TpProxy *proxy,
141                                    gdouble timestamp,
142                                    const gchar *domain,
143                                    guint level,
144                                    const gchar *message,
145                                    gpointer user_data,
146                                    GObject *weak_object)
147 {
148   EmpathyDebugDialog *debug_dialog = (EmpathyDebugDialog *) user_data;
149
150   debug_dialog_add_message (debug_dialog, timestamp, domain, level,
151       message);
152 }
153
154 static void
155 debug_dialog_set_enabled (EmpathyDebugDialog *debug_dialog,
156                           gboolean enabled)
157 {
158   EmpathyDebugDialogPriv *priv = GET_PRIV (debug_dialog);
159   GValue *val;
160
161   val = tp_g_value_slice_new_boolean (enabled);
162
163   tp_cli_dbus_properties_call_set (priv->proxy, -1, EMP_IFACE_DEBUG,
164       "Enabled", val, NULL, NULL, NULL, NULL);
165
166   tp_g_value_slice_free (val);
167 }
168
169 static void
170 debug_dialog_get_messages_cb (TpProxy *proxy,
171                               const GPtrArray *messages,
172                               const GError *error,
173                               gpointer user_data,
174                               GObject *weak_object)
175 {
176   EmpathyDebugDialog *debug_dialog = (EmpathyDebugDialog *) user_data;
177   EmpathyDebugDialogPriv *priv = GET_PRIV (debug_dialog);
178   gint i;
179
180   if (error != NULL)
181     {
182       DEBUG ("GetMessages failed: %s", error->message);
183       return;
184     }
185
186   for (i = 0; i < messages->len; i++)
187     {
188       GValueArray *values = g_ptr_array_index (messages, i);
189
190       debug_dialog_add_message (debug_dialog,
191           g_value_get_double (g_value_array_get_nth (values, 0)),
192           g_value_get_string (g_value_array_get_nth (values, 1)),
193           g_value_get_uint (g_value_array_get_nth (values, 2)),
194           g_value_get_string (g_value_array_get_nth (values, 3)));
195     }
196
197   /* Connect to NewDebugMessage */
198   priv->signal_connection = emp_cli_debug_connect_to_new_debug_message (
199       proxy, debug_dialog_new_debug_message_cb, debug_dialog,
200       NULL, NULL, NULL);
201
202   /* Set Enabled as appropriate */
203   debug_dialog_set_enabled (debug_dialog, !priv->paused);
204 }
205
206 static void
207 debug_dialog_cm_chooser_changed_cb (GtkComboBox *cm_chooser,
208                                     EmpathyDebugDialog *debug_dialog)
209 {
210   EmpathyDebugDialogPriv *priv = GET_PRIV (debug_dialog);
211   gchar *cm;
212   MissionControl *mc;
213   TpDBusDaemon *dbus;
214   GError *error = NULL;
215   gchar *bus_name;
216   TpConnection *connection;
217
218   mc = empathy_mission_control_dup_singleton ();
219   cm = gtk_combo_box_get_active_text (cm_chooser);
220
221   if (cm == NULL)
222     {
223       DEBUG ("No CM is selected");
224       g_object_unref (mc);
225       return;
226     }
227
228   dbus = tp_dbus_daemon_dup (&error);
229
230   if (error != NULL)
231     {
232       DEBUG ("Failed at duping the dbus daemon: %s", error->message);
233       g_free (cm);
234       g_object_unref (mc);
235     }
236
237   /* TODO: Fix this. */
238   bus_name = g_strdup_printf ("org.freedesktop.Telepathy.ConnectionManager.%s", cm);
239   g_free (cm);
240
241   connection = tp_connection_new (dbus, bus_name,
242       "/org/freedesktop/Telepathy/debug", &error);
243   g_free (bus_name);
244
245   if (error != NULL)
246     {
247       DEBUG ("Getting a new TpConnection failed: %s", error->message);
248       g_error_free (error);
249       g_object_unref (dbus);
250       g_object_unref (mc);
251       return;
252     }
253
254   /* Disable debug signalling */
255   if (priv->proxy != NULL)
256     debug_dialog_set_enabled (debug_dialog, FALSE);
257
258   /* Disconnect from previous NewDebugMessage signal */
259   if (priv->signal_connection)
260     {
261       tp_proxy_signal_connection_disconnect (priv->signal_connection);
262       priv->signal_connection = NULL;
263     }
264
265   if (priv->proxy)
266     g_object_unref (priv->proxy);
267
268   priv->proxy = TP_PROXY (connection);
269
270   tp_proxy_add_interface_by_id (priv->proxy, emp_iface_quark_debug ());
271
272   emp_cli_debug_call_get_messages (priv->proxy, -1,
273       debug_dialog_get_messages_cb, debug_dialog, NULL, NULL);
274
275   g_object_unref (dbus);
276   g_object_unref (mc);
277 }
278
279 static void
280 debug_dialog_list_connection_names_cb (const gchar * const *names,
281                                        gsize n,
282                                        const gchar * const *cms,
283                                        const gchar * const *protocols,
284                                        const GError *error,
285                                        gpointer user_data,
286                                        GObject *weak_object)
287 {
288   EmpathyDebugDialog *debug_dialog = (EmpathyDebugDialog *) user_data;
289   EmpathyDebugDialogPriv *priv = GET_PRIV (debug_dialog);
290   int i;
291
292   if (error != NULL)
293     {
294       DEBUG ("list_connection_names failed: %s", error->message);
295       return;
296     }
297
298   for (i = 0; cms[i] != NULL; i++)
299     gtk_combo_box_append_text (GTK_COMBO_BOX (priv->cm_chooser), cms[i]);
300
301   gtk_combo_box_set_active (GTK_COMBO_BOX (priv->cm_chooser), 0);
302
303   /* Fill treeview */
304   debug_dialog_cm_chooser_changed_cb (GTK_COMBO_BOX (priv->cm_chooser), debug_dialog);
305 }
306
307 static void
308 debug_dialog_fill_cm_chooser (EmpathyDebugDialog *debug_dialog)
309 {
310   TpDBusDaemon *dbus;
311   GError *error = NULL;
312
313   dbus = tp_dbus_daemon_dup (&error);
314
315   if (error != NULL)
316     {
317       DEBUG ("Failed to dup dbus daemon: %s", error->message);
318       g_error_free (error);
319       return;
320     }
321
322   tp_list_connection_names (dbus, debug_dialog_list_connection_names_cb,
323       debug_dialog, NULL, NULL);
324
325   g_object_unref (dbus);
326 }
327
328 static void
329 debug_dialog_pause_toggled_cb (GtkToggleToolButton *pause,
330                                EmpathyDebugDialog *debug_dialog)
331 {
332   EmpathyDebugDialogPriv *priv = GET_PRIV (debug_dialog);
333
334   priv->paused = gtk_toggle_tool_button_get_active (pause);
335
336   debug_dialog_set_enabled (debug_dialog, !priv->paused);
337 }
338
339 static GObject *
340 debug_dialog_constructor (GType type,
341                           guint n_construct_params,
342                           GObjectConstructParam *construct_params)
343 {
344   GObject *object;
345   EmpathyDebugDialogPriv *priv;
346   GtkWidget *vbox;
347   GtkWidget *toolbar;
348   GtkWidget *image;
349   GtkWidget *label;
350   GtkToolItem *item;
351   GtkCellRenderer *renderer;
352   GtkWidget *scrolled_win;
353
354   object = G_OBJECT_CLASS (empathy_debug_dialog_parent_class)->constructor
355     (type, n_construct_params, construct_params);
356   priv = GET_PRIV (object);
357
358   gtk_window_set_title (GTK_WINDOW (object), _("Debug Window"));
359   gtk_window_set_default_size (GTK_WINDOW (object), 800, 400);
360   gtk_window_set_transient_for (GTK_WINDOW (object), priv->parent);
361
362   vbox = GTK_DIALOG (object)->vbox;
363
364   toolbar = gtk_toolbar_new ();
365   gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
366   gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE);
367   gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_SMALL_TOOLBAR);
368   gtk_widget_show (toolbar);
369
370   gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
371
372   /* CM */
373   priv->cm_chooser = gtk_combo_box_new_text ();
374   gtk_widget_show (priv->cm_chooser);
375
376   item = gtk_tool_item_new ();
377   gtk_widget_show (GTK_WIDGET (item));
378   gtk_container_add (GTK_CONTAINER (item), priv->cm_chooser);
379   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
380   debug_dialog_fill_cm_chooser (EMPATHY_DEBUG_DIALOG (object));
381   g_signal_connect (priv->cm_chooser, "changed",
382       G_CALLBACK (debug_dialog_cm_chooser_changed_cb), object);
383   gtk_widget_show (GTK_WIDGET (priv->cm_chooser));
384
385   item = gtk_separator_tool_item_new ();
386   gtk_widget_show (GTK_WIDGET (item));
387   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
388
389   /* Save */
390   item = gtk_tool_button_new_from_stock (GTK_STOCK_SAVE);
391   gtk_widget_show (GTK_WIDGET (item));
392   gtk_tool_item_set_is_important (GTK_TOOL_ITEM (item), TRUE);
393   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
394
395   /* Clear */
396   item = gtk_tool_button_new_from_stock (GTK_STOCK_CLEAR);
397   gtk_widget_show (GTK_WIDGET (item));
398   gtk_tool_item_set_is_important (GTK_TOOL_ITEM (item), TRUE);
399   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
400
401   item = gtk_separator_tool_item_new ();
402   gtk_widget_show (GTK_WIDGET (item));
403   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
404
405   /* Pause */
406   priv->paused = FALSE;
407   image = gtk_image_new_from_stock (GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_MENU);
408   gtk_widget_show (image);
409   item = gtk_toggle_tool_button_new ();
410   gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (item), priv->paused);
411   g_signal_connect (item, "toggled", G_CALLBACK (debug_dialog_pause_toggled_cb),
412       object);
413   gtk_widget_show (GTK_WIDGET (item));
414   gtk_tool_item_set_is_important (GTK_TOOL_ITEM (item), TRUE);
415   gtk_tool_button_set_label (GTK_TOOL_BUTTON (item), _("Pause"));
416   gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (item), image);
417   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
418
419   item = gtk_separator_tool_item_new ();
420   gtk_widget_show (GTK_WIDGET (item));
421   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
422
423   /* Level */
424   item = gtk_tool_item_new ();
425   gtk_widget_show (GTK_WIDGET (item));
426   label = gtk_label_new (_("Level "));
427   gtk_widget_show (label);
428   gtk_container_add (GTK_CONTAINER (item), label);
429   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
430
431   priv->filter = gtk_combo_box_new_text ();
432   gtk_widget_show (priv->filter);
433
434   item = gtk_tool_item_new ();
435   gtk_widget_show (GTK_WIDGET (item));
436   gtk_container_add (GTK_CONTAINER (item), priv->filter);
437   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
438
439   gtk_combo_box_append_text (GTK_COMBO_BOX (priv->filter), _("All"));
440   gtk_combo_box_append_text (GTK_COMBO_BOX (priv->filter), _("Debug"));
441   gtk_combo_box_append_text (GTK_COMBO_BOX (priv->filter), _("Info"));
442   gtk_combo_box_append_text (GTK_COMBO_BOX (priv->filter), _("Message"));
443   gtk_combo_box_append_text (GTK_COMBO_BOX (priv->filter), _("Warning"));
444   gtk_combo_box_append_text (GTK_COMBO_BOX (priv->filter), _("Critical"));
445   gtk_combo_box_append_text (GTK_COMBO_BOX (priv->filter), _("Error"));
446
447   gtk_combo_box_set_active (GTK_COMBO_BOX (priv->filter), 0);
448   gtk_widget_show (GTK_WIDGET (priv->filter));
449
450   /* Debug treeview */
451   priv->view = gtk_tree_view_new ();
452
453   renderer = gtk_cell_renderer_text_new ();
454
455   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->view),
456       -1, _("Time"), renderer, "text", COL_TIMESTAMP, NULL);
457   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->view),
458       -1, _("Domain"), renderer, "text", COL_DOMAIN, NULL);
459   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->view),
460       -1, _("Category"), renderer, "text", COL_CATEGORY, NULL);
461   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->view),
462       -1, _("Level"), renderer, "text", COL_LEVEL, NULL);
463   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->view),
464       -1, _("Message"), renderer, "text", COL_MESSAGE, NULL);
465
466   priv->store = gtk_list_store_new (NUM_COLS, G_TYPE_DOUBLE,
467       G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
468   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->view),
469       GTK_TREE_MODEL (priv->store));
470
471   /* Scrolled window */
472   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
473   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
474       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
475
476   gtk_widget_show (priv->view);
477   gtk_container_add (GTK_CONTAINER (scrolled_win), priv->view);
478
479   gtk_widget_show (scrolled_win);
480   gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
481
482   gtk_widget_show (GTK_WIDGET (object));
483
484   return object;
485 }
486
487 static void
488 empathy_debug_dialog_init (EmpathyDebugDialog *empathy_debug_dialog)
489 {
490   EmpathyDebugDialogPriv *priv =
491       G_TYPE_INSTANCE_GET_PRIVATE (empathy_debug_dialog,
492       EMPATHY_TYPE_DEBUG_DIALOG, EmpathyDebugDialogPriv);
493
494   empathy_debug_dialog->priv = priv;
495
496   priv->dispose_run = FALSE;
497 }
498
499 static void
500 debug_dialog_set_property (GObject *object,
501                            guint prop_id,
502                            const GValue *value,
503                            GParamSpec *pspec)
504 {
505   EmpathyDebugDialogPriv *priv = GET_PRIV (object);
506
507   switch (prop_id)
508     {
509       case PROP_PARENT:
510         priv->parent = GTK_WINDOW (g_value_dup_object (value));
511         break;
512       default:
513         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
514         break;
515     }
516 }
517
518 static void
519 debug_dialog_get_property (GObject *object,
520                            guint prop_id,
521                            GValue *value,
522                            GParamSpec *pspec)
523 {
524   EmpathyDebugDialogPriv *priv = GET_PRIV (object);
525
526   switch (prop_id)
527     {
528       case PROP_PARENT:
529         g_value_set_object (value, priv->parent);
530         break;
531       default:
532         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
533         break;
534     }
535 }
536
537 static void
538 debug_dialog_dispose (GObject *object)
539 {
540   EmpathyDebugDialog *selector = EMPATHY_DEBUG_DIALOG (object);
541   EmpathyDebugDialogPriv *priv = GET_PRIV (selector);
542
543   if (priv->dispose_run)
544     return;
545
546   priv->dispose_run = TRUE;
547
548   if (priv->parent)
549     g_object_unref (priv->parent);
550
551   if (priv->store)
552     g_object_unref (priv->store);
553
554   if (priv->proxy)
555     {
556       debug_dialog_set_enabled (EMPATHY_DEBUG_DIALOG (object), FALSE);
557       g_object_unref (priv->proxy);
558     }
559
560   if (priv->signal_connection)
561     tp_proxy_signal_connection_disconnect (priv->signal_connection);
562
563   (G_OBJECT_CLASS (empathy_debug_dialog_parent_class)->dispose) (object);
564 }
565
566 static void
567 empathy_debug_dialog_class_init (EmpathyDebugDialogClass *klass)
568 {
569   GObjectClass *object_class = G_OBJECT_CLASS (klass);
570   object_class->constructor = debug_dialog_constructor;
571   object_class->dispose = debug_dialog_dispose;
572   object_class->set_property = debug_dialog_set_property;
573   object_class->get_property = debug_dialog_get_property;
574   g_type_class_add_private (klass, sizeof (EmpathyDebugDialogPriv));
575
576   g_object_class_install_property (object_class, PROP_PARENT,
577       g_param_spec_object ("parent", "parent", "parent",
578       GTK_TYPE_WINDOW, G_PARAM_CONSTRUCT_ONLY |
579       G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
580 }
581
582 /* public methods */
583
584 GtkWidget *
585 empathy_debug_dialog_new (GtkWindow *parent)
586 {
587   g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
588
589   return GTK_WIDGET (g_object_new (EMPATHY_TYPE_DEBUG_DIALOG,
590       "parent", parent, NULL));
591 }