Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / widget / gtk / nsPrintDialogGTK.cpp
blob66c359d2765f2c91823b96f0192228a66d8a621c
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/. */
6 #include <gtk/gtk.h>
7 #include <gtk/gtkunixprint.h>
8 #include <stdlib.h>
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/Services.h"
13 #include "MozContainer.h"
14 #include "nsIPrintSettings.h"
15 #include "nsIWidget.h"
16 #include "nsPrintDialogGTK.h"
17 #include "nsPrintSettingsGTK.h"
18 #include "nsString.h"
19 #include "nsReadableUtils.h"
20 #include "nsIStringBundle.h"
21 #include "nsIPrintSettingsService.h"
22 #include "nsPIDOMWindow.h"
23 #include "nsPrintfCString.h"
24 #include "nsIGIOService.h"
25 #include "nsServiceManagerUtils.h"
26 #include "WidgetUtils.h"
27 #include "WidgetUtilsGtk.h"
28 #include "nsIObserverService.h"
30 // for gdk_x11_window_get_xid
31 #include <gdk/gdk.h>
32 #ifdef MOZ_X11
33 # include <gdk/gdkx.h>
34 #endif
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <gio/gunixfdlist.h>
40 // for dlsym
41 #include <dlfcn.h>
42 #include "MainThreadUtils.h"
44 using namespace mozilla;
45 using namespace mozilla::widget;
47 static const char header_footer_tags[][4] = {"", "&T", "&U", "&D", "&P", "&PT"};
49 #define CUSTOM_VALUE_INDEX gint(std::size(header_footer_tags))
51 static GtkWindow* get_gtk_window_for_nsiwidget(nsIWidget* widget) {
52 return GTK_WINDOW(widget->GetNativeData(NS_NATIVE_SHELLWIDGET));
55 static void ShowCustomDialog(GtkComboBox* changed_box, gpointer user_data) {
56 if (gtk_combo_box_get_active(changed_box) != CUSTOM_VALUE_INDEX) {
57 g_object_set_data(G_OBJECT(changed_box), "previous-active",
58 GINT_TO_POINTER(gtk_combo_box_get_active(changed_box)));
59 return;
62 GtkWindow* printDialog = GTK_WINDOW(user_data);
63 nsCOMPtr<nsIStringBundleService> bundleSvc =
64 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
66 nsCOMPtr<nsIStringBundle> printBundle;
67 bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties",
68 getter_AddRefs(printBundle));
69 nsAutoString intlString;
71 printBundle->GetStringFromName("headerFooterCustom", intlString);
72 GtkWidget* prompt_dialog = gtk_dialog_new_with_buttons(
73 NS_ConvertUTF16toUTF8(intlString).get(), printDialog,
74 (GtkDialogFlags)(GTK_DIALOG_MODAL), GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
75 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, nullptr);
76 gtk_dialog_set_default_response(GTK_DIALOG(prompt_dialog),
77 GTK_RESPONSE_ACCEPT);
78 gtk_dialog_set_alternative_button_order(
79 GTK_DIALOG(prompt_dialog), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_REJECT, -1);
81 printBundle->GetStringFromName("customHeaderFooterPrompt", intlString);
82 GtkWidget* custom_label =
83 gtk_label_new(NS_ConvertUTF16toUTF8(intlString).get());
84 GtkWidget* custom_entry = gtk_entry_new();
85 GtkWidget* question_icon =
86 gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
88 // To be convenient, prefill the textbox with the existing value, if any, and
89 // select it all so they can easily both edit it and type in a new one.
90 const char* current_text =
91 (const char*)g_object_get_data(G_OBJECT(changed_box), "custom-text");
92 if (current_text) {
93 gtk_entry_set_text(GTK_ENTRY(custom_entry), current_text);
94 gtk_editable_select_region(GTK_EDITABLE(custom_entry), 0, -1);
96 gtk_entry_set_activates_default(GTK_ENTRY(custom_entry), TRUE);
98 GtkWidget* custom_vbox = gtk_vbox_new(TRUE, 2);
99 gtk_box_pack_start(GTK_BOX(custom_vbox), custom_label, FALSE, FALSE, 0);
100 gtk_box_pack_start(GTK_BOX(custom_vbox), custom_entry, FALSE, FALSE,
101 5); // Make entry 5px underneath label
102 GtkWidget* custom_hbox = gtk_hbox_new(FALSE, 2);
103 gtk_box_pack_start(GTK_BOX(custom_hbox), question_icon, FALSE, FALSE, 0);
104 gtk_box_pack_start(GTK_BOX(custom_hbox), custom_vbox, FALSE, FALSE,
105 10); // Make question icon 10px away from content
106 gtk_container_set_border_width(GTK_CONTAINER(custom_hbox), 2);
107 gtk_widget_show_all(custom_hbox);
109 gtk_box_pack_start(
110 GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(prompt_dialog))),
111 custom_hbox, FALSE, FALSE, 0);
112 gint diag_response = gtk_dialog_run(GTK_DIALOG(prompt_dialog));
114 if (diag_response == GTK_RESPONSE_ACCEPT) {
115 const gchar* response_text = gtk_entry_get_text(GTK_ENTRY(custom_entry));
116 g_object_set_data_full(G_OBJECT(changed_box), "custom-text",
117 strdup(response_text), (GDestroyNotify)free);
118 g_object_set_data(G_OBJECT(changed_box), "previous-active",
119 GINT_TO_POINTER(CUSTOM_VALUE_INDEX));
120 } else {
121 // Go back to the previous index
122 gint previous_active = GPOINTER_TO_INT(
123 g_object_get_data(G_OBJECT(changed_box), "previous-active"));
124 gtk_combo_box_set_active(changed_box, previous_active);
127 gtk_widget_destroy(prompt_dialog);
130 class nsPrintDialogWidgetGTK {
131 public:
132 nsPrintDialogWidgetGTK(nsPIDOMWindowOuter* aParent, bool aHaveSelection,
133 nsIPrintSettings* aPrintSettings);
134 ~nsPrintDialogWidgetGTK() { gtk_widget_destroy(dialog); }
135 NS_ConvertUTF16toUTF8 GetUTF8FromBundle(const char* aKey);
136 gint Run();
138 nsresult ImportSettings(nsIPrintSettings* aNSSettings);
139 nsresult ExportSettings(nsIPrintSettings* aNSSettings);
141 private:
142 GtkWidget* dialog;
143 GtkWidget* shrink_to_fit_toggle;
144 GtkWidget* print_bg_colors_toggle;
145 GtkWidget* print_bg_images_toggle;
146 GtkWidget* selection_only_toggle;
147 GtkWidget* header_dropdown[3]; // {left, center, right}
148 GtkWidget* footer_dropdown[3];
150 nsCOMPtr<nsIStringBundle> printBundle;
152 bool useNativeSelection;
154 GtkWidget* ConstructHeaderFooterDropdown(const char16_t* currentString);
155 const char* OptionWidgetToString(GtkWidget* dropdown);
157 /* Code to copy between GTK and NS print settings structures.
158 * In the following,
159 * "Import" means to copy from NS to GTK
160 * "Export" means to copy from GTK to NS
162 void ExportHeaderFooter(nsIPrintSettings* aNS);
165 nsPrintDialogWidgetGTK::nsPrintDialogWidgetGTK(nsPIDOMWindowOuter* aParent,
166 bool aHaveSelection,
167 nsIPrintSettings* aSettings) {
168 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aParent);
169 NS_ASSERTION(widget, "Need a widget for dialog to be modal.");
170 GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget);
171 NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal.");
173 nsCOMPtr<nsIStringBundleService> bundleSvc =
174 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
175 bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties",
176 getter_AddRefs(printBundle));
178 dialog = gtk_print_unix_dialog_new(GetUTF8FromBundle("printTitleGTK").get(),
179 gtkParent);
181 gtk_print_unix_dialog_set_manual_capabilities(
182 GTK_PRINT_UNIX_DIALOG(dialog),
183 GtkPrintCapabilities(
184 GTK_PRINT_CAPABILITY_COPIES | GTK_PRINT_CAPABILITY_COLLATE |
185 GTK_PRINT_CAPABILITY_REVERSE | GTK_PRINT_CAPABILITY_SCALE |
186 GTK_PRINT_CAPABILITY_GENERATE_PDF));
188 // The vast majority of magic numbers in this widget construction are padding.
189 // e.g. for the set_border_width below, 12px matches that of just about every
190 // other window.
191 GtkWidget* custom_options_tab = gtk_vbox_new(FALSE, 0);
192 gtk_container_set_border_width(GTK_CONTAINER(custom_options_tab), 12);
193 GtkWidget* tab_label =
194 gtk_label_new(GetUTF8FromBundle("optionsTabLabelGTK").get());
196 // Check buttons for shrink-to-fit and print selection
197 GtkWidget* check_buttons_container = gtk_vbox_new(TRUE, 2);
198 shrink_to_fit_toggle = gtk_check_button_new_with_mnemonic(
199 GetUTF8FromBundle("shrinkToFit").get());
200 gtk_box_pack_start(GTK_BOX(check_buttons_container), shrink_to_fit_toggle,
201 FALSE, FALSE, 0);
203 // GTK+2.18 and above allow us to add a "Selection" option to the main
204 // settings screen, rather than adding an option on a custom tab like we must
205 // do on older versions.
206 if (gtk_major_version > 2 ||
207 (gtk_major_version == 2 && gtk_minor_version >= 18)) {
208 useNativeSelection = true;
209 g_object_set(dialog, "support-selection", TRUE, "has-selection",
210 aHaveSelection, "embed-page-setup", TRUE, nullptr);
211 } else {
212 useNativeSelection = false;
213 selection_only_toggle = gtk_check_button_new_with_mnemonic(
214 GetUTF8FromBundle("selectionOnly").get());
215 gtk_widget_set_sensitive(selection_only_toggle, aHaveSelection);
216 gtk_box_pack_start(GTK_BOX(check_buttons_container), selection_only_toggle,
217 FALSE, FALSE, 0);
220 // Check buttons for printing background
221 GtkWidget* appearance_buttons_container = gtk_vbox_new(TRUE, 2);
222 print_bg_colors_toggle = gtk_check_button_new_with_mnemonic(
223 GetUTF8FromBundle("printBGColors").get());
224 print_bg_images_toggle = gtk_check_button_new_with_mnemonic(
225 GetUTF8FromBundle("printBGImages").get());
226 gtk_box_pack_start(GTK_BOX(appearance_buttons_container),
227 print_bg_colors_toggle, FALSE, FALSE, 0);
228 gtk_box_pack_start(GTK_BOX(appearance_buttons_container),
229 print_bg_images_toggle, FALSE, FALSE, 0);
231 // "Appearance" options label, bold and center-aligned
232 GtkWidget* appearance_label = gtk_label_new(nullptr);
233 char* pangoMarkup = g_markup_printf_escaped(
234 "<b>%s</b>", GetUTF8FromBundle("printBGOptions").get());
235 gtk_label_set_markup(GTK_LABEL(appearance_label), pangoMarkup);
236 g_free(pangoMarkup);
237 gtk_misc_set_alignment(GTK_MISC(appearance_label), 0, 0);
239 GtkWidget* appearance_container = gtk_alignment_new(0, 0, 0, 0);
240 gtk_alignment_set_padding(GTK_ALIGNMENT(appearance_container), 8, 0, 12, 0);
241 gtk_container_add(GTK_CONTAINER(appearance_container),
242 appearance_buttons_container);
244 GtkWidget* appearance_vertical_squasher = gtk_vbox_new(FALSE, 0);
245 gtk_box_pack_start(GTK_BOX(appearance_vertical_squasher), appearance_label,
246 FALSE, FALSE, 0);
247 gtk_box_pack_start(GTK_BOX(appearance_vertical_squasher),
248 appearance_container, FALSE, FALSE, 0);
250 // "Header & Footer" options label, bold and center-aligned
251 GtkWidget* header_footer_label = gtk_label_new(nullptr);
252 pangoMarkup = g_markup_printf_escaped(
253 "<b>%s</b>", GetUTF8FromBundle("headerFooter").get());
254 gtk_label_set_markup(GTK_LABEL(header_footer_label), pangoMarkup);
255 g_free(pangoMarkup);
256 gtk_misc_set_alignment(GTK_MISC(header_footer_label), 0, 0);
258 GtkWidget* header_footer_container = gtk_alignment_new(0, 0, 0, 0);
259 gtk_alignment_set_padding(GTK_ALIGNMENT(header_footer_container), 8, 0, 12,
262 // --- Table for making the header and footer options ---
263 GtkWidget* header_footer_table = gtk_table_new(3, 3, FALSE); // 3x3 table
264 nsString header_footer_str[3];
266 aSettings->GetHeaderStrLeft(header_footer_str[0]);
267 aSettings->GetHeaderStrCenter(header_footer_str[1]);
268 aSettings->GetHeaderStrRight(header_footer_str[2]);
270 for (unsigned int i = 0; i < std::size(header_dropdown); i++) {
271 header_dropdown[i] =
272 ConstructHeaderFooterDropdown(header_footer_str[i].get());
273 // Those 4 magic numbers in the middle provide the position in the table.
274 // The last two numbers mean 2 px padding on every side.
275 gtk_table_attach(GTK_TABLE(header_footer_table), header_dropdown[i], i,
276 (i + 1), 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 2,
280 const char labelKeys[][7] = {"left", "center", "right"};
281 for (unsigned int i = 0; i < std::size(labelKeys); i++) {
282 gtk_table_attach(GTK_TABLE(header_footer_table),
283 gtk_label_new(GetUTF8FromBundle(labelKeys[i]).get()), i,
284 (i + 1), 1, 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 2,
288 aSettings->GetFooterStrLeft(header_footer_str[0]);
289 aSettings->GetFooterStrCenter(header_footer_str[1]);
290 aSettings->GetFooterStrRight(header_footer_str[2]);
292 for (unsigned int i = 0; i < std::size(footer_dropdown); i++) {
293 footer_dropdown[i] =
294 ConstructHeaderFooterDropdown(header_footer_str[i].get());
295 gtk_table_attach(GTK_TABLE(header_footer_table), footer_dropdown[i], i,
296 (i + 1), 2, 3, (GtkAttachOptions)0, (GtkAttachOptions)0, 2,
299 // ---
301 gtk_container_add(GTK_CONTAINER(header_footer_container),
302 header_footer_table);
304 GtkWidget* header_footer_vertical_squasher = gtk_vbox_new(FALSE, 0);
305 gtk_box_pack_start(GTK_BOX(header_footer_vertical_squasher),
306 header_footer_label, FALSE, FALSE, 0);
307 gtk_box_pack_start(GTK_BOX(header_footer_vertical_squasher),
308 header_footer_container, FALSE, FALSE, 0);
310 // Construction of everything
311 gtk_box_pack_start(GTK_BOX(custom_options_tab), check_buttons_container,
312 FALSE, FALSE, 10); // 10px padding
313 gtk_box_pack_start(GTK_BOX(custom_options_tab), appearance_vertical_squasher,
314 FALSE, FALSE, 10);
315 gtk_box_pack_start(GTK_BOX(custom_options_tab),
316 header_footer_vertical_squasher, FALSE, FALSE, 0);
318 gtk_print_unix_dialog_add_custom_tab(GTK_PRINT_UNIX_DIALOG(dialog),
319 custom_options_tab, tab_label);
320 gtk_widget_show_all(custom_options_tab);
323 NS_ConvertUTF16toUTF8 nsPrintDialogWidgetGTK::GetUTF8FromBundle(
324 const char* aKey) {
325 nsAutoString intlString;
326 printBundle->GetStringFromName(aKey, intlString);
327 return NS_ConvertUTF16toUTF8(
328 intlString); // Return the actual object so we don't lose reference
331 const char* nsPrintDialogWidgetGTK::OptionWidgetToString(GtkWidget* dropdown) {
332 gint index = gtk_combo_box_get_active(GTK_COMBO_BOX(dropdown));
334 NS_ASSERTION(index <= CUSTOM_VALUE_INDEX,
335 "Index of dropdown is higher than expected!");
337 if (index == CUSTOM_VALUE_INDEX)
338 return (const char*)g_object_get_data(G_OBJECT(dropdown), "custom-text");
339 return header_footer_tags[index];
342 gint nsPrintDialogWidgetGTK::Run() {
343 const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
344 gtk_widget_hide(dialog);
345 return response;
348 void nsPrintDialogWidgetGTK::ExportHeaderFooter(nsIPrintSettings* aNS) {
349 const char* header_footer_str;
350 header_footer_str = OptionWidgetToString(header_dropdown[0]);
351 aNS->SetHeaderStrLeft(NS_ConvertUTF8toUTF16(header_footer_str));
353 header_footer_str = OptionWidgetToString(header_dropdown[1]);
354 aNS->SetHeaderStrCenter(NS_ConvertUTF8toUTF16(header_footer_str));
356 header_footer_str = OptionWidgetToString(header_dropdown[2]);
357 aNS->SetHeaderStrRight(NS_ConvertUTF8toUTF16(header_footer_str));
359 header_footer_str = OptionWidgetToString(footer_dropdown[0]);
360 aNS->SetFooterStrLeft(NS_ConvertUTF8toUTF16(header_footer_str));
362 header_footer_str = OptionWidgetToString(footer_dropdown[1]);
363 aNS->SetFooterStrCenter(NS_ConvertUTF8toUTF16(header_footer_str));
365 header_footer_str = OptionWidgetToString(footer_dropdown[2]);
366 aNS->SetFooterStrRight(NS_ConvertUTF8toUTF16(header_footer_str));
369 nsresult nsPrintDialogWidgetGTK::ImportSettings(nsIPrintSettings* aNSSettings) {
370 MOZ_ASSERT(aNSSettings, "aSettings must not be null");
371 NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
373 nsCOMPtr<nsPrintSettingsGTK> aNSSettingsGTK(do_QueryInterface(aNSSettings));
374 if (!aNSSettingsGTK) return NS_ERROR_FAILURE;
376 GtkPrintSettings* settings = aNSSettingsGTK->GetGtkPrintSettings();
377 GtkPageSetup* setup = aNSSettingsGTK->GetGtkPageSetup();
379 // Set our custom fields:
381 bool geckoBool;
382 aNSSettings->GetShrinkToFit(&geckoBool);
383 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shrink_to_fit_toggle),
384 geckoBool);
386 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(print_bg_colors_toggle),
387 aNSSettings->GetPrintBGColors());
389 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(print_bg_images_toggle),
390 aNSSettings->GetPrintBGImages());
392 // Temporarily set the pages-per-sheet on the GtkPrintSettings:
393 int32_t pagesPerSide = 1;
394 aNSSettings->GetNumPagesPerSheet(&pagesPerSide);
395 gtk_print_settings_set_number_up(settings, pagesPerSide);
397 gtk_print_unix_dialog_set_settings(GTK_PRINT_UNIX_DIALOG(dialog), settings);
398 gtk_print_unix_dialog_set_page_setup(GTK_PRINT_UNIX_DIALOG(dialog), setup);
400 return NS_OK;
403 nsresult nsPrintDialogWidgetGTK::ExportSettings(nsIPrintSettings* aNSSettings) {
404 MOZ_ASSERT(aNSSettings, "aSettings must not be null");
405 NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
407 GtkPrintSettings* settings =
408 gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog));
409 GtkPageSetup* setup =
410 gtk_print_unix_dialog_get_page_setup(GTK_PRINT_UNIX_DIALOG(dialog));
411 GtkPrinter* printer =
412 gtk_print_unix_dialog_get_selected_printer(GTK_PRINT_UNIX_DIALOG(dialog));
413 if (settings && setup && printer) {
414 ExportHeaderFooter(aNSSettings);
416 aNSSettings->SetOutputFormat(nsIPrintSettings::kOutputFormatNative);
418 // Print-to-file is true by default. This must be turned off or else
419 // printing won't occur! (We manually copy the spool file when this flag is
420 // set, because we love our embedders) Even if it is print-to-file in GTK's
421 // case, GTK does The Right Thing when we send the job.
422 aNSSettings->SetOutputDestination(
423 nsIPrintSettings::kOutputDestinationPrinter);
425 aNSSettings->SetShrinkToFit(
426 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shrink_to_fit_toggle)));
428 aNSSettings->SetPrintBGColors(gtk_toggle_button_get_active(
429 GTK_TOGGLE_BUTTON(print_bg_colors_toggle)));
430 aNSSettings->SetPrintBGImages(gtk_toggle_button_get_active(
431 GTK_TOGGLE_BUTTON(print_bg_images_toggle)));
433 // Let GTK deal with pages per sheet natively.
434 aNSSettings->SetNumPagesPerSheet(1);
435 // Try to save native settings in the session object
436 nsCOMPtr<nsPrintSettingsGTK> aNSSettingsGTK(do_QueryInterface(aNSSettings));
437 if (aNSSettingsGTK) {
438 aNSSettingsGTK->SetGtkPrintSettings(settings);
439 aNSSettingsGTK->SetGtkPageSetup(setup);
440 aNSSettingsGTK->SetGtkPrinter(printer);
441 bool printSelectionOnly;
442 if (useNativeSelection) {
443 _GtkPrintPages pageSetting =
444 (_GtkPrintPages)gtk_print_settings_get_print_pages(settings);
445 printSelectionOnly = (pageSetting == _GTK_PRINT_PAGES_SELECTION);
446 } else {
447 printSelectionOnly = gtk_toggle_button_get_active(
448 GTK_TOGGLE_BUTTON(selection_only_toggle));
450 aNSSettingsGTK->SetPrintSelectionOnly(printSelectionOnly);
454 if (settings) g_object_unref(settings);
455 return NS_OK;
458 GtkWidget* nsPrintDialogWidgetGTK::ConstructHeaderFooterDropdown(
459 const char16_t* currentString) {
460 GtkWidget* dropdown = gtk_combo_box_text_new();
461 const char hf_options[][22] = {"headerFooterBlank", "headerFooterTitle",
462 "headerFooterURL", "headerFooterDate",
463 "headerFooterPage", "headerFooterPageTotal",
464 "headerFooterCustom"};
466 for (unsigned int i = 0; i < std::size(hf_options); i++) {
467 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(dropdown), nullptr,
468 GetUTF8FromBundle(hf_options[i]).get());
471 bool shouldBeCustom = true;
472 NS_ConvertUTF16toUTF8 currentStringUTF8(currentString);
474 for (unsigned int i = 0; i < std::size(header_footer_tags); i++) {
475 if (!strcmp(currentStringUTF8.get(), header_footer_tags[i])) {
476 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), i);
477 g_object_set_data(G_OBJECT(dropdown), "previous-active",
478 GINT_TO_POINTER(i));
479 shouldBeCustom = false;
480 break;
484 if (shouldBeCustom) {
485 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), CUSTOM_VALUE_INDEX);
486 g_object_set_data(G_OBJECT(dropdown), "previous-active",
487 GINT_TO_POINTER(CUSTOM_VALUE_INDEX));
488 char* custom_string = strdup(currentStringUTF8.get());
489 g_object_set_data_full(G_OBJECT(dropdown), "custom-text", custom_string,
490 (GDestroyNotify)free);
493 g_signal_connect(dropdown, "changed", (GCallback)ShowCustomDialog, dialog);
494 return dropdown;
497 NS_IMPL_ISUPPORTS(nsPrintDialogServiceGTK, nsIPrintDialogService)
499 nsPrintDialogServiceGTK::nsPrintDialogServiceGTK() = default;
501 nsPrintDialogServiceGTK::~nsPrintDialogServiceGTK() = default;
503 NS_IMETHODIMP
504 nsPrintDialogServiceGTK::Init() { return NS_OK; }
506 NS_IMETHODIMP
507 nsPrintDialogServiceGTK::ShowPrintDialog(mozIDOMWindowProxy* aParent,
508 bool aHaveSelection,
509 nsIPrintSettings* aSettings) {
510 MOZ_ASSERT(aParent, "aParent must not be null");
511 MOZ_ASSERT(aSettings, "aSettings must not be null");
513 nsPrintDialogWidgetGTK printDialog(nsPIDOMWindowOuter::From(aParent),
514 aHaveSelection, aSettings);
515 nsresult rv = printDialog.ImportSettings(aSettings);
517 NS_ENSURE_SUCCESS(rv, rv);
519 const gint response = printDialog.Run();
521 // Handle the result
522 switch (response) {
523 case GTK_RESPONSE_OK: // Proceed
524 rv = printDialog.ExportSettings(aSettings);
525 break;
527 case GTK_RESPONSE_CANCEL:
528 case GTK_RESPONSE_CLOSE:
529 case GTK_RESPONSE_DELETE_EVENT:
530 case GTK_RESPONSE_NONE:
531 rv = NS_ERROR_ABORT;
532 break;
534 case GTK_RESPONSE_APPLY: // Print preview
535 default:
536 NS_WARNING("Unexpected response");
537 rv = NS_ERROR_ABORT;
539 return rv;
542 NS_IMETHODIMP
543 nsPrintDialogServiceGTK::ShowPageSetupDialog(mozIDOMWindowProxy* aParent,
544 nsIPrintSettings* aNSSettings) {
545 MOZ_ASSERT(aParent, "aParent must not be null");
546 MOZ_ASSERT(aNSSettings, "aSettings must not be null");
547 NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
549 nsCOMPtr<nsIWidget> widget =
550 WidgetUtils::DOMWindowToWidget(nsPIDOMWindowOuter::From(aParent));
551 NS_ASSERTION(widget, "Need a widget for dialog to be modal.");
552 GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget);
553 NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal.");
555 nsCOMPtr<nsPrintSettingsGTK> aNSSettingsGTK(do_QueryInterface(aNSSettings));
556 if (!aNSSettingsGTK) return NS_ERROR_FAILURE;
558 // We need to init the prefs here because aNSSettings in its current form is a
559 // dummy in both uses of the word
560 nsCOMPtr<nsIPrintSettingsService> psService =
561 do_GetService("@mozilla.org/gfx/printsettings-service;1");
562 if (psService) {
563 nsString printName;
564 aNSSettings->GetPrinterName(printName);
565 if (printName.IsVoid()) {
566 psService->GetLastUsedPrinterName(printName);
567 aNSSettings->SetPrinterName(printName);
569 psService->InitPrintSettingsFromPrefs(aNSSettings, true,
570 nsIPrintSettings::kInitSaveAll);
573 // Frustratingly, gtk_print_run_page_setup_dialog doesn't tell us whether
574 // the user cancelled or confirmed the dialog! So to avoid needlessly
575 // refreshing the preview when Page Setup was cancelled, we compare the
576 // serializations of old and new settings; if they're the same, bail out.
577 GtkPrintSettings* gtkSettings = aNSSettingsGTK->GetGtkPrintSettings();
578 GtkPageSetup* oldPageSetup = aNSSettingsGTK->GetGtkPageSetup();
579 GKeyFile* oldKeyFile = g_key_file_new();
580 gtk_page_setup_to_key_file(oldPageSetup, oldKeyFile, nullptr);
581 gsize oldLength;
582 gchar* oldData = g_key_file_to_data(oldKeyFile, &oldLength, nullptr);
583 g_key_file_free(oldKeyFile);
585 GtkPageSetup* newPageSetup =
586 gtk_print_run_page_setup_dialog(gtkParent, oldPageSetup, gtkSettings);
588 GKeyFile* newKeyFile = g_key_file_new();
589 gtk_page_setup_to_key_file(newPageSetup, newKeyFile, nullptr);
590 gsize newLength;
591 gchar* newData = g_key_file_to_data(newKeyFile, &newLength, nullptr);
592 g_key_file_free(newKeyFile);
594 bool unchanged =
595 (oldLength == newLength && !memcmp(oldData, newData, oldLength));
596 g_free(oldData);
597 g_free(newData);
598 if (unchanged) {
599 g_object_unref(newPageSetup);
600 return NS_ERROR_ABORT;
603 aNSSettingsGTK->SetGtkPageSetup(newPageSetup);
605 // Now newPageSetup has a refcount of 2 (SetGtkPageSetup will addref), put it
606 // to 1 so if this gets replaced we don't leak.
607 g_object_unref(newPageSetup);
609 if (psService)
610 psService->MaybeSavePrintSettingsToPrefs(
611 aNSSettings, nsIPrintSettings::kInitSaveOrientation |
612 nsIPrintSettings::kInitSavePaperSize |
613 nsIPrintSettings::kInitSaveUnwriteableMargins);
615 return NS_OK;