1 /* /////////////////////////////////////////////////////////////////////////// 
   2 // Name:        assertdlg_gtk.c 
   3 // Purpose:     GtkAssertDialog 
   4 // Author:      Francesco Montorsi 
   6 // Copyright:   (c) 2006 Francesco Montorsi 
   7 // Licence:     wxWindows licence 
   8 /////////////////////////////////////////////////////////////////////////// */ 
  11 #define XCheckIfEvent XCHECKIFEVENT 
  14 #include "wx/platform.h" 
  15 #include "wx/gtk/assertdlg_gtk.h" 
  19 #endif /* __cplusplus */ 
  26 #include <gtk/gtkexpander.h> 
  29 /* ---------------------------------------------------------------------------- 
  31  ---------------------------------------------------------------------------- */ 
  34    NB: when changing order of the columns also update the gtk_list_store_new() call 
  35        in gtk_assert_dialog_create_backtrace_list_model() function 
  37 #define STACKFRAME_LEVEL_COLIDX        0 
  38 #define FUNCTION_NAME_COLIDX           1 
  39 #define SOURCE_FILE_COLIDX             2 
  40 #define LINE_NUMBER_COLIDX             3 
  41 #define FUNCTION_ARGS_COLIDX           4 
  46 /* ---------------------------------------------------------------------------- 
  47    GtkAssertDialog helpers 
  48  ---------------------------------------------------------------------------- */ 
  50 GtkWidget 
*gtk_assert_dialog_add_button_to (GtkBox 
*box
, const gchar 
*label
, 
  51                                             const gchar 
*stock
, gint response_id
) 
  53     /* create the button */ 
  54     GtkWidget 
*button 
= gtk_button_new_with_mnemonic (label
); 
  55     GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
); 
  57 #if GTK_CHECK_VERSION(2,6,0) 
  58     if (!gtk_check_version (2, 6, 0)) 
  60         /* add a stock icon inside it */ 
  61         GtkWidget 
*image 
= gtk_image_new_from_stock (stock
, GTK_ICON_SIZE_BUTTON
); 
  62         gtk_button_set_image (GTK_BUTTON (button
), image
); 
  66     /* add to the given (container) widget */ 
  68         gtk_box_pack_end (box
, button
, FALSE
, TRUE
, 8); 
  73 GtkWidget 
*gtk_assert_dialog_add_button (GtkAssertDialog 
*dlg
, const gchar 
*label
, 
  74                                          const gchar 
*stock
, gint response_id
) 
  76     /* create the button */ 
  77     GtkWidget 
*button 
= gtk_assert_dialog_add_button_to (NULL
, label
, stock
, response_id
); 
  79     /* add the button to the dialog's action area */ 
  80     gtk_dialog_add_action_widget (GTK_DIALOG (dlg
), button
, response_id
); 
  85 void gtk_assert_dialog_append_text_column (GtkWidget 
*treeview
, const gchar 
*name
, int index
) 
  87     GtkCellRenderer 
*renderer
; 
  88     GtkTreeViewColumn 
*column
; 
  90     renderer 
= gtk_cell_renderer_text_new (); 
  91     column 
= gtk_tree_view_column_new_with_attributes (name
, renderer
, 
  93     gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview
), column
, index
); 
  94     gtk_tree_view_column_set_resizable (column
, TRUE
); 
  95     gtk_tree_view_column_set_reorderable (column
, TRUE
); 
  98 GtkWidget 
*gtk_assert_dialog_create_backtrace_list_model () 
 103     /* create list store */ 
 104     store 
= gtk_list_store_new (5, 
 105                                 G_TYPE_UINT
,        /* stack frame number */ 
 106                                 G_TYPE_STRING
,      /* function name      */ 
 107                                 G_TYPE_STRING
,      /* source file name   */ 
 108                                 G_TYPE_STRING
,      /* line number        */ 
 109                                 G_TYPE_STRING
);     /* function arguments */ 
 111     /* create the tree view */ 
 112     treeview 
= gtk_tree_view_new_with_model (GTK_TREE_MODEL(store
)); 
 113     g_object_unref (store
); 
 114     gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview
), TRUE
); 
 117     gtk_assert_dialog_append_text_column(treeview
, "#", STACKFRAME_LEVEL_COLIDX
); 
 118     gtk_assert_dialog_append_text_column(treeview
, "Function name", FUNCTION_NAME_COLIDX
); 
 119     gtk_assert_dialog_append_text_column(treeview
, "Function args", FUNCTION_ARGS_COLIDX
); 
 120     gtk_assert_dialog_append_text_column(treeview
, "Source file", SOURCE_FILE_COLIDX
); 
 121     gtk_assert_dialog_append_text_column(treeview
, "Line #", LINE_NUMBER_COLIDX
); 
 126 void gtk_assert_dialog_process_backtrace (GtkAssertDialog 
*dlg
) 
 128     /* set busy cursor */ 
 129     GdkWindow 
*parent 
= GTK_WIDGET(dlg
)->window
; 
 130     GdkCursor 
*cur 
= gdk_cursor_new (GDK_WATCH
); 
 131     gdk_window_set_cursor (parent
, cur
); 
 134     (*dlg
->callback
)(dlg
->userdata
); 
 136     /* toggle busy cursor */ 
 137     gdk_window_set_cursor (parent
, NULL
); 
 138     gdk_cursor_unref (cur
); 
 143 /* ---------------------------------------------------------------------------- 
 144    GtkAssertDialog signal handlers 
 145  ---------------------------------------------------------------------------- */ 
 147 void gtk_assert_dialog_expander_callback (GtkWidget 
*widget
, GtkAssertDialog 
*dlg
) 
 149     /* status is not yet updated so we need to invert it to get the new one */ 
 150     gboolean expanded 
= !gtk_expander_get_expanded (GTK_EXPANDER(dlg
->expander
)); 
 151     gtk_window_set_resizable (GTK_WINDOW (dlg
), expanded
); 
 153     if (dlg
->callback 
== NULL
)      /* was the backtrace already processed? */ 
 156     gtk_assert_dialog_process_backtrace (dlg
); 
 158     /* mark the work as done (so that next activate we won't call the callback again) */ 
 159     dlg
->callback 
= NULL
; 
 162 void gtk_assert_dialog_save_backtrace_callback (GtkWidget 
*widget
, GtkAssertDialog 
*dlg
) 
 166     dialog 
= gtk_file_chooser_dialog_new ("Save assert info to file", GTK_WINDOW(dlg
), 
 167                                           GTK_FILE_CHOOSER_ACTION_SAVE
, 
 168                                           GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, 
 169                                           GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
, 
 172     if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
) 
 174         char *filename
, *msg
, *backtrace
; 
 177         filename 
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
)); 
 180             msg 
= gtk_assert_dialog_get_message (dlg
); 
 181             backtrace 
= gtk_assert_dialog_get_backtrace (dlg
); 
 183             /* open the file and write all info inside it */ 
 184             fp 
= fopen (filename
, "w"); 
 187                 fprintf (fp
, "ASSERT INFO:\n%s\n\nBACKTRACE:\n%s", msg
, backtrace
); 
 197     gtk_widget_destroy (dialog
); 
 200 void gtk_assert_dialog_copy_callback (GtkWidget 
*widget
, GtkAssertDialog 
*dlg
) 
 202     char *msg
, *backtrace
; 
 203     GtkClipboard 
*clipboard
; 
 206     msg 
= gtk_assert_dialog_get_message (dlg
); 
 207     backtrace 
= gtk_assert_dialog_get_backtrace (dlg
); 
 209     /* combine both in a single string */ 
 210     str 
= g_string_new(""); 
 211     g_string_printf (str
, "ASSERT INFO:\n%s\n\nBACKTRACE:\n%s\n\n", msg
, backtrace
); 
 213     /* copy everything in default clipboard */ 
 214     clipboard 
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
); 
 215     gtk_clipboard_set_text (clipboard
, str
->str
, str
->len
); 
 217     /* copy everything in primary clipboard too */ 
 218     clipboard 
= gtk_clipboard_get (GDK_SELECTION_PRIMARY
); 
 219     gtk_clipboard_set_text (clipboard
, str
->str
, str
->len
); 
 223     g_string_free (str
, TRUE
); 
 226 void gtk_assert_dialog_continue_callback (GtkWidget 
*widget
, GtkAssertDialog 
*dlg
) 
 229         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(dlg
->shownexttime
)) ? 
 230             GTK_ASSERT_DIALOG_CONTINUE 
: GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING
; 
 232     gtk_dialog_response (GTK_DIALOG(dlg
), response
); 
 236 /* ---------------------------------------------------------------------------- 
 237    GtkAssertDialogClass implementation 
 238  ---------------------------------------------------------------------------- */ 
 240 static void     gtk_assert_dialog_init              (GtkAssertDialog        
*self
); 
 241 static void     gtk_assert_dialog_class_init        (GtkAssertDialogClass 
*klass
); 
 244 GtkType 
gtk_assert_dialog_get_type (void) 
 246     static GtkType assert_dialog_type 
= 0; 
 248     if (!assert_dialog_type
) 
 250         const GTypeInfo assert_dialog_info 
= 
 252             sizeof (GtkAssertDialogClass
), 
 253             NULL
,           /* base_init */ 
 254             NULL
,           /* base_finalize */ 
 255             (GClassInitFunc
) gtk_assert_dialog_class_init
, 
 256             NULL
,           /* class_finalize */ 
 257             NULL
,           /* class_data */ 
 258             sizeof (GtkAssertDialog
), 
 259             16,             /* n_preallocs */ 
 260             (GInstanceInitFunc
) gtk_assert_dialog_init
, 
 263         assert_dialog_type 
= g_type_register_static (GTK_TYPE_DIALOG
, "GtkAssertDialog", &assert_dialog_info
, (GTypeFlags
)0); 
 266     return assert_dialog_type
; 
 269 void gtk_assert_dialog_class_init(GtkAssertDialogClass 
*klass
) 
 271     /* no special initializations required */ 
 274 void gtk_assert_dialog_init(GtkAssertDialog 
*dlg
) 
 276     GtkWidget 
*continuebtn
; 
 279         GtkWidget 
*vbox
, *hbox
, *image
; 
 281         /* start the main vbox */ 
 282         gtk_widget_push_composite_child (); 
 283         vbox 
= gtk_vbox_new (FALSE
, 8); 
 284         gtk_container_set_border_width (GTK_CONTAINER(vbox
), 8); 
 285         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg
)->vbox
), vbox
, TRUE
, TRUE
, 5); 
 288         /* add the icon+message hbox */ 
 289         hbox 
= gtk_hbox_new (FALSE
, 0); 
 290         gtk_box_pack_start (GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0); 
 293         image 
= gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR
, GTK_ICON_SIZE_DIALOG
); 
 294         gtk_box_pack_start (GTK_BOX(hbox
), image
, FALSE
, FALSE
, 12); 
 297             GtkWidget 
*vbox2
, *info
; 
 300             vbox2 
= gtk_vbox_new (FALSE
, 0); 
 301             gtk_box_pack_start (GTK_BOX (hbox
), vbox2
, TRUE
, TRUE
, 0); 
 302             info 
= gtk_label_new ("An assertion failed!"); 
 303             gtk_box_pack_start (GTK_BOX(vbox2
), info
, TRUE
, TRUE
, 8); 
 306             dlg
->message 
= gtk_label_new (NULL
); 
 307             gtk_label_set_selectable (GTK_LABEL (dlg
->message
), TRUE
); 
 308             gtk_label_set_line_wrap (GTK_LABEL (dlg
->message
), TRUE
); 
 309             gtk_label_set_justify (GTK_LABEL (dlg
->message
), GTK_JUSTIFY_LEFT
); 
 310             gtk_widget_set_size_request (GTK_WIDGET(dlg
->message
), 450, -1); 
 312             gtk_box_pack_end (GTK_BOX(vbox2
), GTK_WIDGET(dlg
->message
), TRUE
, TRUE
, 8); 
 315         /* add the expander */ 
 316         dlg
->expander 
= gtk_expander_new_with_mnemonic ("Back_trace:"); 
 317         gtk_box_pack_start (GTK_BOX(vbox
), dlg
->expander
, TRUE
, TRUE
, 0); 
 318         g_signal_connect (GTK_EXPANDER(dlg
->expander
), "activate", 
 319                             G_CALLBACK(gtk_assert_dialog_expander_callback
), dlg
); 
 323         GtkWidget 
*hbox
, *vbox
, *button
, *sw
; 
 325         /* create expander's vbox */ 
 326         vbox 
= gtk_vbox_new (FALSE
, 0); 
 327         gtk_container_add (GTK_CONTAINER (dlg
->expander
), vbox
); 
 329         /* add a scrollable window under the expander */ 
 330         sw 
= gtk_scrolled_window_new (NULL
, NULL
); 
 331         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw
), GTK_SHADOW_ETCHED_IN
); 
 332         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw
), GTK_POLICY_AUTOMATIC
, 
 333                                         GTK_POLICY_AUTOMATIC
); 
 334         gtk_box_pack_start (GTK_BOX(vbox
), sw
, TRUE
, TRUE
, 8); 
 336         /* add the treeview to the scrollable window */ 
 337         dlg
->treeview 
= gtk_assert_dialog_create_backtrace_list_model (); 
 338         gtk_widget_set_size_request (GTK_WIDGET(dlg
->treeview
), -1, 180); 
 339         gtk_container_add (GTK_CONTAINER (sw
), dlg
->treeview
); 
 341         /* create button's hbox */ 
 342         hbox 
= gtk_hbutton_box_new (); 
 343         gtk_box_pack_end (GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0); 
 344         gtk_button_box_set_layout (GTK_BUTTON_BOX(hbox
), GTK_BUTTONBOX_END
); 
 346         /* add the buttons */ 
 347         button 
= gtk_assert_dialog_add_button_to (GTK_BOX(hbox
), "Save to _file", 
 348                                                 GTK_STOCK_SAVE
, GTK_RESPONSE_NONE
); 
 349         g_signal_connect (button
, "clicked", 
 350                             G_CALLBACK(gtk_assert_dialog_save_backtrace_callback
), dlg
); 
 352         button 
= gtk_assert_dialog_add_button_to (GTK_BOX(hbox
), "Copy to clip_board", 
 353                                                   GTK_STOCK_COPY
, GTK_RESPONSE_NONE
); 
 354         g_signal_connect (button
, "clicked", G_CALLBACK(gtk_assert_dialog_copy_callback
), dlg
); 
 357     /* add the checkbutton */ 
 358     dlg
->shownexttime 
= gtk_check_button_new_with_mnemonic("Show this _dialog the next time"); 
 359     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(dlg
->shownexttime
), TRUE
); 
 360     gtk_box_pack_end (GTK_BOX(GTK_DIALOG(dlg
)->action_area
), dlg
->shownexttime
, FALSE
, TRUE
, 8); 
 362     /* add the stop button */ 
 363     gtk_assert_dialog_add_button (dlg
, "_Stop", GTK_STOCK_QUIT
, GTK_ASSERT_DIALOG_STOP
); 
 365     /* add the continue button */ 
 366     continuebtn 
= gtk_assert_dialog_add_button (dlg
, "_Continue", GTK_STOCK_YES
, GTK_ASSERT_DIALOG_CONTINUE
); 
 367     gtk_dialog_set_default_response (GTK_DIALOG (dlg
), GTK_ASSERT_DIALOG_CONTINUE
); 
 368     g_signal_connect (continuebtn
, "clicked", G_CALLBACK(gtk_assert_dialog_continue_callback
), dlg
); 
 370     /* complete creation */ 
 371     dlg
->callback 
= NULL
; 
 372     dlg
->userdata 
= NULL
; 
 374     /* the resizeable property of this window is modified by the expander: 
 375        when it's collapsed, the window must be non-resizeable! */ 
 376     gtk_window_set_resizable (GTK_WINDOW (dlg
), FALSE
); 
 377     gtk_widget_pop_composite_child (); 
 378     gtk_widget_show_all (GTK_WIDGET(dlg
)); 
 383 /* ---------------------------------------------------------------------------- 
 384    GtkAssertDialog public API 
 385  ---------------------------------------------------------------------------- */ 
 387 gchar 
*gtk_assert_dialog_get_message (GtkAssertDialog 
*dlg
) 
 390        1) returned string must g_free()d ! 
 391        2) Pango markup is automatically stripped off by GTK 
 393     return g_strdup (gtk_label_get_text (GTK_LABEL(dlg
->message
))); 
 396 gchar 
*gtk_assert_dialog_get_backtrace (GtkAssertDialog 
*dlg
) 
 398     gchar 
*function
, *arguments
, *sourcefile
, *linenum
; 
 405     g_return_val_if_fail (GTK_IS_ASSERT_DIALOG (dlg
), NULL
); 
 406     model 
= gtk_tree_view_get_model (GTK_TREE_VIEW(dlg
->treeview
)); 
 407     string 
= g_string_new(""); 
 409     /* iterate over the list */ 
 410     if (!gtk_tree_model_get_iter_first (model
, &iter
)) 
 415         /* append this stack frame's info to the string */ 
 416         gtk_tree_model_get (model
, &iter
, 
 417                             STACKFRAME_LEVEL_COLIDX
, &count
, 
 418                             FUNCTION_NAME_COLIDX
, &function
, 
 419                             FUNCTION_ARGS_COLIDX
, &arguments
, 
 420                             SOURCE_FILE_COLIDX
, &sourcefile
, 
 421                             LINE_NUMBER_COLIDX
, &linenum
, 
 424         g_string_append_printf (string
, "[%u] %s(%s)", 
 425                                 count
, function
, arguments
); 
 426         if (sourcefile
[0] != '\0') 
 427             g_string_append_printf (string
, " %s", sourcefile
); 
 428         if (linenum
[0] != '\0') 
 429             g_string_append_printf (string
, ":%s", linenum
); 
 430         g_string_append (string
, "\n"); 
 437     } while (gtk_tree_model_iter_next (model
, &iter
)); 
 439     /* returned string must g_free()d */ 
 440     return g_string_free (string
, FALSE
); 
 443 void gtk_assert_dialog_set_message(GtkAssertDialog 
*dlg
, const gchar 
*msg
) 
 445     /* prepend and append the <b> tag 
 446        NOTE: g_markup_printf_escaped() is not used because it's available 
 447              only for glib >= 2.4 */ 
 448     gchar 
*escaped_msg 
= g_markup_escape_text (msg
, -1); 
 449     gchar 
*decorated_msg 
= g_strdup_printf ("<b>%s</b>", escaped_msg
); 
 451     g_return_if_fail (GTK_IS_ASSERT_DIALOG (dlg
)); 
 452     gtk_label_set_markup (GTK_LABEL(dlg
->message
), decorated_msg
); 
 454     g_free (decorated_msg
); 
 455     g_free (escaped_msg
); 
 458 void gtk_assert_dialog_set_backtrace_callback(GtkAssertDialog 
*assertdlg
, 
 459                                               GtkAssertDialogStackFrameCallback callback
, 
 462     assertdlg
->callback 
= callback
; 
 463     assertdlg
->userdata 
= userdata
; 
 466 void gtk_assert_dialog_append_stack_frame(GtkAssertDialog 
*dlg
, 
 467                                           const gchar 
*function
, 
 468                                           const gchar 
*arguments
, 
 469                                           const gchar 
*sourcefile
, 
 477     g_return_if_fail (GTK_IS_ASSERT_DIALOG (dlg
)); 
 478     model 
= gtk_tree_view_get_model (GTK_TREE_VIEW(dlg
->treeview
)); 
 480     /* how many items are in the list up to now ? */ 
 481     count 
= gtk_tree_model_iter_n_children (model
, NULL
); 
 483     linenum 
= g_string_new(""); 
 484     if ( line_number 
!= 0 ) 
 485         g_string_printf (linenum
, "%u", line_number
); 
 487     /* add data to the list store */ 
 488     gtk_list_store_append (GTK_LIST_STORE(model
), &iter
); 
 489     gtk_list_store_set (GTK_LIST_STORE(model
), &iter
, 
 490                         STACKFRAME_LEVEL_COLIDX
, count
+1,     /* start from 1 and not from 0 */ 
 491                         FUNCTION_NAME_COLIDX
, function
, 
 492                         FUNCTION_ARGS_COLIDX
, arguments
, 
 493                         SOURCE_FILE_COLIDX
, sourcefile
, 
 494                         LINE_NUMBER_COLIDX
, linenum
->str
, 
 497     g_string_free (linenum
, TRUE
); 
 500 GtkWidget 
*gtk_assert_dialog_new(void) 
 502     GtkAssertDialog 
*dialog 
= g_object_new (GTK_TYPE_ASSERT_DIALOG
, NULL
); 
 504     return GTK_WIDGET (dialog
); 
 509 #endif /* __cplusplus */