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