* Tagging Moonlight 1.9.1
[moon.git] / plugin / plugin-debug.cpp
blob6ae23707125190c63ea9c8aee0e8b7b068b24fb1
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * plugin-debug.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #ifdef HAVE_CONFIG_H
14 #include <config.h>
15 #endif
17 #include <gtk/gtkmessagedialog.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <errno.h>
25 #include "plugin-debug.h"
26 #include "utils.h"
27 #include "uri.h"
28 #include "grid.h"
30 enum TreeColumns {
31 COL_NAME,
32 COL_TYPE_NAME,
33 COL_VALUE,
34 COL_ELEMENT_PTR,
35 NUM_COLUMNS
38 #ifdef DEBUG
40 static void reflect_dependency_object_in_tree (DependencyObject *obj, GtkTreeStore *store,
41 GtkTreeIter *node, bool node_is_self);
43 static char *
44 timespan_to_str (TimeSpan ts)
46 bool negative;
47 int days;
48 int hours;
49 int minutes;
50 double seconds;
52 double ts_frac = (double)ts;
54 negative = (ts < 0);
56 ts_frac /= 10000000.0;
58 days = (int)(ts_frac / 86400);
59 ts_frac -= days * 86400;
60 hours = (int)(ts_frac / 3600);
61 ts_frac -= hours * 3600;
62 minutes = (int)(ts_frac / 60);
63 ts_frac -= minutes * 60;
64 seconds = ts_frac;
66 // XXX someone who can remember printf specifiers should
67 // remove that %s from in there in such a way that we get a
68 // float zero padded to 2 spaces to the left of the decimal.
69 return g_strdup_printf ("%02d:%02d:%02d:%s%.4f", days, hours, minutes, seconds < 10.0 ? "0" : "", seconds);
72 static void
73 reflect_value (GtkTreeStore *store, GtkTreeIter *node, const char *name, const char *type_name, Value *value)
75 DependencyObject *dobj;
76 const char *str = NULL;
77 char *buf = NULL;
79 if (value && value->Is (Type::DEPENDENCY_OBJECT)) {
80 dobj = value->AsDependencyObject ();
82 gtk_tree_store_set (store, node,
83 COL_NAME, name,
84 COL_TYPE_NAME, dobj ? dobj->GetTypeName () : "null",
85 COL_VALUE, "",
86 COL_ELEMENT_PTR, NULL,
87 -1);
89 if (dobj)
90 reflect_dependency_object_in_tree (dobj, store, node, true);
91 return;
94 if (value != NULL) {
95 Type *type = Type::Find (value->GetKind ());
96 type_name = type->GetName ();
98 switch (value->GetKind()) {
99 case Type::DOUBLE:
100 str = buf = g_strdup_printf ("<b>%g</b>", value->AsDouble ());
101 break;
102 case Type::INT32:
103 str = buf = g_strdup_printf ("<b>%d</b>", value->AsInt32 ());
104 break;
105 case Type::INT64:
106 str = buf = g_strdup_printf ("<b>%lld</b>", (long long int) value->AsInt64 ());
107 break;
108 case Type::TIMESPAN: {
109 char *ts_string = timespan_to_str (value->AsTimeSpan());
110 str = buf = g_strdup_printf ("<b>%s</b>", ts_string);
111 g_free (ts_string);
112 break;
114 case Type::UINT64:
115 str = buf = g_strdup_printf ("<b>%llu</b>", (unsigned long long int) value->AsUInt64 ());
116 break;
117 case Type::STRING:
118 str = buf = g_strdup_printf ("<b>%s</b>", value->AsString ());
119 break;
120 case Type::RECT: {
121 Rect *rect = value->AsRect();
122 str = buf = g_strdup_printf ("<b>%g, %g, %g, %g</b>", rect->x, rect->y, rect->width, rect->height);
123 break;
125 case Type::SIZE:
126 str = buf = g_strdup_printf ("<b>%g, %g</b>", value->AsSize()->width, value->AsSize()->height);
127 break;
128 case Type::REPEATBEHAVIOR: {
129 RepeatBehavior *rb = value->AsRepeatBehavior();
130 if (rb->IsForever ())
131 str = "<b>Forever</b>";
132 else if (rb->HasCount())
133 str = buf = g_strdup_printf ("<b>%gx</b>", rb->GetCount());
134 else /*if (rb->HasDuration())*/ {
135 char *ts_string = timespan_to_str (rb->GetDuration());
136 str = buf = g_strdup_printf ("<b>%s</b>", ts_string);
137 g_free (ts_string);
139 break;
141 case Type::DURATION: {
142 Duration *d = value->AsDuration();
143 if (d->IsForever ())
144 str = "<b>Forever</b>";
145 else if (d->IsAutomatic())
146 str = "<b>Automatic</b>";
147 else /*if (d->HasTimeSpan())*/ {
148 char *ts_string = timespan_to_str (d->GetTimeSpan());
149 str = buf = g_strdup_printf ("<b>%s</b>", ts_string);
150 g_free (ts_string);
152 break;
154 case Type::COLOR: {
155 Color *color = value->AsColor();
156 str = buf = g_strdup_printf ("<b>r=%g, g=%g, b=%g, a=%g</b>", color->r, color->g, color->b, color->a);
157 break;
159 case Type::BOOL:
160 str = value->AsBool () ? "<b>true</b>" : "<b>false</b>";
161 break;
162 case Type::GRIDLENGTH: {
163 GridLength *length = value->AsGridLength ();
164 str = buf = g_strdup_printf ("<b>%g (%s)</b>", length->val, length->type == GridUnitTypeAuto ?
165 "Auto" : length->type == GridUnitTypeStar ? "*" : "Pixel");
166 break;
168 case Type::THICKNESS: {
169 Thickness *thickness = value->AsThickness ();
170 str = buf = g_strdup_printf ("<b>%g, %g, %g, %g</b>",
171 thickness->left,
172 thickness->top,
173 thickness->right,
174 thickness->bottom);
175 break;
177 case Type::CORNERRADIUS:
178 case Type::KEYTIME:
179 default:
180 str = "<i>(unknown)</i>";
181 break;
183 } else {
184 str = "<b><i>null</i></b>";
187 gtk_tree_store_set (store, node,
188 COL_NAME, name,
189 COL_TYPE_NAME, type_name,
190 COL_VALUE, str,
191 COL_ELEMENT_PTR, NULL,
192 -1);
194 g_free (buf);
197 static void
198 reflect_dependency_object_in_tree (DependencyObject *obj, GtkTreeStore *store, GtkTreeIter *node, bool node_is_self)
200 if (obj == NULL)
201 return;
203 GtkTreeIter iter;
205 if (!node_is_self) {
206 gtk_tree_store_append (store, &iter, node);
208 char *markup = g_strdup_printf ("<b>%s</b>", obj->GetName() ? obj->GetName() : "");
209 gtk_tree_store_set (store, &iter,
210 COL_NAME, markup,
211 COL_TYPE_NAME, obj->GetTypeName(),
212 COL_ELEMENT_PTR, obj,
213 -1);
214 g_free (markup);
216 node = &iter;
219 DependencyProperty **properties = obj->GetProperties (true);
221 if (properties[0] != NULL) {
222 GtkTreeIter prop_iter, iter;
223 Type *owner_type;
224 Type *prop_type;
225 char *markup;
226 Value *value;
228 gtk_tree_store_append (store, &prop_iter, node);
230 gtk_tree_store_set (store, &prop_iter,
231 COL_NAME, "Properties",
232 COL_TYPE_NAME, "",
233 COL_ELEMENT_PTR, obj,
234 -1);
236 for (int i = 0; properties[i]; i++) {
237 owner_type = Type::Find (properties[i]->GetOwnerType ());
238 markup = g_strdup_printf ("<i>%s.%s</i>", owner_type ? owner_type->GetName () : "(unknown)",
239 properties[i]->GetName ());
241 gtk_tree_store_append (store, &iter, &prop_iter);
243 prop_type = Type::Find (properties[i]->GetPropertyType ());
244 value = obj->GetValue (properties[i]);
246 reflect_value (store, &iter, markup, prop_type ? prop_type->GetName () : "(unknown)", value);
248 g_free (markup);
252 g_free (properties);
254 if (obj->Is(Type::COLLECTION)) {
255 Collection *col = (Collection*)obj;
257 if (col->GetCount() > 0) {
258 GtkTreeIter elements_iter;
260 gtk_tree_store_append (store, &elements_iter, node);
262 gtk_tree_store_set (store, &elements_iter,
263 COL_NAME, "Elements",
264 COL_TYPE_NAME, "",
265 COL_ELEMENT_PTR, obj,
266 -1);
268 for (int i = 0; i < col->GetCount(); i ++) {
269 Value *v = col->GetValueAt (i);
270 char *markup;
272 if (v->Is (Type::DEPENDENCY_OBJECT))
273 markup = g_strdup_printf ("<i>[%d]</i> <b>%s</b>", i, v->AsDependencyObject()->GetName() ? v->AsDependencyObject()->GetName() : "");
274 else
275 markup = g_strdup_printf ("<i>[%d]</i>", i);
277 GtkTreeIter child_iter;
279 gtk_tree_store_append (store, &child_iter, &elements_iter);
281 reflect_value (store, &child_iter, markup, NULL, v);
283 g_free (markup);
288 if (obj->Is(Type::FRAMEWORKELEMENT) && !obj->Is(Type::PANEL) && !obj->Is (Type::BORDER)) {
289 GtkTreeIter subobject_iter;
291 gtk_tree_store_append (store, &subobject_iter, node);
293 Value v(((Control*)obj)->GetSubtreeObject());
295 reflect_value (store, &subobject_iter, "Visual Child", NULL, &v);
299 static void
300 selection_changed (GtkTreeSelection *selection, PluginInstance *plugin)
302 GtkTreeModel *model;
303 GtkTreeIter iter;
304 DependencyObject *el;
306 Deployment::SetCurrent (plugin->GetDeployment ());
308 if (plugin->GetSurface()->debug_selected_element) {
309 UIElement *el = plugin->GetSurface()->debug_selected_element;
310 el->Invalidate (el->GetSubtreeBounds().GrowBy(1).RoundOut());
311 el->unref ();
312 plugin->GetSurface()->debug_selected_element = NULL;
315 if (!gtk_tree_selection_get_selected (selection,
316 &model,
317 &iter)) {
318 return;
321 gtk_tree_model_get (model, &iter,
322 COL_ELEMENT_PTR, &el,
323 -1);
325 if (el && el->Is(Type::UIELEMENT)) {
326 UIElement *ui = (UIElement*)el;
327 ui->Invalidate (ui->GetSubtreeBounds().GrowBy(1).RoundOut());
328 ui->ref ();
329 plugin->GetSurface()->debug_selected_element = ui;
332 Deployment::SetCurrent (NULL);
335 static void
336 surface_destroyed (EventObject *sender, EventArgs *args, gpointer closure)
338 gtk_widget_destroy ((GtkWidget *) closure);
341 static void
342 remove_destroyed_handler (PluginInstance *plugin, GObject *window)
344 Deployment::SetCurrent (plugin->GetDeployment ());
345 plugin->GetSurface ()->RemoveHandler (EventObject::DestroyedEvent, surface_destroyed, window);
346 Deployment::SetCurrent (NULL);
349 void
350 plugin_debug (PluginInstance *plugin)
352 Surface *surface = plugin->GetSurface ();
354 if (!surface) {
355 GtkWidget *d = gtk_message_dialog_new (NULL,
356 GTK_DIALOG_NO_SEPARATOR,
357 GTK_MESSAGE_ERROR,
358 GTK_BUTTONS_CLOSE,
359 "The plugin hasn't been initialized with xaml content yet");
360 gtk_dialog_run (GTK_DIALOG (d));
361 g_object_unref (d);
362 return;
365 GtkWidget *tree_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
366 gtk_window_set_title (GTK_WINDOW (tree_win), "Xaml contents");
367 gtk_window_set_default_size (GTK_WINDOW (tree_win), 300, 400);
369 Deployment::SetCurrent (plugin->GetDeployment ());
371 surface->AddHandler (EventObject::DestroyedEvent, surface_destroyed, tree_win);
372 g_object_weak_ref (G_OBJECT (tree_win), (GWeakNotify) remove_destroyed_handler, plugin);
374 GtkTreeStore *tree_store = gtk_tree_store_new (NUM_COLUMNS,
375 G_TYPE_STRING,
376 G_TYPE_STRING,
377 G_TYPE_STRING,
378 G_TYPE_POINTER);
380 reflect_dependency_object_in_tree (plugin->GetSurface()->GetToplevel (), tree_store, NULL, false);
382 #if false
383 GtkTreeModel *sorted_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree_store));
385 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sorted_model),
386 COL_NAME, GTK_SORT_ASCENDING);
388 GtkWidget* tree_view = gtk_tree_view_new_with_model (sorted_model);
389 #else
390 GtkWidget* tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
391 #endif
393 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
395 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
397 g_signal_connect (G_OBJECT (selection), "changed",
398 G_CALLBACK (selection_changed), plugin);
400 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
401 GtkTreeViewColumn *col;
403 /* The Name column */
404 col = gtk_tree_view_column_new();
405 gtk_tree_view_column_set_title(col, "Name");
406 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
408 gtk_tree_view_column_pack_start(col, renderer, TRUE);
409 gtk_tree_view_column_add_attribute (col, renderer, "markup", COL_NAME);
410 gtk_tree_view_column_set_resizable (col, TRUE);
412 gtk_tree_view_column_set_sort_column_id (col, COL_NAME);
414 /* The Type column */
415 col = gtk_tree_view_column_new();
416 gtk_tree_view_column_set_title(col, "Type");
417 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
419 gtk_tree_view_column_pack_start(col, renderer, TRUE);
420 gtk_tree_view_column_add_attribute (col, renderer, "markup", COL_TYPE_NAME);
421 gtk_tree_view_column_set_resizable (col, TRUE);
423 /* The Value column */
424 col = gtk_tree_view_column_new();
425 gtk_tree_view_column_set_title(col, "Value");
426 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
428 gtk_tree_view_column_pack_start(col, renderer, TRUE);
429 gtk_tree_view_column_add_attribute (col, renderer, "markup", COL_VALUE);
430 gtk_tree_view_column_set_resizable (col, TRUE);
432 GtkWidget *scrolled = gtk_scrolled_window_new (NULL, NULL);
433 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
434 GTK_POLICY_AUTOMATIC,
435 GTK_POLICY_AUTOMATIC);
437 gtk_container_add (GTK_CONTAINER (scrolled), tree_view);
438 gtk_container_add (GTK_CONTAINER (tree_win), scrolled);
440 gtk_widget_show_all (tree_win);
442 Deployment::SetCurrent (NULL);
445 static void
446 populate_tree_from_surface (PluginInstance *plugin, GtkTreeStore *store, GtkTreeIter *parent)
448 if (plugin == NULL)
449 return;
451 GtkTreeIter iter;
452 List *sources;
453 PluginInstance::moon_source *src;
455 sources = plugin->GetSources ();
457 if (sources == NULL)
458 return;
460 src = (PluginInstance::moon_source*) sources->First ();
461 for (; src != NULL; src = (PluginInstance::moon_source*) src->next) {
462 gtk_tree_store_append (store, &iter, parent);
464 gtk_tree_store_set (store, &iter,
465 0, src->uri,
466 1, src->filename,
467 2, src,
468 -1);
473 PluginInstance::moon_source *selected_source = NULL;
475 static void
476 selection_changed_sources (GtkTreeSelection *selection, PluginInstance *plugin)
478 GtkTreeModel *model;
479 GtkTreeIter iter;
481 selected_source = NULL;
483 if (!gtk_tree_selection_get_selected (selection,
484 &model,
485 &iter)) {
486 return;
489 gtk_tree_model_get (model, &iter,
490 2, &selected_source,
491 -1);
494 static void clicked_callback (GtkWidget *widget, gpointer data)
496 if (selected_source == NULL) {
497 printf ("Select a source first.\n");
498 } else {
499 gchar* argv [3];
500 argv [0] = (gchar*) "xdg-open";
501 argv [1] = (gchar*) selected_source->filename;
502 argv [2] = NULL;
503 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
507 static void unxap_callback (GtkWidget *widget, gpointer data)
509 if (selected_source == NULL) {
510 printf ("Select a source first.\n");
511 } else {
512 gchar* argv [3];
513 argv [0] = (gchar*) "munxap";
514 argv [1] = (gchar*) selected_source->filename;
515 argv [2] = NULL;
516 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
521 static size_t
522 get_common_prefix_len (GtkTreeModel *model)
524 char *filename, *path, *url, *buf, *p, *q;
525 size_t max = (size_t) -1;
526 GtkTreeIter iter;
527 Uri *uri;
529 if (!gtk_tree_model_get_iter_first (model, &iter))
530 return 0;
532 gtk_tree_model_get (model, &iter, 0, &url, 1, &filename, -1);
534 uri = new Uri ();
535 if (!uri->Parse (url)) {
536 buf = g_strdup (filename);
537 } else {
538 buf = (char*)uri->GetPath();
539 uri->path = NULL;
542 if ((p = strrchr (buf, '/')))
543 max = (p - buf);
544 else
545 max = 0;
547 delete uri;
549 while (gtk_tree_model_iter_next (model, &iter)) {
550 gtk_tree_model_get (model, &iter, 0, &url, 1, &filename, -1);
552 uri = new Uri ();
553 if (!uri->Parse (url))
554 path = filename;
555 else
556 path = (char*)uri->GetPath();
558 for (p = buf, q = path; *p && *q; p++, q++) {
559 if (*p != *q)
560 break;
563 if ((size_t) (p - buf) < max)
564 max = p - buf;
566 delete uri;
569 g_free (buf);
571 return max;
574 static void
575 save_callback (GtkWidget *widget, gpointer data)
577 GtkTreeModel *model = (GtkTreeModel *) data;
578 char *filename, *dirname, *url, *path;
579 GtkTreeIter iter;
580 size_t prelen;
581 Uri *uri;
582 int fd;
584 if (mkdir ("/tmp/moon-dump", 0777) == -1 && errno != EEXIST)
585 return;
587 prelen = get_common_prefix_len (model);
589 if (!gtk_tree_model_get_iter_first (model, &iter))
590 return;
592 do {
593 gtk_tree_model_get (model, &iter, 0, &url, 1, &filename, -1);
595 uri = new Uri ();
596 if (uri->Parse (url))
597 path = (char*)uri->GetPath();
598 else
599 path = filename;
601 path = g_build_filename ("/tmp/moon-dump", path + prelen, NULL);
602 delete uri;
604 dirname = g_path_get_dirname (path);
605 g_mkdir_with_parents (dirname, 0777);
606 g_free (dirname);
608 if ((fd = open (path, O_CREAT | O_WRONLY | O_EXCL, 0644)) != -1) {
609 if (CopyFileTo (filename, fd) == -1)
610 printf (" Failed: Could not copy file `%s' to `%s': %s\n", filename, path, g_strerror (errno));
611 } else if (errno != EEXIST) {
612 printf (" Failed: Could not create file `%s': %s\n", path, g_strerror (errno));
615 g_free (path);
616 } while (gtk_tree_model_iter_next (model, &iter));
619 void
620 plugin_sources (PluginInstance *plugin)
622 GtkWidget *tree_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
623 gtk_window_set_title (GTK_WINDOW (tree_win), "Sources");
624 gtk_window_set_default_size (GTK_WINDOW (tree_win), 600, 400);
625 GtkBox *vbox = GTK_BOX (gtk_vbox_new (false, 0));
627 GtkTreeStore *tree_store = gtk_tree_store_new (3,
628 G_TYPE_STRING,
629 G_TYPE_STRING,
630 G_TYPE_POINTER);
632 populate_tree_from_surface (plugin, tree_store, NULL);
634 GtkWidget *tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
636 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
638 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
640 g_signal_connect (G_OBJECT (selection), "changed",
641 G_CALLBACK (selection_changed_sources), plugin);
643 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
644 GtkTreeViewColumn *col;
646 /* The Name column */
647 col = gtk_tree_view_column_new();
648 gtk_tree_view_column_set_title(col, "Uri");
649 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
651 gtk_tree_view_column_pack_start(col, renderer, TRUE);
652 gtk_tree_view_column_add_attribute (col, renderer, "text", 0);
654 /* The Type column */
655 col = gtk_tree_view_column_new();
656 gtk_tree_view_column_set_title(col, "Filename");
657 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
659 gtk_tree_view_column_pack_start(col, renderer, TRUE);
660 gtk_tree_view_column_add_attribute (col, renderer, "text", 1);
662 GtkWidget *scrolled = gtk_scrolled_window_new (NULL, NULL);
663 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
664 GTK_POLICY_AUTOMATIC,
665 GTK_POLICY_AUTOMATIC);
667 gtk_container_add (GTK_CONTAINER (scrolled), tree_view);
668 //gtk_container_add (GTK_CONTAINER (tree_win), scrolled);
669 gtk_box_pack_start (vbox, scrolled, TRUE, TRUE, 0);
671 GtkWidget *button;
672 button = gtk_button_new_with_label ("Open file");
673 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (clicked_callback), NULL);
674 gtk_box_pack_start (vbox, button, FALSE, FALSE, 0);
676 button = gtk_button_new_with_label ("Unxap");
677 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (unxap_callback), tree_store);
678 gtk_box_pack_start (vbox, button, FALSE, FALSE, 0);
680 button = gtk_button_new_with_label ("Save (to /tmp/moon-dump/)");
681 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (save_callback), tree_store);
682 gtk_box_pack_start (vbox, button, FALSE, FALSE, 0);
684 gtk_container_add (GTK_CONTAINER (tree_win), GTK_WIDGET (vbox));
686 gtk_widget_show_all (tree_win);
689 #endif