Chromium Code Reviews| 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(); | |
|
sanjeevr
2011/02/17 21:09:48
What thread does this run on? Waiting for the docu
Lei Zhang
2011/02/18 00:10:08
This is on the print worker thread. I understand i
| |
| 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; | |
| 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 |