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/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 |