2 * Copyright (C) 2009 Collabora Ltd.
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.
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.
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
18 * Authors: Jonny Lamb <jonny.lamb@collabora.co.uk>
19 * Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
23 #include "empathy-debug-window.h"
25 #include <glib/gi18n.h>
26 #include <libsoup/soup.h>
27 #include <tp-account-widgets/tpaw-utils.h>
28 #include <telepathy-glib/telepathy-glib-dbus.h>
30 #include "empathy-geometry.h"
31 #include "empathy-ui-utils.h"
32 #include "empathy-utils.h"
34 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
35 #include "empathy-debug.h"
37 G_DEFINE_TYPE (EmpathyDebugWindow
, empathy_debug_window
,
49 COL_DEBUG_MESSAGE
= 0,
71 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyDebugWindow)
72 struct _EmpathyDebugWindowPriv
76 GtkToolItem
*save_button
;
77 GtkToolItem
*send_to_pastebin
;
78 GtkToolItem
*copy_button
;
79 GtkToolItem
*clear_button
;
80 GtkToolItem
*pause_button
;
81 GtkToolItem
*level_label
;
82 GtkWidget
*level_filter
;
85 GtkTreeModel
*store_filter
;
87 GtkWidget
*scrolled_win
;
88 GtkWidget
*not_supported_label
;
89 gboolean view_visible
;
93 TpProxySignalConnection
*name_owner_changed_signal
;
95 /* Whether NewDebugMessage will be fired */
98 /* Service (CM, Client) chooser store */
99 GtkListStore
*service_store
;
101 /* Counters on services detected and added */
102 guint services_detected
;
103 guint name_owner_cb_count
;
105 /* Debug to show upon creation */
109 gboolean dispose_run
;
110 TpAccountManager
*am
;
111 GtkListStore
*all_active_buffer
;
115 log_level_to_string (GLogLevelFlags level
)
119 case G_LOG_LEVEL_ERROR
:
122 case G_LOG_LEVEL_CRITICAL
:
125 case G_LOG_LEVEL_WARNING
:
128 case G_LOG_LEVEL_MESSAGE
:
131 case G_LOG_LEVEL_INFO
:
134 case G_LOG_LEVEL_DEBUG
:
138 g_assert_not_reached ();
144 get_active_service_name (EmpathyDebugWindow
*self
)
149 if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self
->priv
->chooser
),
153 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->service_store
), &iter
,
154 COL_NAME
, &name
, -1);
160 copy_buffered_messages (GtkTreeModel
*buffer
,
165 GtkListStore
*active_buffer
= data
;
166 GtkTreeIter active_buffer_iter
;
169 gtk_tree_model_get (buffer
, iter
,
170 COL_DEBUG_MESSAGE
, &msg
,
172 gtk_list_store_insert_with_values (active_buffer
, &active_buffer_iter
, -1,
173 COL_DEBUG_MESSAGE
, msg
,
176 g_object_unref (msg
);
182 insert_values_in_buffer (GtkListStore
*store
,
187 gtk_list_store_insert_with_values (store
, &iter
, -1,
188 COL_DEBUG_MESSAGE
, msg
,
193 debug_window_add_message (EmpathyDebugWindow
*self
,
194 TpDebugClient
*debug
,
197 GtkListStore
*active_buffer
, *pause_buffer
;
199 pause_buffer
= g_object_get_data (G_OBJECT (debug
), "pause-buffer");
200 active_buffer
= g_object_get_data (G_OBJECT (debug
), "active-buffer");
202 if (self
->priv
->paused
)
204 insert_values_in_buffer (pause_buffer
, msg
);
208 /* Append 'this' message to this service's and All's active-buffers */
209 insert_values_in_buffer (active_buffer
, msg
);
211 insert_values_in_buffer (self
->priv
->all_active_buffer
, msg
);
216 debug_window_new_debug_message_cb (TpDebugClient
*debug
,
220 EmpathyDebugWindow
*self
= user_data
;
222 debug_window_add_message (self
, debug
, msg
);
226 set_enabled_cb (GObject
*source
,
227 GAsyncResult
*result
,
230 TpDebugClient
*debug
= TP_DEBUG_CLIENT (source
);
231 gboolean enabled
= GPOINTER_TO_UINT (user_data
);
232 GError
*error
= NULL
;
234 if (!tp_debug_client_set_enabled_finish (debug
, result
, &error
))
236 DEBUG ("Failed to %s debugging on %s", enabled
? "enable" : "disable",
237 tp_proxy_get_bus_name (debug
));
238 g_error_free (error
);
243 debug_window_set_enabled (TpDebugClient
*debug
,
246 g_return_if_fail (debug
!= NULL
);
248 tp_debug_client_set_enabled_async (debug
, enabled
,
249 set_enabled_cb
, GUINT_TO_POINTER (enabled
));
253 debug_window_set_toolbar_sensitivity (EmpathyDebugWindow
*self
,
256 GtkWidget
*vbox
= gtk_bin_get_child (GTK_BIN (self
));
258 gtk_widget_set_sensitive (GTK_WIDGET (self
->priv
->save_button
), sensitive
);
259 gtk_widget_set_sensitive (GTK_WIDGET (self
->priv
->send_to_pastebin
),
261 gtk_widget_set_sensitive (GTK_WIDGET (self
->priv
->copy_button
), sensitive
);
262 gtk_widget_set_sensitive (GTK_WIDGET (self
->priv
->clear_button
), sensitive
);
263 gtk_widget_set_sensitive (GTK_WIDGET (self
->priv
->pause_button
), sensitive
);
264 gtk_widget_set_sensitive (GTK_WIDGET (self
->priv
->level_label
), sensitive
);
265 gtk_widget_set_sensitive (GTK_WIDGET (self
->priv
->level_filter
), sensitive
);
266 gtk_widget_set_sensitive (GTK_WIDGET (self
->priv
->view
), sensitive
);
268 if (sensitive
&& !self
->priv
->view_visible
)
270 /* Add view and remove label */
271 gtk_container_remove (GTK_CONTAINER (vbox
),
272 self
->priv
->not_supported_label
);
273 gtk_box_pack_start (GTK_BOX (vbox
),
274 self
->priv
->scrolled_win
, TRUE
, TRUE
, 0);
275 self
->priv
->view_visible
= TRUE
;
277 else if (!sensitive
&& self
->priv
->view_visible
)
279 /* Add label and remove view */
280 gtk_container_remove (GTK_CONTAINER (vbox
), self
->priv
->scrolled_win
);
281 gtk_box_pack_start (GTK_BOX (vbox
), self
->priv
->not_supported_label
,
283 self
->priv
->view_visible
= FALSE
;
288 debug_window_get_iter_for_active_buffer (GtkListStore
*active_buffer
,
290 EmpathyDebugWindow
*self
)
293 GtkTreeModel
*model
= GTK_TREE_MODEL (self
->priv
->service_store
);
295 gtk_tree_model_get_iter_first (model
, iter
);
296 for (valid_iter
= gtk_tree_model_iter_next (model
, iter
);
298 valid_iter
= gtk_tree_model_iter_next (model
, iter
))
300 GtkListStore
*stored_active_buffer
;
302 gtk_tree_model_get (model
, iter
,
303 COL_ACTIVE_BUFFER
, &stored_active_buffer
,
305 if (active_buffer
== stored_active_buffer
)
307 g_object_unref (stored_active_buffer
);
310 g_object_unref (stored_active_buffer
);
316 static void refresh_all_buffer (EmpathyDebugWindow
*self
);
319 proxy_invalidated_cb (TpProxy
*proxy
,
325 EmpathyDebugWindow
*self
= (EmpathyDebugWindow
*) user_data
;
326 GtkTreeModel
*service_store
;
327 TpProxy
*stored_proxy
;
331 if (self
->priv
->service_store
== NULL
)
334 service_store
= GTK_TREE_MODEL (self
->priv
->service_store
);
336 /* Proxy has been invalidated so we find and set it to NULL
337 * in service store */
338 gtk_tree_model_get_iter_first (service_store
, &iter
);
339 for (valid_iter
= gtk_tree_model_iter_next (service_store
, &iter
);
341 valid_iter
= gtk_tree_model_iter_next (service_store
, &iter
))
343 gtk_tree_model_get (service_store
, &iter
,
344 COL_PROXY
, &stored_proxy
,
347 if (proxy
== stored_proxy
)
348 gtk_list_store_set (self
->priv
->service_store
, &iter
,
352 g_object_unref (stored_proxy
);
355 /* Also, we refresh "All" selection's active buffer since it should not
356 * show messages obtained from the proxy getting destroyed above */
357 refresh_all_buffer (self
);
361 debug_window_get_messages_cb (GObject
*object
,
362 GAsyncResult
*result
,
365 TpDebugClient
*debug
= TP_DEBUG_CLIENT (object
);
366 EmpathyDebugWindow
*self
= user_data
;
367 gchar
*active_service_name
;
369 GtkListStore
*active_buffer
;
372 gchar
*proxy_service_name
;
374 GError
*error
= NULL
;
376 active_buffer
= g_object_get_data (object
, "active-buffer");
377 valid_iter
= debug_window_get_iter_for_active_buffer (active_buffer
, &iter
,
379 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->service_store
), &iter
,
380 COL_NAME
, &proxy_service_name
,
383 active_service_name
= get_active_service_name (self
);
385 messages
= tp_debug_client_get_messages_finish (debug
, result
, &error
);
386 if (messages
== NULL
)
388 DEBUG ("Failed to get debug messages: %s", error
->message
);
389 g_error_free (error
);
391 /* We want to set the window sensitivity to false only when proxy for the
392 * selected service is unable to fetch debug messages */
393 if (!tp_strdiff (active_service_name
, proxy_service_name
))
394 debug_window_set_toolbar_sensitivity (self
, FALSE
);
398 DEBUG ("Retrieved debug messages for %s", active_service_name
);
399 g_free (active_service_name
);
400 debug_window_set_toolbar_sensitivity (self
, TRUE
);
402 for (i
= 0; i
< messages
->len
; i
++)
404 TpDebugMessage
*msg
= g_ptr_array_index (messages
, i
);
406 debug_window_add_message (self
, debug
, msg
);
409 /* Now we save this precious proxy in the service_store along its service */
412 DEBUG ("Proxy for service: %s was successful in fetching debug"
413 " messages. Saving it.", proxy_service_name
);
415 /* The store will take its own ref on the proxy preventing it to be
416 * destroyed when leaving this callback. */
417 gtk_list_store_set (self
->priv
->service_store
, &iter
,
421 g_ptr_array_unref (messages
);
423 g_free (proxy_service_name
);
425 /* Connect to "invalidated" signal */
426 g_signal_connect_object (debug
, "invalidated",
427 G_CALLBACK (proxy_invalidated_cb
), self
, 0);
429 /* Connect to NewDebugMessage */
430 tp_g_signal_connect_object (debug
, "new-debug-message",
431 G_CALLBACK (debug_window_new_debug_message_cb
), self
, 0);
433 /* Now that active-buffer is up to date, we can see which messages are
435 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (
436 self
->priv
->store_filter
));
438 /* Set the proxy to signal for new debug messages */
439 debug_window_set_enabled (debug
, TRUE
);
443 create_proxy_to_get_messages (EmpathyDebugWindow
*self
,
447 gchar
*bus_name
, *name
= NULL
;
448 TpDebugClient
*new_proxy
, *stored_proxy
= NULL
;
449 GtkTreeModel
*pause_buffer
, *active_buffer
;
451 GError
*error
= NULL
;
453 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->service_store
), iter
,
456 COL_ACTIVE_BUFFER
, &active_buffer
,
457 COL_PAUSE_BUFFER
, &pause_buffer
,
458 COL_PROXY
, &stored_proxy
,
461 /* If the stored_proxy is not NULL then messages have been obtained and
462 * new-debug-message-signal has been set on it. Also, the proxy is valid.
463 * If the service is gone, we still display the messages-cached till now. */
465 (!gone
&& stored_proxy
!= NULL
))
467 /* Nothing needs to be done. The associated active-buffer has already
468 * been set as view's model */
472 DEBUG ("Preparing proxy to obtain messages for service %s", name
);
474 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->service_store
), iter
,
475 COL_UNIQUE_NAME
, &bus_name
, -1);
477 new_proxy
= tp_debug_client_new (dbus
, bus_name
, &error
);
479 if (new_proxy
== NULL
)
481 DEBUG ("Failed to create TpDebugClient on bus %s: %s", bus_name
,
489 g_object_set_data (G_OBJECT (new_proxy
), "active-buffer", active_buffer
);
490 g_object_set_data (G_OBJECT (new_proxy
), "pause-buffer", pause_buffer
);
492 /* Now we call GetMessages with fresh proxy.
493 * The old proxy is NULL due to one of the following -
494 * * Wasn't saved as last GetMessages call failed
495 * * The service has newly arrived and no proxy has been prepared yet for it
496 * * A service with the same name has reappeared but the owner maybe new */
498 tp_debug_client_get_messages_async (TP_DEBUG_CLIENT (new_proxy
),
499 debug_window_get_messages_cb
, self
);
501 g_object_unref (new_proxy
);
505 tp_clear_object (&stored_proxy
);
506 g_object_unref (active_buffer
);
507 g_object_unref (pause_buffer
);
510 static GtkListStore
*
511 new_list_store_for_service (void)
513 return gtk_list_store_new (NUM_DEBUG_COLS
,
514 TP_TYPE_DEBUG_MESSAGE
); /* COL_DEBUG_MESSAGE */
518 debug_window_visible_func (GtkTreeModel
*model
,
522 EmpathyDebugWindow
*self
= user_data
;
523 GLogLevelFlags filter_value
;
524 GtkTreeModel
*filter_model
;
525 GtkTreeIter filter_iter
;
529 filter_model
= gtk_combo_box_get_model (
530 GTK_COMBO_BOX (self
->priv
->level_filter
));
531 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self
->priv
->level_filter
),
534 gtk_tree_model_get (model
, iter
, COL_DEBUG_MESSAGE
, &msg
, -1);
535 gtk_tree_model_get (filter_model
, &filter_iter
,
536 COL_LEVEL_VALUE
, &filter_value
, -1);
538 result
= (tp_debug_message_get_level (msg
) <= filter_value
);
539 g_object_unref (msg
);
545 tree_view_search_equal_func_cb (GtkTreeModel
*model
,
549 gpointer search_data
)
555 gboolean ret
= TRUE
; /* The return value is counter-intuitive */
557 gtk_tree_model_get (model
, iter
, column
, &str
, -1);
559 key_len
= strlen (key
);
560 len
= strlen (str
) - key_len
;
562 for (i
= 0; i
<= len
; ++i
)
564 if (!g_ascii_strncasecmp (key
, str
+ i
, key_len
))
576 update_store_filter (EmpathyDebugWindow
*self
,
577 GtkListStore
*active_buffer
)
579 debug_window_set_toolbar_sensitivity (self
, FALSE
);
581 tp_clear_object (&self
->priv
->store_filter
);
582 self
->priv
->store_filter
= gtk_tree_model_filter_new (
583 GTK_TREE_MODEL (active_buffer
), NULL
);
585 gtk_tree_model_filter_set_visible_func (
586 GTK_TREE_MODEL_FILTER (self
->priv
->store_filter
),
587 debug_window_visible_func
, self
, NULL
);
588 gtk_tree_view_set_model (GTK_TREE_VIEW (self
->priv
->view
),
589 self
->priv
->store_filter
);
591 /* Since view's model has changed, reset the search column and
592 * search_equal_func */
593 gtk_tree_view_set_search_column (GTK_TREE_VIEW (self
->priv
->view
),
595 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self
->priv
->view
),
596 tree_view_search_equal_func_cb
, NULL
, NULL
);
598 debug_window_set_toolbar_sensitivity (self
, TRUE
);
602 refresh_all_buffer (EmpathyDebugWindow
*self
)
606 GtkTreeModel
*service_store
= GTK_TREE_MODEL (self
->priv
->service_store
);
608 /* Clear All's active-buffer */
609 gtk_list_store_clear (self
->priv
->all_active_buffer
);
611 /* Skipping the first service store iter which is reserved for "All" */
612 gtk_tree_model_get_iter_first (service_store
, &iter
);
613 for (valid_iter
= gtk_tree_model_iter_next (service_store
, &iter
);
615 valid_iter
= gtk_tree_model_iter_next (service_store
, &iter
))
617 TpProxy
*proxy
= NULL
;
618 GtkListStore
*service_active_buffer
;
621 gtk_tree_model_get (service_store
, &iter
,
624 COL_ACTIVE_BUFFER
, &service_active_buffer
,
629 gtk_tree_model_foreach (GTK_TREE_MODEL (service_active_buffer
),
630 copy_buffered_messages
, self
->priv
->all_active_buffer
);
636 if (service_active_buffer
== NULL
)
639 /* Copy the debug messages to all_active_buffer */
640 gtk_tree_model_foreach (GTK_TREE_MODEL (service_active_buffer
),
641 copy_buffered_messages
, self
->priv
->all_active_buffer
);
645 GError
*error
= NULL
;
646 TpDBusDaemon
*dbus
= tp_dbus_daemon_dup (&error
);
650 DEBUG ("Failed at duping the dbus daemon: %s", error
->message
);
651 g_error_free (error
);
654 create_proxy_to_get_messages (self
, &iter
, dbus
);
656 g_object_unref (dbus
);
660 g_object_unref (service_active_buffer
);
661 tp_clear_object (&proxy
);
666 debug_window_service_chooser_changed_cb (GtkComboBox
*chooser
,
667 EmpathyDebugWindow
*self
)
670 GError
*error
= NULL
;
671 GtkListStore
*stored_active_buffer
= NULL
;
676 if (!gtk_combo_box_get_active_iter (chooser
, &iter
))
678 DEBUG ("No CM is selected");
679 if (gtk_tree_model_iter_n_children (
680 GTK_TREE_MODEL (self
->priv
->service_store
), NULL
) > 0)
682 gtk_combo_box_set_active (chooser
, 0);
687 debug_window_set_toolbar_sensitivity (self
, TRUE
);
689 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->service_store
), &iter
,
692 COL_ACTIVE_BUFFER
, &stored_active_buffer
,
695 DEBUG ("Service chosen: %s", name
);
697 if (tp_strdiff (name
, "All") && stored_active_buffer
== NULL
)
699 DEBUG ("No list store assigned to service %s", name
);
703 if (!tp_strdiff (name
, "All"))
705 update_store_filter (self
, self
->priv
->all_active_buffer
);
709 update_store_filter (self
, stored_active_buffer
);
711 dbus
= tp_dbus_daemon_dup (&error
);
715 DEBUG ("Failed at duping the dbus daemon: %s", error
->message
);
718 create_proxy_to_get_messages (self
, &iter
, dbus
);
720 g_object_unref (dbus
);
724 tp_clear_object (&stored_active_buffer
);
732 GtkTreeIter
**found_iter
;
733 } CmInModelForeachData
;
736 debug_window_service_foreach (GtkTreeModel
*model
,
741 CmInModelForeachData
*data
= (CmInModelForeachData
*) user_data
;
744 gtk_tree_model_get (model
, iter
,
745 (data
->use_name
? COL_NAME
: COL_UNIQUE_NAME
),
749 if (!tp_strdiff (store_name
, data
->name
))
753 if (data
->found_iter
!= NULL
)
754 *(data
->found_iter
) = gtk_tree_iter_copy (iter
);
763 debug_window_service_is_in_model (EmpathyDebugWindow
*self
,
768 CmInModelForeachData
*data
;
771 data
= g_slice_new0 (CmInModelForeachData
);
774 data
->found_iter
= iter
;
775 data
->use_name
= use_name
;
777 gtk_tree_model_foreach (GTK_TREE_MODEL (self
->priv
->service_store
),
778 debug_window_service_foreach
, data
);
782 g_slice_free (CmInModelForeachData
, data
);
788 get_cm_display_name (EmpathyDebugWindow
*self
,
791 GHashTable
*protocols
= g_hash_table_new (g_str_hash
, g_str_equal
);
792 GList
*accounts
, *ptr
;
795 accounts
= tp_account_manager_dup_valid_accounts (self
->priv
->am
);
797 for (ptr
= accounts
; ptr
!= NULL
; ptr
= ptr
->next
)
799 TpAccount
*account
= TP_ACCOUNT (ptr
->data
);
801 if (!tp_strdiff (tp_account_get_cm_name (account
), cm_name
))
803 g_hash_table_insert (protocols
,
804 (char *) tp_account_get_protocol_name (account
),
805 GUINT_TO_POINTER (TRUE
));
809 g_list_free_full (accounts
, g_object_unref
);
811 if (g_hash_table_size (protocols
) > 0)
818 protocolsv
= g_new0 (char *, g_hash_table_size (protocols
) + 1);
820 g_hash_table_iter_init (&iter
, protocols
);
821 for (i
= 0; g_hash_table_iter_next (&iter
, (gpointer
) &key
, NULL
); i
++)
826 str
= g_strjoinv (", ", protocolsv
);
827 retval
= g_strdup_printf ("%s (%s)", cm_name
, str
);
834 retval
= g_strdup (cm_name
);
837 g_hash_table_unref (protocols
);
844 EmpathyDebugWindow
*self
;
847 } FillServiceChooserData
;
849 static FillServiceChooserData
*
850 fill_service_chooser_data_new (EmpathyDebugWindow
*window
,
854 FillServiceChooserData
* data
= g_slice_new (FillServiceChooserData
);
857 data
->name
= g_strdup (name
);
858 data
->type
= SERVICE_TYPE_CM
;
863 fill_service_chooser_data_free (FillServiceChooserData
*data
)
866 g_slice_free (FillServiceChooserData
, data
);
870 service_type_to_string (ServiceType type
)
874 case SERVICE_TYPE_CM
:
876 case SERVICE_TYPE_CLIENT
:
878 case SERVICE_TYPE_MC
:
886 service_dup_display_name (EmpathyDebugWindow
*self
,
890 if (type
== SERVICE_TYPE_CM
)
891 return get_cm_display_name (self
, name
);
893 return g_strdup (name
);
897 debug_window_get_name_owner_cb (TpDBusDaemon
*proxy
,
901 GObject
*weak_object
)
903 FillServiceChooserData
*data
= (FillServiceChooserData
*) user_data
;
904 EmpathyDebugWindow
*self
= EMPATHY_DEBUG_WINDOW (data
->self
);
907 self
->priv
->name_owner_cb_count
++;
911 DEBUG ("GetNameOwner failed: %s", error
->message
);
915 if (!debug_window_service_is_in_model (data
->self
, out
, NULL
, FALSE
))
918 GtkListStore
*active_buffer
, *pause_buffer
;
920 DEBUG ("Adding %s to list: %s at unique name: %s",
921 service_type_to_string (data
->type
),
924 name
= service_dup_display_name (self
, data
->type
, data
->name
);
926 active_buffer
= new_list_store_for_service ();
927 pause_buffer
= new_list_store_for_service ();
929 gtk_list_store_insert_with_values (self
->priv
->service_store
, &iter
, -1,
931 COL_UNIQUE_NAME
, out
,
933 COL_ACTIVE_BUFFER
, active_buffer
,
934 COL_PAUSE_BUFFER
, pause_buffer
,
938 g_object_unref (active_buffer
);
939 g_object_unref (pause_buffer
);
941 if (self
->priv
->select_name
!= NULL
&&
942 !tp_strdiff (name
, self
->priv
->select_name
))
944 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self
->priv
->chooser
),
946 tp_clear_pointer (&self
->priv
->select_name
, g_free
);
952 if (self
->priv
->services_detected
== self
->priv
->name_owner_cb_count
)
954 /* Time to add "All" selection to service_store */
955 gtk_list_store_insert_with_values (self
->priv
->service_store
, &iter
, 0,
957 COL_ACTIVE_BUFFER
, NULL
,
960 self
->priv
->all_active_buffer
= new_list_store_for_service ();
962 /* Populate active buffers for all services */
963 refresh_all_buffer (self
);
965 gtk_combo_box_set_active (GTK_COMBO_BOX (self
->priv
->chooser
), 0);
969 fill_service_chooser_data_free (data
);
973 debug_window_name_owner_changed_cb (TpDBusDaemon
*proxy
,
978 GObject
*weak_object
)
980 EmpathyDebugWindow
*self
= EMPATHY_DEBUG_WINDOW (user_data
);
984 if (g_str_has_prefix (arg0
, TP_CM_BUS_NAME_BASE
))
986 type
= SERVICE_TYPE_CM
;
987 name
= arg0
+ strlen (TP_CM_BUS_NAME_BASE
);
989 else if (g_str_has_prefix (arg0
, TP_CLIENT_BUS_NAME_BASE
))
991 type
= SERVICE_TYPE_CLIENT
;
992 name
= arg0
+ strlen (TP_CLIENT_BUS_NAME_BASE
);
999 if (TPAW_STR_EMPTY (arg1
) && !TPAW_STR_EMPTY (arg2
))
1001 GtkTreeIter
*found_at_iter
= NULL
;
1002 gchar
*display_name
;
1004 display_name
= service_dup_display_name (self
, type
, name
);
1006 /* A service joined */
1007 if (!debug_window_service_is_in_model (user_data
, display_name
,
1008 &found_at_iter
, TRUE
))
1011 GtkListStore
*active_buffer
, *pause_buffer
;
1013 DEBUG ("Adding new service '%s' at %s.", name
, arg2
);
1015 active_buffer
= new_list_store_for_service ();
1016 pause_buffer
= new_list_store_for_service ();
1018 gtk_list_store_insert_with_values (self
->priv
->service_store
,
1020 COL_NAME
, display_name
,
1021 COL_UNIQUE_NAME
, arg2
,
1023 COL_ACTIVE_BUFFER
, active_buffer
,
1024 COL_PAUSE_BUFFER
, pause_buffer
,
1028 g_object_unref (active_buffer
);
1029 g_object_unref (pause_buffer
);
1033 /* a service with the same name is already in the service_store,
1034 * update it and set it as re-enabled.
1036 GtkListStore
*active_buffer
, *pause_buffer
;
1037 TpProxy
*stored_proxy
;
1039 DEBUG ("Refreshing CM '%s' at '%s'.", name
, arg2
);
1041 active_buffer
= new_list_store_for_service ();
1042 pause_buffer
= new_list_store_for_service ();
1044 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->service_store
),
1045 found_at_iter
, COL_PROXY
, &stored_proxy
, -1);
1047 tp_clear_object (&stored_proxy
);
1049 gtk_list_store_set (self
->priv
->service_store
, found_at_iter
,
1050 COL_NAME
, display_name
,
1051 COL_UNIQUE_NAME
, arg2
,
1053 COL_ACTIVE_BUFFER
, active_buffer
,
1054 COL_PAUSE_BUFFER
, pause_buffer
,
1058 g_object_unref (active_buffer
);
1059 g_object_unref (pause_buffer
);
1061 gtk_tree_iter_free (found_at_iter
);
1063 debug_window_service_chooser_changed_cb
1064 (GTK_COMBO_BOX (self
->priv
->chooser
), user_data
);
1067 /* If a new service arrives when "All" is selected, the view will
1068 * not show its messages which we do not want. So we refresh All's
1070 * Similarly for when a service with an already seen service name
1072 refresh_all_buffer (self
);
1074 g_free (display_name
);
1076 else if (!TPAW_STR_EMPTY (arg1
) && TPAW_STR_EMPTY (arg2
))
1078 /* A service died */
1079 GtkTreeIter
*iter
= NULL
;
1081 DEBUG ("Setting service disabled from %s.", arg1
);
1083 /* set the service as disabled in the model */
1084 if (debug_window_service_is_in_model (user_data
, arg1
, &iter
, FALSE
))
1086 gtk_list_store_set (self
->priv
->service_store
,
1087 iter
, COL_GONE
, TRUE
, -1);
1088 gtk_tree_iter_free (iter
);
1091 /* Refresh all's active buffer */
1092 refresh_all_buffer (self
);
1097 add_service (EmpathyDebugWindow
*self
,
1098 const gchar
*bus_name
,
1099 const gchar
*display_name
,
1102 FillServiceChooserData
*data
;
1104 data
= fill_service_chooser_data_new (self
, display_name
, type
);
1106 tp_cli_dbus_daemon_call_get_name_owner (self
->priv
->dbus
, -1,
1107 bus_name
, debug_window_get_name_owner_cb
, data
, NULL
, NULL
);
1109 self
->priv
->services_detected
++;
1113 list_names_cb (TpDBusDaemon
*bus_daemon
,
1114 const gchar
* const *names
,
1115 const GError
*error
,
1117 GObject
*weak_object
)
1119 EmpathyDebugWindow
*self
= EMPATHY_DEBUG_WINDOW (weak_object
);
1124 DEBUG ("Failed to list names: %s", error
->message
);
1128 for (i
= 0; names
[i
] != NULL
; i
++)
1130 if (g_str_has_prefix (names
[i
], TP_CLIENT_BUS_NAME_BASE
))
1132 add_service (self
, names
[i
],
1133 names
[i
] + strlen (TP_CLIENT_BUS_NAME_BASE
), SERVICE_TYPE_CLIENT
);
1135 else if (g_str_has_prefix (names
[i
], TP_CM_BUS_NAME_BASE
))
1137 add_service (self
, names
[i
],
1138 names
[i
] + strlen (TP_CM_BUS_NAME_BASE
), SERVICE_TYPE_CM
);
1140 else if (!tp_strdiff (names
[i
], TP_ACCOUNT_MANAGER_BUS_NAME
))
1142 add_service (self
, names
[i
], "Mission-Control", SERVICE_TYPE_MC
);
1148 debug_window_fill_service_chooser (EmpathyDebugWindow
*self
)
1150 GError
*error
= NULL
;
1152 self
->priv
->dbus
= tp_dbus_daemon_dup (&error
);
1156 DEBUG ("Failed to dup dbus daemon: %s", error
->message
);
1157 g_error_free (error
);
1161 /* Keep a count of the services detected and added */
1162 self
->priv
->services_detected
= 0;
1163 self
->priv
->name_owner_cb_count
= 0;
1165 tp_dbus_daemon_list_names (self
->priv
->dbus
, 2000,
1166 list_names_cb
, NULL
, NULL
, G_OBJECT (self
));
1168 self
->priv
->name_owner_changed_signal
=
1169 tp_cli_dbus_daemon_connect_to_name_owner_changed (self
->priv
->dbus
,
1170 debug_window_name_owner_changed_cb
, self
, NULL
, NULL
, NULL
);
1174 debug_window_pause_toggled_cb (GtkToggleToolButton
*pause_
,
1175 EmpathyDebugWindow
*self
)
1178 gboolean valid_iter
;
1179 GtkTreeModel
*model
= GTK_TREE_MODEL (self
->priv
->service_store
);
1181 self
->priv
->paused
= gtk_toggle_tool_button_get_active (pause_
);
1183 if (!self
->priv
->paused
)
1185 /* Pause has been released - flush all pause buffers */
1186 GtkTreeModel
*service_store
= GTK_TREE_MODEL (self
->priv
->service_store
);
1188 /* Skipping the first iter which is reserved for "All" */
1189 gtk_tree_model_get_iter_first (model
, &iter
);
1190 for (valid_iter
= gtk_tree_model_iter_next (model
, &iter
);
1192 valid_iter
= gtk_tree_model_iter_next (model
, &iter
))
1194 GtkListStore
*pause_buffer
, *active_buffer
;
1196 gtk_tree_model_get (service_store
, &iter
,
1197 COL_PAUSE_BUFFER
, &pause_buffer
,
1198 COL_ACTIVE_BUFFER
, &active_buffer
,
1201 gtk_tree_model_foreach (GTK_TREE_MODEL (pause_buffer
),
1202 copy_buffered_messages
, active_buffer
);
1203 gtk_tree_model_foreach (GTK_TREE_MODEL (pause_buffer
),
1204 copy_buffered_messages
, self
->priv
->all_active_buffer
);
1206 gtk_list_store_clear (pause_buffer
);
1208 g_object_unref (active_buffer
);
1209 g_object_unref (pause_buffer
);
1215 debug_window_filter_changed_cb (GtkComboBox
*filter
,
1216 EmpathyDebugWindow
*self
)
1218 gtk_tree_model_filter_refilter (
1219 GTK_TREE_MODEL_FILTER (self
->priv
->store_filter
));
1223 debug_window_clear_clicked_cb (GtkToolButton
*clear_button
,
1224 EmpathyDebugWindow
*self
)
1227 GtkListStore
*active_buffer
;
1229 /* "All" is the first choice in the service chooser and it's buffer is
1230 * not saved in the service-store but is accessed using a self->private
1232 if (gtk_combo_box_get_active (GTK_COMBO_BOX (self
->priv
->chooser
)) == 0)
1234 gtk_list_store_clear (self
->priv
->all_active_buffer
);
1238 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self
->priv
->chooser
), &iter
);
1239 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->service_store
), &iter
,
1240 COL_ACTIVE_BUFFER
, &active_buffer
, -1);
1242 gtk_list_store_clear (active_buffer
);
1244 g_object_unref (active_buffer
);
1248 debug_window_menu_copy_activate_cb (GtkMenuItem
*menu_item
,
1249 EmpathyDebugWindow
*self
)
1252 GtkTreeViewColumn
*focus_column
;
1254 TpDebugMessage
*msg
;
1255 const gchar
*message
;
1256 GtkClipboard
*clipboard
;
1258 gtk_tree_view_get_cursor (GTK_TREE_VIEW (self
->priv
->view
),
1259 &path
, &focus_column
);
1263 DEBUG ("No row is in focus");
1267 gtk_tree_model_get_iter (self
->priv
->store_filter
, &iter
, path
);
1269 gtk_tree_model_get (self
->priv
->store_filter
, &iter
,
1270 COL_DEBUG_MESSAGE
, &msg
,
1273 message
= tp_debug_message_get_message (msg
);
1275 if (TPAW_STR_EMPTY (message
))
1277 DEBUG ("Log message is empty");
1281 clipboard
= gtk_clipboard_get_for_display (
1282 gtk_widget_get_display (GTK_WIDGET (menu_item
)),
1283 GDK_SELECTION_CLIPBOARD
);
1285 gtk_clipboard_set_text (clipboard
, message
, -1);
1287 g_object_unref (msg
);
1292 EmpathyDebugWindow
*self
;
1298 debug_window_show_menu (gpointer user_data
)
1300 MenuPopupData
*data
= (MenuPopupData
*) user_data
;
1301 GtkWidget
*menu
, *item
;
1302 GtkMenuShell
*shell
;
1304 menu
= empathy_context_menu_new (GTK_WIDGET (data
->self
));
1305 shell
= GTK_MENU_SHELL (menu
);
1307 item
= gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY
, NULL
);
1309 g_signal_connect (item
, "activate",
1310 G_CALLBACK (debug_window_menu_copy_activate_cb
), data
->self
);
1312 gtk_menu_shell_append (shell
, item
);
1313 gtk_widget_show (item
);
1315 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
1316 data
->button
, data
->time
);
1318 g_slice_free (MenuPopupData
, user_data
);
1324 debug_window_button_press_event_cb (GtkTreeView
*view
,
1325 GdkEventButton
*event
,
1328 /* A mouse button was pressed on the tree view. */
1330 if (event
->button
== 3)
1332 /* The tree view was right-clicked. (3 == third mouse button) */
1333 MenuPopupData
*data
;
1334 data
= g_slice_new0 (MenuPopupData
);
1335 data
->self
= user_data
;
1336 data
->button
= event
->button
;
1337 data
->time
= event
->time
;
1338 g_idle_add (debug_window_show_menu
, data
);
1345 debug_window_format_timestamp (TpDebugMessage
*msg
)
1348 gchar
*time_str
, *text
;
1351 t
= tp_debug_message_get_time (msg
);
1353 time_str
= g_date_time_format (t
, "%x %T");
1355 ms
= g_date_time_get_microsecond (t
);
1356 text
= g_strdup_printf ("%s.%d", time_str
, ms
);
1363 debug_window_time_formatter (GtkTreeViewColumn
*tree_column
,
1364 GtkCellRenderer
*cell
,
1365 GtkTreeModel
*tree_model
,
1369 TpDebugMessage
*msg
;
1372 gtk_tree_model_get (tree_model
, iter
, COL_DEBUG_MESSAGE
, &msg
, -1);
1374 time_str
= debug_window_format_timestamp (msg
);
1376 g_object_set (G_OBJECT (cell
), "text", time_str
, NULL
);
1378 g_object_unref (msg
);
1382 debug_window_domain_formatter (GtkTreeViewColumn
*tree_column
,
1383 GtkCellRenderer
*cell
,
1384 GtkTreeModel
*tree_model
,
1388 TpDebugMessage
*msg
;
1390 gtk_tree_model_get (tree_model
, iter
, COL_DEBUG_MESSAGE
, &msg
, -1);
1392 g_object_set (G_OBJECT (cell
), "text", tp_debug_message_get_domain (msg
),
1395 g_object_unref (msg
);
1399 debug_window_category_formatter (GtkTreeViewColumn
*tree_column
,
1400 GtkCellRenderer
*cell
,
1401 GtkTreeModel
*tree_model
,
1405 TpDebugMessage
*msg
;
1406 const gchar
*category
;
1408 gtk_tree_model_get (tree_model
, iter
, COL_DEBUG_MESSAGE
, &msg
, -1);
1410 category
= tp_debug_message_get_category (msg
);
1412 g_object_set (G_OBJECT (cell
), "text", category
? category
: "", NULL
);
1414 g_object_unref (msg
);
1418 debug_window_message_formatter (GtkTreeViewColumn
*tree_column
,
1419 GtkCellRenderer
*cell
,
1420 GtkTreeModel
*tree_model
,
1424 TpDebugMessage
*msg
;
1426 gtk_tree_model_get (tree_model
, iter
, COL_DEBUG_MESSAGE
, &msg
, -1);
1428 g_object_set (G_OBJECT (cell
), "text",
1429 tp_debug_message_get_message (msg
), NULL
);
1431 g_object_unref (msg
);
1435 debug_window_level_formatter (GtkTreeViewColumn
*tree_column
,
1436 GtkCellRenderer
*cell
,
1437 GtkTreeModel
*tree_model
,
1441 TpDebugMessage
*msg
;
1444 gtk_tree_model_get (tree_model
, iter
, COL_DEBUG_MESSAGE
, &msg
, -1);
1446 level
= log_level_to_string (tp_debug_message_get_level (msg
));
1448 g_object_set (G_OBJECT (cell
), "text", level
, NULL
);
1450 g_object_unref (msg
);
1454 debug_window_copy_model_foreach (GtkTreeModel
*model
,
1459 gchar
**text
= (gchar
**) user_data
;
1462 const gchar
*level_str
, *category
;
1463 gchar
*line
, *time_str
;
1464 TpDebugMessage
*msg
;
1467 *text
= g_strdup ("");
1469 gtk_tree_model_get (model
, iter
,
1470 COL_DEBUG_MESSAGE
, &msg
,
1473 level_str
= log_level_to_string (tp_debug_message_get_level (msg
));
1474 level_upper
= g_ascii_strup (level_str
, -1);
1476 time_str
= debug_window_format_timestamp (msg
);
1477 category
= tp_debug_message_get_category (msg
);
1479 line
= g_strdup_printf ("%s%s%s-%s: %s: %s\n",
1480 tp_debug_message_get_domain (msg
),
1481 category
? "" : "/", category
? category
: "",
1482 level_upper
, time_str
, tp_debug_message_get_message (msg
));
1486 tmp
= g_strconcat (*text
, line
, NULL
);
1490 g_free (level_upper
);
1491 g_object_unref (msg
);
1499 debug_window_save_file_chooser_response_cb (GtkDialog
*dialog
,
1501 EmpathyDebugWindow
*self
)
1503 gchar
*filename
= NULL
;
1504 GFile
*gfile
= NULL
;
1505 gchar
*debug_data
= NULL
;
1506 GFileOutputStream
*output_stream
= NULL
;
1507 GError
*file_open_error
= NULL
;
1508 GError
*file_write_error
= NULL
;
1510 if (response_id
!= GTK_RESPONSE_ACCEPT
)
1513 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
1515 DEBUG ("Saving log as %s", filename
);
1517 gfile
= g_file_new_for_path (filename
);
1518 output_stream
= g_file_replace (gfile
, NULL
, FALSE
,
1519 G_FILE_CREATE_NONE
, NULL
, &file_open_error
);
1521 if (file_open_error
!= NULL
)
1523 DEBUG ("Failed to open file for writing: %s", file_open_error
->message
);
1524 g_error_free (file_open_error
);
1528 gtk_tree_model_foreach (self
->priv
->store_filter
,
1529 debug_window_copy_model_foreach
, &debug_data
);
1531 g_output_stream_write (G_OUTPUT_STREAM (output_stream
), debug_data
,
1532 strlen (debug_data
), NULL
, &file_write_error
);
1533 g_free (debug_data
);
1535 if (file_write_error
!= NULL
)
1537 DEBUG ("Failed to write to file: %s", file_write_error
->message
);
1538 g_error_free (file_write_error
);
1543 g_object_unref (gfile
);
1545 if (output_stream
!= NULL
)
1546 g_object_unref (output_stream
);
1548 if (filename
!= NULL
)
1551 gtk_widget_destroy (GTK_WIDGET (dialog
));
1555 debug_window_save_clicked_cb (GtkToolButton
*tool_button
,
1556 EmpathyDebugWindow
*self
)
1558 GtkWidget
*file_chooser
;
1559 gchar
*name
, *tmp
= NULL
;
1564 file_chooser
= gtk_file_chooser_dialog_new (_("Save"),
1565 GTK_WINDOW (self
), GTK_FILE_CHOOSER_ACTION_SAVE
,
1566 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1567 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
1570 gtk_window_set_modal (GTK_WINDOW (file_chooser
), TRUE
);
1571 gtk_file_chooser_set_do_overwrite_confirmation (
1572 GTK_FILE_CHOOSER (file_chooser
), TRUE
);
1574 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_chooser
),
1577 name
= get_active_service_name (self
);
1580 tm_s
= localtime (&t
);
1583 if (strftime (time_str
, sizeof (time_str
), "%d-%m-%y_%H-%M-%S", tm_s
))
1584 tmp
= g_strdup_printf ("%s-%s.log", name
, time_str
);
1588 tmp
= g_strdup_printf ("%s.log", name
);
1591 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (file_chooser
), tmp
);
1594 g_signal_connect (file_chooser
, "response",
1595 G_CALLBACK (debug_window_save_file_chooser_response_cb
),
1598 gtk_widget_show (file_chooser
);
1602 debug_window_pastebin_response_dialog_closed_cb (GtkDialog
*dialog
,
1606 soup_buffer_free (buffer
);
1608 gtk_widget_destroy (GTK_WIDGET (dialog
));
1612 debug_window_pastebin_callback (SoupSession
*session
,
1619 buffer
= soup_message_body_flatten (msg
->response_body
);
1620 if (g_str_has_prefix (buffer
->data
, "http://pastebin.com/"))
1622 dialog
= gtk_message_dialog_new (GTK_WINDOW (self
),
1623 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_MESSAGE_INFO
, GTK_BUTTONS_CLOSE
,
1624 _("Pastebin link"));
1626 gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog
),
1627 "<a href=\"%s\">%s</a>", buffer
->data
, buffer
->data
);
1631 dialog
= gtk_message_dialog_new (GTK_WINDOW (self
),
1632 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_MESSAGE_INFO
, GTK_BUTTONS_CLOSE
,
1633 _("Pastebin response"));
1635 if (!tp_str_empty (buffer
->data
))
1636 gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog
),
1637 "%s", buffer
->data
);
1639 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog
),
1640 _("Data too large for a single paste. Please save logs to file."));
1643 g_object_unref (session
);
1645 gtk_window_set_transient_for (GTK_WINDOW (dialog
), self
);
1647 gtk_widget_show_all (GTK_WIDGET (dialog
));
1649 g_signal_connect_after (dialog
, "response", G_CALLBACK (
1650 debug_window_pastebin_response_dialog_closed_cb
), buffer
);
1654 debug_window_message_dialog (EmpathyDebugWindow
*self
,
1655 const gchar
*primary_text
,
1656 const gchar
*secondary_text
)
1660 dialog
= gtk_message_dialog_new (GTK_WINDOW (self
),
1661 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_MESSAGE_INFO
, GTK_BUTTONS_OK
,
1662 "%s", _(primary_text
));
1663 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog
),
1664 "%s", _(secondary_text
));
1665 gtk_window_set_transient_for (GTK_WINDOW (dialog
),
1668 gtk_dialog_run (GTK_DIALOG (dialog
));
1669 gtk_widget_destroy (dialog
);
1673 debug_window_send_to_pastebin (EmpathyDebugWindow
*self
,
1676 SoupSession
*session
;
1678 gchar
*api_dev_key
, *api_paste_code
, *api_paste_name
, *formdata
;
1680 if (tp_str_empty (debug_data
))
1682 debug_window_message_dialog (self
, "Error", "No data to send");
1686 /* Constructing a valid URL for http post. See http://pastebin.com/api#2 */
1688 /* The api_dev_key is the author's developer key to access the Pastebin API
1689 * This developer key is published here with the autorization of pastebin;
1690 * see PASTEBIN-API-KEY.txt */
1691 api_dev_key
= soup_uri_encode ("f6ccfabfdcd4b77b825ee38a30d11d52", NULL
);
1692 api_paste_code
= soup_uri_encode (debug_data
, NULL
);
1693 api_paste_name
= soup_uri_encode ("Empathy debug data", NULL
);
1694 formdata
= g_strdup_printf ("api_dev_key=%s&api_paste_code=%s"
1695 "&api_paste_name=%s&api_paste_format=text&api_option=paste",
1696 api_dev_key
, api_paste_code
, api_paste_name
);
1698 session
= soup_session_async_new ();
1700 msg
= soup_message_new ("POST", "http://pastebin.com/api/api_post.php");
1701 soup_message_set_request (msg
,
1702 "application/x-www-form-urlencoded;charset=UTF-8", SOUP_MEMORY_COPY
,
1703 formdata
, strlen (formdata
));
1705 g_free (api_dev_key
);
1706 g_free (api_paste_code
);
1707 g_free (api_paste_name
);
1710 soup_session_queue_message (session
, msg
, debug_window_pastebin_callback
,
1715 debug_window_send_to_pastebin_cb (GtkToolButton
*tool_button
,
1716 EmpathyDebugWindow
*self
)
1718 gchar
*debug_data
= NULL
;
1720 DEBUG ("Preparing debug data for sending to pastebin.");
1722 gtk_tree_model_foreach (self
->priv
->store_filter
,
1723 debug_window_copy_model_foreach
, &debug_data
);
1725 debug_window_send_to_pastebin (self
, debug_data
);
1726 g_free (debug_data
);
1730 debug_window_copy_clicked_cb (GtkToolButton
*tool_button
,
1731 EmpathyDebugWindow
*self
)
1733 GtkClipboard
*clipboard
;
1736 gtk_tree_model_foreach (self
->priv
->store_filter
,
1737 debug_window_copy_model_foreach
, &text
);
1739 clipboard
= gtk_clipboard_get_for_display (
1740 gtk_widget_get_display (GTK_WIDGET (tool_button
)),
1741 GDK_SELECTION_CLIPBOARD
);
1743 DEBUG ("Copying text to clipboard (length: %" G_GSIZE_FORMAT
")",
1746 gtk_clipboard_set_text (clipboard
, text
, -1);
1752 debug_window_key_press_event_cb (GtkWidget
*widget
,
1756 if ((event
->state
& GDK_CONTROL_MASK
&& event
->keyval
== GDK_KEY_w
)
1757 || event
->keyval
== GDK_KEY_Escape
)
1759 gtk_widget_destroy (widget
);
1767 empathy_debug_window_select_name (EmpathyDebugWindow
*self
,
1770 GtkTreeModel
*model
= GTK_TREE_MODEL (self
->priv
->service_store
);
1773 gboolean valid
, found
= FALSE
;
1775 for (valid
= gtk_tree_model_get_iter_first (model
, &iter
);
1777 valid
= gtk_tree_model_iter_next (model
, &iter
))
1779 gtk_tree_model_get (model
, &iter
,
1780 COL_NAME
, &iter_name
,
1783 if (!tp_strdiff (name
, iter_name
))
1793 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self
->priv
->chooser
), &iter
);
1797 am_prepared_cb (GObject
*am
,
1801 EmpathyDebugWindow
*self
= user_data
;
1802 GObject
*object
= user_data
;
1808 GtkCellRenderer
*renderer
;
1809 GtkListStore
*level_store
;
1811 GError
*error
= NULL
;
1812 GtkWidget
*infobar
, *content
;
1814 if (!tp_proxy_prepare_finish (am
, res
, &error
))
1816 g_warning ("Failed to prepare AM: %s", error
->message
);
1817 g_clear_error (&error
);
1820 empathy_set_css_provider (GTK_WIDGET (object
));
1822 gtk_window_set_title (GTK_WINDOW (object
), _("Debug Window"));
1823 gtk_widget_set_size_request (GTK_WIDGET (object
), 600, 300);
1824 empathy_geometry_bind (GTK_WINDOW (object
), "debug-window");
1826 g_signal_connect (object
, "key-press-event",
1827 G_CALLBACK (debug_window_key_press_event_cb
), NULL
);
1829 vbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 0);
1830 gtk_container_add (GTK_CONTAINER (object
), vbox
);
1831 gtk_widget_show (vbox
);
1833 toolbar
= gtk_toolbar_new ();
1834 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar
), GTK_TOOLBAR_BOTH_HORIZ
);
1835 gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar
), TRUE
);
1836 gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar
),
1837 GTK_ICON_SIZE_SMALL_TOOLBAR
);
1838 gtk_style_context_add_class (gtk_widget_get_style_context (toolbar
),
1839 GTK_STYLE_CLASS_PRIMARY_TOOLBAR
);
1840 gtk_widget_show (toolbar
);
1842 gtk_box_pack_start (GTK_BOX (vbox
), toolbar
, FALSE
, FALSE
, 0);
1845 self
->priv
->chooser
= gtk_combo_box_text_new ();
1846 self
->priv
->service_store
= gtk_list_store_new (NUM_COLS
,
1847 G_TYPE_STRING
, /* COL_NAME */
1848 G_TYPE_STRING
, /* COL_UNIQUE_NAME */
1849 G_TYPE_BOOLEAN
, /* COL_GONE */
1850 G_TYPE_OBJECT
, /* COL_ACTIVE_BUFFER */
1851 G_TYPE_OBJECT
, /* COL_PAUSE_BUFFER */
1852 TP_TYPE_PROXY
); /* COL_PROXY */
1853 gtk_combo_box_set_model (GTK_COMBO_BOX (self
->priv
->chooser
),
1854 GTK_TREE_MODEL (self
->priv
->service_store
));
1855 gtk_widget_show (self
->priv
->chooser
);
1857 item
= gtk_tool_item_new ();
1858 gtk_widget_show (GTK_WIDGET (item
));
1859 gtk_container_add (GTK_CONTAINER (item
), self
->priv
->chooser
);
1860 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), item
, -1);
1861 g_signal_connect (self
->priv
->chooser
, "changed",
1862 G_CALLBACK (debug_window_service_chooser_changed_cb
), object
);
1863 gtk_widget_show (GTK_WIDGET (self
->priv
->chooser
));
1865 item
= gtk_separator_tool_item_new ();
1866 gtk_widget_show (GTK_WIDGET (item
));
1867 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), item
, -1);
1870 self
->priv
->save_button
= gtk_tool_button_new_from_stock (GTK_STOCK_SAVE
);
1871 g_signal_connect (self
->priv
->save_button
, "clicked",
1872 G_CALLBACK (debug_window_save_clicked_cb
), object
);
1873 gtk_widget_show (GTK_WIDGET (self
->priv
->save_button
));
1874 gtk_tool_item_set_is_important (GTK_TOOL_ITEM (self
->priv
->save_button
),
1876 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), self
->priv
->save_button
, -1);
1878 /* Send to pastebin */
1879 self
->priv
->send_to_pastebin
= gtk_tool_button_new_from_stock (
1881 gtk_tool_button_set_label (GTK_TOOL_BUTTON (self
->priv
->send_to_pastebin
),
1882 _("Send to pastebin"));
1883 g_signal_connect (self
->priv
->send_to_pastebin
, "clicked",
1884 G_CALLBACK (debug_window_send_to_pastebin_cb
), object
);
1885 gtk_widget_show (GTK_WIDGET (self
->priv
->send_to_pastebin
));
1886 gtk_tool_item_set_is_important (GTK_TOOL_ITEM (self
->priv
->send_to_pastebin
),
1888 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), self
->priv
->send_to_pastebin
, -1);
1891 self
->priv
->copy_button
= gtk_tool_button_new_from_stock (GTK_STOCK_COPY
);
1892 g_signal_connect (self
->priv
->copy_button
, "clicked",
1893 G_CALLBACK (debug_window_copy_clicked_cb
), object
);
1894 gtk_widget_show (GTK_WIDGET (self
->priv
->copy_button
));
1895 gtk_tool_item_set_is_important (GTK_TOOL_ITEM (self
->priv
->copy_button
),
1897 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), self
->priv
->copy_button
, -1);
1900 self
->priv
->clear_button
= gtk_tool_button_new_from_stock (GTK_STOCK_CLEAR
);
1901 g_signal_connect (self
->priv
->clear_button
, "clicked",
1902 G_CALLBACK (debug_window_clear_clicked_cb
), object
);
1903 gtk_widget_show (GTK_WIDGET (self
->priv
->clear_button
));
1904 gtk_tool_item_set_is_important (GTK_TOOL_ITEM (self
->priv
->clear_button
),
1906 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), self
->priv
->clear_button
, -1);
1908 item
= gtk_separator_tool_item_new ();
1909 gtk_widget_show (GTK_WIDGET (item
));
1910 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), item
, -1);
1913 self
->priv
->paused
= FALSE
;
1914 image
= gtk_image_new_from_stock (GTK_STOCK_MEDIA_PAUSE
,
1915 GTK_ICON_SIZE_MENU
);
1916 gtk_widget_show (image
);
1917 self
->priv
->pause_button
= gtk_toggle_tool_button_new ();
1918 gtk_toggle_tool_button_set_active (
1919 GTK_TOGGLE_TOOL_BUTTON (self
->priv
->pause_button
), self
->priv
->paused
);
1920 g_signal_connect (self
->priv
->pause_button
, "toggled",
1921 G_CALLBACK (debug_window_pause_toggled_cb
), object
);
1922 gtk_widget_show (GTK_WIDGET (self
->priv
->pause_button
));
1923 gtk_tool_item_set_is_important (GTK_TOOL_ITEM (self
->priv
->pause_button
),
1925 gtk_tool_button_set_label (GTK_TOOL_BUTTON (self
->priv
->pause_button
),
1927 gtk_tool_button_set_icon_widget (
1928 GTK_TOOL_BUTTON (self
->priv
->pause_button
), image
);
1929 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), self
->priv
->pause_button
, -1);
1931 item
= gtk_separator_tool_item_new ();
1932 gtk_widget_show (GTK_WIDGET (item
));
1933 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), item
, -1);
1936 self
->priv
->level_label
= gtk_tool_item_new ();
1937 gtk_widget_show (GTK_WIDGET (self
->priv
->level_label
));
1938 label
= gtk_label_new (_("Level "));
1939 gtk_widget_show (label
);
1940 gtk_container_add (GTK_CONTAINER (self
->priv
->level_label
), label
);
1941 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), self
->priv
->level_label
, -1);
1943 self
->priv
->level_filter
= gtk_combo_box_text_new ();
1944 gtk_widget_show (self
->priv
->level_filter
);
1946 item
= gtk_tool_item_new ();
1947 gtk_widget_show (GTK_WIDGET (item
));
1948 gtk_container_add (GTK_CONTAINER (item
), self
->priv
->level_filter
);
1949 gtk_toolbar_insert (GTK_TOOLBAR (toolbar
), item
, -1);
1951 level_store
= gtk_list_store_new (NUM_COLS_LEVEL
,
1952 G_TYPE_STRING
, G_TYPE_UINT
);
1953 gtk_combo_box_set_model (GTK_COMBO_BOX (self
->priv
->level_filter
),
1954 GTK_TREE_MODEL (level_store
));
1956 gtk_list_store_insert_with_values (level_store
, &iter
, -1,
1957 COL_LEVEL_NAME
, _("Debug"),
1958 COL_LEVEL_VALUE
, G_LOG_LEVEL_DEBUG
,
1961 gtk_list_store_insert_with_values (level_store
, &iter
, -1,
1962 COL_LEVEL_NAME
, _("Info"),
1963 COL_LEVEL_VALUE
, G_LOG_LEVEL_INFO
,
1966 gtk_list_store_insert_with_values (level_store
, &iter
, -1,
1967 COL_LEVEL_NAME
, _("Message"),
1968 COL_LEVEL_VALUE
, G_LOG_LEVEL_MESSAGE
,
1971 gtk_list_store_insert_with_values (level_store
, &iter
, -1,
1972 COL_LEVEL_NAME
, _("Warning"),
1973 COL_LEVEL_VALUE
, G_LOG_LEVEL_WARNING
,
1976 gtk_list_store_insert_with_values (level_store
, &iter
, -1,
1977 COL_LEVEL_NAME
, _("Critical"),
1978 COL_LEVEL_VALUE
, G_LOG_LEVEL_CRITICAL
,
1981 gtk_list_store_insert_with_values (level_store
, &iter
, -1,
1982 COL_LEVEL_NAME
, _("Error"),
1983 COL_LEVEL_VALUE
, G_LOG_LEVEL_ERROR
,
1986 gtk_combo_box_set_active (GTK_COMBO_BOX (self
->priv
->level_filter
), 0);
1987 g_signal_connect (self
->priv
->level_filter
, "changed",
1988 G_CALLBACK (debug_window_filter_changed_cb
), object
);
1991 infobar
= gtk_info_bar_new ();
1992 gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar
), GTK_MESSAGE_INFO
);
1994 label
= gtk_label_new (
1995 _("Even if they don't display passwords, logs can contain sensitive "
1996 "information such as your list of contacts or the messages you "
1997 "recently sent or received.\nIf you don't want to see such "
1998 "information available in a public bug report, you "
1999 "can choose to limit the visibility of your bug to "
2000 "Empathy developers when reporting it by displaying "
2001 "the advanced fields in the "
2002 "<a href=\"https://bugzilla.gnome.org/enter_bug.cgi?product=empathy\">"
2003 "bug report</a>."));
2004 gtk_label_set_use_markup (GTK_LABEL (label
), TRUE
);
2005 gtk_label_set_line_wrap (GTK_LABEL (label
), TRUE
);
2006 gtk_style_context_add_class (gtk_widget_get_style_context (label
),
2007 GTK_STYLE_CLASS_DIM_LABEL
);
2009 content
= gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar
));
2010 gtk_box_pack_start (GTK_BOX (content
), label
, FALSE
, FALSE
, 0);
2012 gtk_widget_show (infobar
);
2013 gtk_widget_show (label
);
2014 gtk_box_pack_start (GTK_BOX (vbox
), infobar
, FALSE
, FALSE
, 0);
2016 /* Debug treeview */
2017 self
->priv
->view
= gtk_tree_view_new ();
2018 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (self
->priv
->view
), TRUE
);
2020 g_signal_connect (self
->priv
->view
, "button-press-event",
2021 G_CALLBACK (debug_window_button_press_event_cb
), object
);
2023 renderer
= gtk_cell_renderer_text_new ();
2024 g_object_set (renderer
, "yalign", (gfloat
) 0, NULL
);
2026 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (self
->priv
->view
),
2027 -1, _("Time"), renderer
,
2028 (GtkTreeCellDataFunc
) debug_window_time_formatter
, NULL
, NULL
);
2029 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (self
->priv
->view
),
2030 -1, _("Domain"), renderer
,
2031 (GtkTreeCellDataFunc
) debug_window_domain_formatter
, NULL
, NULL
);
2032 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (self
->priv
->view
),
2033 -1, _("Category"), renderer
,
2034 (GtkTreeCellDataFunc
) debug_window_category_formatter
, NULL
, NULL
);
2035 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (self
->priv
->view
),
2036 -1, _("Level"), renderer
,
2037 (GtkTreeCellDataFunc
) debug_window_level_formatter
, NULL
, NULL
);
2039 renderer
= gtk_cell_renderer_text_new ();
2041 g_object_set (renderer
,
2042 "family", "Monospace",
2043 "ellipsize", PANGO_ELLIPSIZE_END
,
2046 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (self
->priv
->view
),
2047 -1, _("Message"), renderer
,
2048 (GtkTreeCellDataFunc
) debug_window_message_formatter
, NULL
, NULL
);
2050 self
->priv
->store_filter
= NULL
;
2052 gtk_tree_view_set_model (GTK_TREE_VIEW (self
->priv
->view
),
2053 self
->priv
->store_filter
);
2055 /* Scrolled window */
2056 self
->priv
->scrolled_win
= g_object_ref (gtk_scrolled_window_new (
2058 gtk_scrolled_window_set_policy (
2059 GTK_SCROLLED_WINDOW (self
->priv
->scrolled_win
),
2060 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2062 gtk_widget_show (self
->priv
->view
);
2063 gtk_container_add (GTK_CONTAINER (self
->priv
->scrolled_win
),
2066 gtk_widget_show (self
->priv
->scrolled_win
);
2068 /* Not supported label */
2069 self
->priv
->not_supported_label
= g_object_ref (gtk_label_new (
2070 _("The selected connection manager does not support the remote "
2071 "debugging extension.")));
2072 gtk_widget_show (self
->priv
->not_supported_label
);
2073 gtk_box_pack_start (GTK_BOX (vbox
), self
->priv
->not_supported_label
,
2076 self
->priv
->view_visible
= FALSE
;
2078 self
->priv
->all_active_buffer
= NULL
;
2080 debug_window_set_toolbar_sensitivity (EMPATHY_DEBUG_WINDOW (object
), FALSE
);
2081 debug_window_fill_service_chooser (EMPATHY_DEBUG_WINDOW (object
));
2082 gtk_widget_show (GTK_WIDGET (object
));
2086 debug_window_constructed (GObject
*object
)
2088 EmpathyDebugWindow
*self
= EMPATHY_DEBUG_WINDOW (object
);
2090 self
->priv
->am
= tp_account_manager_dup ();
2091 tp_proxy_prepare_async (self
->priv
->am
, NULL
, am_prepared_cb
, object
);
2095 empathy_debug_window_init (EmpathyDebugWindow
*self
)
2097 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
2098 EMPATHY_TYPE_DEBUG_WINDOW
, EmpathyDebugWindowPriv
);
2102 debug_window_set_property (GObject
*object
,
2104 const GValue
*value
,
2110 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
2116 debug_window_get_property (GObject
*object
,
2124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
2130 debug_window_finalize (GObject
*object
)
2132 EmpathyDebugWindow
*self
= EMPATHY_DEBUG_WINDOW (object
);
2134 g_free (self
->priv
->select_name
);
2136 (G_OBJECT_CLASS (empathy_debug_window_parent_class
)->finalize
) (object
);
2140 disable_all_debug_clients (EmpathyDebugWindow
*self
)
2143 gboolean valid_iter
;
2144 GtkTreeModel
*model
;
2146 if (self
->priv
->service_store
== NULL
)
2148 model
= GTK_TREE_MODEL (self
->priv
->service_store
);
2150 /* Skipping the first service store iter which is reserved for "All" */
2151 gtk_tree_model_get_iter_first (model
, &iter
);
2152 for (valid_iter
= gtk_tree_model_iter_next (model
, &iter
);
2154 valid_iter
= gtk_tree_model_iter_next (model
, &iter
))
2156 TpDebugClient
*debug
;
2158 gtk_tree_model_get (model
, &iter
,
2162 debug_window_set_enabled (debug
, FALSE
);
2164 g_object_unref (debug
);
2169 debug_window_dispose (GObject
*object
)
2171 EmpathyDebugWindow
*self
= EMPATHY_DEBUG_WINDOW (object
);
2173 if (self
->priv
->name_owner_changed_signal
!= NULL
)
2174 tp_proxy_signal_connection_disconnect (
2175 self
->priv
->name_owner_changed_signal
);
2177 /* Disable Debug on all proxies */
2178 disable_all_debug_clients (self
);
2180 g_clear_object (&self
->priv
->service_store
);
2181 g_clear_object (&self
->priv
->dbus
);
2182 g_clear_object (&self
->priv
->am
);
2183 g_clear_object (&self
->priv
->all_active_buffer
);
2185 (G_OBJECT_CLASS (empathy_debug_window_parent_class
)->dispose
) (object
);
2189 empathy_debug_window_class_init (EmpathyDebugWindowClass
*klass
)
2191 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
2192 object_class
->constructed
= debug_window_constructed
;
2193 object_class
->dispose
= debug_window_dispose
;
2194 object_class
->finalize
= debug_window_finalize
;
2195 object_class
->set_property
= debug_window_set_property
;
2196 object_class
->get_property
= debug_window_get_property
;
2198 g_type_class_add_private (klass
, sizeof (EmpathyDebugWindowPriv
));
2201 /* public methods */
2204 empathy_debug_window_new (GtkWindow
*parent
)
2206 g_return_val_if_fail (parent
== NULL
|| GTK_IS_WINDOW (parent
), NULL
);
2208 return GTK_WIDGET (g_object_new (EMPATHY_TYPE_DEBUG_WINDOW
,
2209 "transient-for", parent
, NULL
));
2213 empathy_debug_window_show (EmpathyDebugWindow
*self
,
2216 if (self
->priv
->service_store
!= NULL
)
2218 empathy_debug_window_select_name (self
, name
);
2222 g_free (self
->priv
->select_name
);
2223 self
->priv
->select_name
= g_strdup (name
);