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/string_util.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; | |
Evan Stade
2011/02/15 00:40:46
unite these
Lei Zhang
2011/02/15 01:20:26
O
| |
40 event.reset(new base::WaitableEvent(false, false)); | |
Evan Stade
2011/02/15 00:40:46
two lines
Lei Zhang
2011/02/15 01:20:26
K
| |
41 dialog->set_save_document_event(event.get()); | |
42 BrowserThread::PostTask( | |
43 BrowserThread::FILE, FROM_HERE, | |
44 NewRunnableMethod(dialog, | |
45 &PrintDialogGtk::SaveDocumentToDisk, | |
46 metafile, | |
47 document_name)); | |
48 event->Wait(); // Wait for SaveDocumentToDisk() to finish. | |
Evan Stade
2011/02/15 00:40:46
can we at least be consistent between comment on l
Lei Zhang
2011/02/15 01:20:26
Yes.
| |
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 gtk_print_unix_dialog_set_embed_page_setup(GTK_PRINT_UNIX_DIALOG(dialog_), | |
Evan Stade
2011/02/15 00:40:46
if this is 2.18+ then we need a workaround until A
Lei Zhang
2011/02/15 01:20:26
Done.
| |
82 TRUE); | |
109 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); | 83 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); |
110 | 84 |
111 gtk_widget_show(dialog_); | 85 gtk_widget_show(dialog_); |
112 } | 86 } |
113 | 87 |
114 PrintDialogGtk::~PrintDialogGtk() { | 88 PrintDialogGtk::~PrintDialogGtk() { |
115 base::AutoLock lock(DialogLock()); | 89 gtk_widget_destroy(dialog_); |
116 DCHECK_EQ(this, g_print_dialog); | 90 dialog_ = NULL; |
117 g_print_dialog = NULL; | 91 page_setup_ = NULL; |
92 printer_ = NULL; | |
93 if (gtk_settings_) { | |
94 g_object_unref(gtk_settings_); | |
95 gtk_settings_ = NULL; | |
96 } | |
118 } | 97 } |
119 | 98 |
120 void PrintDialogGtk::OnResponse(GtkWidget* dialog, gint response_id) { | 99 void PrintDialogGtk::OnResponse(GtkWidget* dialog, gint response_id) { |
121 gtk_widget_hide(dialog_); | 100 gtk_widget_hide(dialog_); |
122 | 101 |
123 switch (response_id) { | 102 switch (response_id) { |
124 case GTK_RESPONSE_OK: { | 103 case GTK_RESPONSE_OK: { |
125 GtkPrinter* printer = | 104 // |gtk_settings_| is a new object. |
126 gtk_print_unix_dialog_get_selected_printer( | 105 gtk_settings_ = gtk_print_unix_dialog_get_settings( |
127 GTK_PRINT_UNIX_DIALOG(dialog_)); | 106 GTK_PRINT_UNIX_DIALOG(dialog_)); |
128 // Attempt to track down bug 70166. | 107 // |printer_| and |page_setup_| are owned by |dialog_|. |
129 CHECK(printer != NULL); | 108 page_setup_ = gtk_print_unix_dialog_get_page_setup( |
130 if (!gtk_printer_accepts_pdf(printer)) { | 109 GTK_PRINT_UNIX_DIALOG(dialog_)); |
131 browser_->GetSelectedTabContents()->AddInfoBar( | 110 printer_ = gtk_print_unix_dialog_get_selected_printer( |
132 new PdfUnsupportedInfoBarDelegate(browser_)); | 111 GTK_PRINT_UNIX_DIALOG(dialog_)); |
133 break; | 112 |
113 printing::PageRanges ranges_vector; | |
114 gint num_ranges; | |
115 GtkPageRange* gtk_range = | |
116 gtk_print_settings_get_page_ranges(gtk_settings_, &num_ranges); | |
117 if (gtk_range) { | |
118 for (int i = 0; i < num_ranges; ++i) { | |
119 printing::PageRange* range = new printing::PageRange; | |
120 range->from = gtk_range[i].start; | |
121 range->to = gtk_range[i].end; | |
122 ranges_vector.push_back(*range); | |
123 } | |
124 g_free(gtk_range); | |
134 } | 125 } |
135 | 126 |
136 GtkPrintSettings* settings = | 127 printing::PrintSettings settings; |
137 gtk_print_unix_dialog_get_settings( | 128 printing::PrintSettingsInitializerGtk::InitPrintSettings( |
138 GTK_PRINT_UNIX_DIALOG(dialog_)); | 129 gtk_settings_, page_setup_, ranges_vector, false, &settings); |
139 GtkPageSetup* setup = gtk_print_unix_dialog_get_page_setup( | 130 context_->InitWithSettings(settings); |
140 GTK_PRINT_UNIX_DIALOG(dialog_)); | 131 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; | 132 return; |
150 } | 133 } |
151 case GTK_RESPONSE_DELETE_EVENT: // Fall through. | 134 case GTK_RESPONSE_DELETE_EVENT: // Fall through. |
152 case GTK_RESPONSE_CANCEL: { | 135 case GTK_RESPONSE_CANCEL: { |
153 break; | 136 callback_->Run(PrintingContextCairo::CANCEL); |
137 Release(); // Printing cancelled. | |
138 return; | |
154 } | 139 } |
155 case GTK_RESPONSE_APPLY: | 140 case GTK_RESPONSE_APPLY: |
156 default: { | 141 default: { |
157 NOTREACHED(); | 142 NOTREACHED(); |
158 } | 143 } |
159 } | 144 } |
160 | |
161 // Delete this dialog. | |
162 OnJobCompleted(NULL, NULL); | |
163 } | 145 } |
164 | 146 |
147 void PrintDialogGtk::SaveDocumentToDisk(const NativeMetafile* metafile, | |
148 const string16& document_name) { | |
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
150 | |
151 bool error = false; | |
152 if (!file_util::CreateTemporaryFile(&path_to_pdf_)) { | |
153 LOG(ERROR) << "Creating temporary file failed"; | |
154 error = true; | |
155 } | |
156 | |
157 if (!error) { | |
158 base::FileDescriptor temp_file_fd; | |
159 temp_file_fd.fd = open(path_to_pdf_.value().c_str(), O_WRONLY); | |
160 temp_file_fd.auto_close = true; | |
161 if (!metafile->SaveTo(temp_file_fd)) { | |
162 LOG(ERROR) << "Saving metafile failed"; | |
163 file_util::Delete(path_to_pdf_, false); | |
164 error = true; | |
165 } | |
166 } | |
167 | |
168 // Done saving, let PrintDialogGtk::PrintDocument() continue. | |
169 save_document_event_->Signal(); | |
170 | |
171 if (error) { | |
172 Release(); | |
173 } else { | |
174 // No errors, continue printing. | |
175 BrowserThread::PostTask( | |
176 BrowserThread::UI, FROM_HERE, | |
177 NewRunnableMethod(this, | |
178 &PrintDialogGtk::SendDocumentToPrinter, | |
179 document_name)); | |
180 } | |
181 } | |
182 | |
183 void PrintDialogGtk::SendDocumentToPrinter(const string16& document_name) { | |
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
185 GtkPrintJob* print_job = gtk_print_job_new( | |
186 UTF16ToASCII(document_name).c_str(), | |
Evan Stade
2011/02/15 00:40:46
is the document name guaranteed to be ascii? I sus
Lei Zhang
2011/02/15 01:20:26
Done.
| |
187 printer_, | |
188 gtk_settings_, | |
189 page_setup_); | |
190 gtk_print_job_set_source_file(print_job, path_to_pdf_.value().c_str(), NULL); | |
191 gtk_print_job_send(print_job, OnJobCompletedThunk, this, NULL); | |
192 } | |
193 | |
194 // static | |
165 void PrintDialogGtk::OnJobCompletedThunk(GtkPrintJob* print_job, | 195 void PrintDialogGtk::OnJobCompletedThunk(GtkPrintJob* print_job, |
Evan Stade
2011/02/15 00:40:46
convert this to CHROMEGTK_SIGNAL macro style
Lei Zhang
2011/02/15 01:20:26
Can't. |user_data| is not the last parameter. All
| |
166 gpointer user_data, | 196 gpointer user_data, |
167 GError* error) { | 197 GError* error) { |
168 reinterpret_cast<PrintDialogGtk*>(user_data)->OnJobCompleted(print_job, | 198 static_cast<PrintDialogGtk*>(user_data)->OnJobCompleted(print_job, error); |
169 error); | |
170 } | 199 } |
171 | 200 |
172 void PrintDialogGtk::OnJobCompleted(GtkPrintJob* job, GError* error) { | 201 void PrintDialogGtk::OnJobCompleted(GtkPrintJob* print_job, GError* error) { |
173 gtk_widget_destroy(dialog_); | |
174 | |
175 if (error) | 202 if (error) |
176 LOG(ERROR) << "Printing failed: " << error->message; | 203 LOG(ERROR) << "Printing failed: " << error->message; |
177 | 204 if (print_job) |
178 if (job) | 205 g_object_unref(print_job); |
179 g_object_unref(job); | |
180 | |
181 base::FileUtilProxy::Delete( | 206 base::FileUtilProxy::Delete( |
182 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 207 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
183 path_to_pdf_, | 208 path_to_pdf_, |
184 false, | 209 false, |
185 NULL); | 210 NULL); |
211 Release(); // Printing finished. | |
212 } | |
186 | 213 |
187 delete this; | 214 void PrintDialogGtk::set_save_document_event(base::WaitableEvent* event) { |
215 DCHECK(event); | |
216 DCHECK(!save_document_event_); | |
217 save_document_event_ = event; | |
188 } | 218 } |
OLD | NEW |