2009-08-11 Chris Toshok <toshok@ximian.com>
[moon.git] / plugin / plugin-debug.cpp
blob23b910f2cbf9e9987e615efd178bd0f2a5bbf650
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 #include <config.h>
15 #include <gtk/gtkmessagedialog.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <errno.h>
23 #include "plugin-debug.h"
24 #include "utils.h"
25 #include "uri.h"
26 #include "grid.h"
28 enum TreeColumns {
29 COL_NAME,
30 COL_TYPE_NAME,
31 COL_VALUE,
32 COL_ELEMENT_PTR,
33 NUM_COLUMNS
36 #ifdef DEBUG
38 static void reflect_dependency_object_in_tree (DependencyObject *obj, GtkTreeStore *store,
39 GtkTreeIter *node, bool node_is_self);
41 static char *
42 timespan_to_str (TimeSpan ts)
44 bool negative;
45 int days;
46 int hours;
47 int minutes;
48 double seconds;
50 double ts_frac = (double)ts;
52 negative = (ts < 0);
54 ts_frac /= 10000000.0;
56 days = (int)(ts_frac / 86400);
57 ts_frac -= days * 86400;
58 hours = (int)(ts_frac / 3600);
59 ts_frac -= hours * 3600;
60 minutes = (int)(ts_frac / 60);
61 ts_frac -= minutes * 60;
62 seconds = ts_frac;
64 // XXX someone who can remember printf specifiers should
65 // remove that %s from in there in such a way that we get a
66 // float zero padded to 2 spaces to the left of the decimal.
67 return g_strdup_printf ("%02d:%02d:%02d:%s%.4f", days, hours, minutes, seconds < 10.0 ? "0" : "", seconds);
70 static void
71 reflect_value (GtkTreeStore *store, GtkTreeIter *node, const char *name, const char *type_name, Value *value)
73 DependencyObject *dobj;
74 const char *str = NULL;
75 char *buf = NULL;
77 if (value && value->Is (Type::DEPENDENCY_OBJECT)) {
78 dobj = value->AsDependencyObject ();
80 gtk_tree_store_set (store, node,
81 COL_NAME, name,
82 COL_TYPE_NAME, dobj ? dobj->GetTypeName () : "null",
83 COL_VALUE, "",
84 COL_ELEMENT_PTR, NULL,
85 -1);
87 if (dobj)
88 reflect_dependency_object_in_tree (dobj, store, node, true);
89 return;
92 if (value != NULL) {
93 Type *type = Type::Find (value->GetKind ());
94 type_name = type->GetName ();
96 switch (value->GetKind()) {
97 case Type::DOUBLE:
98 str = buf = g_strdup_printf ("<b>%g</b>", value->AsDouble ());
99 break;
100 case Type::INT32:
101 str = buf = g_strdup_printf ("<b>%d</b>", value->AsInt32 ());
102 break;
103 case Type::INT64:
104 str = buf = g_strdup_printf ("<b>%lld</b>", (long long int) value->AsInt64 ());
105 break;
106 case Type::TIMESPAN: {
107 char *ts_string = timespan_to_str (value->AsTimeSpan());
108 str = buf = g_strdup_printf ("<b>%s</b>", ts_string);
109 g_free (ts_string);
110 break;
112 case Type::UINT64:
113 str = buf = g_strdup_printf ("<b>%llu</b>", (unsigned long long int) value->AsUInt64 ());
114 break;
115 case Type::STRING:
116 str = buf = g_strdup_printf ("<b>%s</b>", value->AsString ());
117 break;
118 case Type::RECT: {
119 Rect *rect = value->AsRect();
120 str = buf = g_strdup_printf ("<b>%g, %g, %g, %g</b>", rect->x, rect->y, rect->width, rect->height);
121 break;
123 case Type::SIZE:
124 str = buf = g_strdup_printf ("<b>%g, %g</b>", value->AsSize()->width, value->AsSize()->height);
125 break;
126 case Type::REPEATBEHAVIOR: {
127 RepeatBehavior *rb = value->AsRepeatBehavior();
128 if (rb->IsForever ())
129 str = "<b>Forever</b>";
130 else if (rb->HasCount())
131 str = buf = g_strdup_printf ("<b>%gx</b>", rb->GetCount());
132 else /*if (rb->HasDuration())*/ {
133 char *ts_string = timespan_to_str (rb->GetDuration());
134 str = buf = g_strdup_printf ("<b>%s</b>", ts_string);
135 g_free (ts_string);
137 break;
139 case Type::DURATION: {
140 Duration *d = value->AsDuration();
141 if (d->IsForever ())
142 str = "<b>Forever</b>";
143 else if (d->IsAutomatic())
144 str = "<b>Automatic</b>";
145 else /*if (d->HasTimeSpan())*/ {
146 char *ts_string = timespan_to_str (d->GetTimeSpan());
147 str = buf = g_strdup_printf ("<b>%s</b>", ts_string);
148 g_free (ts_string);
150 break;
152 case Type::COLOR: {
153 Color *color = value->AsColor();
154 str = buf = g_strdup_printf ("<b>r=%g, g=%g, b=%g, a=%g</b>", color->r, color->g, color->b, color->a);
155 break;
157 case Type::BOOL:
158 str = value->AsBool () ? "<b>true</b>" : "<b>false</b>";
159 break;
160 case Type::GRIDLENGTH: {
161 GridLength *length = value->AsGridLength ();
162 str = buf = g_strdup_printf ("<b>%g (%s)</b>", length->val, length->type == GridUnitTypeAuto ?
163 "Auto" : length->type == GridUnitTypeStar ? "*" : "Pixel");
164 break;
166 case Type::THICKNESS: {
167 Thickness *thickness = value->AsThickness ();
168 str = buf = g_strdup_printf ("<b>%g, %g, %g, %g</b>",
169 thickness->left,
170 thickness->top,
171 thickness->right,
172 thickness->bottom);
173 break;
175 case Type::POINT: {
176 Point *point = value->AsPoint ();
177 str = buf = g_strdup_printf ("<b>(%g, %g)</b>", point->x, point->y);
178 break;
180 case Type::CORNERRADIUS: {
181 CornerRadius *CornerRadius = value->AsCornerRadius ();
182 str = buf = g_strdup_printf ("<b>%g, %g, %g, %g</b>",
183 CornerRadius->topLeft,
184 CornerRadius->topRight,
185 CornerRadius->bottomLeft,
186 CornerRadius->bottomRight);
187 break;
189 case Type::KEYTIME:
190 default:
191 str = "<i>(unknown)</i>";
192 break;
194 } else {
195 str = "<b><i>null</i></b>";
198 gtk_tree_store_set (store, node,
199 COL_NAME, name,
200 COL_TYPE_NAME, type_name,
201 COL_VALUE, str,
202 COL_ELEMENT_PTR, NULL,
203 -1);
205 g_free (buf);
208 static void
209 reflect_dependency_object_in_tree (DependencyObject *obj, GtkTreeStore *store, GtkTreeIter *node, bool node_is_self)
211 if (obj == NULL)
212 return;
214 GtkTreeIter iter;
216 if (!node_is_self) {
217 gtk_tree_store_append (store, &iter, node);
219 char *markup = g_strdup_printf ("<b>%s</b>", obj->GetName() ? obj->GetName() : "");
220 gtk_tree_store_set (store, &iter,
221 COL_NAME, markup,
222 COL_TYPE_NAME, obj->GetTypeName(),
223 COL_ELEMENT_PTR, obj,
224 -1);
225 g_free (markup);
227 node = &iter;
230 DependencyProperty **properties = obj->GetProperties (true);
232 if (properties[0] != NULL) {
233 GtkTreeIter prop_iter, iter;
234 Type *owner_type;
235 Type *prop_type;
236 char *markup;
237 Value *value;
239 gtk_tree_store_append (store, &prop_iter, node);
241 gtk_tree_store_set (store, &prop_iter,
242 COL_NAME, "Properties",
243 COL_TYPE_NAME, "",
244 COL_ELEMENT_PTR, obj,
245 -1);
247 for (int i = 0; properties[i]; i++) {
248 owner_type = Type::Find (properties[i]->GetOwnerType ());
249 markup = g_strdup_printf ("<i>%s.%s</i>", owner_type ? owner_type->GetName () : "(unknown)",
250 properties[i]->GetName ());
252 gtk_tree_store_append (store, &iter, &prop_iter);
254 prop_type = Type::Find (properties[i]->GetPropertyType ());
255 value = obj->GetValue (properties[i]);
257 reflect_value (store, &iter, markup, prop_type ? prop_type->GetName () : "(unknown)", value);
259 g_free (markup);
263 g_free (properties);
265 if (obj->Is(Type::COLLECTION)) {
266 Collection *col = (Collection*)obj;
268 if (col->GetCount() > 0) {
269 GtkTreeIter elements_iter;
271 gtk_tree_store_append (store, &elements_iter, node);
273 gtk_tree_store_set (store, &elements_iter,
274 COL_NAME, "Elements",
275 COL_TYPE_NAME, "",
276 COL_ELEMENT_PTR, obj,
277 -1);
279 for (int i = 0; i < col->GetCount(); i ++) {
280 Value *v = col->GetValueAt (i);
281 char *markup;
283 if (v->Is (Type::DEPENDENCY_OBJECT))
284 markup = g_strdup_printf ("<i>[%d]</i> <b>%s</b>", i, v->AsDependencyObject()->GetName() ? v->AsDependencyObject()->GetName() : "");
285 else
286 markup = g_strdup_printf ("<i>[%d]</i>", i);
288 GtkTreeIter child_iter;
290 gtk_tree_store_append (store, &child_iter, &elements_iter);
292 reflect_value (store, &child_iter, markup, NULL, v);
294 g_free (markup);
299 if (obj->Is(Type::FRAMEWORKELEMENT) && !obj->Is(Type::PANEL) && !obj->Is (Type::BORDER)) {
300 GtkTreeIter subobject_iter;
302 gtk_tree_store_append (store, &subobject_iter, node);
304 Value v(((Control*)obj)->GetSubtreeObject());
306 reflect_value (store, &subobject_iter, "Visual Child", NULL, &v);
310 static void
311 selection_changed (GtkTreeSelection *selection, PluginInstance *plugin)
313 GtkTreeModel *model;
314 GtkTreeIter iter;
315 DependencyObject *el;
317 Deployment::SetCurrent (plugin->GetDeployment ());
319 if (plugin->GetSurface()->debug_selected_element) {
320 UIElement *el = plugin->GetSurface()->debug_selected_element;
321 el->Invalidate (el->GetSubtreeBounds().GrowBy(1).RoundOut());
322 el->unref ();
323 plugin->GetSurface()->debug_selected_element = NULL;
326 if (!gtk_tree_selection_get_selected (selection,
327 &model,
328 &iter)) {
329 return;
332 gtk_tree_model_get (model, &iter,
333 COL_ELEMENT_PTR, &el,
334 -1);
336 if (el && el->Is(Type::UIELEMENT)) {
337 UIElement *ui = (UIElement*)el;
338 ui->Invalidate (ui->GetSubtreeBounds().GrowBy(1).RoundOut());
339 ui->ref ();
340 plugin->GetSurface()->debug_selected_element = ui;
343 Deployment::SetCurrent (NULL);
346 static void
347 surface_destroyed (EventObject *sender, EventArgs *args, gpointer closure)
349 gtk_widget_destroy ((GtkWidget *) closure);
352 static void
353 remove_destroyed_handler (PluginInstance *plugin, GObject *window)
355 Deployment::SetCurrent (plugin->GetDeployment ());
356 plugin->GetSurface ()->RemoveHandler (EventObject::DestroyedEvent, surface_destroyed, window);
357 Deployment::SetCurrent (NULL);
360 void
361 plugin_debug (PluginInstance *plugin)
363 Surface *surface = plugin->GetSurface ();
365 if (!surface) {
366 GtkWidget *d = gtk_message_dialog_new (NULL,
367 GTK_DIALOG_NO_SEPARATOR,
368 GTK_MESSAGE_ERROR,
369 GTK_BUTTONS_CLOSE,
370 "The plugin hasn't been initialized with xaml content yet");
371 gtk_dialog_run (GTK_DIALOG (d));
372 g_object_unref (d);
373 return;
376 GtkWidget *tree_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
377 gtk_window_set_title (GTK_WINDOW (tree_win), "Xaml contents");
378 gtk_window_set_default_size (GTK_WINDOW (tree_win), 300, 400);
380 Deployment::SetCurrent (plugin->GetDeployment ());
382 surface->AddHandler (EventObject::DestroyedEvent, surface_destroyed, tree_win);
383 g_object_weak_ref (G_OBJECT (tree_win), (GWeakNotify) remove_destroyed_handler, plugin);
385 GtkTreeStore *tree_store = gtk_tree_store_new (NUM_COLUMNS,
386 G_TYPE_STRING,
387 G_TYPE_STRING,
388 G_TYPE_STRING,
389 G_TYPE_POINTER);
391 reflect_dependency_object_in_tree (plugin->GetSurface()->GetToplevel (), tree_store, NULL, false);
393 #if false
394 GtkTreeModel *sorted_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree_store));
396 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sorted_model),
397 COL_NAME, GTK_SORT_ASCENDING);
399 GtkWidget* tree_view = gtk_tree_view_new_with_model (sorted_model);
400 #else
401 GtkWidget* tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
402 #endif
404 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
406 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
408 g_signal_connect (G_OBJECT (selection), "changed",
409 G_CALLBACK (selection_changed), plugin);
411 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
412 GtkTreeViewColumn *col;
414 /* The Name column */
415 col = gtk_tree_view_column_new();
416 gtk_tree_view_column_set_title(col, "Name");
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_NAME);
421 gtk_tree_view_column_set_resizable (col, TRUE);
423 gtk_tree_view_column_set_sort_column_id (col, COL_NAME);
425 /* The Type column */
426 col = gtk_tree_view_column_new();
427 gtk_tree_view_column_set_title(col, "Type");
428 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
430 gtk_tree_view_column_pack_start(col, renderer, TRUE);
431 gtk_tree_view_column_add_attribute (col, renderer, "markup", COL_TYPE_NAME);
432 gtk_tree_view_column_set_resizable (col, TRUE);
434 /* The Value column */
435 col = gtk_tree_view_column_new();
436 gtk_tree_view_column_set_title(col, "Value");
437 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
439 gtk_tree_view_column_pack_start(col, renderer, TRUE);
440 gtk_tree_view_column_add_attribute (col, renderer, "markup", COL_VALUE);
441 gtk_tree_view_column_set_resizable (col, TRUE);
443 GtkWidget *scrolled = gtk_scrolled_window_new (NULL, NULL);
444 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
445 GTK_POLICY_AUTOMATIC,
446 GTK_POLICY_AUTOMATIC);
448 gtk_container_add (GTK_CONTAINER (scrolled), tree_view);
449 gtk_container_add (GTK_CONTAINER (tree_win), scrolled);
451 gtk_widget_show_all (tree_win);
453 Deployment::SetCurrent (NULL);
456 static void
457 populate_tree_from_surface (PluginInstance *plugin, GtkTreeStore *store, GtkTreeIter *parent)
459 if (plugin == NULL)
460 return;
462 GtkTreeIter iter;
463 List *sources;
464 PluginInstance::moon_source *src;
466 sources = plugin->GetSources ();
468 if (sources == NULL)
469 return;
471 src = (PluginInstance::moon_source*) sources->First ();
472 for (; src != NULL; src = (PluginInstance::moon_source*) src->next) {
473 gtk_tree_store_append (store, &iter, parent);
475 gtk_tree_store_set (store, &iter,
476 0, src->uri,
477 1, src->filename,
478 2, src,
479 -1);
484 PluginInstance::moon_source *selected_source = NULL;
486 static void
487 selection_changed_sources (GtkTreeSelection *selection, PluginInstance *plugin)
489 GtkTreeModel *model;
490 GtkTreeIter iter;
492 selected_source = NULL;
494 if (!gtk_tree_selection_get_selected (selection,
495 &model,
496 &iter)) {
497 return;
500 gtk_tree_model_get (model, &iter,
501 2, &selected_source,
502 -1);
505 static void clicked_callback (GtkWidget *widget, gpointer data)
507 if (selected_source == NULL) {
508 printf ("Select a source first.\n");
509 } else {
510 gchar* argv [3];
511 argv [0] = (gchar*) "xdg-open";
512 argv [1] = (gchar*) selected_source->filename;
513 argv [2] = NULL;
514 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
518 static void unxap_callback (GtkWidget *widget, gpointer data)
520 if (selected_source == NULL) {
521 printf ("Select a source first.\n");
522 } else {
523 gchar* argv [3];
524 argv [0] = (gchar*) "munxap";
525 argv [1] = (gchar*) selected_source->filename;
526 argv [2] = NULL;
527 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
532 static size_t
533 get_common_prefix_len (GtkTreeModel *model)
535 char *filename, *path, *url, *buf, *p, *q;
536 size_t max = (size_t) -1;
537 GtkTreeIter iter;
538 Uri *uri;
540 if (!gtk_tree_model_get_iter_first (model, &iter))
541 return 0;
543 gtk_tree_model_get (model, &iter, 0, &url, 1, &filename, -1);
545 uri = new Uri ();
546 if (!uri->Parse (url)) {
547 buf = g_strdup (filename);
548 } else {
549 buf = (char*)uri->GetPath();
550 uri->path = NULL;
553 if ((p = strrchr (buf, '/')))
554 max = (p - buf);
555 else
556 max = 0;
558 delete uri;
560 while (gtk_tree_model_iter_next (model, &iter)) {
561 gtk_tree_model_get (model, &iter, 0, &url, 1, &filename, -1);
563 uri = new Uri ();
564 if (!uri->Parse (url))
565 path = filename;
566 else
567 path = (char*)uri->GetPath();
569 for (p = buf, q = path; *p && *q; p++, q++) {
570 if (*p != *q)
571 break;
574 if ((size_t) (p - buf) < max)
575 max = p - buf;
577 delete uri;
580 g_free (buf);
582 return max;
585 static void
586 save_callback (GtkWidget *widget, gpointer data)
588 GtkTreeModel *model = (GtkTreeModel *) data;
589 char *filename, *dirname, *url, *path;
590 GtkTreeIter iter;
591 size_t prelen;
592 Uri *uri;
593 int fd;
595 if (mkdir ("/tmp/moon-dump", 0777) == -1 && errno != EEXIST)
596 return;
598 prelen = get_common_prefix_len (model);
600 if (!gtk_tree_model_get_iter_first (model, &iter))
601 return;
603 do {
604 gtk_tree_model_get (model, &iter, 0, &url, 1, &filename, -1);
606 uri = new Uri ();
607 if (uri->Parse (url))
608 path = (char*)uri->GetPath();
609 else
610 path = filename;
612 path = g_build_filename ("/tmp/moon-dump", path + prelen, NULL);
613 delete uri;
615 dirname = g_path_get_dirname (path);
616 g_mkdir_with_parents (dirname, 0777);
617 g_free (dirname);
619 if ((fd = open (path, O_CREAT | O_WRONLY | O_EXCL, 0644)) != -1) {
620 if (CopyFileTo (filename, fd) == -1)
621 printf (" Failed: Could not copy file `%s' to `%s': %s\n", filename, path, g_strerror (errno));
622 } else if (errno != EEXIST) {
623 printf (" Failed: Could not create file `%s': %s\n", path, g_strerror (errno));
626 g_free (path);
627 } while (gtk_tree_model_iter_next (model, &iter));
630 void
631 plugin_sources (PluginInstance *plugin)
633 GtkWidget *tree_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
634 gtk_window_set_title (GTK_WINDOW (tree_win), "Sources");
635 gtk_window_set_default_size (GTK_WINDOW (tree_win), 600, 400);
636 GtkBox *vbox = GTK_BOX (gtk_vbox_new (false, 0));
638 GtkTreeStore *tree_store = gtk_tree_store_new (3,
639 G_TYPE_STRING,
640 G_TYPE_STRING,
641 G_TYPE_POINTER);
643 populate_tree_from_surface (plugin, tree_store, NULL);
645 GtkWidget *tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
647 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
649 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
651 g_signal_connect (G_OBJECT (selection), "changed",
652 G_CALLBACK (selection_changed_sources), plugin);
654 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
655 GtkTreeViewColumn *col;
657 /* The Name column */
658 col = gtk_tree_view_column_new();
659 gtk_tree_view_column_set_title(col, "Uri");
660 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
662 gtk_tree_view_column_pack_start(col, renderer, TRUE);
663 gtk_tree_view_column_add_attribute (col, renderer, "text", 0);
665 /* The Type column */
666 col = gtk_tree_view_column_new();
667 gtk_tree_view_column_set_title(col, "Filename");
668 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
670 gtk_tree_view_column_pack_start(col, renderer, TRUE);
671 gtk_tree_view_column_add_attribute (col, renderer, "text", 1);
673 GtkWidget *scrolled = gtk_scrolled_window_new (NULL, NULL);
674 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
675 GTK_POLICY_AUTOMATIC,
676 GTK_POLICY_AUTOMATIC);
678 gtk_container_add (GTK_CONTAINER (scrolled), tree_view);
679 //gtk_container_add (GTK_CONTAINER (tree_win), scrolled);
680 gtk_box_pack_start (vbox, scrolled, TRUE, TRUE, 0);
682 GtkWidget *button;
683 button = gtk_button_new_with_label ("Open file");
684 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (clicked_callback), NULL);
685 gtk_box_pack_start (vbox, button, FALSE, FALSE, 0);
687 button = gtk_button_new_with_label ("Unxap");
688 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (unxap_callback), tree_store);
689 gtk_box_pack_start (vbox, button, FALSE, FALSE, 0);
691 button = gtk_button_new_with_label ("Save (to /tmp/moon-dump/)");
692 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (save_callback), tree_store);
693 gtk_box_pack_start (vbox, button, FALSE, FALSE, 0);
695 gtk_container_add (GTK_CONTAINER (tree_win), GTK_WIDGET (vbox));
697 gtk_widget_show_all (tree_win);
700 #endif