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/. */
7 #include <gtk/gtkunixprint.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"
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
33 # include <gdk/gdkx.h>
35 #include <sys/types.h>
38 #include <gio/gunixfdlist.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
)));
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
),
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");
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
);
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
));
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
{
132 nsPrintDialogWidgetGTK(nsPIDOMWindowOuter
* aParent
, bool aHaveSelection
,
133 nsIPrintSettings
* aPrintSettings
);
134 ~nsPrintDialogWidgetGTK() { gtk_widget_destroy(dialog
); }
135 NS_ConvertUTF16toUTF8
GetUTF8FromBundle(const char* aKey
);
138 nsresult
ImportSettings(nsIPrintSettings
* aNSSettings
);
139 nsresult
ExportSettings(nsIPrintSettings
* aNSSettings
);
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.
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
,
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(),
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
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
,
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);
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
,
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
);
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
,
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
);
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
++) {
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
++) {
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,
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
,
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(
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
);
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:
382 aNSSettings
->GetShrinkToFit(&geckoBool
);
383 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shrink_to_fit_toggle
),
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
);
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
);
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
);
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",
479 shouldBeCustom
= false;
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
);
497 NS_IMPL_ISUPPORTS(nsPrintDialogServiceGTK
, nsIPrintDialogService
)
499 nsPrintDialogServiceGTK::nsPrintDialogServiceGTK() = default;
501 nsPrintDialogServiceGTK::~nsPrintDialogServiceGTK() = default;
504 nsPrintDialogServiceGTK::Init() { return NS_OK
; }
507 nsPrintDialogServiceGTK::ShowPrintDialog(mozIDOMWindowProxy
* aParent
,
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();
523 case GTK_RESPONSE_OK
: // Proceed
524 rv
= printDialog
.ExportSettings(aSettings
);
527 case GTK_RESPONSE_CANCEL
:
528 case GTK_RESPONSE_CLOSE
:
529 case GTK_RESPONSE_DELETE_EVENT
:
530 case GTK_RESPONSE_NONE
:
534 case GTK_RESPONSE_APPLY
: // Print preview
536 NS_WARNING("Unexpected response");
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");
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);
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);
591 gchar
* newData
= g_key_file_to_data(newKeyFile
, &newLength
, nullptr);
592 g_key_file_free(newKeyFile
);
595 (oldLength
== newLength
&& !memcmp(oldData
, newData
, oldLength
));
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
);
610 psService
->MaybeSavePrintSettingsToPrefs(
611 aNSSettings
, nsIPrintSettings::kInitSaveOrientation
|
612 nsIPrintSettings::kInitSavePaperSize
|
613 nsIPrintSettings::kInitSaveUnwriteableMargins
);