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 dialog->AddRef(); // Manual AddRef since PrintingContextCairo is not suppose | |
Evan Stade
2011/02/14 23:30:54
nit: supposed
nit: comment above the code, rather
Lei Zhang
2011/02/14 23:47:31
Done.
| |
31 // to know about PrintDialogGtk. Thus it cannot put this | |
32 // ref-counted class in a scoped_refptr. | |
33 return dialog; | |
76 } | 34 } |
77 | 35 |
78 // static | 36 // static |
79 bool PrintDialogGtk::DialogShowing() { | 37 void PrintDialogGtk::PrintDocument(void* print_dialog, |
80 base::AutoLock lock(DialogLock()); | 38 const NativeMetafile* metafile, |
81 return !!g_print_dialog; | 39 const string16& document_name) { |
40 PrintDialogGtk* dialog = static_cast<PrintDialogGtk*>(print_dialog); | |
41 | |
42 scoped_ptr<base::WaitableEvent> event; | |
43 event.reset(new base::WaitableEvent(false, false)); | |
44 dialog->set_save_document_event(event.get()); | |
45 BrowserThread::PostTask( | |
46 BrowserThread::FILE, FROM_HERE, | |
47 NewRunnableMethod(dialog, | |
48 &PrintDialogGtk::SaveDocumentToDisk, | |
49 metafile, | |
50 document_name)); | |
51 event->Wait(); // Wait for SaveDocumentToDisk() to finish. | |
82 } | 52 } |
83 | 53 |
84 // static | 54 PrintDialogGtk::PrintDialogGtk( |
85 void PrintDialogGtk::CreateDialogImpl(const FilePath& path) { | 55 PrintingContextCairo::PrintSettingsCallback* callback, |
86 // Only show one print dialog at once. This is to prevent a page from | 56 PrintingContextCairo* context) |
87 // locking up the system with | 57 : callback_(callback), |
88 // | 58 context_(context), |
89 // while(true){print();} | 59 dialog_(NULL), |
90 base::AutoLock lock(DialogLock()); | 60 page_setup_(NULL), |
91 if (g_print_dialog) { | 61 printer_(NULL), |
92 // Clean up the temporary file. | 62 gtk_settings_(NULL), |
93 base::FileUtilProxy::Delete( | 63 save_document_event_(NULL) { |
94 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 64 GtkWindow* parent = BrowserList::GetLastActive()->window()->GetNativeHandle(); |
95 path, false, NULL); | |
96 return; | |
97 } | |
98 | |
99 g_print_dialog = new PrintDialogGtk(path); | |
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_), | |
Lei Zhang
2011/02/14 23:47:31
This is GTK 2.18+. Does that mean I need to remove
| |
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 // Fall through. | |
Evan Stade
2011/02/14 23:30:54
why is it unexpected to get a cancel?
Lei Zhang
2011/02/14 23:47:31
Whoops, meant to put a break there.
| |
154 } | 138 } |
155 case GTK_RESPONSE_APPLY: | 139 case GTK_RESPONSE_APPLY: |
156 default: { | 140 default: { |
157 NOTREACHED(); | 141 NOTREACHED(); |
158 } | 142 } |
159 } | 143 } |
160 | 144 Release(); // Printing cancelled. |
Evan Stade
2011/02/14 23:30:54
from the comment, it seems like this would make mo
Lei Zhang
2011/02/14 23:47:31
Done.
| |
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 base::FileDescriptor temp_file_fd; | |
158 temp_file_fd.fd = open(path_to_pdf_.value().c_str(), O_WRONLY); | |
Evan Stade
2011/02/14 23:30:54
seems odd to do this stuff if error is true
Lei Zhang
2011/02/14 23:47:31
Done.
| |
159 temp_file_fd.auto_close = true; | |
160 if (!metafile->SaveTo(temp_file_fd)) { | |
161 LOG(ERROR) << "Saving metafile failed"; | |
162 file_util::Delete(path_to_pdf_, false); | |
163 error = true; | |
164 } | |
165 | |
166 // Done saving, let PrintDialogGtk::PrintDocument() continue. | |
167 save_document_event_->Signal(); | |
168 | |
169 if (error) { | |
170 Release(); // Error encountered, bail out. | |
Evan Stade
2011/02/14 23:30:54
comment a little obvious imo
Lei Zhang
2011/02/14 23:47:31
Removed.
| |
171 } else { | |
172 // No errors, continue printing. | |
173 BrowserThread::PostTask( | |
174 BrowserThread::UI, FROM_HERE, | |
175 NewRunnableMethod(this, | |
176 &PrintDialogGtk::SendDocumentToPrinter, | |
177 document_name)); | |
178 } | |
179 } | |
180 | |
181 void PrintDialogGtk::SendDocumentToPrinter(const string16& document_name) { | |
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
183 GtkPrintJob* print_job = gtk_print_job_new( | |
184 UTF16ToASCII(document_name).c_str(), | |
185 printer_, | |
186 gtk_settings_, | |
187 page_setup_); | |
188 gtk_print_job_set_source_file(print_job, path_to_pdf_.value().c_str(), NULL); | |
189 gtk_print_job_send(print_job, OnJobCompletedThunk, this, NULL); | |
190 } | |
191 | |
192 // static | |
165 void PrintDialogGtk::OnJobCompletedThunk(GtkPrintJob* print_job, | 193 void PrintDialogGtk::OnJobCompletedThunk(GtkPrintJob* print_job, |
166 gpointer user_data, | 194 gpointer user_data, |
167 GError* error) { | 195 GError* error) { |
168 reinterpret_cast<PrintDialogGtk*>(user_data)->OnJobCompleted(print_job, | 196 static_cast<PrintDialogGtk*>(user_data)->OnJobCompleted(print_job, error); |
169 error); | |
170 } | 197 } |
171 | 198 |
172 void PrintDialogGtk::OnJobCompleted(GtkPrintJob* job, GError* error) { | 199 void PrintDialogGtk::OnJobCompleted(GtkPrintJob* print_job, GError* error) { |
173 gtk_widget_destroy(dialog_); | |
174 | |
175 if (error) | 200 if (error) |
176 LOG(ERROR) << "Printing failed: " << error->message; | 201 LOG(ERROR) << "Printing failed: " << error->message; |
177 | 202 if (print_job) |
178 if (job) | 203 g_object_unref(print_job); |
179 g_object_unref(job); | |
180 | |
181 base::FileUtilProxy::Delete( | 204 base::FileUtilProxy::Delete( |
182 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 205 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
183 path_to_pdf_, | 206 path_to_pdf_, |
184 false, | 207 false, |
185 NULL); | 208 NULL); |
209 Release(); // Printing finished. | |
210 } | |
186 | 211 |
187 delete this; | 212 void PrintDialogGtk::set_save_document_event(base::WaitableEvent* event) { |
213 DCHECK(event); | |
214 DCHECK(!save_document_event_); | |
215 save_document_event_ = event; | |
188 } | 216 } |
OLD | NEW |