]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/assertdlg_gtk.cpp
add wrappers to avoid compiling code which will never be used
[wxWidgets.git] / src / gtk / assertdlg_gtk.cpp
1 /* ///////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/assertdlg_gtk.cpp
3 // Purpose: GtkAssertDialog
4 // Author: Francesco Montorsi
5 // Id: $Id$
6 // Copyright: (c) 2006 Francesco Montorsi
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////// */
9
10 #include "wx/wxprec.h"
11
12 #if wxDEBUG_LEVEL
13
14 #include <gtk/gtk.h>
15 #include "wx/gtk/assertdlg_gtk.h"
16 #include "wx/gtk/private/gtk2-compat.h"
17
18 #include <stdio.h>
19
20 /* ----------------------------------------------------------------------------
21 Constants
22 ---------------------------------------------------------------------------- */
23
24 /*
25 NB: when changing order of the columns also update the gtk_list_store_new() call
26 in gtk_assert_dialog_create_backtrace_list_model() function
27 */
28 #define STACKFRAME_LEVEL_COLIDX 0
29 #define FUNCTION_PROTOTYPE_COLIDX 1
30 #define SOURCE_FILE_COLIDX 2
31 #define LINE_NUMBER_COLIDX 3
32
33 /* ----------------------------------------------------------------------------
34 GtkAssertDialog helpers
35 ---------------------------------------------------------------------------- */
36
37 GtkWidget *gtk_assert_dialog_add_button_to (GtkBox *box, const gchar *label,
38 const gchar *stock)
39 {
40 /* create the button */
41 GtkWidget *button = gtk_button_new_with_mnemonic (label);
42 gtk_widget_set_can_default(button, true);
43
44 /* add a stock icon inside it */
45 GtkWidget *image = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON);
46 gtk_button_set_image (GTK_BUTTON (button), image);
47
48 /* add to the given (container) widget */
49 if (box)
50 gtk_box_pack_end (box, button, FALSE, TRUE, 8);
51
52 return button;
53 }
54
55 GtkWidget *gtk_assert_dialog_add_button (GtkAssertDialog *dlg, const gchar *label,
56 const gchar *stock, gint response_id)
57 {
58 /* create the button */
59 GtkWidget* button = gtk_assert_dialog_add_button_to(NULL, label, stock);
60
61 /* add the button to the dialog's action area */
62 gtk_dialog_add_action_widget (GTK_DIALOG (dlg), button, response_id);
63
64 return button;
65 }
66
67 #if wxUSE_STACKWALKER
68
69 void gtk_assert_dialog_append_text_column (GtkWidget *treeview, const gchar *name, int index)
70 {
71 GtkCellRenderer *renderer;
72 GtkTreeViewColumn *column;
73
74 renderer = gtk_cell_renderer_text_new ();
75 column = gtk_tree_view_column_new_with_attributes (name, renderer,
76 "text", index, NULL);
77 gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, index);
78 gtk_tree_view_column_set_resizable (column, TRUE);
79 gtk_tree_view_column_set_reorderable (column, TRUE);
80 }
81
82 GtkWidget *gtk_assert_dialog_create_backtrace_list_model ()
83 {
84 GtkListStore *store;
85 GtkWidget *treeview;
86
87 /* create list store */
88 store = gtk_list_store_new (4,
89 G_TYPE_UINT, /* stack frame number */
90 G_TYPE_STRING, /* function prototype */
91 G_TYPE_STRING, /* source file name */
92 G_TYPE_STRING); /* line number */
93
94 /* create the tree view */
95 treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store));
96 g_object_unref (store);
97 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
98
99 /* append columns */
100 gtk_assert_dialog_append_text_column(treeview, "#", STACKFRAME_LEVEL_COLIDX);
101 gtk_assert_dialog_append_text_column(treeview, "Function Prototype", FUNCTION_PROTOTYPE_COLIDX);
102 gtk_assert_dialog_append_text_column(treeview, "Source file", SOURCE_FILE_COLIDX);
103 gtk_assert_dialog_append_text_column(treeview, "Line #", LINE_NUMBER_COLIDX);
104
105 return treeview;
106 }
107
108 void gtk_assert_dialog_process_backtrace (GtkAssertDialog *dlg)
109 {
110 /* set busy cursor */
111 GdkWindow *parent = gtk_widget_get_window(GTK_WIDGET(dlg));
112 GdkCursor *cur = gdk_cursor_new (GDK_WATCH);
113 gdk_window_set_cursor (parent, cur);
114 gdk_flush ();
115
116 (*dlg->callback)(dlg->userdata);
117
118 /* toggle busy cursor */
119 gdk_window_set_cursor (parent, NULL);
120 #ifdef __WXGTK3__
121 g_object_unref(cur);
122 #else
123 gdk_cursor_unref (cur);
124 #endif
125 }
126
127 extern "C" {
128 /* ----------------------------------------------------------------------------
129 GtkAssertDialog signal handlers
130 ---------------------------------------------------------------------------- */
131
132 static void gtk_assert_dialog_expander_callback(GtkWidget*, GtkAssertDialog* dlg)
133 {
134 /* status is not yet updated so we need to invert it to get the new one */
135 gboolean expanded = !gtk_expander_get_expanded (GTK_EXPANDER(dlg->expander));
136 gtk_window_set_resizable (GTK_WINDOW (dlg), expanded);
137
138 if (dlg->callback == NULL) /* was the backtrace already processed? */
139 return;
140
141 gtk_assert_dialog_process_backtrace (dlg);
142
143 /* mark the work as done (so that next activate we won't call the callback again) */
144 dlg->callback = NULL;
145 }
146
147 static void gtk_assert_dialog_save_backtrace_callback(GtkWidget*, GtkAssertDialog* dlg)
148 {
149 GtkWidget *dialog;
150
151 dialog = gtk_file_chooser_dialog_new ("Save assert info to file", GTK_WINDOW(dlg),
152 GTK_FILE_CHOOSER_ACTION_SAVE,
153 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
154 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
155 NULL);
156
157 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
158 {
159 char *filename, *msg, *backtrace;
160 FILE *fp;
161
162 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
163 if ( filename )
164 {
165 msg = gtk_assert_dialog_get_message (dlg);
166 backtrace = gtk_assert_dialog_get_backtrace (dlg);
167
168 /* open the file and write all info inside it */
169 fp = fopen (filename, "w");
170 if (fp)
171 {
172 fprintf (fp, "ASSERT INFO:\n%s\n\nBACKTRACE:\n%s", msg, backtrace);
173 fclose (fp);
174 }
175
176 g_free (filename);
177 g_free (msg);
178 g_free (backtrace);
179 }
180 }
181
182 gtk_widget_destroy (dialog);
183 }
184
185 static void gtk_assert_dialog_copy_callback(GtkWidget*, GtkAssertDialog* dlg)
186 {
187 char *msg, *backtrace;
188 GtkClipboard *clipboard;
189 GString *str;
190
191 msg = gtk_assert_dialog_get_message (dlg);
192 backtrace = gtk_assert_dialog_get_backtrace (dlg);
193
194 /* combine both in a single string */
195 str = g_string_new("");
196 g_string_printf (str, "ASSERT INFO:\n%s\n\nBACKTRACE:\n%s\n\n", msg, backtrace);
197
198 /* copy everything in default clipboard */
199 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
200 gtk_clipboard_set_text (clipboard, str->str, str->len);
201
202 /* copy everything in primary clipboard too */
203 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
204 gtk_clipboard_set_text (clipboard, str->str, str->len);
205
206 g_free (msg);
207 g_free (backtrace);
208 g_string_free (str, TRUE);
209 }
210 } // extern "C"
211
212 #endif // wxUSE_STACKWALKER
213
214 extern "C" {
215 static void gtk_assert_dialog_continue_callback(GtkWidget*, GtkAssertDialog* dlg)
216 {
217 gint response =
218 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(dlg->shownexttime)) ?
219 GTK_ASSERT_DIALOG_CONTINUE : GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING;
220
221 gtk_dialog_response (GTK_DIALOG(dlg), response);
222 }
223 } // extern "C"
224
225 /* ----------------------------------------------------------------------------
226 GtkAssertDialogClass implementation
227 ---------------------------------------------------------------------------- */
228
229 static void gtk_assert_dialog_init (GtkAssertDialog *self);
230 static void gtk_assert_dialog_class_init (GtkAssertDialogClass *klass);
231
232 GType gtk_assert_dialog_get_type()
233 {
234 static GType assert_dialog_type;
235
236 if (!assert_dialog_type)
237 {
238 const GTypeInfo assert_dialog_info =
239 {
240 sizeof (GtkAssertDialogClass),
241 NULL, /* base_init */
242 NULL, /* base_finalize */
243 (GClassInitFunc) gtk_assert_dialog_class_init,
244 NULL, /* class_finalize */
245 NULL, /* class_data */
246 sizeof (GtkAssertDialog),
247 16, /* n_preallocs */
248 (GInstanceInitFunc) gtk_assert_dialog_init,
249 NULL
250 };
251 assert_dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "GtkAssertDialog", &assert_dialog_info, (GTypeFlags)0);
252 }
253
254 return assert_dialog_type;
255 }
256
257 static void gtk_assert_dialog_class_init(GtkAssertDialogClass*)
258 {
259 /* no special initializations required */
260 }
261
262 static void gtk_assert_dialog_init(GtkAssertDialog* dlg)
263 {
264 GtkWidget *continuebtn;
265
266 {
267 GtkWidget *vbox, *hbox, *image;
268
269 /* start the main vbox */
270 gtk_widget_push_composite_child ();
271 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
272 gtk_container_set_border_width (GTK_CONTAINER(vbox), 8);
273 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), vbox, true, true, 5);
274
275
276 /* add the icon+message hbox */
277 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
278 gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
279
280 /* icon */
281 image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG);
282 gtk_box_pack_start (GTK_BOX(hbox), image, FALSE, FALSE, 12);
283
284 {
285 GtkWidget *vbox2, *info;
286
287 /* message */
288 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
289 gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
290 info = gtk_label_new ("An assertion failed!");
291 gtk_box_pack_start (GTK_BOX(vbox2), info, TRUE, TRUE, 8);
292
293 /* assert message */
294 dlg->message = gtk_label_new (NULL);
295 gtk_label_set_selectable (GTK_LABEL (dlg->message), TRUE);
296 gtk_label_set_line_wrap (GTK_LABEL (dlg->message), TRUE);
297 gtk_label_set_justify (GTK_LABEL (dlg->message), GTK_JUSTIFY_LEFT);
298 gtk_widget_set_size_request (GTK_WIDGET(dlg->message), 450, -1);
299
300 gtk_box_pack_end (GTK_BOX(vbox2), GTK_WIDGET(dlg->message), TRUE, TRUE, 8);
301 }
302
303 #if wxUSE_STACKWALKER
304 /* add the expander */
305 dlg->expander = gtk_expander_new_with_mnemonic ("Back_trace:");
306 gtk_box_pack_start (GTK_BOX(vbox), dlg->expander, TRUE, TRUE, 0);
307 g_signal_connect (dlg->expander, "activate",
308 G_CALLBACK(gtk_assert_dialog_expander_callback), dlg);
309 #endif // wxUSE_STACKWALKER
310 }
311 #if wxUSE_STACKWALKER
312 {
313 GtkWidget *hbox, *vbox, *button, *sw;
314
315 /* create expander's vbox */
316 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
317 gtk_container_add (GTK_CONTAINER (dlg->expander), vbox);
318
319 /* add a scrollable window under the expander */
320 sw = gtk_scrolled_window_new (NULL, NULL);
321 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN);
322 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
323 GTK_POLICY_AUTOMATIC);
324 gtk_box_pack_start (GTK_BOX(vbox), sw, TRUE, TRUE, 8);
325
326 /* add the treeview to the scrollable window */
327 dlg->treeview = gtk_assert_dialog_create_backtrace_list_model ();
328 gtk_widget_set_size_request (GTK_WIDGET(dlg->treeview), -1, 180);
329 gtk_container_add (GTK_CONTAINER (sw), dlg->treeview);
330
331 /* create button's hbox */
332 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
333 gtk_box_pack_end (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
334 gtk_button_box_set_layout (GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
335
336 /* add the buttons */
337 button = gtk_assert_dialog_add_button_to (GTK_BOX(hbox), "Save to _file",
338 GTK_STOCK_SAVE);
339 g_signal_connect (button, "clicked",
340 G_CALLBACK(gtk_assert_dialog_save_backtrace_callback), dlg);
341
342 button = gtk_assert_dialog_add_button_to (GTK_BOX(hbox), "Copy to clip_board",
343 GTK_STOCK_COPY);
344 g_signal_connect (button, "clicked", G_CALLBACK(gtk_assert_dialog_copy_callback), dlg);
345 }
346 #endif // wxUSE_STACKWALKER
347
348 /* add the checkbutton */
349 dlg->shownexttime = gtk_check_button_new_with_mnemonic("Show this _dialog the next time");
350 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(dlg->shownexttime), TRUE);
351 gtk_box_pack_end(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dlg))), dlg->shownexttime, false, true, 8);
352
353 /* add the stop button */
354 gtk_assert_dialog_add_button (dlg, "_Stop", GTK_STOCK_QUIT, GTK_ASSERT_DIALOG_STOP);
355
356 /* add the continue button */
357 continuebtn = gtk_assert_dialog_add_button (dlg, "_Continue", GTK_STOCK_YES, GTK_ASSERT_DIALOG_CONTINUE);
358 gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_ASSERT_DIALOG_CONTINUE);
359 g_signal_connect (continuebtn, "clicked", G_CALLBACK(gtk_assert_dialog_continue_callback), dlg);
360
361 /* complete creation */
362 dlg->callback = NULL;
363 dlg->userdata = NULL;
364
365 /* the resizable property of this window is modified by the expander:
366 when it's collapsed, the window must be non-resizable! */
367 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
368 gtk_widget_pop_composite_child ();
369 gtk_widget_show_all (GTK_WIDGET(dlg));
370 }
371
372 /* ----------------------------------------------------------------------------
373 GtkAssertDialog public API
374 ---------------------------------------------------------------------------- */
375
376 gchar *gtk_assert_dialog_get_message (GtkAssertDialog *dlg)
377 {
378 /* NOTES:
379 1) returned string must g_free()d !
380 2) Pango markup is automatically stripped off by GTK
381 */
382 return g_strdup (gtk_label_get_text (GTK_LABEL(dlg->message)));
383 }
384
385 #if wxUSE_STACKWALKER
386
387 gchar *gtk_assert_dialog_get_backtrace (GtkAssertDialog *dlg)
388 {
389 gchar *function, *sourcefile, *linenum;
390 guint count;
391
392 GtkTreeModel *model;
393 GtkTreeIter iter;
394 GString *string;
395
396 g_return_val_if_fail (GTK_IS_ASSERT_DIALOG (dlg), NULL);
397 model = gtk_tree_view_get_model (GTK_TREE_VIEW(dlg->treeview));
398 string = g_string_new("");
399
400 /* iterate over the list */
401 if (!gtk_tree_model_get_iter_first (model, &iter))
402 return NULL;
403
404 do
405 {
406 /* append this stack frame's info to the string */
407 gtk_tree_model_get(model, &iter,
408 STACKFRAME_LEVEL_COLIDX, &count,
409 FUNCTION_PROTOTYPE_COLIDX, &function,
410 SOURCE_FILE_COLIDX, &sourcefile,
411 LINE_NUMBER_COLIDX, &linenum,
412 -1);
413
414 g_string_append_printf(string, "[%u] %s",
415 count, function);
416 if (sourcefile[0] != '\0')
417 g_string_append_printf (string, " %s", sourcefile);
418 if (linenum[0] != '\0')
419 g_string_append_printf (string, ":%s", linenum);
420 g_string_append (string, "\n");
421
422 g_free (function);
423 g_free (sourcefile);
424 g_free (linenum);
425
426 } while (gtk_tree_model_iter_next (model, &iter));
427
428 /* returned string must g_free()d */
429 return g_string_free (string, FALSE);
430 }
431
432 void gtk_assert_dialog_set_message(GtkAssertDialog *dlg, const gchar *msg)
433 {
434 /* prepend and append the <b> tag
435 NOTE: g_markup_printf_escaped() is not used because it's available
436 only for glib >= 2.4 */
437 gchar *escaped_msg = g_markup_escape_text (msg, -1);
438 gchar *decorated_msg = g_strdup_printf ("<b>%s</b>", escaped_msg);
439
440 g_return_if_fail (GTK_IS_ASSERT_DIALOG (dlg));
441 gtk_label_set_markup (GTK_LABEL(dlg->message), decorated_msg);
442
443 g_free (decorated_msg);
444 g_free (escaped_msg);
445 }
446
447 void gtk_assert_dialog_set_backtrace_callback(GtkAssertDialog *assertdlg,
448 GtkAssertDialogStackFrameCallback callback,
449 void *userdata)
450 {
451 assertdlg->callback = callback;
452 assertdlg->userdata = userdata;
453 }
454
455 void gtk_assert_dialog_append_stack_frame(GtkAssertDialog *dlg,
456 const gchar *function,
457 const gchar *sourcefile,
458 guint line_number)
459 {
460 GtkTreeModel *model;
461 GtkTreeIter iter;
462 GString *linenum;
463 gint count;
464
465 g_return_if_fail (GTK_IS_ASSERT_DIALOG (dlg));
466 model = gtk_tree_view_get_model (GTK_TREE_VIEW(dlg->treeview));
467
468 /* how many items are in the list up to now ? */
469 count = gtk_tree_model_iter_n_children (model, NULL);
470
471 linenum = g_string_new("");
472 if ( line_number != 0 )
473 g_string_printf (linenum, "%u", line_number);
474
475 /* add data to the list store */
476 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
477 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
478 STACKFRAME_LEVEL_COLIDX, count+1, /* start from 1 and not from 0 */
479 FUNCTION_PROTOTYPE_COLIDX, function,
480 SOURCE_FILE_COLIDX, sourcefile,
481 LINE_NUMBER_COLIDX, linenum->str,
482 -1);
483
484 g_string_free (linenum, TRUE);
485 }
486
487 #endif // wxUSE_STACKWALKER
488
489 GtkWidget *gtk_assert_dialog_new(void)
490 {
491 void* dialog = g_object_new(GTK_TYPE_ASSERT_DIALOG, NULL);
492
493 return GTK_WIDGET (dialog);
494 }
495
496 #endif // wxDEBUG_LEVEL