Fix build break
[chromium-blink-merge.git] / chrome / browser / printing / print_dialog_gtk.cc
blobd8cb419c49f09730b1ef9c9d571e97c07fd89dc4
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"
7 #include <fcntl.h>
8 #include <gtk/gtkunixprint.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
12 #include <string>
13 #include <vector>
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;
31 namespace {
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 {
41 public:
42 GtkPrinterList() : default_printer_(NULL) {
43 gtk_enumerate_printers(SetPrinter, this, NULL, TRUE);
46 ~GtkPrinterList() {
47 for (std::vector<GtkPrinter*>::iterator it = printers_.begin();
48 it < printers_.end(); ++it) {
49 g_object_unref(*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) {
63 if (!name || !*name)
64 return NULL;
66 for (std::vector<GtkPrinter*>::iterator it = printers_.begin();
67 it < printers_.end(); ++it) {
68 if (strcmp(name, gtk_printer_get_name(*it)) == 0) {
69 return *it;
73 return NULL;
76 private:
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);
86 return FALSE;
89 std::vector<GtkPrinter*> printers_;
90 GtkPrinter* default_printer_;
93 } // namespace
95 // static
96 printing::PrintDialogGtkInterface* PrintDialogGtk::CreatePrintDialog(
97 PrintingContextGtk* context) {
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
99 return new PrintDialogGtk(context);
102 PrintDialogGtk::PrintDialogGtk(PrintingContextGtk* context)
103 : context_(context),
104 dialog_(NULL),
105 gtk_settings_(NULL),
106 page_setup_(NULL),
107 printer_(NULL) {
110 PrintDialogGtk::~PrintDialogGtk() {
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
113 if (dialog_) {
114 gtk_widget_destroy(dialog_);
115 dialog_ = NULL;
117 if (gtk_settings_) {
118 g_object_unref(gtk_settings_);
119 gtk_settings_ = NULL;
121 if (page_setup_) {
122 g_object_unref(page_setup_);
123 page_setup_ = NULL;
125 if (printer_) {
126 g_object_unref(printer_);
127 printer_ = NULL;
131 void PrintDialogGtk::UseDefaultSettings() {
132 DCHECK(!page_setup_);
133 DCHECK(!printer_);
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) {
148 bool collate;
149 int color;
150 bool landscape;
151 bool print_to_pdf;
152 int copies;
153 int duplex_mode;
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)) {
163 return false;
166 bool is_cloud_print = job_settings.HasKey(printing::kSettingCloudPrintId);
168 if (!gtk_settings_)
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());
174 if (printer_) {
175 g_object_ref(printer_);
176 gtk_print_settings_set_printer(gtk_settings_,
177 gtk_printer_get_name(printer_));
178 if (!page_setup_) {
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;
198 break;
199 case printing::SHORT_EDGE:
200 cups_duplex_mode = kDuplexTumble;
201 break;
202 case printing::SIMPLEX:
203 cups_duplex_mode = kDuplexNone;
204 break;
205 default: // UNKNOWN_DUPLEX_MODE
206 NOTREACHED();
207 break;
209 gtk_print_settings_set(gtk_settings_, kCUPSDuplex, cups_duplex_mode);
211 #endif
213 if (!page_setup_)
214 page_setup_ = gtk_page_setup_new();
216 gtk_print_settings_set_orientation(
217 gtk_settings_,
218 landscape ? GTK_PAGE_ORIENTATION_LANDSCAPE :
219 GTK_PAGE_ORIENTATION_PORTRAIT);
221 InitPrintSettings(ranges, settings);
222 return true;
225 void PrintDialogGtk::ShowDialog(
226 gfx::NativeView parent_view,
227 bool has_selection,
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_),
250 cap);
251 gtk_print_unix_dialog_set_embed_page_setup(GTK_PRINT_UNIX_DIALOG(dialog_),
252 TRUE);
253 gtk_print_unix_dialog_set_support_selection(GTK_PRINT_UNIX_DIALOG(dialog_),
254 TRUE);
255 gtk_print_unix_dialog_set_has_selection(GTK_PRINT_UNIX_DIALOG(dialog_),
256 has_selection);
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
267 // this dialog.
268 AddRef();
270 bool error = false;
271 if (!file_util::CreateTemporaryFile(&path_to_pdf_)) {
272 LOG(ERROR) << "Creating temporary file failed";
273 error = true;
276 if (!error && !metafile->SaveTo(path_to_pdf_)) {
277 LOG(ERROR) << "Saving metafile failed";
278 file_util::Delete(path_to_pdf_, false);
279 error = true;
282 if (error) {
283 // Matches AddRef() above.
284 Release();
285 } else {
286 // No errors, continue printing.
287 BrowserThread::PostTask(
288 BrowserThread::UI, FROM_HERE,
289 base::Bind(&PrintDialogGtk::SendDocumentToPrinter, this,
290 document_name));
294 void PrintDialogGtk::AddRefToDialog() {
295 AddRef();
298 void PrintDialogGtk::ReleaseDialog() {
299 Release();
302 void PrintDialogGtk::OnResponse(GtkWidget* dialog, int response_id) {
303 gtk_widget_hide(dialog_);
305 switch (response_id) {
306 case GTK_RESPONSE_OK: {
307 if (gtk_settings_)
308 g_object_unref(gtk_settings_);
309 gtk_settings_ = gtk_print_unix_dialog_get_settings(
310 GTK_PRINT_UNIX_DIALOG(dialog_));
312 if (printer_)
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_);
318 if (page_setup_)
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;
326 gint num_ranges;
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);
332 if (gtk_range) {
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);
339 g_free(gtk_range);
341 break;
343 case GTK_PRINT_PAGES_SELECTION:
344 print_selection_only = true;
345 break;
346 case GTK_PRINT_PAGES_ALL:
347 // Leave |ranges_vector| empty to indicate print all pages.
348 break;
349 case GTK_PRINT_PAGES_CURRENT:
350 default:
351 NOTREACHED();
352 break;
355 PrintSettings settings;
356 printing::PrintSettingsInitializerGtk::InitPrintSettings(
357 gtk_settings_, page_setup_, ranges_vector, print_selection_only,
358 &settings);
359 context_->InitWithSettings(settings);
360 callback_.Run(PrintingContextGtk::OK);
361 callback_.Reset();
362 return;
364 case GTK_RESPONSE_DELETE_EVENT: // Fall through.
365 case GTK_RESPONSE_CANCEL: {
366 callback_.Run(PrintingContextGtk::CANCEL);
367 callback_.Reset();
368 return;
370 case GTK_RESPONSE_APPLY:
371 default: {
372 NOTREACHED();
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.
382 if (!printer_) {
383 // Matches AddRef() in PrintDocument();
384 Release();
385 return;
388 GtkPrintJob* print_job = gtk_print_job_new(
389 UTF16ToUTF8(document_name).c_str(),
390 printer_,
391 gtk_settings_,
392 page_setup_);
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);
397 // static
398 void PrintDialogGtk::OnJobCompletedThunk(GtkPrintJob* print_job,
399 gpointer user_data,
400 GError* error) {
401 static_cast<PrintDialogGtk*>(user_data)->OnJobCompleted(print_job, error);
404 void PrintDialogGtk::OnJobCompleted(GtkPrintJob* print_job, GError* error) {
405 if (error)
406 LOG(ERROR) << "Printing failed: " << error->message;
407 if (print_job)
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();
413 Release();
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);