OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/printing/print_dialog_gtk.h" | 5 #include "chrome/browser/printing/print_dialog_gtk.h" |
6 | 6 |
7 #include <fcntl.h> | |
8 #include <gtk/gtkpagesetupunixdialog.h> | |
7 #include <gtk/gtkprintjob.h> | 9 #include <gtk/gtkprintjob.h> |
8 #include <gtk/gtkprintunixdialog.h> | 10 #include <sys/stat.h> |
9 #include <gtk/gtkpagesetupunixdialog.h> | 11 #include <sys/types.h> |
10 | 12 |
11 #include "base/file_util.h" | 13 #include "base/file_util.h" |
12 #include "base/file_util_proxy.h" | 14 #include "base/file_util_proxy.h" |
13 #include "base/lazy_instance.h" | |
14 #include "base/logging.h" | 15 #include "base/logging.h" |
15 #include "base/synchronization/lock.h" | 16 #include "base/synchronization/waitable_event.h" |
16 #include "base/threading/thread_restrictions.h" | |
17 #include "base/utf_string_conversions.h" | 17 #include "base/utf_string_conversions.h" |
18 #include "chrome/browser/browser_list.h" | 18 #include "chrome/browser/browser_list.h" |
19 #include "chrome/browser/browser_thread.h" | 19 #include "chrome/browser/browser_thread.h" |
20 #include "chrome/browser/browser_window.h" | 20 #include "chrome/browser/browser_window.h" |
21 #include "chrome/browser/tab_contents/infobar_delegate.h" | 21 #include "printing/print_settings_initializer_gtk.h" |
22 #include "chrome/browser/tab_contents/tab_contents.h" | |
23 | |
24 namespace { | |
25 | |
26 PrintDialogGtk* g_print_dialog = NULL; | |
27 | |
28 // Used to make accesses to the above thread safe. | |
29 base::Lock& DialogLock() { | |
30 static base::LazyInstance<base::Lock> dialog_lock(base::LINKER_INITIALIZED); | |
31 return dialog_lock.Get(); | |
32 } | |
33 | |
34 // This is a temporary infobar designed to help gauge how many users are trying | |
35 // to print to printers that don't support PDF. | |
36 class PdfUnsupportedInfoBarDelegate : public LinkInfoBarDelegate { | |
37 public: | |
38 explicit PdfUnsupportedInfoBarDelegate(Browser* browser) | |
39 : LinkInfoBarDelegate(NULL), | |
40 browser_(browser) { | |
41 } | |
42 | |
43 virtual ~PdfUnsupportedInfoBarDelegate() {} | |
44 | |
45 virtual string16 GetMessageTextWithOffset(size_t* link_offset) const { | |
46 string16 message = UTF8ToUTF16("Oops! Your printer does not support PDF. " | |
47 "Please report this to us."); | |
48 *link_offset = message.length() - 1; | |
49 return message; | |
50 } | |
51 | |
52 virtual string16 GetLinkText() const { | |
53 return UTF8ToUTF16("here"); | |
54 } | |
55 | |
56 virtual Type GetInfoBarType() const { return WARNING_TYPE; } | |
57 | |
58 virtual bool LinkClicked(WindowOpenDisposition disposition) { | |
59 browser_->OpenURL( | |
60 GURL("http://code.google.com/p/chromium/issues/detail?id=22027"), | |
61 GURL(), NEW_FOREGROUND_TAB, PageTransition::TYPED); | |
62 return true; | |
63 } | |
64 | |
65 private: | |
66 Browser* browser_; | |
67 }; | |
68 | |
69 } // namespace | |
70 | 22 |
71 // static | 23 // static |
72 void PrintDialogGtk::CreatePrintDialogForPdf(const FilePath& path) { | 24 void* PrintDialogGtk::CreatePrintDialog( |
73 BrowserThread::PostTask( | 25 PrintingContextCairo::PrintSettingsCallback* callback, |
74 BrowserThread::UI, FROM_HERE, | 26 PrintingContextCairo* context) { |
75 NewRunnableFunction(&PrintDialogGtk::CreateDialogImpl, path)); | 27 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
28 | |
29 PrintDialogGtk* dialog = new PrintDialogGtk(callback, context); | |
30 return dialog; | |
76 } | 31 } |
77 | 32 |
78 // static | 33 // static |
79 bool PrintDialogGtk::DialogShowing() { | 34 void PrintDialogGtk::PrintDocument(void* print_dialog, |
80 base::AutoLock lock(DialogLock()); | 35 const NativeMetafile* metafile, |
81 return !!g_print_dialog; | 36 const string16& document_name) { |
37 PrintDialogGtk* dialog = static_cast<PrintDialogGtk*>(print_dialog); | |
38 | |
39 scoped_ptr<base::WaitableEvent> event(new base::WaitableEvent(false, false)); | |
40 dialog->set_save_document_event(event.get()); | |
41 BrowserThread::PostTask( | |
42 BrowserThread::FILE, FROM_HERE, | |
43 NewRunnableMethod(dialog, | |
44 &PrintDialogGtk::SaveDocumentToDisk, | |
45 metafile, | |
46 document_name)); | |
47 // Wait for SaveDocumentToDisk() to finish. | |
48 event->Wait(); | |
82 } | 49 } |
83 | 50 |
84 // static | 51 PrintDialogGtk::PrintDialogGtk( |
85 void PrintDialogGtk::CreateDialogImpl(const FilePath& path) { | 52 PrintingContextCairo::PrintSettingsCallback* callback, |
86 // Only show one print dialog at once. This is to prevent a page from | 53 PrintingContextCairo* context) |
87 // locking up the system with | 54 : callback_(callback), |
88 // | 55 context_(context), |
89 // while(true){print();} | 56 dialog_(NULL), |
90 base::AutoLock lock(DialogLock()); | 57 page_setup_(NULL), |
91 if (g_print_dialog) { | 58 printer_(NULL), |
92 // Clean up the temporary file. | 59 gtk_settings_(NULL), |
93 base::FileUtilProxy::Delete( | 60 save_document_event_(NULL) { |
94 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 61 // Manual AddRef since PrintDialogGtk manages its own lifetime. |
95 path, false, NULL); | 62 AddRef(); |
96 return; | |
97 } | |
98 | 63 |
99 g_print_dialog = new PrintDialogGtk(path); | 64 GtkWindow* parent = BrowserList::GetLastActive()->window()->GetNativeHandle(); |
100 } | |
101 | |
102 PrintDialogGtk::PrintDialogGtk(const FilePath& path_to_pdf) | |
103 : path_to_pdf_(path_to_pdf), | |
104 browser_(BrowserList::GetLastActive()) { | |
105 GtkWindow* parent = browser_->window()->GetNativeHandle(); | |
106 | 65 |
107 // TODO(estade): We need a window title here. | 66 // TODO(estade): We need a window title here. |
108 dialog_ = gtk_print_unix_dialog_new(NULL, parent); | 67 dialog_ = gtk_print_unix_dialog_new(NULL, parent); |
68 // Set modal so user cannot focus the same tab and press print again. | |
69 gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); | |
70 | |
71 // Since we only generate PDF, only show printers that support PDF. | |
72 // TODO(thestig) Add more capabilities to support? | |
73 GtkPrintCapabilities cap = static_cast<GtkPrintCapabilities>( | |
74 GTK_PRINT_CAPABILITY_GENERATE_PDF | | |
75 GTK_PRINT_CAPABILITY_PAGE_SET | | |
76 GTK_PRINT_CAPABILITY_COPIES | | |
77 GTK_PRINT_CAPABILITY_COLLATE | | |
78 GTK_PRINT_CAPABILITY_REVERSE); | |
79 gtk_print_unix_dialog_set_manual_capabilities(GTK_PRINT_UNIX_DIALOG(dialog_), | |
80 cap); | |
81 #if GTK_CHECK_VERSION(2, 18, 0) | |
82 gtk_print_unix_dialog_set_embed_page_setup(GTK_PRINT_UNIX_DIALOG(dialog_), | |
83 TRUE); | |
84 #endif | |
109 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); | 85 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); |
110 | 86 |
111 gtk_widget_show(dialog_); | 87 gtk_widget_show(dialog_); |
112 } | 88 } |
113 | 89 |
114 PrintDialogGtk::~PrintDialogGtk() { | 90 PrintDialogGtk::~PrintDialogGtk() { |
115 base::AutoLock lock(DialogLock()); | 91 gtk_widget_destroy(dialog_); |
116 DCHECK_EQ(this, g_print_dialog); | 92 dialog_ = NULL; |
117 g_print_dialog = NULL; | 93 page_setup_ = NULL; |
94 printer_ = NULL; | |
95 if (gtk_settings_) { | |
96 g_object_unref(gtk_settings_); | |
97 gtk_settings_ = NULL; | |
98 } | |
118 } | 99 } |
119 | 100 |
120 void PrintDialogGtk::OnResponse(GtkWidget* dialog, gint response_id) { | 101 void PrintDialogGtk::OnResponse(GtkWidget* dialog, gint response_id) { |
121 gtk_widget_hide(dialog_); | 102 gtk_widget_hide(dialog_); |
122 | 103 |
123 switch (response_id) { | 104 switch (response_id) { |
124 case GTK_RESPONSE_OK: { | 105 case GTK_RESPONSE_OK: { |
125 GtkPrinter* printer = | 106 // |gtk_settings_| is a new object. |
126 gtk_print_unix_dialog_get_selected_printer( | 107 gtk_settings_ = gtk_print_unix_dialog_get_settings( |
127 GTK_PRINT_UNIX_DIALOG(dialog_)); | 108 GTK_PRINT_UNIX_DIALOG(dialog_)); |
128 // Attempt to track down bug 70166. | 109 // |printer_| and |page_setup_| are owned by |dialog_|. |
129 CHECK(printer != NULL); | 110 page_setup_ = gtk_print_unix_dialog_get_page_setup( |
130 if (!gtk_printer_accepts_pdf(printer)) { | 111 GTK_PRINT_UNIX_DIALOG(dialog_)); |
131 browser_->GetSelectedTabContents()->AddInfoBar( | 112 printer_ = gtk_print_unix_dialog_get_selected_printer( |
132 new PdfUnsupportedInfoBarDelegate(browser_)); | 113 GTK_PRINT_UNIX_DIALOG(dialog_)); |
133 break; | 114 |
115 printing::PageRanges ranges_vector; | |
116 gint num_ranges; | |
117 GtkPageRange* gtk_range = | |
118 gtk_print_settings_get_page_ranges(gtk_settings_, &num_ranges); | |
119 if (gtk_range) { | |
120 for (int i = 0; i < num_ranges; ++i) { | |
121 printing::PageRange* range = new printing::PageRange; | |
James Hawkins
2011/02/25 20:53:59
You're leaking |range|. There's no need to make |r
| |
122 range->from = gtk_range[i].start; | |
123 range->to = gtk_range[i].end; | |
124 ranges_vector.push_back(*range); | |
125 } | |
126 g_free(gtk_range); | |
134 } | 127 } |
135 | 128 |
136 GtkPrintSettings* settings = | 129 printing::PrintSettings settings; |
137 gtk_print_unix_dialog_get_settings( | 130 printing::PrintSettingsInitializerGtk::InitPrintSettings( |
138 GTK_PRINT_UNIX_DIALOG(dialog_)); | 131 gtk_settings_, page_setup_, ranges_vector, false, &settings); |
139 GtkPageSetup* setup = gtk_print_unix_dialog_get_page_setup( | 132 context_->InitWithSettings(settings); |
140 GTK_PRINT_UNIX_DIALOG(dialog_)); | 133 callback_->Run(PrintingContextCairo::OK); |
141 | |
142 GtkPrintJob* job = | |
143 gtk_print_job_new(path_to_pdf_.value().c_str(), printer, | |
144 settings, setup); | |
145 gtk_print_job_set_source_file(job, path_to_pdf_.value().c_str(), NULL); | |
146 gtk_print_job_send(job, OnJobCompletedThunk, this, NULL); | |
147 g_object_unref(settings); | |
148 // Success; return early. | |
149 return; | 134 return; |
150 } | 135 } |
151 case GTK_RESPONSE_DELETE_EVENT: // Fall through. | 136 case GTK_RESPONSE_DELETE_EVENT: // Fall through. |
152 case GTK_RESPONSE_CANCEL: { | 137 case GTK_RESPONSE_CANCEL: { |
153 break; | 138 callback_->Run(PrintingContextCairo::CANCEL); |
139 Release(); | |
140 return; | |
154 } | 141 } |
155 case GTK_RESPONSE_APPLY: | 142 case GTK_RESPONSE_APPLY: |
156 default: { | 143 default: { |
157 NOTREACHED(); | 144 NOTREACHED(); |
158 } | 145 } |
159 } | 146 } |
160 | |
161 // Delete this dialog. | |
162 OnJobCompleted(NULL, NULL); | |
163 } | 147 } |
164 | 148 |
149 void PrintDialogGtk::SaveDocumentToDisk(const NativeMetafile* metafile, | |
150 const string16& document_name) { | |
151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
152 | |
153 bool error = false; | |
154 if (!file_util::CreateTemporaryFile(&path_to_pdf_)) { | |
155 LOG(ERROR) << "Creating temporary file failed"; | |
156 error = true; | |
157 } | |
158 | |
159 if (!error) { | |
160 base::FileDescriptor temp_file_fd; | |
161 temp_file_fd.fd = open(path_to_pdf_.value().c_str(), O_WRONLY); | |
162 temp_file_fd.auto_close = true; | |
163 if (!metafile->SaveTo(temp_file_fd)) { | |
164 LOG(ERROR) << "Saving metafile failed"; | |
165 file_util::Delete(path_to_pdf_, false); | |
166 error = true; | |
167 } | |
168 } | |
169 | |
170 // Done saving, let PrintDialogGtk::PrintDocument() continue. | |
171 save_document_event_->Signal(); | |
172 | |
173 if (error) { | |
174 Release(); | |
175 } else { | |
176 // No errors, continue printing. | |
177 BrowserThread::PostTask( | |
178 BrowserThread::UI, FROM_HERE, | |
179 NewRunnableMethod(this, | |
180 &PrintDialogGtk::SendDocumentToPrinter, | |
181 document_name)); | |
182 } | |
183 } | |
184 | |
185 void PrintDialogGtk::SendDocumentToPrinter(const string16& document_name) { | |
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
187 GtkPrintJob* print_job = gtk_print_job_new( | |
188 UTF16ToUTF8(document_name).c_str(), | |
189 printer_, | |
190 gtk_settings_, | |
191 page_setup_); | |
192 gtk_print_job_set_source_file(print_job, path_to_pdf_.value().c_str(), NULL); | |
193 gtk_print_job_send(print_job, OnJobCompletedThunk, this, NULL); | |
194 } | |
195 | |
196 // static | |
165 void PrintDialogGtk::OnJobCompletedThunk(GtkPrintJob* print_job, | 197 void PrintDialogGtk::OnJobCompletedThunk(GtkPrintJob* print_job, |
166 gpointer user_data, | 198 gpointer user_data, |
167 GError* error) { | 199 GError* error) { |
168 reinterpret_cast<PrintDialogGtk*>(user_data)->OnJobCompleted(print_job, | 200 static_cast<PrintDialogGtk*>(user_data)->OnJobCompleted(print_job, error); |
169 error); | |
170 } | 201 } |
171 | 202 |
172 void PrintDialogGtk::OnJobCompleted(GtkPrintJob* job, GError* error) { | 203 void PrintDialogGtk::OnJobCompleted(GtkPrintJob* print_job, GError* error) { |
173 gtk_widget_destroy(dialog_); | |
174 | |
175 if (error) | 204 if (error) |
176 LOG(ERROR) << "Printing failed: " << error->message; | 205 LOG(ERROR) << "Printing failed: " << error->message; |
177 | 206 if (print_job) |
178 if (job) | 207 g_object_unref(print_job); |
179 g_object_unref(job); | |
180 | |
181 base::FileUtilProxy::Delete( | 208 base::FileUtilProxy::Delete( |
182 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 209 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
183 path_to_pdf_, | 210 path_to_pdf_, |
184 false, | 211 false, |
185 NULL); | 212 NULL); |
213 // Printing finished. | |
214 Release(); | |
215 } | |
186 | 216 |
187 delete this; | 217 void PrintDialogGtk::set_save_document_event(base::WaitableEvent* event) { |
218 DCHECK(event); | |
219 DCHECK(!save_document_event_); | |
220 save_document_event_ = event; | |
188 } | 221 } |
OLD | NEW |