1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h"
7 #include "mozilla/gfx/PrintTargetCG.h"
8 #include "mozilla/Preferences.h"
10 #include "nsPrintDialogX.h"
11 #include "nsIPrintSettings.h"
12 #include "nsIPrintSettingsService.h"
13 #include "nsPrintSettingsX.h"
15 #include "nsQueryObject.h"
16 #include "nsServiceManagerUtils.h"
17 #include "nsIStringBundle.h"
20 #import <Cocoa/Cocoa.h>
21 #include "nsObjCExceptions.h"
23 using namespace mozilla;
24 using mozilla::gfx::PrintTarget;
26 NS_IMPL_ISUPPORTS(nsPrintDialogServiceX, nsIPrintDialogService)
28 // Splits our single pages-per-sheet count for native NSPrintInfo:
29 static void setPagesPerSheet(NSPrintInfo* aPrintInfo, int32_t aPPS) {
31 // Assumes portrait - we'll swap if landscape.
58 if ([aPrintInfo orientation] == NSPaperOrientationLandscape) {
59 std::swap(across, down);
62 NSMutableDictionary* dict = [aPrintInfo dictionary];
64 [dict setObject:[NSNumber numberWithInt:across] forKey:@"NSPagesAcross"];
65 [dict setObject:[NSNumber numberWithInt:down] forKey:@"NSPagesDown"];
68 nsPrintDialogServiceX::nsPrintDialogServiceX() {}
70 nsPrintDialogServiceX::~nsPrintDialogServiceX() {}
73 nsPrintDialogServiceX::Init() { return NS_OK; }
76 nsPrintDialogServiceX::ShowPrintDialog(mozIDOMWindowProxy* aParent,
78 nsIPrintSettings* aSettings) {
79 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
81 MOZ_ASSERT(aSettings, "aSettings must not be null");
83 RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(aSettings));
85 return NS_ERROR_FAILURE;
88 NSPrintInfo* printInfo =
89 settingsX->CreateOrCopyPrintInfo(/* aWithScaling = */ true);
90 if (NS_WARN_IF(!printInfo)) {
91 return NS_ERROR_FAILURE;
93 [printInfo autorelease];
95 // Set the print job title
97 nsresult rv = aSettings->GetTitle(docName);
98 if (NS_SUCCEEDED(rv)) {
99 nsAutoString adjustedTitle;
100 PrintTarget::AdjustPrintJobNameForIPP(docName, adjustedTitle);
101 CFStringRef cfTitleString = CFStringCreateWithCharacters(
102 NULL, reinterpret_cast<const UniChar*>(adjustedTitle.BeginReading()),
103 adjustedTitle.Length());
105 auto pmPrintSettings =
106 static_cast<PMPrintSettings>([printInfo PMPrintSettings]);
107 ::PMPrintSettingsSetJobName(pmPrintSettings, cfTitleString);
108 [printInfo updateFromPMPrintSettings];
109 CFRelease(cfTitleString);
113 // Temporarily set the pages-per-sheet count set in our print preview to
114 // pre-populate the system dialog with the same value:
115 int32_t pagesPerSheet;
116 aSettings->GetNumPagesPerSheet(&pagesPerSheet);
117 setPagesPerSheet(printInfo, pagesPerSheet);
119 // Put the print info into the current print operation, since that's where
120 // [panel runModal] will look for it. We create the view because otherwise
121 // we'll get unrelated warnings printed to the console.
122 NSView* tmpView = [[NSView alloc] init];
123 NSPrintOperation* printOperation =
124 [NSPrintOperation printOperationWithView:tmpView printInfo:printInfo];
125 [NSPrintOperation setCurrentOperation:printOperation];
127 NSPrintPanel* panel = [NSPrintPanel printPanel];
128 [panel setOptions:NSPrintPanelShowsCopies | NSPrintPanelShowsPageRange |
129 NSPrintPanelShowsPaperSize | NSPrintPanelShowsOrientation |
130 NSPrintPanelShowsScaling];
131 PrintPanelAccessoryController* viewController =
132 [[PrintPanelAccessoryController alloc] initWithSettings:aSettings
133 haveSelection:aHaveSelection];
134 [panel addAccessoryController:viewController];
135 [viewController release];
138 nsCocoaUtils::PrepareForNativeAppModalDialog();
139 int button = [panel runModal];
140 nsCocoaUtils::CleanUpAfterNativeAppModalDialog();
142 // Retrieve a printInfo with the updated settings. (The NSPrintOperation
143 // operates on a copy, so the object we passed in will not have been
145 NSPrintInfo* result = [[NSPrintOperation currentOperation] printInfo];
147 return NS_ERROR_FAILURE;
150 [NSPrintOperation setCurrentOperation:nil];
153 if (button != NSModalResponseOK) {
154 return NS_ERROR_ABORT;
157 // We handle pages-per-sheet internally and we want to prevent the macOS
158 // printing code from also applying the pages-per-sheet count. So we need
159 // to move the count off the NSPrintInfo and over to the nsIPrintSettings.
160 NSMutableDictionary* dict = [result dictionary];
161 auto pagesAcross = [[dict objectForKey:@"NSPagesAcross"] intValue];
162 auto pagesDown = [[dict objectForKey:@"NSPagesDown"] intValue];
163 [dict setObject:[NSNumber numberWithUnsignedInt:1] forKey:@"NSPagesAcross"];
164 [dict setObject:[NSNumber numberWithUnsignedInt:1] forKey:@"NSPagesDown"];
165 aSettings->SetNumPagesPerSheet(pagesAcross * pagesDown);
168 [viewController exportSettings];
170 // Update our settings object based on the user's choices in the dialog.
171 // We tell settingsX to adopt this printInfo so that it will be used to run
172 // print job, so that any printer-specific custom settings from print dialog
173 // extension panels will be carried through.
174 settingsX->SetFromPrintInfo(result, /* aAdoptPrintInfo = */ true);
178 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
182 nsPrintDialogServiceX::ShowPageSetupDialog(mozIDOMWindowProxy* aParent,
183 nsIPrintSettings* aNSSettings) {
184 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
186 MOZ_ASSERT(aParent, "aParent must not be null");
187 MOZ_ASSERT(aNSSettings, "aSettings must not be null");
188 NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
190 RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(aNSSettings));
192 return NS_ERROR_FAILURE;
195 NSPrintInfo* printInfo =
196 settingsX->CreateOrCopyPrintInfo(/* aWithScaling = */ true);
197 if (NS_WARN_IF(!printInfo)) {
198 return NS_ERROR_FAILURE;
200 [printInfo autorelease];
202 NSPageLayout* pageLayout = [NSPageLayout pageLayout];
203 nsCocoaUtils::PrepareForNativeAppModalDialog();
204 int button = [pageLayout runModalWithPrintInfo:printInfo];
205 nsCocoaUtils::CleanUpAfterNativeAppModalDialog();
207 if (button == NSModalResponseOK) {
208 // The Page Setup dialog does not include non-standard settings that need to
209 // be preserved, separate from what the base printSettings object handles,
210 // so we do not need it to adopt the printInfo object here.
211 settingsX->SetFromPrintInfo(printInfo, /* aAdoptPrintInfo = */ false);
212 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
213 do_GetService("@mozilla.org/gfx/printsettings-service;1");
214 if (printSettingsService &&
215 Preferences::GetBool("print.save_print_settings", false)) {
216 uint32_t flags = nsIPrintSettings::kInitSavePaperSize |
217 nsIPrintSettings::kInitSaveOrientation |
218 nsIPrintSettings::kInitSaveScaling;
219 printSettingsService->MaybeSavePrintSettingsToPrefs(aNSSettings, flags);
223 return NS_ERROR_ABORT;
225 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
230 @interface PrintPanelAccessoryView (Private)
232 - (NSString*)localizedString:(const char*)aKey;
234 - (const char*)headerFooterStringForList:(NSPopUpButton*)aList;
236 - (void)exportHeaderFooterSettings;
240 - (NSTextField*)label:(const char*)aLabel
241 withFrame:(NSRect)aRect
242 alignment:(NSTextAlignment)aAlignment;
244 - (void)addLabel:(const char*)aLabel
245 withFrame:(NSRect)aRect
246 alignment:(NSTextAlignment)aAlignment;
248 - (void)addLabel:(const char*)aLabel withFrame:(NSRect)aRect;
250 - (void)addCenteredLabel:(const char*)aLabel withFrame:(NSRect)aRect;
252 - (NSButton*)checkboxWithLabel:(const char*)aLabel andFrame:(NSRect)aRect;
254 - (NSPopUpButton*)headerFooterItemListWithFrame:(NSRect)aRect
256 (const nsAString&)aCurrentString;
258 - (void)addOptionsSection:(bool)aHaveSelection;
260 - (void)addAppearanceSection;
262 - (void)addHeaderFooterSection;
264 - (NSString*)summaryValueForCheckbox:(NSButton*)aCheckbox;
266 - (NSString*)headerSummaryValue;
268 - (NSString*)footerSummaryValue;
272 static const char sHeaderFooterTags[][4] = {"", "&T", "&U", "&D", "&P", "&PT"};
274 @implementation PrintPanelAccessoryView
278 - (id)initWithSettings:(nsIPrintSettings*)aSettings
279 haveSelection:(bool)aHaveSelection {
280 [super initWithFrame:NSMakeRect(0, 0, 540, 185)];
282 mSettings = aSettings;
284 [self addOptionsSection:aHaveSelection];
285 [self addAppearanceSection];
286 [self addHeaderFooterSection];
291 - (void)exportSettings {
292 mSettings->SetPrintSelectionOnly([mPrintSelectionOnlyCheckbox state] ==
293 NSControlStateValueOn);
294 mSettings->SetShrinkToFit([mShrinkToFitCheckbox state] ==
295 NSControlStateValueOn);
296 mSettings->SetPrintBGColors([mPrintBGColorsCheckbox state] ==
297 NSControlStateValueOn);
298 mSettings->SetPrintBGImages([mPrintBGImagesCheckbox state] ==
299 NSControlStateValueOn);
301 [self exportHeaderFooterSettings];
305 NS_IF_RELEASE(mPrintBundle);
312 nsCOMPtr<nsIStringBundleService> bundleSvc =
313 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
314 bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties",
318 - (NSString*)localizedString:(const char*)aKey {
319 if (!mPrintBundle) return @"";
321 nsAutoString intlString;
322 mPrintBundle->GetStringFromName(aKey, intlString);
323 NSMutableString* s = [NSMutableString
324 stringWithUTF8String:NS_ConvertUTF16toUTF8(intlString).get()];
326 // Remove all underscores (they're used in the GTK dialog for accesskeys).
327 [s replaceOccurrencesOfString:@"_"
330 range:NSMakeRange(0, [s length])];
336 - (NSTextField*)label:(const char*)aLabel
337 withFrame:(NSRect)aRect
338 alignment:(NSTextAlignment)aAlignment {
339 NSTextField* label = [[[NSTextField alloc] initWithFrame:aRect] autorelease];
340 [label setStringValue:[self localizedString:aLabel]];
341 [label setEditable:NO];
342 [label setSelectable:NO];
343 [label setBezeled:NO];
344 [label setBordered:NO];
345 [label setDrawsBackground:NO];
346 [label setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
347 [label setAlignment:aAlignment];
351 - (void)addLabel:(const char*)aLabel
352 withFrame:(NSRect)aRect
353 alignment:(NSTextAlignment)aAlignment {
354 NSTextField* label = [self label:aLabel withFrame:aRect alignment:aAlignment];
355 [self addSubview:label];
358 - (void)addLabel:(const char*)aLabel withFrame:(NSRect)aRect {
359 [self addLabel:aLabel withFrame:aRect alignment:NSTextAlignmentRight];
362 - (void)addCenteredLabel:(const char*)aLabel withFrame:(NSRect)aRect {
363 [self addLabel:aLabel withFrame:aRect alignment:NSTextAlignmentCenter];
366 - (NSButton*)checkboxWithLabel:(const char*)aLabel andFrame:(NSRect)aRect {
367 aRect.origin.y += 4.0f;
368 NSButton* checkbox = [[[NSButton alloc] initWithFrame:aRect] autorelease];
369 [checkbox setButtonType:NSButtonTypeSwitch];
370 [checkbox setTitle:[self localizedString:aLabel]];
371 [checkbox setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
372 [checkbox sizeToFit];
376 - (NSPopUpButton*)headerFooterItemListWithFrame:(NSRect)aRect
378 (const nsAString&)aCurrentString {
379 NSPopUpButton* list = [[[NSPopUpButton alloc] initWithFrame:aRect
380 pullsDown:NO] autorelease];
381 [list setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
382 [[list cell] setControlSize:NSControlSizeSmall];
383 NSArray* items = [NSArray
384 arrayWithObjects:[self localizedString:"headerFooterBlank"],
385 [self localizedString:"headerFooterTitle"],
386 [self localizedString:"headerFooterURL"],
387 [self localizedString:"headerFooterDate"],
388 [self localizedString:"headerFooterPage"],
389 [self localizedString:"headerFooterPageTotal"], nil];
390 [list addItemsWithTitles:items];
392 NS_ConvertUTF16toUTF8 currentStringUTF8(aCurrentString);
393 for (unsigned int i = 0; i < ArrayLength(sHeaderFooterTags); i++) {
394 if (!strcmp(currentStringUTF8.get(), sHeaderFooterTags[i])) {
395 [list selectItemAtIndex:i];
405 - (void)addOptionsSection:(bool)aHaveSelection {
407 [self addLabel:"optionsTitleMac" withFrame:NSMakeRect(0, 155, 151, 22)];
409 // "Print Selection Only"
410 mPrintSelectionOnlyCheckbox =
411 [self checkboxWithLabel:"selectionOnly"
412 andFrame:NSMakeRect(156, 155, 0, 0)];
413 [mPrintSelectionOnlyCheckbox setEnabled:aHaveSelection];
415 if (mSettings->GetPrintSelectionOnly()) {
416 [mPrintSelectionOnlyCheckbox setState:NSControlStateValueOn];
419 [self addSubview:mPrintSelectionOnlyCheckbox];
422 mShrinkToFitCheckbox = [self checkboxWithLabel:"shrinkToFit"
423 andFrame:NSMakeRect(156, 133, 0, 0)];
426 mSettings->GetShrinkToFit(&shrinkToFit);
427 [mShrinkToFitCheckbox
428 setState:(shrinkToFit ? NSControlStateValueOn : NSControlStateValueOff)];
430 [self addSubview:mShrinkToFitCheckbox];
433 - (void)addAppearanceSection {
435 [self addLabel:"appearanceTitleMac" withFrame:NSMakeRect(0, 103, 151, 22)];
437 // "Print Background Colors"
438 mPrintBGColorsCheckbox = [self checkboxWithLabel:"printBGColors"
439 andFrame:NSMakeRect(156, 103, 0, 0)];
441 bool geckoBool = mSettings->GetPrintBGColors();
442 [mPrintBGColorsCheckbox
443 setState:(geckoBool ? NSControlStateValueOn : NSControlStateValueOff)];
445 [self addSubview:mPrintBGColorsCheckbox];
447 // "Print Background Images"
448 mPrintBGImagesCheckbox = [self checkboxWithLabel:"printBGImages"
449 andFrame:NSMakeRect(156, 81, 0, 0)];
451 geckoBool = mSettings->GetPrintBGImages();
452 [mPrintBGImagesCheckbox
453 setState:(geckoBool ? NSControlStateValueOn : NSControlStateValueOff)];
455 [self addSubview:mPrintBGImagesCheckbox];
458 - (void)addHeaderFooterSection {
460 [self addLabel:"pageHeadersTitleMac" withFrame:NSMakeRect(0, 44, 151, 22)];
461 [self addLabel:"pageFootersTitleMac" withFrame:NSMakeRect(0, 0, 151, 22)];
462 [self addCenteredLabel:"left" withFrame:NSMakeRect(156, 22, 100, 22)];
463 [self addCenteredLabel:"center" withFrame:NSMakeRect(256, 22, 100, 22)];
464 [self addCenteredLabel:"right" withFrame:NSMakeRect(356, 22, 100, 22)];
469 mSettings->GetHeaderStrLeft(sel);
471 [self headerFooterItemListWithFrame:NSMakeRect(156, 44, 100, 22)
473 [self addSubview:mHeaderLeftList];
475 mSettings->GetHeaderStrCenter(sel);
477 [self headerFooterItemListWithFrame:NSMakeRect(256, 44, 100, 22)
479 [self addSubview:mHeaderCenterList];
481 mSettings->GetHeaderStrRight(sel);
483 [self headerFooterItemListWithFrame:NSMakeRect(356, 44, 100, 22)
485 [self addSubview:mHeaderRightList];
487 mSettings->GetFooterStrLeft(sel);
489 [self headerFooterItemListWithFrame:NSMakeRect(156, 0, 100, 22)
491 [self addSubview:mFooterLeftList];
493 mSettings->GetFooterStrCenter(sel);
495 [self headerFooterItemListWithFrame:NSMakeRect(256, 0, 100, 22)
497 [self addSubview:mFooterCenterList];
499 mSettings->GetFooterStrRight(sel);
501 [self headerFooterItemListWithFrame:NSMakeRect(356, 0, 100, 22)
503 [self addSubview:mFooterRightList];
508 - (const char*)headerFooterStringForList:(NSPopUpButton*)aList {
509 NSInteger index = [aList indexOfSelectedItem];
510 NS_ASSERTION(index < NSInteger(ArrayLength(sHeaderFooterTags)),
511 "Index of dropdown is higher than expected!");
512 return sHeaderFooterTags[index];
515 - (void)exportHeaderFooterSettings {
516 const char* headerFooterStr;
517 headerFooterStr = [self headerFooterStringForList:mHeaderLeftList];
518 mSettings->SetHeaderStrLeft(NS_ConvertUTF8toUTF16(headerFooterStr));
520 headerFooterStr = [self headerFooterStringForList:mHeaderCenterList];
521 mSettings->SetHeaderStrCenter(NS_ConvertUTF8toUTF16(headerFooterStr));
523 headerFooterStr = [self headerFooterStringForList:mHeaderRightList];
524 mSettings->SetHeaderStrRight(NS_ConvertUTF8toUTF16(headerFooterStr));
526 headerFooterStr = [self headerFooterStringForList:mFooterLeftList];
527 mSettings->SetFooterStrLeft(NS_ConvertUTF8toUTF16(headerFooterStr));
529 headerFooterStr = [self headerFooterStringForList:mFooterCenterList];
530 mSettings->SetFooterStrCenter(NS_ConvertUTF8toUTF16(headerFooterStr));
532 headerFooterStr = [self headerFooterStringForList:mFooterRightList];
533 mSettings->SetFooterStrRight(NS_ConvertUTF8toUTF16(headerFooterStr));
538 - (NSString*)summaryValueForCheckbox:(NSButton*)aCheckbox {
539 if (![aCheckbox isEnabled]) return [self localizedString:"summaryNAValue"];
541 return [aCheckbox state] == NSControlStateValueOn
542 ? [self localizedString:"summaryOnValue"]
543 : [self localizedString:"summaryOffValue"];
546 - (NSString*)headerSummaryValue {
547 return [[mHeaderLeftList titleOfSelectedItem]
548 stringByAppendingString:
550 stringByAppendingString:
551 [[mHeaderCenterList titleOfSelectedItem]
552 stringByAppendingString:
553 [@", " stringByAppendingString:
554 [mHeaderRightList titleOfSelectedItem]]]]];
557 - (NSString*)footerSummaryValue {
558 return [[mFooterLeftList titleOfSelectedItem]
559 stringByAppendingString:
561 stringByAppendingString:
562 [[mFooterCenterList titleOfSelectedItem]
563 stringByAppendingString:
564 [@", " stringByAppendingString:
565 [mFooterRightList titleOfSelectedItem]]]]];
568 - (NSArray*)localizedSummaryItems {
572 dictionaryWithObjectsAndKeys:
573 [self localizedString:"summarySelectionOnlyTitle"],
574 NSPrintPanelAccessorySummaryItemNameKey,
575 [self summaryValueForCheckbox:mPrintSelectionOnlyCheckbox],
576 NSPrintPanelAccessorySummaryItemDescriptionKey, nil],
577 [NSDictionary dictionaryWithObjectsAndKeys:
578 [self localizedString:"summaryShrinkToFitTitle"],
579 NSPrintPanelAccessorySummaryItemNameKey,
580 [self summaryValueForCheckbox:mShrinkToFitCheckbox],
581 NSPrintPanelAccessorySummaryItemDescriptionKey,
584 dictionaryWithObjectsAndKeys:
585 [self localizedString:"summaryPrintBGColorsTitle"],
586 NSPrintPanelAccessorySummaryItemNameKey,
587 [self summaryValueForCheckbox:mPrintBGColorsCheckbox],
588 NSPrintPanelAccessorySummaryItemDescriptionKey, nil],
590 dictionaryWithObjectsAndKeys:
591 [self localizedString:"summaryPrintBGImagesTitle"],
592 NSPrintPanelAccessorySummaryItemNameKey,
593 [self summaryValueForCheckbox:mPrintBGImagesCheckbox],
594 NSPrintPanelAccessorySummaryItemDescriptionKey, nil],
595 [NSDictionary dictionaryWithObjectsAndKeys:
596 [self localizedString:"summaryHeaderTitle"],
597 NSPrintPanelAccessorySummaryItemNameKey,
598 [self headerSummaryValue],
599 NSPrintPanelAccessorySummaryItemDescriptionKey,
601 [NSDictionary dictionaryWithObjectsAndKeys:
602 [self localizedString:"summaryFooterTitle"],
603 NSPrintPanelAccessorySummaryItemNameKey,
604 [self footerSummaryValue],
605 NSPrintPanelAccessorySummaryItemDescriptionKey,
612 // Accessory controller
614 @implementation PrintPanelAccessoryController
616 - (id)initWithSettings:(nsIPrintSettings*)aSettings
617 haveSelection:(bool)aHaveSelection {
618 [super initWithNibName:nil bundle:nil];
621 [[PrintPanelAccessoryView alloc] initWithSettings:aSettings
622 haveSelection:aHaveSelection];
623 [self setView:accView];
628 - (void)exportSettings {
629 return [(PrintPanelAccessoryView*)[self view] exportSettings];
632 - (NSArray*)localizedSummaryItems {
633 return [(PrintPanelAccessoryView*)[self view] localizedSummaryItems];