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