chromeos: dbus: add Bluetooth properties support
[chromium-blink-merge.git] / printing / printing_context_mac.mm
blob36971c87e249d74f4b25224081b2208b87329d15
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
3 // found in the LICENSE file.
5 #include "printing/printing_context_mac.h"
7 #import <ApplicationServices/ApplicationServices.h>
8 #import <AppKit/AppKit.h>
10 #include "base/logging.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/mac/scoped_nsautorelease_pool.h"
13 #include "base/mac/scoped_nsexception_enabler.h"
14 #include "base/sys_string_conversions.h"
15 #include "base/values.h"
16 #include "printing/print_settings_initializer_mac.h"
18 namespace printing {
20 // static
21 PrintingContext* PrintingContext::Create(const std::string& app_locale) {
22   return static_cast<PrintingContext*>(new PrintingContextMac(app_locale));
25 PrintingContextMac::PrintingContextMac(const std::string& app_locale)
26     : PrintingContext(app_locale),
27       print_info_([[NSPrintInfo sharedPrintInfo] copy]),
28       context_(NULL) {
31 PrintingContextMac::~PrintingContextMac() {
32   ReleaseContext();
35 void PrintingContextMac::AskUserForSettings(
36     gfx::NativeView parent_view,
37     int max_pages,
38     bool has_selection,
39     const PrintSettingsCallback& callback) {
40   // Third-party print drivers seem to be an area prone to raising exceptions.
41   // This will allow exceptions to be raised, but does not handle them.  The
42   // NSPrintPanel appears to have appropriate NSException handlers.
43   base::mac::ScopedNSExceptionEnabler enabler;
45   // Exceptions can also happen when the NSPrintPanel is being
46   // deallocated, so it must be autoreleased within this scope.
47   base::mac::ScopedNSAutoreleasePool pool;
49   DCHECK([NSThread isMainThread]);
51   // We deliberately don't feed max_pages into the dialog, because setting
52   // NSPrintLastPage makes the print dialog pre-select the option to only print
53   // a range.
55   // TODO(stuartmorgan): implement 'print selection only' (probably requires
56   // adding a new custom view to the panel on 10.5; 10.6 has
57   // NSPrintPanelShowsPrintSelection).
58   NSPrintPanel* panel = [NSPrintPanel printPanel];
59   NSPrintInfo* printInfo = print_info_.get();
61   NSPrintPanelOptions options = [panel options];
62   options |= NSPrintPanelShowsPaperSize;
63   options |= NSPrintPanelShowsOrientation;
64   options |= NSPrintPanelShowsScaling;
65   [panel setOptions:options];
67   // Set the print job title text.
68   if (parent_view) {
69     NSString* job_title = [[parent_view window] title];
70     if (job_title) {
71       PMPrintSettings printSettings =
72           (PMPrintSettings)[printInfo PMPrintSettings];
73       PMPrintSettingsSetJobName(printSettings, (CFStringRef)job_title);
74       [printInfo updateFromPMPrintSettings];
75     }
76   }
78   // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
79   // Will require restructuring the PrintingContext API to use a callback.
80   NSInteger selection = [panel runModalWithPrintInfo:printInfo];
81   if (selection == NSOKButton) {
82     print_info_.reset([[panel printInfo] retain]);
83     InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
84     callback.Run(OK);
85   } else {
86     callback.Run(CANCEL);
87   }
90 PrintingContext::Result PrintingContextMac::UseDefaultSettings() {
91   DCHECK(!in_print_job_);
93   print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
94   InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
96   return OK;
99 PrintingContext::Result PrintingContextMac::UpdatePrinterSettings(
100     const DictionaryValue& job_settings, const PageRanges& ranges) {
101   DCHECK(!in_print_job_);
103   // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
104   // with a clean slate.
105   print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
107   bool collate;
108   int color;
109   bool landscape;
110   bool print_to_pdf;
111   bool is_cloud_dialog;
112   int copies;
113   int duplex_mode;
114   std::string device_name;
116   if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
117       !job_settings.GetBoolean(kSettingCollate, &collate) ||
118       !job_settings.GetInteger(kSettingColor, &color) ||
119       !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
120       !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
121       !job_settings.GetInteger(kSettingCopies, &copies) ||
122       !job_settings.GetString(kSettingDeviceName, &device_name) ||
123       !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog)) {
124     return OnError();
125   }
127   bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
128   bool open_pdf_in_preview = job_settings.HasKey(kSettingOpenPDFInPreview);
130   if (!print_to_pdf && !print_to_cloud && !is_cloud_dialog) {
131     if (!SetPrinter(device_name))
132       return OnError();
134     if (!SetCopiesInPrintSettings(copies))
135       return OnError();
137     if (!SetCollateInPrintSettings(collate))
138       return OnError();
140     if (!SetDuplexModeInPrintSettings(
141             static_cast<DuplexMode>(duplex_mode))) {
142       return OnError();
143     }
145     if (!SetOutputColor(color))
146       return OnError();
147   }
148   if (open_pdf_in_preview) {
149     if (!SetPrintPreviewJob())
150       return OnError();
151   }
153   if (!UpdatePageFormatWithPaperInfo())
154     return OnError();
156   if (!SetOrientationIsLandscape(landscape))
157     return OnError();
159   [print_info_.get() updateFromPMPrintSettings];
161   InitPrintSettingsFromPrintInfo(ranges);
162   return OK;
165 bool PrintingContextMac::SetPrintPreviewJob() {
166   PMPrintSession print_session =
167       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
168   PMPrintSettings print_settings =
169       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
170   return PMSessionSetDestination(
171       print_session, print_settings, kPMDestinationPreview,
172       NULL, NULL) == noErr;
175 void PrintingContextMac::InitPrintSettingsFromPrintInfo(
176     const PageRanges& ranges) {
177   PMPrintSession print_session =
178       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
179   PMPageFormat page_format =
180       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
181   PMPrinter printer;
182   PMSessionGetCurrentPrinter(print_session, &printer);
183   PrintSettingsInitializerMac::InitPrintSettings(
184       printer, page_format, ranges, false, &settings_);
187 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
188   DCHECK(print_info_.get());
189   PMPrintSession print_session =
190       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
192   PMPrinter current_printer;
193   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
194     return false;
196   CFStringRef current_printer_id = PMPrinterGetID(current_printer);
197   if (!current_printer_id)
198     return false;
200   base::mac::ScopedCFTypeRef<CFStringRef> new_printer_id(
201       base::SysUTF8ToCFStringRef(device_name));
202   if (!new_printer_id.get())
203     return false;
205   if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
206           kCFCompareEqualTo) {
207     return true;
208   }
210   PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
211   if (new_printer == NULL)
212     return false;
214   OSStatus status = PMSessionSetCurrentPMPrinter(print_session, new_printer);
215   PMRelease(new_printer);
216   return status == noErr;
219 bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
220   PMPrintSession print_session =
221       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
223   PMPageFormat default_page_format =
224       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
226   PMPaper default_paper;
227   if (PMGetPageFormatPaper(default_page_format, &default_paper) != noErr)
228     return false;
230   double default_page_width, default_page_height;
231   if (PMPaperGetWidth(default_paper, &default_page_width) != noErr)
232     return false;
234   if (PMPaperGetHeight(default_paper, &default_page_height) != noErr)
235     return false;
237   PMPrinter current_printer = NULL;
238   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
239     return false;
241   if (current_printer == nil)
242     return false;
244   CFArrayRef paper_list = NULL;
245   if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
246     return false;
248   PMPaper best_matching_paper = kPMNoData;
249   int num_papers = CFArrayGetCount(paper_list);
250   for (int i = 0; i < num_papers; ++i) {
251     PMPaper paper = (PMPaper) [(NSArray* ) paper_list objectAtIndex: i];
252     double paper_width, paper_height;
253     PMPaperGetWidth(paper, &paper_width);
254     PMPaperGetHeight(paper, &paper_height);
255     if (default_page_width == paper_width &&
256         default_page_height == paper_height) {
257       best_matching_paper = paper;
258       break;
259     }
260     // Trying to find the best matching paper.
261     if (fabs(default_page_width - paper_width) < 2 &&
262         fabs(default_page_height - paper_height) < 2) {
263       best_matching_paper = paper;
264     }
265   }
267   if (best_matching_paper == kPMNoData) {
268     PMPaper paper = kPMNoData;
269     // Create a custom paper for the specified default page size.
270     PMPaperMargins default_margins;
271     if (PMPaperGetMargins(default_paper, &default_margins) != noErr)
272       return false;
274     const PMPaperMargins margins =
275         {default_margins.top, default_margins.left, default_margins.bottom,
276          default_margins.right};
277     CFStringRef paper_id = CFSTR("Custom paper ID");
278     CFStringRef paper_name = CFSTR("Custom paper");
279     if (PMPaperCreateCustom(current_printer, paper_id, paper_name,
280             default_page_width, default_page_height, &margins, &paper) !=
281             noErr) {
282       return false;
283     }
284     [print_info_.get() updateFromPMPageFormat];
285     PMRelease(paper);
286   } else {
287     PMPageFormat chosen_page_format = NULL;
288     if (PMCreatePageFormat((PMPageFormat*) &chosen_page_format) != noErr)
289       return false;
291     // Create page format from that paper.
292     if (PMCreatePageFormatWithPMPaper(&chosen_page_format,
293             best_matching_paper) != noErr) {
294       PMRelease(chosen_page_format);
295       return false;
296     }
297     // Copy over the original format with the new page format.
298     if (PMCopyPageFormat(chosen_page_format, default_page_format) != noErr) {
299       PMRelease(chosen_page_format);
300       return false;
301     }
302     [print_info_.get() updateFromPMPageFormat];
303     PMRelease(chosen_page_format);
304   }
305   return true;
308 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
309   if (copies < 1)
310     return false;
312   PMPrintSettings pmPrintSettings =
313       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
314   return PMSetCopies(pmPrintSettings, copies, false) == noErr;
317 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
318   PMPrintSettings pmPrintSettings =
319       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
320   return PMSetCollate(pmPrintSettings, collate) == noErr;
323 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
324   PMPageFormat page_format =
325       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
327   PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
329   if (PMSetOrientation(page_format, orientation, false) != noErr)
330     return false;
332   PMPrintSession print_session =
333       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
335   PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
337   [print_info_.get() updateFromPMPageFormat];
338   return true;
341 bool PrintingContextMac::SetDuplexModeInPrintSettings(DuplexMode mode) {
342   PMDuplexMode duplexSetting;
343   switch (mode) {
344     case LONG_EDGE:
345       duplexSetting = kPMDuplexNoTumble;
346       break;
347     case SHORT_EDGE:
348       duplexSetting = kPMDuplexTumble;
349       break;
350     case SIMPLEX:
351       duplexSetting = kPMDuplexNone;
352       break;
353     default:  // UNKNOWN_DUPLEX_MODE
354       return true;
355   }
357   PMPrintSettings pmPrintSettings =
358       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
359   return PMSetDuplex(pmPrintSettings, duplexSetting) == noErr;
362 bool PrintingContextMac::SetOutputColor(int color_mode) {
363   PMPrintSettings pmPrintSettings =
364       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
365   std::string color_setting_name;
366   std::string color_value;
367   GetColorModelForMode(color_mode, &color_setting_name, &color_value);
368   base::mac::ScopedCFTypeRef<CFStringRef> color_setting(
369       base::SysUTF8ToCFStringRef(color_setting_name));
370   base::mac::ScopedCFTypeRef<CFStringRef> output_color(
371       base::SysUTF8ToCFStringRef(color_value));
373   return PMPrintSettingsSetValue(pmPrintSettings,
374                                  color_setting.get(),
375                                  output_color.get(),
376                                  false) == noErr;
379 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
380   PageRanges page_ranges;
381   NSDictionary* print_info_dict = [print_info_.get() dictionary];
382   if (![[print_info_dict objectForKey:NSPrintAllPages] boolValue]) {
383     PageRange range;
384     range.from = [[print_info_dict objectForKey:NSPrintFirstPage] intValue] - 1;
385     range.to = [[print_info_dict objectForKey:NSPrintLastPage] intValue] - 1;
386     page_ranges.push_back(range);
387   }
388   return page_ranges;
391 PrintingContext::Result PrintingContextMac::InitWithSettings(
392     const PrintSettings& settings) {
393   DCHECK(!in_print_job_);
395   settings_ = settings;
397   NOTIMPLEMENTED();
399   return FAILED;
402 PrintingContext::Result PrintingContextMac::NewDocument(
403     const string16& document_name) {
404   DCHECK(!in_print_job_);
406   in_print_job_ = true;
408   PMPrintSession print_session =
409       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
410   PMPrintSettings print_settings =
411       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
412   PMPageFormat page_format =
413       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
415   base::mac::ScopedCFTypeRef<CFStringRef> job_title(
416       base::SysUTF16ToCFStringRef(document_name));
417   PMPrintSettingsSetJobName(print_settings, job_title.get());
419   OSStatus status = PMSessionBeginCGDocumentNoDialog(print_session,
420                                                      print_settings,
421                                                      page_format);
422   if (status != noErr)
423     return OnError();
425   return OK;
428 PrintingContext::Result PrintingContextMac::NewPage() {
429   if (abort_printing_)
430     return CANCEL;
431   DCHECK(in_print_job_);
432   DCHECK(!context_);
434   PMPrintSession print_session =
435       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
436   PMPageFormat page_format =
437       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
438   OSStatus status;
439   status = PMSessionBeginPageNoDialog(print_session, page_format, NULL);
440   if (status != noErr)
441     return OnError();
442   status = PMSessionGetCGGraphicsContext(print_session, &context_);
443   if (status != noErr)
444     return OnError();
446   return OK;
449 PrintingContext::Result PrintingContextMac::PageDone() {
450   if (abort_printing_)
451     return CANCEL;
452   DCHECK(in_print_job_);
453   DCHECK(context_);
455   PMPrintSession print_session =
456       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
457   OSStatus status = PMSessionEndPageNoDialog(print_session);
458   if (status != noErr)
459     OnError();
460   context_ = NULL;
462   return OK;
465 PrintingContext::Result PrintingContextMac::DocumentDone() {
466   if (abort_printing_)
467     return CANCEL;
468   DCHECK(in_print_job_);
470   PMPrintSession print_session =
471       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
472   OSStatus status = PMSessionEndDocumentNoDialog(print_session);
473   if (status != noErr)
474     OnError();
476   ResetSettings();
477   return OK;
480 void PrintingContextMac::Cancel() {
481   abort_printing_ = true;
482   in_print_job_ = false;
483   context_ = NULL;
485   PMPrintSession print_session =
486       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
487   PMSessionEndPageNoDialog(print_session);
490 void PrintingContextMac::ReleaseContext() {
491   print_info_.reset();
492   context_ = NULL;
495 gfx::NativeDrawingContext PrintingContextMac::context() const {
496   return context_;
499 }  // namespace printing