|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include <dlfcn.h> |
|
7 #include <fcntl.h> |
|
8 #include <glib.h> |
|
9 #include <gtk/gtk.h> |
|
10 #include <string.h> |
|
11 |
|
12 #include <cctype> |
|
13 |
|
14 #include "mozilla/NullPtr.h" |
|
15 #include "crashreporter.h" |
|
16 #include "crashreporter_gtk_common.h" |
|
17 |
|
18 using std::string; |
|
19 using std::vector; |
|
20 |
|
21 using namespace CrashReporter; |
|
22 |
|
23 static GtkWidget* gViewReportButton = 0; |
|
24 static GtkWidget* gCommentTextLabel = 0; |
|
25 static GtkWidget* gCommentText = 0; |
|
26 static GtkWidget* gEmailMeCheck = 0; |
|
27 static GtkWidget* gEmailEntryLabel = 0; |
|
28 static GtkWidget* gEmailEntry = 0; |
|
29 |
|
30 static bool gEmailFieldHint = true; |
|
31 static bool gCommentFieldHint = true; |
|
32 |
|
33 // handle from dlopen'ing libgnome |
|
34 static void* gnomeLib = nullptr; |
|
35 // handle from dlopen'ing libgnomeui |
|
36 static void* gnomeuiLib = nullptr; |
|
37 |
|
38 static void LoadSettings() |
|
39 { |
|
40 /* |
|
41 * NOTE! This code needs to stay in sync with the preference checking |
|
42 * code in in nsExceptionHandler.cpp. |
|
43 */ |
|
44 |
|
45 StringTable settings; |
|
46 if (ReadStringsFromFile(gSettingsPath + "/" + kIniFile, settings, true)) { |
|
47 if (settings.find("Email") != settings.end()) { |
|
48 gtk_entry_set_text(GTK_ENTRY(gEmailEntry), settings["Email"].c_str()); |
|
49 gEmailFieldHint = false; |
|
50 } |
|
51 if (settings.find("EmailMe") != settings.end()) { |
|
52 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gEmailMeCheck), |
|
53 settings["EmailMe"][0] != '0'); |
|
54 } |
|
55 if (settings.find("IncludeURL") != settings.end() && |
|
56 gIncludeURLCheck != 0) { |
|
57 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck), |
|
58 settings["IncludeURL"][0] != '0'); |
|
59 } |
|
60 bool enabled; |
|
61 if (settings.find("SubmitReport") != settings.end()) |
|
62 enabled = settings["SubmitReport"][0] != '0'; |
|
63 else |
|
64 enabled = ShouldEnableSending(); |
|
65 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck), |
|
66 enabled); |
|
67 } |
|
68 } |
|
69 |
|
70 void SaveSettings() |
|
71 { |
|
72 /* |
|
73 * NOTE! This code needs to stay in sync with the preference setting |
|
74 * code in in nsExceptionHandler.cpp. |
|
75 */ |
|
76 |
|
77 StringTable settings; |
|
78 |
|
79 ReadStringsFromFile(gSettingsPath + "/" + kIniFile, settings, true); |
|
80 if (!gEmailFieldHint) |
|
81 settings["Email"] = gtk_entry_get_text(GTK_ENTRY(gEmailEntry)); |
|
82 else |
|
83 settings.erase("Email"); |
|
84 |
|
85 settings["EmailMe"] = |
|
86 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gEmailMeCheck)) ? "1" : "0"; |
|
87 if (gIncludeURLCheck != 0) |
|
88 settings["IncludeURL"] = |
|
89 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck)) |
|
90 ? "1" : "0"; |
|
91 settings["SubmitReport"] = |
|
92 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck)) |
|
93 ? "1" : "0"; |
|
94 |
|
95 WriteStringsToFile(gSettingsPath + "/" + kIniFile, |
|
96 "Crash Reporter", settings, true); |
|
97 } |
|
98 |
|
99 void SendReport() |
|
100 { |
|
101 // disable all our gui controls, show the throbber + change the progress text |
|
102 gtk_widget_set_sensitive(gSubmitReportCheck, FALSE); |
|
103 gtk_widget_set_sensitive(gViewReportButton, FALSE); |
|
104 gtk_widget_set_sensitive(gCommentText, FALSE); |
|
105 if (gIncludeURLCheck) |
|
106 gtk_widget_set_sensitive(gIncludeURLCheck, FALSE); |
|
107 gtk_widget_set_sensitive(gEmailMeCheck, FALSE); |
|
108 gtk_widget_set_sensitive(gEmailEntry, FALSE); |
|
109 gtk_widget_set_sensitive(gCloseButton, FALSE); |
|
110 if (gRestartButton) |
|
111 gtk_widget_set_sensitive(gRestartButton, FALSE); |
|
112 gtk_widget_show_all(gThrobber); |
|
113 gtk_label_set_text(GTK_LABEL(gProgressLabel), |
|
114 gStrings[ST_REPORTDURINGSUBMIT].c_str()); |
|
115 |
|
116 #ifdef MOZ_ENABLE_GCONF |
|
117 LoadProxyinfo(); |
|
118 #endif |
|
119 |
|
120 // and spawn a thread to do the sending |
|
121 GError* err; |
|
122 gSendThreadID = g_thread_create(SendThread, nullptr, TRUE, &err); |
|
123 } |
|
124 |
|
125 static void ShowReportInfo(GtkTextView* viewReportTextView) |
|
126 { |
|
127 GtkTextBuffer* buffer = |
|
128 gtk_text_view_get_buffer(viewReportTextView); |
|
129 |
|
130 GtkTextIter start, end; |
|
131 gtk_text_buffer_get_start_iter(buffer, &start); |
|
132 gtk_text_buffer_get_end_iter(buffer, &end); |
|
133 |
|
134 gtk_text_buffer_delete(buffer, &start, &end); |
|
135 |
|
136 for (StringTable::iterator iter = gQueryParameters.begin(); |
|
137 iter != gQueryParameters.end(); |
|
138 iter++) { |
|
139 gtk_text_buffer_insert(buffer, &end, iter->first.c_str(), -1); |
|
140 gtk_text_buffer_insert(buffer, &end, ": ", -1); |
|
141 gtk_text_buffer_insert(buffer, &end, iter->second.c_str(), -1); |
|
142 gtk_text_buffer_insert(buffer, &end, "\n", -1); |
|
143 } |
|
144 |
|
145 gtk_text_buffer_insert(buffer, &end, "\n", -1); |
|
146 gtk_text_buffer_insert(buffer, &end, |
|
147 gStrings[ST_EXTRAREPORTINFO].c_str(), -1); |
|
148 } |
|
149 |
|
150 void UpdateSubmit() |
|
151 { |
|
152 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) { |
|
153 gtk_widget_set_sensitive(gViewReportButton, TRUE); |
|
154 gtk_widget_set_sensitive(gCommentText, TRUE); |
|
155 if (gIncludeURLCheck) |
|
156 gtk_widget_set_sensitive(gIncludeURLCheck, TRUE); |
|
157 gtk_widget_set_sensitive(gEmailMeCheck, TRUE); |
|
158 gtk_widget_set_sensitive(gEmailEntry, |
|
159 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gEmailMeCheck))); |
|
160 gtk_label_set_text(GTK_LABEL(gProgressLabel), |
|
161 gStrings[ST_REPORTPRESUBMIT].c_str()); |
|
162 } else { |
|
163 gtk_widget_set_sensitive(gViewReportButton, FALSE); |
|
164 gtk_widget_set_sensitive(gCommentText, FALSE); |
|
165 if (gIncludeURLCheck) |
|
166 gtk_widget_set_sensitive(gIncludeURLCheck, FALSE); |
|
167 gtk_widget_set_sensitive(gEmailMeCheck, FALSE); |
|
168 gtk_widget_set_sensitive(gEmailEntry, FALSE); |
|
169 gtk_label_set_text(GTK_LABEL(gProgressLabel), ""); |
|
170 } |
|
171 } |
|
172 |
|
173 static void ViewReportClicked(GtkButton* button, |
|
174 gpointer userData) |
|
175 { |
|
176 GtkDialog* dialog = |
|
177 GTK_DIALOG(gtk_dialog_new_with_buttons(gStrings[ST_VIEWREPORTTITLE].c_str(), |
|
178 GTK_WINDOW(gWindow), |
|
179 GTK_DIALOG_MODAL, |
|
180 GTK_STOCK_OK, |
|
181 GTK_RESPONSE_OK, |
|
182 nullptr)); |
|
183 |
|
184 GtkWidget* scrolled = gtk_scrolled_window_new(0, 0); |
|
185 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(dialog)), scrolled); |
|
186 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), |
|
187 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); |
|
188 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), |
|
189 GTK_SHADOW_IN); |
|
190 |
|
191 GtkWidget* viewReportTextView = gtk_text_view_new(); |
|
192 gtk_container_add(GTK_CONTAINER(scrolled), viewReportTextView); |
|
193 gtk_text_view_set_editable(GTK_TEXT_VIEW(viewReportTextView), FALSE); |
|
194 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(viewReportTextView), |
|
195 GTK_WRAP_WORD); |
|
196 gtk_widget_set_size_request(GTK_WIDGET(viewReportTextView), -1, 100); |
|
197 |
|
198 ShowReportInfo(GTK_TEXT_VIEW(viewReportTextView)); |
|
199 |
|
200 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK); |
|
201 gtk_widget_set_size_request(GTK_WIDGET(dialog), 400, 200); |
|
202 gtk_widget_show_all(GTK_WIDGET(dialog)); |
|
203 gtk_dialog_run(dialog); |
|
204 gtk_widget_destroy(GTK_WIDGET(dialog)); |
|
205 } |
|
206 |
|
207 static void CommentChanged(GtkTextBuffer* buffer, gpointer userData) |
|
208 { |
|
209 GtkTextIter start, end; |
|
210 gtk_text_buffer_get_start_iter(buffer, &start); |
|
211 gtk_text_buffer_get_end_iter(buffer, &end); |
|
212 const char* comment = gtk_text_buffer_get_text(buffer, &start, &end, TRUE); |
|
213 if (comment[0] == '\0' || gCommentFieldHint) |
|
214 gQueryParameters.erase("Comments"); |
|
215 else |
|
216 gQueryParameters["Comments"] = comment; |
|
217 } |
|
218 |
|
219 static void CommentInsert(GtkTextBuffer* buffer, |
|
220 GtkTextIter* location, |
|
221 gchar* text, |
|
222 gint len, |
|
223 gpointer userData) |
|
224 { |
|
225 GtkTextIter start, end; |
|
226 gtk_text_buffer_get_start_iter(buffer, &start); |
|
227 gtk_text_buffer_get_end_iter(buffer, &end); |
|
228 const char* comment = gtk_text_buffer_get_text(buffer, &start, &end, TRUE); |
|
229 |
|
230 // limit to 500 bytes in utf-8 |
|
231 if (strlen(comment) + len > MAX_COMMENT_LENGTH) { |
|
232 g_signal_stop_emission_by_name(buffer, "insert-text"); |
|
233 } |
|
234 } |
|
235 |
|
236 static void UpdateHintText(GtkWidget* widget, gboolean gainedFocus, |
|
237 bool* hintShowing, const char* hintText) |
|
238 { |
|
239 GtkTextBuffer* buffer = nullptr; |
|
240 if (GTK_IS_TEXT_VIEW(widget)) |
|
241 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); |
|
242 |
|
243 if (gainedFocus) { |
|
244 if (*hintShowing) { |
|
245 if (buffer == nullptr) { // sort of cheating |
|
246 gtk_entry_set_text(GTK_ENTRY(widget), ""); |
|
247 } |
|
248 else { // GtkTextView |
|
249 gtk_text_buffer_set_text(buffer, "", 0); |
|
250 } |
|
251 gtk_widget_modify_text(widget, GTK_STATE_NORMAL, nullptr); |
|
252 *hintShowing = false; |
|
253 } |
|
254 } |
|
255 else { |
|
256 // lost focus |
|
257 const char* text = nullptr; |
|
258 if (buffer == nullptr) { |
|
259 text = gtk_entry_get_text(GTK_ENTRY(widget)); |
|
260 } |
|
261 else { |
|
262 GtkTextIter start, end; |
|
263 gtk_text_buffer_get_start_iter(buffer, &start); |
|
264 gtk_text_buffer_get_end_iter(buffer, &end); |
|
265 text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE); |
|
266 } |
|
267 |
|
268 if (text == nullptr || text[0] == '\0') { |
|
269 *hintShowing = true; |
|
270 |
|
271 if (buffer == nullptr) { |
|
272 gtk_entry_set_text(GTK_ENTRY(widget), hintText); |
|
273 } |
|
274 else { |
|
275 gtk_text_buffer_set_text(buffer, hintText, -1); |
|
276 } |
|
277 |
|
278 gtk_widget_modify_text(widget, GTK_STATE_NORMAL, |
|
279 >k_widget_get_style(widget)->text[GTK_STATE_INSENSITIVE]); |
|
280 } |
|
281 } |
|
282 } |
|
283 |
|
284 static gboolean CommentFocusChange(GtkWidget* widget, GdkEventFocus* event, |
|
285 gpointer userData) |
|
286 { |
|
287 UpdateHintText(widget, event->in, &gCommentFieldHint, |
|
288 gStrings[ST_COMMENTGRAYTEXT].c_str()); |
|
289 |
|
290 return FALSE; |
|
291 } |
|
292 |
|
293 static void UpdateEmail() |
|
294 { |
|
295 const char* email = gtk_entry_get_text(GTK_ENTRY(gEmailEntry)); |
|
296 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gEmailMeCheck))) { |
|
297 gtk_widget_set_sensitive(gEmailEntry, TRUE); |
|
298 } else { |
|
299 email = ""; |
|
300 gtk_widget_set_sensitive(gEmailEntry, FALSE); |
|
301 } |
|
302 if (email[0] == '\0' || gEmailFieldHint) |
|
303 gQueryParameters.erase("Email"); |
|
304 else |
|
305 gQueryParameters["Email"] = email; |
|
306 } |
|
307 |
|
308 static void EmailMeClicked(GtkButton* sender, gpointer userData) |
|
309 { |
|
310 UpdateEmail(); |
|
311 } |
|
312 |
|
313 static void EmailChanged(GtkEditable* editable, gpointer userData) |
|
314 { |
|
315 UpdateEmail(); |
|
316 } |
|
317 |
|
318 static gboolean EmailFocusChange(GtkWidget* widget, GdkEventFocus* event, |
|
319 gpointer userData) |
|
320 { |
|
321 UpdateHintText(widget, event->in, &gEmailFieldHint, |
|
322 gStrings[ST_EMAILGRAYTEXT].c_str()); |
|
323 |
|
324 return FALSE; |
|
325 } |
|
326 |
|
327 typedef struct _GnomeProgram GnomeProgram; |
|
328 typedef struct _GnomeModuleInfo GnomeModuleInfo; |
|
329 typedef GnomeProgram * (*_gnome_program_init_fn)(const char *, const char *, |
|
330 const GnomeModuleInfo *, int, |
|
331 char **, const char *, ...); |
|
332 typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)(); |
|
333 |
|
334 void TryInitGnome() |
|
335 { |
|
336 gnomeLib = dlopen("libgnome-2.so.0", RTLD_LAZY); |
|
337 if (!gnomeLib) |
|
338 return; |
|
339 |
|
340 gnomeuiLib = dlopen("libgnomeui-2.so.0", RTLD_LAZY); |
|
341 if (!gnomeuiLib) |
|
342 return; |
|
343 |
|
344 _gnome_program_init_fn gnome_program_init = |
|
345 (_gnome_program_init_fn)(dlsym(gnomeLib, "gnome_program_init")); |
|
346 _libgnomeui_module_info_get_fn libgnomeui_module_info_get = |
|
347 (_libgnomeui_module_info_get_fn)(dlsym(gnomeuiLib, "libgnomeui_module_info_get")); |
|
348 |
|
349 if (gnome_program_init && libgnomeui_module_info_get) { |
|
350 gnome_program_init("crashreporter", "1.0", libgnomeui_module_info_get(), |
|
351 gArgc, gArgv, nullptr); |
|
352 } |
|
353 |
|
354 } |
|
355 |
|
356 /* === Crashreporter UI Functions === */ |
|
357 |
|
358 /* |
|
359 * Anything not listed here is in crashreporter_gtk_common.cpp: |
|
360 * UIInit |
|
361 * UIShowDefaultUI |
|
362 * UIError_impl |
|
363 * UIGetIniPath |
|
364 * UIGetSettingsPath |
|
365 * UIEnsurePathExists |
|
366 * UIFileExists |
|
367 * UIMoveFile |
|
368 * UIDeleteFile |
|
369 * UIOpenRead |
|
370 * UIOpenWrite |
|
371 */ |
|
372 |
|
373 void UIShutdown() |
|
374 { |
|
375 if (gnomeuiLib) |
|
376 dlclose(gnomeuiLib); |
|
377 // Don't dlclose gnomeLib as libgnomevfs and libORBit-2 use atexit(). |
|
378 } |
|
379 |
|
380 bool UIShowCrashUI(const string& dumpfile, |
|
381 const StringTable& queryParameters, |
|
382 const string& sendURL, |
|
383 const vector<string>& restartArgs) |
|
384 { |
|
385 gDumpFile = dumpfile; |
|
386 gQueryParameters = queryParameters; |
|
387 gSendURL = sendURL; |
|
388 gRestartArgs = restartArgs; |
|
389 if (gQueryParameters.find("URL") != gQueryParameters.end()) |
|
390 gURLParameter = gQueryParameters["URL"]; |
|
391 |
|
392 gWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
|
393 gtk_window_set_title(GTK_WINDOW(gWindow), |
|
394 gStrings[ST_CRASHREPORTERTITLE].c_str()); |
|
395 gtk_window_set_resizable(GTK_WINDOW(gWindow), FALSE); |
|
396 gtk_window_set_position(GTK_WINDOW(gWindow), GTK_WIN_POS_CENTER); |
|
397 gtk_container_set_border_width(GTK_CONTAINER(gWindow), 12); |
|
398 g_signal_connect(gWindow, "delete-event", G_CALLBACK(WindowDeleted), 0); |
|
399 g_signal_connect(gWindow, "key_press_event", G_CALLBACK(check_escape), nullptr); |
|
400 |
|
401 GtkWidget* vbox = gtk_vbox_new(FALSE, 6); |
|
402 gtk_container_add(GTK_CONTAINER(gWindow), vbox); |
|
403 |
|
404 GtkWidget* titleLabel = gtk_label_new(""); |
|
405 gtk_box_pack_start(GTK_BOX(vbox), titleLabel, FALSE, FALSE, 0); |
|
406 gtk_misc_set_alignment(GTK_MISC(titleLabel), 0, 0.5); |
|
407 char* markup = g_strdup_printf("<b>%s</b>", |
|
408 gStrings[ST_CRASHREPORTERHEADER].c_str()); |
|
409 gtk_label_set_markup(GTK_LABEL(titleLabel), markup); |
|
410 g_free(markup); |
|
411 |
|
412 GtkWidget* descriptionLabel = |
|
413 gtk_label_new(gStrings[ST_CRASHREPORTERDESCRIPTION].c_str()); |
|
414 gtk_box_pack_start(GTK_BOX(vbox), descriptionLabel, TRUE, TRUE, 0); |
|
415 // force the label to line wrap |
|
416 gtk_widget_set_size_request(descriptionLabel, 400, -1); |
|
417 gtk_label_set_line_wrap(GTK_LABEL(descriptionLabel), TRUE); |
|
418 gtk_label_set_selectable(GTK_LABEL(descriptionLabel), TRUE); |
|
419 gtk_misc_set_alignment(GTK_MISC(descriptionLabel), 0, 0.5); |
|
420 |
|
421 // this is honestly how they suggest you indent a section |
|
422 GtkWidget* indentBox = gtk_hbox_new(FALSE, 0); |
|
423 gtk_box_pack_start(GTK_BOX(vbox), indentBox, FALSE, FALSE, 0); |
|
424 gtk_box_pack_start(GTK_BOX(indentBox), gtk_label_new(""), FALSE, FALSE, 6); |
|
425 |
|
426 GtkWidget* innerVBox1 = gtk_vbox_new(FALSE, 0); |
|
427 gtk_box_pack_start(GTK_BOX(indentBox), innerVBox1, TRUE, TRUE, 0); |
|
428 |
|
429 gSubmitReportCheck = |
|
430 gtk_check_button_new_with_label(gStrings[ST_CHECKSUBMIT].c_str()); |
|
431 gtk_box_pack_start(GTK_BOX(innerVBox1), gSubmitReportCheck, FALSE, FALSE, 0); |
|
432 g_signal_connect(gSubmitReportCheck, "clicked", |
|
433 G_CALLBACK(SubmitReportChecked), 0); |
|
434 |
|
435 // indent again, below the "submit report" checkbox |
|
436 GtkWidget* indentBox2 = gtk_hbox_new(FALSE, 0); |
|
437 gtk_box_pack_start(GTK_BOX(innerVBox1), indentBox2, FALSE, FALSE, 0); |
|
438 gtk_box_pack_start(GTK_BOX(indentBox2), gtk_label_new(""), FALSE, FALSE, 6); |
|
439 |
|
440 GtkWidget* innerVBox = gtk_vbox_new(FALSE, 0); |
|
441 gtk_box_pack_start(GTK_BOX(indentBox2), innerVBox, TRUE, TRUE, 0); |
|
442 gtk_box_set_spacing(GTK_BOX(innerVBox), 6); |
|
443 |
|
444 GtkWidget* viewReportButtonBox = gtk_hbutton_box_new(); |
|
445 gtk_box_pack_start(GTK_BOX(innerVBox), viewReportButtonBox, FALSE, FALSE, 0); |
|
446 gtk_box_set_spacing(GTK_BOX(viewReportButtonBox), 6); |
|
447 gtk_button_box_set_layout(GTK_BUTTON_BOX(viewReportButtonBox), GTK_BUTTONBOX_START); |
|
448 |
|
449 gViewReportButton = |
|
450 gtk_button_new_with_label(gStrings[ST_VIEWREPORT].c_str()); |
|
451 gtk_box_pack_start(GTK_BOX(viewReportButtonBox), gViewReportButton, FALSE, FALSE, 0); |
|
452 g_signal_connect(gViewReportButton, "clicked", G_CALLBACK(ViewReportClicked), 0); |
|
453 |
|
454 GtkWidget* scrolled = gtk_scrolled_window_new(0, 0); |
|
455 gtk_container_add(GTK_CONTAINER(innerVBox), scrolled); |
|
456 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), |
|
457 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); |
|
458 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), |
|
459 GTK_SHADOW_IN); |
|
460 |
|
461 gCommentTextLabel = gtk_label_new(gStrings[ST_COMMENTGRAYTEXT].c_str()); |
|
462 gCommentText = gtk_text_view_new(); |
|
463 gtk_label_set_mnemonic_widget(GTK_LABEL(gCommentTextLabel), gCommentText); |
|
464 gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(gCommentText), FALSE); |
|
465 g_signal_connect(gCommentText, "focus-in-event", G_CALLBACK(CommentFocusChange), 0); |
|
466 g_signal_connect(gCommentText, "focus-out-event", G_CALLBACK(CommentFocusChange), 0); |
|
467 |
|
468 GtkTextBuffer* commentBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gCommentText)); |
|
469 g_signal_connect(commentBuffer, "changed", G_CALLBACK(CommentChanged), 0); |
|
470 g_signal_connect(commentBuffer, "insert-text", G_CALLBACK(CommentInsert), 0); |
|
471 |
|
472 gtk_container_add(GTK_CONTAINER(scrolled), gCommentText); |
|
473 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gCommentText), |
|
474 GTK_WRAP_WORD); |
|
475 gtk_widget_set_size_request(GTK_WIDGET(gCommentText), -1, 100); |
|
476 |
|
477 if (gQueryParameters.find("URL") != gQueryParameters.end()) { |
|
478 gIncludeURLCheck = |
|
479 gtk_check_button_new_with_label(gStrings[ST_CHECKURL].c_str()); |
|
480 gtk_box_pack_start(GTK_BOX(innerVBox), gIncludeURLCheck, FALSE, FALSE, 0); |
|
481 g_signal_connect(gIncludeURLCheck, "clicked", G_CALLBACK(IncludeURLClicked), 0); |
|
482 // on by default |
|
483 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck), TRUE); |
|
484 } |
|
485 |
|
486 gEmailMeCheck = |
|
487 gtk_check_button_new_with_label(gStrings[ST_CHECKEMAIL].c_str()); |
|
488 gtk_box_pack_start(GTK_BOX(innerVBox), gEmailMeCheck, FALSE, FALSE, 0); |
|
489 g_signal_connect(gEmailMeCheck, "clicked", G_CALLBACK(EmailMeClicked), 0); |
|
490 |
|
491 GtkWidget* emailIndentBox = gtk_hbox_new(FALSE, 0); |
|
492 gtk_box_pack_start(GTK_BOX(innerVBox), emailIndentBox, FALSE, FALSE, 0); |
|
493 gtk_box_pack_start(GTK_BOX(emailIndentBox), gtk_label_new(""), |
|
494 FALSE, FALSE, 9); |
|
495 |
|
496 gEmailEntryLabel = gtk_label_new(gStrings[ST_EMAILGRAYTEXT].c_str()); |
|
497 gEmailEntry = gtk_entry_new(); |
|
498 gtk_label_set_mnemonic_widget(GTK_LABEL(gEmailEntryLabel), gEmailEntry); |
|
499 gtk_box_pack_start(GTK_BOX(emailIndentBox), gEmailEntry, TRUE, TRUE, 0); |
|
500 g_signal_connect(gEmailEntry, "changed", G_CALLBACK(EmailChanged), 0); |
|
501 g_signal_connect(gEmailEntry, "focus-in-event", G_CALLBACK(EmailFocusChange), 0); |
|
502 g_signal_connect(gEmailEntry, "focus-out-event", G_CALLBACK(EmailFocusChange), 0); |
|
503 |
|
504 GtkWidget* progressBox = gtk_hbox_new(FALSE, 6); |
|
505 gtk_box_pack_start(GTK_BOX(vbox), progressBox, TRUE, TRUE, 0); |
|
506 |
|
507 // Get the throbber image from alongside the executable |
|
508 char* dir = g_path_get_dirname(gArgv[0]); |
|
509 char* path = g_build_filename(dir, "Throbber-small.gif", nullptr); |
|
510 g_free(dir); |
|
511 gThrobber = gtk_image_new_from_file(path); |
|
512 gtk_box_pack_start(GTK_BOX(progressBox), gThrobber, FALSE, FALSE, 0); |
|
513 |
|
514 gProgressLabel = |
|
515 gtk_label_new(gStrings[ST_REPORTPRESUBMIT].c_str()); |
|
516 gtk_box_pack_start(GTK_BOX(progressBox), gProgressLabel, TRUE, TRUE, 0); |
|
517 // force the label to line wrap |
|
518 gtk_widget_set_size_request(gProgressLabel, 400, -1); |
|
519 gtk_label_set_line_wrap(GTK_LABEL(gProgressLabel), TRUE); |
|
520 |
|
521 GtkWidget* buttonBox = gtk_hbutton_box_new(); |
|
522 gtk_box_pack_end(GTK_BOX(vbox), buttonBox, FALSE, FALSE, 0); |
|
523 gtk_box_set_spacing(GTK_BOX(buttonBox), 6); |
|
524 gtk_button_box_set_layout(GTK_BUTTON_BOX(buttonBox), GTK_BUTTONBOX_END); |
|
525 |
|
526 gCloseButton = |
|
527 gtk_button_new_with_label(gStrings[ST_QUIT].c_str()); |
|
528 gtk_box_pack_start(GTK_BOX(buttonBox), gCloseButton, FALSE, FALSE, 0); |
|
529 gtk_widget_set_can_default(gCloseButton, TRUE); |
|
530 g_signal_connect(gCloseButton, "clicked", G_CALLBACK(CloseClicked), 0); |
|
531 |
|
532 gRestartButton = 0; |
|
533 if (restartArgs.size() > 0) { |
|
534 gRestartButton = gtk_button_new_with_label(gStrings[ST_RESTART].c_str()); |
|
535 gtk_box_pack_start(GTK_BOX(buttonBox), gRestartButton, FALSE, FALSE, 0); |
|
536 gtk_widget_set_can_default(gRestartButton, TRUE); |
|
537 g_signal_connect(gRestartButton, "clicked", G_CALLBACK(RestartClicked), 0); |
|
538 } |
|
539 |
|
540 gtk_widget_grab_focus(gSubmitReportCheck); |
|
541 |
|
542 gtk_widget_grab_default(gRestartButton ? gRestartButton : gCloseButton); |
|
543 |
|
544 LoadSettings(); |
|
545 |
|
546 UpdateEmail(); |
|
547 UpdateSubmit(); |
|
548 |
|
549 UpdateHintText(gCommentText, FALSE, &gCommentFieldHint, |
|
550 gStrings[ST_COMMENTGRAYTEXT].c_str()); |
|
551 UpdateHintText(gEmailEntry, FALSE, &gEmailFieldHint, |
|
552 gStrings[ST_EMAILGRAYTEXT].c_str()); |
|
553 |
|
554 gtk_widget_show_all(gWindow); |
|
555 // stick this here to avoid the show_all above... |
|
556 gtk_widget_hide(gThrobber); |
|
557 |
|
558 gtk_main(); |
|
559 |
|
560 return gDidTrySend; |
|
561 } |