1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/printing/print_dialog_gtk.h"
8 #include <gtk/gtkunixprint.h>
10 #include <sys/types.h>
15 #include "base/bind.h"
16 #include "base/file_util.h"
17 #include "base/files/file_util_proxy.h"
18 #include "base/logging.h"
19 #include "base/message_loop_proxy.h"
20 #include "base/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "printing/metafile.h"
23 #include "printing/print_job_constants.h"
24 #include "printing/print_settings.h"
25 #include "printing/print_settings_initializer_gtk.h"
27 using content::BrowserThread
;
28 using printing::PageRanges
;
29 using printing::PrintSettings
;
33 // CUPS Duplex attribute and values.
34 const char kCUPSDuplex
[] = "cups-Duplex";
35 const char kDuplexNone
[] = "None";
36 const char kDuplexTumble
[] = "DuplexTumble";
37 const char kDuplexNoTumble
[] = "DuplexNoTumble";
39 // Helper class to track GTK printers.
40 class GtkPrinterList
{
42 GtkPrinterList() : default_printer_(NULL
) {
43 gtk_enumerate_printers(SetPrinter
, this, NULL
, TRUE
);
47 for (std::vector
<GtkPrinter
*>::iterator it
= printers_
.begin();
48 it
< printers_
.end(); ++it
) {
53 // Can return NULL if there's no default printer. E.g. Printer on a laptop
54 // is "home_printer", but the laptop is at work.
55 GtkPrinter
* default_printer() {
56 return default_printer_
;
59 // Can return NULL if the printer cannot be found due to:
60 // - Printer list out of sync with printer dialog UI.
61 // - Querying for non-existant printers like 'Print to PDF'.
62 GtkPrinter
* GetPrinterWithName(const char* name
) {
66 for (std::vector
<GtkPrinter
*>::iterator it
= printers_
.begin();
67 it
< printers_
.end(); ++it
) {
68 if (strcmp(name
, gtk_printer_get_name(*it
)) == 0) {
77 // Callback function used by gtk_enumerate_printers() to get all printer.
78 static gboolean
SetPrinter(GtkPrinter
* printer
, gpointer data
) {
79 GtkPrinterList
*printer_list
= (GtkPrinterList
*)data
;
80 if (gtk_printer_is_default(printer
))
81 printer_list
->default_printer_
= printer
;
83 g_object_ref(printer
);
84 printer_list
->printers_
.push_back(printer
);
89 std::vector
<GtkPrinter
*> printers_
;
90 GtkPrinter
* default_printer_
;
96 printing::PrintDialogGtkInterface
* PrintDialogGtk::CreatePrintDialog(
97 PrintingContextGtk
* context
) {
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
99 return new PrintDialogGtk(context
);
102 PrintDialogGtk::PrintDialogGtk(PrintingContextGtk
* context
)
110 PrintDialogGtk::~PrintDialogGtk() {
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
114 gtk_widget_destroy(dialog_
);
118 g_object_unref(gtk_settings_
);
119 gtk_settings_
= NULL
;
122 g_object_unref(page_setup_
);
126 g_object_unref(printer_
);
131 void PrintDialogGtk::UseDefaultSettings() {
132 DCHECK(!page_setup_
);
135 // |gtk_settings_| is a new object.
136 gtk_settings_
= gtk_print_settings_new();
137 page_setup_
= gtk_page_setup_new();
139 // No page range to initialize for default settings.
140 PageRanges ranges_vector
;
141 PrintSettings settings
;
142 InitPrintSettings(ranges_vector
, &settings
);
145 bool PrintDialogGtk::UpdateSettings(const base::DictionaryValue
& job_settings
,
146 const printing::PageRanges
& ranges
,
147 printing::PrintSettings
* settings
) {
154 std::string device_name
;
156 if (!job_settings
.GetBoolean(printing::kSettingLandscape
, &landscape
) ||
157 !job_settings
.GetBoolean(printing::kSettingCollate
, &collate
) ||
158 !job_settings
.GetInteger(printing::kSettingColor
, &color
) ||
159 !job_settings
.GetBoolean(printing::kSettingPrintToPDF
, &print_to_pdf
) ||
160 !job_settings
.GetInteger(printing::kSettingDuplexMode
, &duplex_mode
) ||
161 !job_settings
.GetInteger(printing::kSettingCopies
, &copies
) ||
162 !job_settings
.GetString(printing::kSettingDeviceName
, &device_name
)) {
166 bool is_cloud_print
= job_settings
.HasKey(printing::kSettingCloudPrintId
);
169 gtk_settings_
= gtk_print_settings_new();
171 if (!print_to_pdf
&& !is_cloud_print
) {
172 scoped_ptr
<GtkPrinterList
> printer_list(new GtkPrinterList
);
173 printer_
= printer_list
->GetPrinterWithName(device_name
.c_str());
175 g_object_ref(printer_
);
176 gtk_print_settings_set_printer(gtk_settings_
,
177 gtk_printer_get_name(printer_
));
179 page_setup_
= gtk_printer_get_default_page_size(printer_
);
183 gtk_print_settings_set_n_copies(gtk_settings_
, copies
);
184 gtk_print_settings_set_collate(gtk_settings_
, collate
);
186 #if defined(USE_CUPS)
187 std::string color_value
;
188 std::string color_setting_name
;
189 printing::GetColorModelForMode(color
, &color_setting_name
, &color_value
);
190 gtk_print_settings_set(gtk_settings_
, color_setting_name
.c_str(),
191 color_value
.c_str());
193 if (duplex_mode
!= printing::UNKNOWN_DUPLEX_MODE
) {
194 const char* cups_duplex_mode
= NULL
;
195 switch (duplex_mode
) {
196 case printing::LONG_EDGE
:
197 cups_duplex_mode
= kDuplexNoTumble
;
199 case printing::SHORT_EDGE
:
200 cups_duplex_mode
= kDuplexTumble
;
202 case printing::SIMPLEX
:
203 cups_duplex_mode
= kDuplexNone
;
205 default: // UNKNOWN_DUPLEX_MODE
209 gtk_print_settings_set(gtk_settings_
, kCUPSDuplex
, cups_duplex_mode
);
214 page_setup_
= gtk_page_setup_new();
216 gtk_print_settings_set_orientation(
218 landscape
? GTK_PAGE_ORIENTATION_LANDSCAPE
:
219 GTK_PAGE_ORIENTATION_PORTRAIT
);
221 InitPrintSettings(ranges
, settings
);
225 void PrintDialogGtk::ShowDialog(
226 gfx::NativeView parent_view
,
228 const PrintingContextGtk::PrintSettingsCallback
& callback
) {
229 callback_
= callback
;
231 GtkWindow
* parent
= GTK_WINDOW(gtk_widget_get_toplevel(parent_view
));
232 // TODO(estade): We need a window title here.
233 dialog_
= gtk_print_unix_dialog_new(NULL
, parent
);
234 g_signal_connect(dialog_
, "delete-event",
235 G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
238 // Set modal so user cannot focus the same tab and press print again.
239 gtk_window_set_modal(GTK_WINDOW(dialog_
), TRUE
);
241 // Since we only generate PDF, only show printers that support PDF.
242 // TODO(thestig) Add more capabilities to support?
243 GtkPrintCapabilities cap
= static_cast<GtkPrintCapabilities
>(
244 GTK_PRINT_CAPABILITY_GENERATE_PDF
|
245 GTK_PRINT_CAPABILITY_PAGE_SET
|
246 GTK_PRINT_CAPABILITY_COPIES
|
247 GTK_PRINT_CAPABILITY_COLLATE
|
248 GTK_PRINT_CAPABILITY_REVERSE
);
249 gtk_print_unix_dialog_set_manual_capabilities(GTK_PRINT_UNIX_DIALOG(dialog_
),
251 gtk_print_unix_dialog_set_embed_page_setup(GTK_PRINT_UNIX_DIALOG(dialog_
),
253 gtk_print_unix_dialog_set_support_selection(GTK_PRINT_UNIX_DIALOG(dialog_
),
255 gtk_print_unix_dialog_set_has_selection(GTK_PRINT_UNIX_DIALOG(dialog_
),
257 g_signal_connect(dialog_
, "response", G_CALLBACK(OnResponseThunk
), this);
258 gtk_widget_show(dialog_
);
261 void PrintDialogGtk::PrintDocument(const printing::Metafile
* metafile
,
262 const string16
& document_name
) {
263 // This runs on the print worker thread, does not block the UI thread.
264 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
266 // The document printing tasks can outlive the PrintingContext that created
271 if (!file_util::CreateTemporaryFile(&path_to_pdf_
)) {
272 LOG(ERROR
) << "Creating temporary file failed";
276 if (!error
&& !metafile
->SaveTo(path_to_pdf_
)) {
277 LOG(ERROR
) << "Saving metafile failed";
278 file_util::Delete(path_to_pdf_
, false);
283 // Matches AddRef() above.
286 // No errors, continue printing.
287 BrowserThread::PostTask(
288 BrowserThread::UI
, FROM_HERE
,
289 base::Bind(&PrintDialogGtk::SendDocumentToPrinter
, this,
294 void PrintDialogGtk::AddRefToDialog() {
298 void PrintDialogGtk::ReleaseDialog() {
302 void PrintDialogGtk::OnResponse(GtkWidget
* dialog
, int response_id
) {
303 gtk_widget_hide(dialog_
);
305 switch (response_id
) {
306 case GTK_RESPONSE_OK
: {
308 g_object_unref(gtk_settings_
);
309 gtk_settings_
= gtk_print_unix_dialog_get_settings(
310 GTK_PRINT_UNIX_DIALOG(dialog_
));
313 g_object_unref(printer_
);
314 printer_
= gtk_print_unix_dialog_get_selected_printer(
315 GTK_PRINT_UNIX_DIALOG(dialog_
));
316 g_object_ref(printer_
);
319 g_object_unref(page_setup_
);
320 page_setup_
= gtk_print_unix_dialog_get_page_setup(
321 GTK_PRINT_UNIX_DIALOG(dialog_
));
322 g_object_ref(page_setup_
);
324 // Handle page ranges.
325 PageRanges ranges_vector
;
327 bool print_selection_only
= false;
328 switch (gtk_print_settings_get_print_pages(gtk_settings_
)) {
329 case GTK_PRINT_PAGES_RANGES
: {
330 GtkPageRange
* gtk_range
=
331 gtk_print_settings_get_page_ranges(gtk_settings_
, &num_ranges
);
333 for (int i
= 0; i
< num_ranges
; ++i
) {
334 printing::PageRange range
;
335 range
.from
= gtk_range
[i
].start
;
336 range
.to
= gtk_range
[i
].end
;
337 ranges_vector
.push_back(range
);
343 case GTK_PRINT_PAGES_SELECTION
:
344 print_selection_only
= true;
346 case GTK_PRINT_PAGES_ALL
:
347 // Leave |ranges_vector| empty to indicate print all pages.
349 case GTK_PRINT_PAGES_CURRENT
:
355 PrintSettings settings
;
356 printing::PrintSettingsInitializerGtk::InitPrintSettings(
357 gtk_settings_
, page_setup_
, ranges_vector
, print_selection_only
,
359 context_
->InitWithSettings(settings
);
360 callback_
.Run(PrintingContextGtk::OK
);
364 case GTK_RESPONSE_DELETE_EVENT
: // Fall through.
365 case GTK_RESPONSE_CANCEL
: {
366 callback_
.Run(PrintingContextGtk::CANCEL
);
370 case GTK_RESPONSE_APPLY
:
377 void PrintDialogGtk::SendDocumentToPrinter(const string16
& document_name
) {
378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
380 // If |printer_| is NULL then somehow the GTK printer list changed out under
381 // us. In which case, just bail out.
383 // Matches AddRef() in PrintDocument();
388 GtkPrintJob
* print_job
= gtk_print_job_new(
389 UTF16ToUTF8(document_name
).c_str(),
393 gtk_print_job_set_source_file(print_job
, path_to_pdf_
.value().c_str(), NULL
);
394 gtk_print_job_send(print_job
, OnJobCompletedThunk
, this, NULL
);
398 void PrintDialogGtk::OnJobCompletedThunk(GtkPrintJob
* print_job
,
401 static_cast<PrintDialogGtk
*>(user_data
)->OnJobCompleted(print_job
, error
);
404 void PrintDialogGtk::OnJobCompleted(GtkPrintJob
* print_job
, GError
* error
) {
406 LOG(ERROR
) << "Printing failed: " << error
->message
;
408 g_object_unref(print_job
);
409 base::FileUtilProxy::Delete(
410 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
411 path_to_pdf_
, false, base::FileUtilProxy::StatusCallback());
412 // Printing finished. Matches AddRef() in PrintDocument();
416 void PrintDialogGtk::InitPrintSettings(const PageRanges
& page_ranges
,
417 PrintSettings
* settings
) {
418 printing::PrintSettingsInitializerGtk::InitPrintSettings(
419 gtk_settings_
, page_setup_
, page_ranges
, false, settings
);
420 context_
->InitWithSettings(*settings
);