1 /* ///////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/assertdlg_gtk.cpp
3 // Purpose: GtkAssertDialog
4 // Author: Francesco Montorsi
5 // Copyright: (c) 2006 Francesco Montorsi
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////// */
8
9 #include "wx/wxprec.h"
10
11 #if wxDEBUG_LEVEL
12
13 #include "wx/gtk/private.h"
14 #include "wx/gtk/assertdlg_gtk.h"
15 #include "wx/gtk/private/mnemonics.h"
16 #include "wx/translation.h"
17 #include "wx/stockitem.h"
18
19 #include <stdio.h>
20
21 /* ----------------------------------------------------------------------------
22 Constants
23 ---------------------------------------------------------------------------- */
24
25 /*
26 NB: when changing order of the columns also update the gtk_list_store_new() call
27 in gtk_assert_dialog_create_backtrace_list_model() function
28 */
29 #define STACKFRAME_LEVEL_COLIDX 0
30 #define FUNCTION_PROTOTYPE_COLIDX 1
31 #define SOURCE_FILE_COLIDX 2
32 #define LINE_NUMBER_COLIDX 3
33
34 /* ----------------------------------------------------------------------------
35 GtkAssertDialog helpers
36 ---------------------------------------------------------------------------- */
37
38 // This function is called only for GTK+ < 3.10
39 static
gtk_assert_dialog_add_button_to(GtkBox * box,const gchar * label,const gchar * stock)40 GtkWidget *gtk_assert_dialog_add_button_to (GtkBox *box, const gchar *label,
41 const gchar *stock)
42 {
43 /* create the button */
44 GtkWidget *button = gtk_button_new_with_mnemonic (label);
45 gtk_widget_set_can_default(button, true);
46
47 /* add a stock icon inside it */
48 #ifdef __WXGTK4__
49 gtk_button_set_icon_name (GTK_BUTTON (button), stock);
50 #else
51 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
52 GtkWidget *image = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON);
53 wxGCC_WARNING_RESTORE()
54 gtk_button_set_image (GTK_BUTTON (button), image);
55 #endif
56
57 /* add to the given (container) widget */
58 if (box)
59 #ifdef __WXGTK4__
60 gtk_box_pack_end (box, button);
61 #else
62 gtk_box_pack_end (box, button, FALSE, TRUE, 8);
63 #endif
64
65 return button;
66 }
67
68 // This function is called only for GTK+ < 3.10
69 static
gtk_assert_dialog_add_button(GtkAssertDialog * dlg,const gchar * label,const gchar * stock,gint response_id)70 GtkWidget *gtk_assert_dialog_add_button (GtkAssertDialog *dlg, const gchar *label,
71 const gchar *stock, gint response_id)
72 {
73 /* create the button */
74 GtkWidget* button = gtk_assert_dialog_add_button_to(NULL, label, stock);
75
76 /* add the button to the dialog's action area */
77 gtk_dialog_add_action_widget (GTK_DIALOG (dlg), button, response_id);
78
79 return button;
80 }
81
82 #if wxUSE_STACKWALKER
83
84 // This function is called only for GTK+ < 3.10
85 static
gtk_assert_dialog_append_text_column(GtkWidget * treeview,const gchar * name,int index)86 void gtk_assert_dialog_append_text_column (GtkWidget *treeview, const gchar *name, int index)
87 {
88 GtkCellRenderer *renderer;
89 GtkTreeViewColumn *column;
90
91 renderer = gtk_cell_renderer_text_new ();
92 column = gtk_tree_view_column_new_with_attributes (name, renderer,
93 "text", index, NULL);
94 gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, index);
95 gtk_tree_view_column_set_resizable (column, TRUE);
96 gtk_tree_view_column_set_reorderable (column, TRUE);
97 }
98
99 // This function is called only for GTK+ < 3.10
100 static
gtk_assert_dialog_create_backtrace_list_model()101 GtkWidget *gtk_assert_dialog_create_backtrace_list_model ()
102 {
103 GtkListStore *store;
104 GtkWidget *treeview;
105
106 /* create list store */
107 store = gtk_list_store_new (4,
108 G_TYPE_UINT, /* stack frame number */
109 G_TYPE_STRING, /* function prototype */
110 G_TYPE_STRING, /* source file name */
111 G_TYPE_STRING); /* line number */
112
113 /* create the tree view */
114 treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store));
115 g_object_unref (store);
116 #ifndef __WXGTK4__
117 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
118 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
119 wxGCC_WARNING_RESTORE()
120 #endif
121
122 /* append columns */
123 gtk_assert_dialog_append_text_column(treeview, "#", STACKFRAME_LEVEL_COLIDX);
124 gtk_assert_dialog_append_text_column(treeview, "Function Prototype", FUNCTION_PROTOTYPE_COLIDX);
125 gtk_assert_dialog_append_text_column(treeview, "Source file", SOURCE_FILE_COLIDX);
126 gtk_assert_dialog_append_text_column(treeview, "Line #", LINE_NUMBER_COLIDX);
127
128 return treeview;
129 }
130
131 static
gtk_assert_dialog_process_backtrace(GtkAssertDialog * dlg)132 void gtk_assert_dialog_process_backtrace (GtkAssertDialog *dlg)
133 {
134 /* set busy cursor */
135 GdkWindow *parent = gtk_widget_get_window(GTK_WIDGET(dlg));
136 GdkDisplay* display = gdk_window_get_display(parent);
137 GdkCursor* cur = gdk_cursor_new_for_display(display, GDK_WATCH);
138 gdk_window_set_cursor (parent, cur);
139 gdk_flush ();
140
141 (*dlg->callback)(dlg->userdata);
142
143 /* toggle busy cursor */
144 gdk_window_set_cursor (parent, NULL);
145 #ifdef __WXGTK3__
146 g_object_unref(cur);
147 #else
148 gdk_cursor_unref (cur);
149 #endif
150 }
151
152 extern "C" {
153 /* ----------------------------------------------------------------------------
154 GtkAssertDialog signal handlers
155 ---------------------------------------------------------------------------- */
156
gtk_assert_dialog_expander_callback(GtkWidget *,GtkAssertDialog * dlg)157 static void gtk_assert_dialog_expander_callback(GtkWidget*, GtkAssertDialog* dlg)
158 {
159 /* status is not yet updated so we need to invert it to get the new one */
160 gboolean expanded = !gtk_expander_get_expanded (GTK_EXPANDER(dlg->expander));
161 gtk_window_set_resizable (GTK_WINDOW (dlg), expanded);
162
163 if (dlg->callback == NULL) /* was the backtrace already processed? */
164 return;
165
166 gtk_assert_dialog_process_backtrace (dlg);
167
168 /* mark the work as done (so that next activate we won't call the callback again) */
169 dlg->callback = NULL;
170 }
171
gtk_assert_dialog_save_backtrace_callback(GtkWidget *,GtkAssertDialog * dlg)172 static void gtk_assert_dialog_save_backtrace_callback(GtkWidget*, GtkAssertDialog* dlg)
173 {
174 GtkWidget *dialog;
175
176 dialog = gtk_file_chooser_dialog_new ("Save assert info to file", GTK_WINDOW(dlg),
177 GTK_FILE_CHOOSER_ACTION_SAVE,
178 static_cast<const char*>(wxConvertMnemonicsToGTK(wxGetStockLabel(wxID_CANCEL)).utf8_str()), GTK_RESPONSE_CANCEL,
179 static_cast<const char*>(wxConvertMnemonicsToGTK(wxGetStockLabel(wxID_SAVE)).utf8_str()), GTK_RESPONSE_ACCEPT,
180 NULL);
181
182 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
183 {
184 char *filename, *msg, *backtrace;
185 FILE *fp;
186
187 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
188 if ( filename )
189 {
190 msg = gtk_assert_dialog_get_message (dlg);
191 backtrace = gtk_assert_dialog_get_backtrace (dlg);
192
193 /* open the file and write all info inside it */
194 fp = fopen (filename, "w");
195 if (fp)
196 {
197 fprintf (fp, "ASSERT INFO:\n%s\n\nBACKTRACE:\n%s", msg, backtrace);
198 fclose (fp);
199 }
200
201 g_free (filename);
202 g_free (msg);
203 g_free (backtrace);
204 }
205 }
206
207 gtk_widget_destroy (dialog);
208 }
209
gtk_assert_dialog_copy_callback(GtkWidget *,GtkAssertDialog * dlg)210 static void gtk_assert_dialog_copy_callback(GtkWidget*, GtkAssertDialog* dlg)
211 {
212 char *msg, *backtrace;
213 GtkClipboard *clipboard;
214 GString *str;
215
216 msg = gtk_assert_dialog_get_message (dlg);
217 backtrace = gtk_assert_dialog_get_backtrace (dlg);
218
219 /* combine both in a single string */
220 str = g_string_new("");
221 g_string_printf (str, "ASSERT INFO:\n%s\n\nBACKTRACE:\n%s\n\n", msg, backtrace);
222
223 /* copy everything in default clipboard */
224 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
225 gtk_clipboard_set_text (clipboard, str->str, str->len);
226
227 /* copy everything in primary clipboard too */
228 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
229 gtk_clipboard_set_text (clipboard, str->str, str->len);
230
231 g_free (msg);
232 g_free (backtrace);
233 g_string_free (str, TRUE);
234 }
235 } // extern "C"
236
237 #endif // wxUSE_STACKWALKER
238
239 extern "C" {
gtk_assert_dialog_continue_callback(GtkWidget *,GtkAssertDialog * dlg)240 static void gtk_assert_dialog_continue_callback(GtkWidget*, GtkAssertDialog* dlg)
241 {
242 gint response =
243 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(dlg->shownexttime)) ?
244 GTK_ASSERT_DIALOG_CONTINUE : GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING;
245
246 gtk_dialog_response (GTK_DIALOG(dlg), response);
247 }
248 } // extern "C"
249
250 /* ----------------------------------------------------------------------------
251 GtkAssertDialogClass implementation
252 ---------------------------------------------------------------------------- */
253
254 extern "C" {
255 #if GTK_CHECK_VERSION(3,10,0)
256 static void gtk_assert_dialog_class_init(gpointer g_class, void*);
257 #endif // GTK+ >= 3.10
258 static void gtk_assert_dialog_init(GTypeInstance* instance, void*);
259 }
260
gtk_assert_dialog_get_type()261 GType gtk_assert_dialog_get_type()
262 {
263 static GType assert_dialog_type;
264
265 if (!assert_dialog_type)
266 {
267 const GTypeInfo assert_dialog_info =
268 {
269 sizeof (GtkAssertDialogClass),
270 NULL, /* base_init */
271 NULL, /* base_finalize */
272 #if GTK_CHECK_VERSION(3,10,0)
273 gtk_assert_dialog_class_init, /* class init */
274 #else
275 NULL,
276 #endif // GTK+ >= 3.10 / < 3.10
277 NULL, /* class_finalize */
278 NULL, /* class_data */
279 sizeof (GtkAssertDialog),
280 16, /* n_preallocs */
281 gtk_assert_dialog_init,
282 NULL
283 };
284 assert_dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "GtkAssertDialog", &assert_dialog_info, (GTypeFlags)0);
285 }
286
287 return assert_dialog_type;
288 }
289
290 extern "C" {
291 // For GTK+ >= 3.10, Composite Widget Templates are used to define composite widgets.
292 #if GTK_CHECK_VERSION(3,10,0)
gtk_assert_dialog_class_init(gpointer g_class,void *)293 static void gtk_assert_dialog_class_init(gpointer g_class, void*)
294 {
295 if (gtk_check_version(3,10,0) == NULL)
296 {
297 // GtkBuilder XML to be bound to the dialog class data
298 static const char dlgTempl[] =
299 "<interface>"
300 "<object class='GtkListStore' id='backtrace_list_store'>"
301 "<columns>"
302 "<!-- column-name column_index -->"
303 "<column type='gint'/>"
304 "<!-- column-name column_func_prototype -->"
305 "<column type='gchararray'/>"
306 "<!-- column-name column_src_file -->"
307 "<column type='gchararray'/>"
308 "<!-- column-name column_line_no -->"
309 "<column type='gchararray'/>"
310 "</columns>"
311 "</object>"
312 "<object class='GtkImage' id='imageBtnContinue'>"
313 "<property name='visible'>True</property>"
314 "<property name='can_focus'>False</property>"
315 "<property name='icon_name'>go-next</property>"
316 "</object>"
317 "<object class='GtkImage' id='imageBtnCopy'>"
318 "<property name='visible'>True</property>"
319 "<property name='can_focus'>False</property>"
320 "<property name='icon_name'>edit-copy</property>"
321 "</object>"
322 "<object class='GtkImage' id='imageBtnSave'>"
323 "<property name='visible'>True</property>"
324 "<property name='can_focus'>False</property>"
325 "<property name='icon_name'>document-save</property>"
326 "</object>"
327 "<object class='GtkImage' id='imageBtnStop'>"
328 "<property name='visible'>True</property>"
329 "<property name='can_focus'>False</property>"
330 "<property name='icon_name'>application-exit</property>"
331 "</object>"
332 "<template class='GtkAssertDialog' parent='GtkDialog'>"
333 "<property name='can_focus'>False</property>"
334 "<property name='resizable'>False</property>"
335 "<property name='type_hint'>dialog</property>"
336 "<child internal-child='vbox'>"
337 "<object class='GtkBox' id='dialog_vbox'>"
338 "<property name='can_focus'>False</property>"
339 "<property name='orientation'>vertical</property>"
340 "<property name='spacing'>2</property>"
341 "<child internal-child='action_area'>"
342 "<object class='GtkButtonBox' id='dialog_buttons'>"
343 "<property name='can_focus'>False</property>"
344 "<property name='layout_style'>end</property>"
345 "<child>"
346 "<object class='GtkCheckButton' id='shownexttime'>"
347 "<property name='label' translatable='yes'>Show this _dialog the next time</property>"
348 "<property name='visible'>True</property>"
349 "<property name='can_focus'>True</property>"
350 "<property name='receives_default'>False</property>"
351 "<property name='use_underline'>True</property>"
352 "<property name='xalign'>0.5</property>"
353 "<property name='active'>True</property>"
354 "<property name='draw_indicator'>True</property>"
355 "</object>"
356 "<packing>"
357 "<property name='expand'>True</property>"
358 "<property name='fill'>False</property>"
359 "<property name='padding'>8</property>"
360 "<property name='pack_type'>end</property>"
361 "<property name='position'>0</property>"
362 "</packing>"
363 "</child>"
364 "<child>"
365 "<object class='GtkButton' id='button_stop'>"
366 "<property name='label' translatable='yes'>_Stop</property>"
367 "<property name='visible'>True</property>"
368 "<property name='can_focus'>True</property>"
369 "<property name='receives_default'>True</property>"
370 "<property name='image'>imageBtnStop</property>"
371 "<property name='use_underline'>True</property>"
372 "</object>"
373 "<packing>"
374 "<property name='expand'>True</property>"
375 "<property name='fill'>True</property>"
376 "<property name='position'>1</property>"
377 "</packing>"
378 "</child>"
379 "<child>"
380 "<object class='GtkButton' id='button_continue'>"
381 "<property name='label' translatable='yes'>_Continue</property>"
382 "<property name='visible'>True</property>"
383 "<property name='can_focus'>True</property>"
384 "<property name='can_default'>True</property>"
385 "<property name='has_default'>True</property>"
386 "<property name='receives_default'>True</property>"
387 "<property name='image'>imageBtnContinue</property>"
388 "<property name='use_underline'>True</property>"
389 "<signal name='clicked' handler='gtk_assert_dialog_continue_callback' swapped='no'/>"
390 "</object>"
391 "<packing>"
392 "<property name='expand'>True</property>"
393 "<property name='fill'>True</property>"
394 "<property name='position'>2</property>"
395 "</packing>"
396 "</child>"
397 "</object>"
398 "<packing>"
399 "<property name='expand'>False</property>"
400 "<property name='fill'>False</property>"
401 "<property name='position'>0</property>"
402 "</packing>"
403 "</child>"
404 "<child>"
405 "<object class='GtkBox' id='vbox'>"
406 "<property name='visible'>True</property>"
407 "<property name='can_focus'>False</property>"
408 "<property name='orientation'>vertical</property>"
409 "<child>"
410 "<object class='GtkBox' id='hbox'>"
411 "<property name='visible'>True</property>"
412 "<property name='can_focus'>False</property>"
413 "<property name='border_width'>8</property>"
414 "<child>"
415 "<object class='GtkImage' id='image'>"
416 "<property name='visible'>True</property>"
417 "<property name='can_focus'>False</property>"
418 "<property name='icon_name'>dialog-error</property>"
419 "<property name='icon_size'>6</property>"
420 "</object>"
421 "<packing>"
422 "<property name='expand'>False</property>"
423 "<property name='fill'>False</property>"
424 "<property name='padding'>12</property>"
425 "<property name='position'>0</property>"
426 "</packing>"
427 "</child>"
428 "<child>"
429 "<object class='GtkBox' id='vbox2'>"
430 "<property name='visible'>True</property>"
431 "<property name='can_focus'>False</property>"
432 "<property name='orientation'>vertical</property>"
433 "<child>"
434 "<object class='GtkLabel' id='info'>"
435 "<property name='visible'>True</property>"
436 "<property name='can_focus'>False</property>"
437 "<property name='label' translatable='yes'>An assertion failed!</property>"
438 "</object>"
439 "<packing>"
440 "<property name='expand'>True</property>"
441 "<property name='fill'>True</property>"
442 "<property name='padding'>8</property>"
443 "<property name='position'>0</property>"
444 "</packing>"
445 "</child>"
446 "<child>"
447 "<object class='GtkLabel' id='message'>"
448 "<property name='width_request'>450</property>"
449 "<property name='visible'>True</property>"
450 "<property name='can_focus'>False</property>"
451 "<property name='wrap'>True</property>"
452 "<property name='selectable'>True</property>"
453 "</object>"
454 "<packing>"
455 "<property name='expand'>True</property>"
456 "<property name='fill'>True</property>"
457 "<property name='padding'>8</property>"
458 "<property name='pack_type'>end</property>"
459 "<property name='position'>1</property>"
460 "</packing>"
461 "</child>"
462 "</object>"
463 "<packing>"
464 "<property name='expand'>True</property>"
465 "<property name='fill'>True</property>"
466 "<property name='position'>1</property>"
467 "</packing>"
468 "</child>"
469 "</object>"
470 "<packing>"
471 "<property name='expand'>False</property>"
472 "<property name='fill'>False</property>"
473 "<property name='position'>0</property>"
474 "</packing>"
475 "</child>"
476 #if wxUSE_STACKWALKER // expander is needed only if backtrace is enabled
477 "<child>"
478 "<object class='GtkExpander' id='expander'>"
479 "<property name='visible'>True</property>"
480 "<property name='can_focus'>True</property>"
481 "<signal name='activate' handler='gtk_assert_dialog_expander_callback' swapped='no'/>"
482 "<child>"
483 "<object class='GtkBox' id='vbox_exp'>"
484 "<property name='visible'>True</property>"
485 "<property name='can_focus'>False</property>"
486 "<property name='orientation'>vertical</property>"
487 "<child>"
488 "<object class='GtkScrolledWindow' id='sw'>"
489 "<property name='visible'>True</property>"
490 "<property name='can_focus'>True</property>"
491 "<property name='shadow_type'>etched-in</property>"
492 "<property name='min-content-height'>180</property>"
493 "<child>"
494 "<object class='GtkTreeView' id='treeview'>"
495 "<property name='visible'>True</property>"
496 "<property name='can_focus'>True</property>"
497 "<property name='model'>backtrace_list_store</property>"
498 "<child internal-child='selection'>"
499 "<object class='GtkTreeSelection' id='treeview-selection'/>"
500 "</child>"
501 "<child>"
502 "<object class='GtkTreeViewColumn' id='column_index'>"
503 "<property name='resizable'>True</property>"
504 "<property name='spacing'>4</property>"
505 "<property name='title' translatable='yes'>#</property>"
506 "<property name='reorderable'>True</property>"
507 "<child>"
508 "<object class='GtkCellRendererText' id='index_renderer'/>"
509 "<attributes>"
510 "<attribute name='text'>0</attribute>"
511 "</attributes>"
512 "</child>"
513 "</object>"
514 "</child>"
515 "<child>"
516 "<object class='GtkTreeViewColumn' id='column_func_prototype'>"
517 "<property name='resizable'>True</property>"
518 "<property name='spacing'>4</property>"
519 "<property name='title' translatable='yes'>Function Prototype</property>"
520 "<property name='reorderable'>True</property>"
521 "<child>"
522 "<object class='GtkCellRendererText' id='function_renderer'/>"
523 "<attributes>"
524 "<attribute name='text'>1</attribute>"
525 "</attributes>"
526 "</child>"
527 "</object>"
528 "</child>"
529 "<child>"
530 "<object class='GtkTreeViewColumn' id='column_src_file'>"
531 "<property name='resizable'>True</property>"
532 "<property name='spacing'>4</property>"
533 "<property name='title' translatable='yes'>Source file</property>"
534 "<property name='reorderable'>True</property>"
535 "<child>"
536 "<object class='GtkCellRendererText' id='src_file_renderer'/>"
537 "<attributes>"
538 "<attribute name='text'>2</attribute>"
539 "</attributes>"
540 "</child>"
541 "</object>"
542 "</child>"
543 "<child>"
544 "<object class='GtkTreeViewColumn' id='column_line_no'>"
545 "<property name='resizable'>True</property>"
546 "<property name='spacing'>4</property>"
547 "<property name='title' translatable='yes'>Line #</property>"
548 "<property name='reorderable'>True</property>"
549 "<child>"
550 "<object class='GtkCellRendererText' id='line_no_renderer'/>"
551 "<attributes>"
552 "<attribute name='text'>3</attribute>"
553 "</attributes>"
554 "</child>"
555 "</object>"
556 "</child>"
557 "</object>"
558 "</child>"
559 "</object>"
560 "<packing>"
561 "<property name='expand'>True</property>"
562 "<property name='fill'>True</property>"
563 "<property name='padding'>8</property>"
564 "<property name='position'>0</property>"
565 "</packing>"
566 "</child>"
567 "<child>"
568 "<object class='GtkButtonBox' id='buttonbox_exp'>"
569 "<property name='visible'>True</property>"
570 "<property name='can_focus'>False</property>"
571 "<property name='layout_style'>end</property>"
572 "<child>"
573 "<object class='GtkButton' id='button_save'>"
574 "<property name='label' translatable='yes'>Save to _file</property>"
575 "<property name='visible'>True</property>"
576 "<property name='can_focus'>True</property>"
577 "<property name='receives_default'>True</property>"
578 "<property name='image'>imageBtnSave</property>"
579 "<property name='use_underline'>True</property>"
580 "<signal name='clicked' handler='gtk_assert_dialog_save_backtrace_callback' swapped='no'/>"
581 "</object>"
582 "<packing>"
583 "<property name='expand'>True</property>"
584 "<property name='fill'>True</property>"
585 "<property name='position'>0</property>"
586 "</packing>"
587 "</child>"
588 "<child>"
589 "<object class='GtkButton' id='button_copy'>"
590 "<property name='label' translatable='yes'>Copy to clip_board</property>"
591 "<property name='visible'>True</property>"
592 "<property name='can_focus'>True</property>"
593 "<property name='receives_default'>True</property>"
594 "<property name='image'>imageBtnCopy</property>"
595 "<property name='use_underline'>True</property>"
596 "<signal name='clicked' handler='gtk_assert_dialog_copy_callback' swapped='no'/>"
597 "</object>"
598 "<packing>"
599 "<property name='expand'>True</property>"
600 "<property name='fill'>True</property>"
601 "<property name='position'>1</property>"
602 "</packing>"
603 "</child>"
604 "</object>"
605 "<packing>"
606 "<property name='expand'>False</property>"
607 "<property name='fill'>True</property>"
608 "<property name='pack_type'>end</property>"
609 "<property name='position'>1</property>"
610 "</packing>"
611 "</child>"
612 "</object>"
613 "</child>"
614 "<child type='label'>"
615 "<object class='GtkLabel' id='label_exp'>"
616 "<property name='visible'>True</property>"
617 "<property name='can_focus'>False</property>"
618 "<property name='label' translatable='yes'>Back_trace:</property>"
619 "<property name='use_underline'>True</property>"
620 "</object>"
621 "</child>"
622 "</object>"
623 "<packing>"
624 "<property name='expand'>True</property>"
625 "<property name='fill'>True</property>"
626 "<property name='position'>1</property>"
627 "</packing>"
628 "</child>"
629 #endif // wxUSE_STACKWALKER
630 "</object>"
631 "<packing>"
632 "<property name='expand'>True</property>"
633 "<property name='fill'>True</property>"
634 "<property name='padding'>5</property>"
635 "<property name='position'>1</property>"
636 "</packing>"
637 "</child>"
638 "</object>"
639 "</child>"
640 "<action-widgets>"
641 "<action-widget response='0'>button_stop</action-widget>"
642 "<action-widget response='1'>button_continue</action-widget>"
643 "</action-widgets>"
644 "</template>"
645 "</interface>";
646
647 // Verify numeric values of response codes hard-coded in the XML
648 wxASSERT(GTK_ASSERT_DIALOG_STOP == 0);
649 wxASSERT(GTK_ASSERT_DIALOG_CONTINUE == 1);
650
651 GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(g_class);
652
653 GBytes* templBytes = g_bytes_new_static(dlgTempl, sizeof(dlgTempl)-1);
654 gtk_widget_class_set_template(widgetClass, templBytes);
655
656 // Define the relationship of the entries in GtkAssertDialog and entries defined in the XML
657 gtk_widget_class_bind_template_child(widgetClass, GtkAssertDialog, message);
658 #if wxUSE_STACKWALKER
659 gtk_widget_class_bind_template_child(widgetClass, GtkAssertDialog, expander);
660 gtk_widget_class_bind_template_child(widgetClass, GtkAssertDialog, treeview);
661 #endif // wxUSE_STACKWALKER
662 gtk_widget_class_bind_template_child(widgetClass, GtkAssertDialog, shownexttime);
663
664 // Bind <signal> connections defined in the GtkBuilder XML
665 // with callbacks exposed by GtkAssertDialog.
666 #if wxUSE_STACKWALKER
667 gtk_widget_class_bind_template_callback(widgetClass, gtk_assert_dialog_expander_callback);
668 gtk_widget_class_bind_template_callback(widgetClass, gtk_assert_dialog_save_backtrace_callback);
669 gtk_widget_class_bind_template_callback(widgetClass, gtk_assert_dialog_copy_callback);
670 #endif // wxUSE_STACKWALKER
671 gtk_widget_class_bind_template_callback(widgetClass, gtk_assert_dialog_continue_callback);
672 }
673 }
674 #endif // GTK+ >= 3.10
675
gtk_assert_dialog_init(GTypeInstance * instance,void *)676 static void gtk_assert_dialog_init(GTypeInstance* instance, void*)
677 {
678 // For GTK+ >= 3.10 create and initialize the dialog from the already assigned template
679 // or create the dialog "manually" otherwise.
680 #if GTK_CHECK_VERSION(3,10,0)
681 if (gtk_check_version(3,10,0) == NULL)
682 {
683 GtkAssertDialog* dlg = GTK_ASSERT_DIALOG(instance);
684 gtk_widget_init_template(GTK_WIDGET(dlg));
685 /* complete creation */
686 dlg->callback = NULL;
687 dlg->userdata = NULL;
688 }
689 else
690 #endif // GTK+ >= 3.10
691 {
692 GtkAssertDialog* dlg = GTK_ASSERT_DIALOG(instance);
693 GtkWidget *continuebtn;
694
695 // This code is called only for GTK+ < 3.10
696 {
697 GtkWidget *vbox, *hbox, *image;
698
699 /* start the main vbox */
700 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
701 gtk_widget_push_composite_child ();
702 wxGCC_WARNING_RESTORE()
703 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
704 gtk_container_set_border_width (GTK_CONTAINER(vbox), 8);
705 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), vbox, true, true, 5);
706
707
708 /* add the icon+message hbox */
709 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
710 gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
711
712 /* icon */
713 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
714 image = gtk_image_new_from_stock("gtk-dialog-error", GTK_ICON_SIZE_DIALOG);
715 wxGCC_WARNING_RESTORE()
716 gtk_box_pack_start (GTK_BOX(hbox), image, FALSE, FALSE, 12);
717
718 {
719 GtkWidget *vbox2, *info;
720
721 /* message */
722 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
723 gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
724 info = gtk_label_new ("An assertion failed!");
725 gtk_box_pack_start (GTK_BOX(vbox2), info, TRUE, TRUE, 8);
726
727 /* assert message */
728 dlg->message = gtk_label_new (NULL);
729 gtk_label_set_selectable (GTK_LABEL (dlg->message), TRUE);
730 gtk_label_set_line_wrap (GTK_LABEL (dlg->message), TRUE);
731 gtk_label_set_justify (GTK_LABEL (dlg->message), GTK_JUSTIFY_LEFT);
732 gtk_widget_set_size_request (GTK_WIDGET(dlg->message), 450, -1);
733
734 gtk_box_pack_end (GTK_BOX(vbox2), GTK_WIDGET(dlg->message), TRUE, TRUE, 8);
735 }
736
737 #if wxUSE_STACKWALKER
738 /* add the expander */
739 dlg->expander = gtk_expander_new_with_mnemonic ("Back_trace:");
740 gtk_box_pack_start (GTK_BOX(vbox), dlg->expander, TRUE, TRUE, 0);
741 g_signal_connect (dlg->expander, "activate",
742 G_CALLBACK(gtk_assert_dialog_expander_callback), dlg);
743 #endif // wxUSE_STACKWALKER
744 }
745 #if wxUSE_STACKWALKER
746 {
747 GtkWidget *hbox, *vbox, *button, *sw;
748
749 /* create expander's vbox */
750 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
751 gtk_container_add (GTK_CONTAINER (dlg->expander), vbox);
752
753 /* add a scrollable window under the expander */
754 sw = gtk_scrolled_window_new (NULL, NULL);
755 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN);
756 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
757 GTK_POLICY_AUTOMATIC);
758 gtk_widget_set_size_request(GTK_WIDGET(sw), -1, 180);
759 gtk_box_pack_start (GTK_BOX(vbox), sw, TRUE, TRUE, 8);
760
761 /* add the treeview to the scrollable window */
762 dlg->treeview = gtk_assert_dialog_create_backtrace_list_model ();
763 gtk_container_add (GTK_CONTAINER (sw), dlg->treeview);
764
765 /* create button's hbox */
766 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
767 gtk_box_pack_end (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
768 gtk_button_box_set_layout (GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
769
770 /* add the buttons */
771 button = gtk_assert_dialog_add_button_to(GTK_BOX(hbox), "Save to _file", "gtk-save");
772 g_signal_connect (button, "clicked",
773 G_CALLBACK(gtk_assert_dialog_save_backtrace_callback), dlg);
774
775 button = gtk_assert_dialog_add_button_to(GTK_BOX(hbox), "Copy to clip_board", "gtk-copy");
776 g_signal_connect (button, "clicked", G_CALLBACK(gtk_assert_dialog_copy_callback), dlg);
777 }
778 #endif // wxUSE_STACKWALKER
779
780 /* add the checkbutton */
781 dlg->shownexttime = gtk_check_button_new_with_mnemonic("Show this _dialog the next time");
782 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(dlg->shownexttime), TRUE);
783 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
784 gtk_box_pack_end(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dlg))), dlg->shownexttime, false, true, 8);
785 wxGCC_WARNING_RESTORE()
786
787 /* add the stop button */
788 gtk_assert_dialog_add_button(dlg, "_Stop", "gtk-quit", GTK_ASSERT_DIALOG_STOP);
789
790 /* add the continue button */
791 continuebtn = gtk_assert_dialog_add_button(dlg, "_Continue", "gtk-yes", GTK_ASSERT_DIALOG_CONTINUE);
792 gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_ASSERT_DIALOG_CONTINUE);
793 g_signal_connect (continuebtn, "clicked", G_CALLBACK(gtk_assert_dialog_continue_callback), dlg);
794
795 /* complete creation */
796 dlg->callback = NULL;
797 dlg->userdata = NULL;
798
799 /* the resizable property of this window is modified by the expander:
800 when it's collapsed, the window must be non-resizable! */
801 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
802 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
803 gtk_widget_pop_composite_child ();
804 wxGCC_WARNING_RESTORE()
805 gtk_widget_show_all (GTK_WIDGET(dlg));
806 }
807 }
808 }
809
810 /* ----------------------------------------------------------------------------
811 GtkAssertDialog public API
812 ---------------------------------------------------------------------------- */
813
gtk_assert_dialog_get_message(GtkAssertDialog * dlg)814 gchar *gtk_assert_dialog_get_message (GtkAssertDialog *dlg)
815 {
816 /* NOTES:
817 1) returned string must g_free()d !
818 2) Pango markup is automatically stripped off by GTK
819 */
820 return g_strdup (gtk_label_get_text (GTK_LABEL(dlg->message)));
821 }
822
823 #if wxUSE_STACKWALKER
824
gtk_assert_dialog_get_backtrace(GtkAssertDialog * dlg)825 gchar *gtk_assert_dialog_get_backtrace (GtkAssertDialog *dlg)
826 {
827 gchar *function, *sourcefile, *linenum;
828 guint count;
829
830 GtkTreeModel *model;
831 GtkTreeIter iter;
832 GString *string;
833
834 g_return_val_if_fail (GTK_IS_ASSERT_DIALOG (dlg), NULL);
835 model = gtk_tree_view_get_model (GTK_TREE_VIEW(dlg->treeview));
836
837 /* iterate over the list */
838 if (!gtk_tree_model_get_iter_first (model, &iter))
839 return NULL;
840
841 string = g_string_new("");
842 do
843 {
844 /* append this stack frame's info to the string */
845 gtk_tree_model_get(model, &iter,
846 STACKFRAME_LEVEL_COLIDX, &count,
847 FUNCTION_PROTOTYPE_COLIDX, &function,
848 SOURCE_FILE_COLIDX, &sourcefile,
849 LINE_NUMBER_COLIDX, &linenum,
850 -1);
851
852 g_string_append_printf(string, "[%u] %s",
853 count, function);
854 if (sourcefile[0] != '\0')
855 g_string_append_printf (string, " %s", sourcefile);
856 if (linenum[0] != '\0')
857 g_string_append_printf (string, ":%s", linenum);
858 g_string_append (string, "\n");
859
860 g_free (function);
861 g_free (sourcefile);
862 g_free (linenum);
863
864 } while (gtk_tree_model_iter_next (model, &iter));
865
866 /* returned string must g_free()d */
867 return g_string_free (string, FALSE);
868 }
869
870 #endif // wxUSE_STACKWALKER
871
gtk_assert_dialog_set_message(GtkAssertDialog * dlg,const gchar * msg)872 void gtk_assert_dialog_set_message(GtkAssertDialog *dlg, const gchar *msg)
873 {
874 g_return_if_fail (GTK_IS_ASSERT_DIALOG (dlg));
875 /* prepend and append the <b> tag
876 NOTE: g_markup_printf_escaped() is not used because it's available
877 only for glib >= 2.4 */
878 gchar *escaped_msg = g_markup_escape_text (msg, -1);
879 gchar *decorated_msg = g_strdup_printf ("<b>%s</b>", escaped_msg);
880
881 gtk_label_set_markup (GTK_LABEL(dlg->message), decorated_msg);
882
883 g_free (decorated_msg);
884 g_free (escaped_msg);
885 }
886
887 #if wxUSE_STACKWALKER
888
gtk_assert_dialog_set_backtrace_callback(GtkAssertDialog * assertdlg,GtkAssertDialogStackFrameCallback callback,void * userdata)889 void gtk_assert_dialog_set_backtrace_callback(GtkAssertDialog *assertdlg,
890 GtkAssertDialogStackFrameCallback callback,
891 void *userdata)
892 {
893 assertdlg->callback = callback;
894 assertdlg->userdata = userdata;
895 }
896
gtk_assert_dialog_append_stack_frame(GtkAssertDialog * dlg,const gchar * function,const gchar * sourcefile,guint line_number)897 void gtk_assert_dialog_append_stack_frame(GtkAssertDialog *dlg,
898 const gchar *function,
899 const gchar *sourcefile,
900 guint line_number)
901 {
902 GtkTreeModel *model;
903 GtkTreeIter iter;
904 GString *linenum;
905 gint count;
906
907 g_return_if_fail (GTK_IS_ASSERT_DIALOG (dlg));
908 model = gtk_tree_view_get_model (GTK_TREE_VIEW(dlg->treeview));
909
910 /* how many items are in the list up to now ? */
911 count = gtk_tree_model_iter_n_children (model, NULL);
912
913 linenum = g_string_new("");
914 if ( line_number != 0 )
915 g_string_printf (linenum, "%u", line_number);
916
917 /* add data to the list store */
918 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
919 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
920 STACKFRAME_LEVEL_COLIDX, count+1, /* start from 1 and not from 0 */
921 FUNCTION_PROTOTYPE_COLIDX, function,
922 SOURCE_FILE_COLIDX, sourcefile,
923 LINE_NUMBER_COLIDX, linenum->str,
924 -1);
925
926 g_string_free (linenum, TRUE);
927 }
928
929 #endif // wxUSE_STACKWALKER
930
gtk_assert_dialog_new(void)931 GtkWidget *gtk_assert_dialog_new(void)
932 {
933 void* dialog = g_object_new(GTK_TYPE_ASSERT_DIALOG, NULL);
934
935 return GTK_WIDGET (dialog);
936 }
937
938 #endif // wxDEBUG_LEVEL
939