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