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/. */ 5 6#include "mozilla/ArrayUtils.h" 7#include "mozilla/gfx/PrintTargetCG.h" 8#include "mozilla/Preferences.h" 9 10#include "nsPrintDialogX.h" 11#include "nsIPrintSettings.h" 12#include "nsIPrintSettingsService.h" 13#include "nsPrintSettingsX.h" 14#include "nsCOMPtr.h" 15#include "nsQueryObject.h" 16#include "nsServiceManagerUtils.h" 17#include "nsIStringBundle.h" 18#include "nsCRT.h" 19 20#import <Cocoa/Cocoa.h> 21#include "nsObjCExceptions.h" 22 23using namespace mozilla; 24using mozilla::gfx::PrintTarget; 25 26NS_IMPL_ISUPPORTS(nsPrintDialogServiceX, nsIPrintDialogService) 27 28nsPrintDialogServiceX::nsPrintDialogServiceX() {} 29 30nsPrintDialogServiceX::~nsPrintDialogServiceX() {} 31 32NS_IMETHODIMP 33nsPrintDialogServiceX::Init() { return NS_OK; } 34 35NS_IMETHODIMP 36nsPrintDialogServiceX::Show(nsPIDOMWindowOuter* aParent, nsIPrintSettings* aSettings) { 37 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 38 39 MOZ_ASSERT(aSettings, "aSettings must not be null"); 40 41 RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(aSettings)); 42 if (!settingsX) { 43 return NS_ERROR_FAILURE; 44 } 45 46 nsCOMPtr<nsIPrintSettingsService> printSettingsSvc = 47 do_GetService("@mozilla.org/gfx/printsettings-service;1"); 48 49 // Read the saved printer settings from prefs. (This relies on the printer name 50 // stored in settingsX to read the printer-specific prefs.) 51 printSettingsSvc->InitPrintSettingsFromPrefs(settingsX, true, nsIPrintSettings::kInitSaveAll); 52 53 NSPrintInfo* printInfo = settingsX->CreateOrCopyPrintInfo(/* aWithScaling = */ true); 54 if (NS_WARN_IF(!printInfo)) { 55 return NS_ERROR_FAILURE; 56 } 57 [printInfo autorelease]; 58 59 // Set the print job title 60 nsAutoString docName; 61 nsresult rv = aSettings->GetTitle(docName); 62 if (NS_SUCCEEDED(rv)) { 63 nsAutoString adjustedTitle; 64 PrintTarget::AdjustPrintJobNameForIPP(docName, adjustedTitle); 65 CFStringRef cfTitleString = CFStringCreateWithCharacters( 66 NULL, reinterpret_cast<const UniChar*>(adjustedTitle.BeginReading()), 67 adjustedTitle.Length()); 68 if (cfTitleString) { 69 auto pmPrintSettings = static_cast<PMPrintSettings>([printInfo PMPrintSettings]); 70 ::PMPrintSettingsSetJobName(pmPrintSettings, cfTitleString); 71 [printInfo updateFromPMPrintSettings]; 72 CFRelease(cfTitleString); 73 } 74 } 75 76 // Put the print info into the current print operation, since that's where 77 // [panel runModal] will look for it. We create the view because otherwise 78 // we'll get unrelated warnings printed to the console. 79 NSView* tmpView = [[NSView alloc] init]; 80 NSPrintOperation* printOperation = [NSPrintOperation printOperationWithView:tmpView 81 printInfo:printInfo]; 82 [NSPrintOperation setCurrentOperation:printOperation]; 83 84 NSPrintPanel* panel = [NSPrintPanel printPanel]; 85 [panel setOptions:NSPrintPanelShowsCopies | NSPrintPanelShowsPageRange | 86 NSPrintPanelShowsPaperSize | NSPrintPanelShowsOrientation | 87 NSPrintPanelShowsScaling]; 88 PrintPanelAccessoryController* viewController = 89 [[PrintPanelAccessoryController alloc] initWithSettings:aSettings]; 90 [panel addAccessoryController:viewController]; 91 [viewController release]; 92 93 // Show the dialog. 94 nsCocoaUtils::PrepareForNativeAppModalDialog(); 95 int button = [panel runModal]; 96 nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 97 98 // Retrieve a printInfo with the updated settings. (The NSPrintOperation operates on a 99 // copy, so the object we passed in will not have been modified.) 100 NSPrintInfo* result = [[NSPrintOperation currentOperation] printInfo]; 101 if (!result) { 102 return NS_ERROR_FAILURE; 103 } 104 105 [NSPrintOperation setCurrentOperation:nil]; 106 [tmpView release]; 107 108 if (button != NSFileHandlingPanelOKButton) { 109 return NS_ERROR_ABORT; 110 } 111 112 // Export settings. 113 [viewController exportSettings]; 114 115 // Update our settings object based on the user's choices in the dialog. 116 // We tell settingsX to adopt this printInfo so that it will be used to run print job, 117 // so that any printer-specific custom settings from print dialog extension panels 118 // will be carried through. 119 settingsX->SetFromPrintInfo(result, /* aAdoptPrintInfo = */ true); 120 121 // Save settings unless saving is pref'd off 122 if (Preferences::GetBool("print.save_print_settings", false)) { 123 printSettingsSvc->SavePrintSettingsToPrefs(settingsX, true, 124 nsIPrintSettings::kInitSaveNativeData); 125 } 126 127 return NS_OK; 128 129 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); 130} 131 132NS_IMETHODIMP 133nsPrintDialogServiceX::ShowPageSetup(nsPIDOMWindowOuter* aParent, nsIPrintSettings* aNSSettings) { 134 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 135 136 MOZ_ASSERT(aParent, "aParent must not be null"); 137 MOZ_ASSERT(aNSSettings, "aSettings must not be null"); 138 NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE); 139 140 RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(aNSSettings)); 141 if (!settingsX) { 142 return NS_ERROR_FAILURE; 143 } 144 145 NSPrintInfo* printInfo = settingsX->CreateOrCopyPrintInfo(/* aWithScaling = */ true); 146 if (NS_WARN_IF(!printInfo)) { 147 return NS_ERROR_FAILURE; 148 } 149 [printInfo autorelease]; 150 151 NSPageLayout* pageLayout = [NSPageLayout pageLayout]; 152 nsCocoaUtils::PrepareForNativeAppModalDialog(); 153 int button = [pageLayout runModalWithPrintInfo:printInfo]; 154 nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 155 156 if (button == NSFileHandlingPanelOKButton) { 157 // The Page Setup dialog does not include non-standard settings that need to be preserved, 158 // separate from what the base printSettings object handles, so we do not need it to adopt 159 // the printInfo object here. 160 settingsX->SetFromPrintInfo(printInfo, /* aAdoptPrintInfo = */ false); 161 nsCOMPtr<nsIPrintSettingsService> printSettingsService = 162 do_GetService("@mozilla.org/gfx/printsettings-service;1"); 163 if (printSettingsService && Preferences::GetBool("print.save_print_settings", false)) { 164 uint32_t flags = nsIPrintSettings::kInitSaveNativeData | 165 nsIPrintSettings::kInitSavePaperSize | 166 nsIPrintSettings::kInitSaveOrientation | nsIPrintSettings::kInitSaveScaling; 167 printSettingsService->SavePrintSettingsToPrefs(aNSSettings, true, flags); 168 } 169 return NS_OK; 170 } 171 return NS_ERROR_ABORT; 172 173 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); 174} 175 176// Accessory view 177 178@interface PrintPanelAccessoryView (Private) 179 180- (NSString*)localizedString:(const char*)aKey; 181 182- (const char*)headerFooterStringForList:(NSPopUpButton*)aList; 183 184- (void)exportHeaderFooterSettings; 185 186- (void)initBundle; 187 188- (NSTextField*)label:(const char*)aLabel 189 withFrame:(NSRect)aRect 190 alignment:(NSTextAlignment)aAlignment; 191 192- (void)addLabel:(const char*)aLabel withFrame:(NSRect)aRect alignment:(NSTextAlignment)aAlignment; 193 194- (void)addLabel:(const char*)aLabel withFrame:(NSRect)aRect; 195 196- (void)addCenteredLabel:(const char*)aLabel withFrame:(NSRect)aRect; 197 198- (NSButton*)checkboxWithLabel:(const char*)aLabel andFrame:(NSRect)aRect; 199 200- (NSPopUpButton*)headerFooterItemListWithFrame:(NSRect)aRect 201 selectedItem:(const nsAString&)aCurrentString; 202 203- (void)addOptionsSection; 204 205- (void)addAppearanceSection; 206 207- (void)addHeaderFooterSection; 208 209- (NSString*)summaryValueForCheckbox:(NSButton*)aCheckbox; 210 211- (NSString*)headerSummaryValue; 212 213- (NSString*)footerSummaryValue; 214 215@end 216 217static const char sHeaderFooterTags[][4] = {"", "&T", "&U", "&D", "&P", "&PT"}; 218 219@implementation PrintPanelAccessoryView 220 221// Public methods 222 223- (id)initWithSettings:(nsIPrintSettings*)aSettings { 224 [super initWithFrame:NSMakeRect(0, 0, 540, 185)]; 225 226 mSettings = aSettings; 227 [self initBundle]; 228 [self addOptionsSection]; 229 [self addAppearanceSection]; 230 [self addHeaderFooterSection]; 231 232 return self; 233} 234 235- (void)exportSettings { 236 mSettings->SetPrintSelectionOnly([mPrintSelectionOnlyCheckbox state] == NSOnState); 237 mSettings->SetShrinkToFit([mShrinkToFitCheckbox state] == NSOnState); 238 mSettings->SetPrintBGColors([mPrintBGColorsCheckbox state] == NSOnState); 239 mSettings->SetPrintBGImages([mPrintBGImagesCheckbox state] == NSOnState); 240 241 [self exportHeaderFooterSettings]; 242} 243 244- (void)dealloc { 245 NS_IF_RELEASE(mPrintBundle); 246 [super dealloc]; 247} 248 249// Localization 250 251- (void)initBundle { 252 nsCOMPtr<nsIStringBundleService> bundleSvc = do_GetService(NS_STRINGBUNDLE_CONTRACTID); 253 bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties", &mPrintBundle); 254} 255 256- (NSString*)localizedString:(const char*)aKey { 257 if (!mPrintBundle) return @""; 258 259 nsAutoString intlString; 260 mPrintBundle->GetStringFromName(aKey, intlString); 261 NSMutableString* s = 262 [NSMutableString stringWithUTF8String:NS_ConvertUTF16toUTF8(intlString).get()]; 263 264 // Remove all underscores (they're used in the GTK dialog for accesskeys). 265 [s replaceOccurrencesOfString:@"_" withString:@"" options:0 range:NSMakeRange(0, [s length])]; 266 return s; 267} 268 269// Widget helpers 270 271- (NSTextField*)label:(const char*)aLabel 272 withFrame:(NSRect)aRect 273 alignment:(NSTextAlignment)aAlignment { 274 NSTextField* label = [[[NSTextField alloc] initWithFrame:aRect] autorelease]; 275 [label setStringValue:[self localizedString:aLabel]]; 276 [label setEditable:NO]; 277 [label setSelectable:NO]; 278 [label setBezeled:NO]; 279 [label setBordered:NO]; 280 [label setDrawsBackground:NO]; 281 [label setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; 282 [label setAlignment:aAlignment]; 283 return label; 284} 285 286- (void)addLabel:(const char*)aLabel withFrame:(NSRect)aRect alignment:(NSTextAlignment)aAlignment { 287 NSTextField* label = [self label:aLabel withFrame:aRect alignment:aAlignment]; 288 [self addSubview:label]; 289} 290 291- (void)addLabel:(const char*)aLabel withFrame:(NSRect)aRect { 292 [self addLabel:aLabel withFrame:aRect alignment:NSTextAlignmentRight]; 293} 294 295- (void)addCenteredLabel:(const char*)aLabel withFrame:(NSRect)aRect { 296 [self addLabel:aLabel withFrame:aRect alignment:NSTextAlignmentCenter]; 297} 298 299- (NSButton*)checkboxWithLabel:(const char*)aLabel andFrame:(NSRect)aRect { 300 aRect.origin.y += 4.0f; 301 NSButton* checkbox = [[[NSButton alloc] initWithFrame:aRect] autorelease]; 302 [checkbox setButtonType:NSSwitchButton]; 303 [checkbox setTitle:[self localizedString:aLabel]]; 304 [checkbox setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; 305 [checkbox sizeToFit]; 306 return checkbox; 307} 308 309- (NSPopUpButton*)headerFooterItemListWithFrame:(NSRect)aRect 310 selectedItem:(const nsAString&)aCurrentString { 311 NSPopUpButton* list = [[[NSPopUpButton alloc] initWithFrame:aRect pullsDown:NO] autorelease]; 312 [list setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; 313 [[list cell] setControlSize:NSControlSizeSmall]; 314 NSArray* items = [NSArray arrayWithObjects:[self localizedString:"headerFooterBlank"], 315 [self localizedString:"headerFooterTitle"], 316 [self localizedString:"headerFooterURL"], 317 [self localizedString:"headerFooterDate"], 318 [self localizedString:"headerFooterPage"], 319 [self localizedString:"headerFooterPageTotal"], nil]; 320 [list addItemsWithTitles:items]; 321 322 NS_ConvertUTF16toUTF8 currentStringUTF8(aCurrentString); 323 for (unsigned int i = 0; i < ArrayLength(sHeaderFooterTags); i++) { 324 if (!strcmp(currentStringUTF8.get(), sHeaderFooterTags[i])) { 325 [list selectItemAtIndex:i]; 326 break; 327 } 328 } 329 330 return list; 331} 332 333// Build sections 334 335- (void)addOptionsSection { 336 // Title 337 [self addLabel:"optionsTitleMac" withFrame:NSMakeRect(0, 155, 151, 22)]; 338 339 // "Print Selection Only" 340 mPrintSelectionOnlyCheckbox = [self checkboxWithLabel:"selectionOnly" 341 andFrame:NSMakeRect(156, 155, 0, 0)]; 342 343 bool canPrintSelection = mSettings->GetIsPrintSelectionRBEnabled(); 344 [mPrintSelectionOnlyCheckbox setEnabled:canPrintSelection]; 345 346 if (mSettings->GetPrintSelectionOnly()) { 347 [mPrintSelectionOnlyCheckbox setState:NSOnState]; 348 } 349 350 [self addSubview:mPrintSelectionOnlyCheckbox]; 351 352 // "Shrink To Fit" 353 mShrinkToFitCheckbox = [self checkboxWithLabel:"shrinkToFit" andFrame:NSMakeRect(156, 133, 0, 0)]; 354 355 bool shrinkToFit; 356 mSettings->GetShrinkToFit(&shrinkToFit); 357 [mShrinkToFitCheckbox setState:(shrinkToFit ? NSOnState : NSOffState)]; 358 359 [self addSubview:mShrinkToFitCheckbox]; 360} 361 362- (void)addAppearanceSection { 363 // Title 364 [self addLabel:"appearanceTitleMac" withFrame:NSMakeRect(0, 103, 151, 22)]; 365 366 // "Print Background Colors" 367 mPrintBGColorsCheckbox = [self checkboxWithLabel:"printBGColors" 368 andFrame:NSMakeRect(156, 103, 0, 0)]; 369 370 bool geckoBool = mSettings->GetPrintBGColors(); 371 [mPrintBGColorsCheckbox setState:(geckoBool ? NSOnState : NSOffState)]; 372 373 [self addSubview:mPrintBGColorsCheckbox]; 374 375 // "Print Background Images" 376 mPrintBGImagesCheckbox = [self checkboxWithLabel:"printBGImages" 377 andFrame:NSMakeRect(156, 81, 0, 0)]; 378 379 geckoBool = mSettings->GetPrintBGImages(); 380 [mPrintBGImagesCheckbox setState:(geckoBool ? NSOnState : NSOffState)]; 381 382 [self addSubview:mPrintBGImagesCheckbox]; 383} 384 385- (void)addHeaderFooterSection { 386 // Labels 387 [self addLabel:"pageHeadersTitleMac" withFrame:NSMakeRect(0, 44, 151, 22)]; 388 [self addLabel:"pageFootersTitleMac" withFrame:NSMakeRect(0, 0, 151, 22)]; 389 [self addCenteredLabel:"left" withFrame:NSMakeRect(156, 22, 100, 22)]; 390 [self addCenteredLabel:"center" withFrame:NSMakeRect(256, 22, 100, 22)]; 391 [self addCenteredLabel:"right" withFrame:NSMakeRect(356, 22, 100, 22)]; 392 393 // Lists 394 nsString sel; 395 396 mSettings->GetHeaderStrLeft(sel); 397 mHeaderLeftList = [self headerFooterItemListWithFrame:NSMakeRect(156, 44, 100, 22) 398 selectedItem:sel]; 399 [self addSubview:mHeaderLeftList]; 400 401 mSettings->GetHeaderStrCenter(sel); 402 mHeaderCenterList = [self headerFooterItemListWithFrame:NSMakeRect(256, 44, 100, 22) 403 selectedItem:sel]; 404 [self addSubview:mHeaderCenterList]; 405 406 mSettings->GetHeaderStrRight(sel); 407 mHeaderRightList = [self headerFooterItemListWithFrame:NSMakeRect(356, 44, 100, 22) 408 selectedItem:sel]; 409 [self addSubview:mHeaderRightList]; 410 411 mSettings->GetFooterStrLeft(sel); 412 mFooterLeftList = [self headerFooterItemListWithFrame:NSMakeRect(156, 0, 100, 22) 413 selectedItem:sel]; 414 [self addSubview:mFooterLeftList]; 415 416 mSettings->GetFooterStrCenter(sel); 417 mFooterCenterList = [self headerFooterItemListWithFrame:NSMakeRect(256, 0, 100, 22) 418 selectedItem:sel]; 419 [self addSubview:mFooterCenterList]; 420 421 mSettings->GetFooterStrRight(sel); 422 mFooterRightList = [self headerFooterItemListWithFrame:NSMakeRect(356, 0, 100, 22) 423 selectedItem:sel]; 424 [self addSubview:mFooterRightList]; 425} 426 427// Export settings 428 429- (const char*)headerFooterStringForList:(NSPopUpButton*)aList { 430 NSInteger index = [aList indexOfSelectedItem]; 431 NS_ASSERTION(index < NSInteger(ArrayLength(sHeaderFooterTags)), 432 "Index of dropdown is higher than expected!"); 433 return sHeaderFooterTags[index]; 434} 435 436- (void)exportHeaderFooterSettings { 437 const char* headerFooterStr; 438 headerFooterStr = [self headerFooterStringForList:mHeaderLeftList]; 439 mSettings->SetHeaderStrLeft(NS_ConvertUTF8toUTF16(headerFooterStr)); 440 441 headerFooterStr = [self headerFooterStringForList:mHeaderCenterList]; 442 mSettings->SetHeaderStrCenter(NS_ConvertUTF8toUTF16(headerFooterStr)); 443 444 headerFooterStr = [self headerFooterStringForList:mHeaderRightList]; 445 mSettings->SetHeaderStrRight(NS_ConvertUTF8toUTF16(headerFooterStr)); 446 447 headerFooterStr = [self headerFooterStringForList:mFooterLeftList]; 448 mSettings->SetFooterStrLeft(NS_ConvertUTF8toUTF16(headerFooterStr)); 449 450 headerFooterStr = [self headerFooterStringForList:mFooterCenterList]; 451 mSettings->SetFooterStrCenter(NS_ConvertUTF8toUTF16(headerFooterStr)); 452 453 headerFooterStr = [self headerFooterStringForList:mFooterRightList]; 454 mSettings->SetFooterStrRight(NS_ConvertUTF8toUTF16(headerFooterStr)); 455} 456 457// Summary 458 459- (NSString*)summaryValueForCheckbox:(NSButton*)aCheckbox { 460 if (![aCheckbox isEnabled]) return [self localizedString:"summaryNAValue"]; 461 462 return [aCheckbox state] == NSOnState ? [self localizedString:"summaryOnValue"] 463 : [self localizedString:"summaryOffValue"]; 464} 465 466- (NSString*)headerSummaryValue { 467 return [[mHeaderLeftList titleOfSelectedItem] 468 stringByAppendingString: 469 [@", " 470 stringByAppendingString: 471 [[mHeaderCenterList titleOfSelectedItem] 472 stringByAppendingString: 473 [@", " stringByAppendingString:[mHeaderRightList titleOfSelectedItem]]]]]; 474} 475 476- (NSString*)footerSummaryValue { 477 return [[mFooterLeftList titleOfSelectedItem] 478 stringByAppendingString: 479 [@", " 480 stringByAppendingString: 481 [[mFooterCenterList titleOfSelectedItem] 482 stringByAppendingString: 483 [@", " stringByAppendingString:[mFooterRightList titleOfSelectedItem]]]]]; 484} 485 486- (NSArray*)localizedSummaryItems { 487 return [NSArray 488 arrayWithObjects: 489 [NSDictionary 490 dictionaryWithObjectsAndKeys:[self localizedString:"summarySelectionOnlyTitle"], 491 NSPrintPanelAccessorySummaryItemNameKey, 492 [self 493 summaryValueForCheckbox:mPrintSelectionOnlyCheckbox], 494 NSPrintPanelAccessorySummaryItemDescriptionKey, nil], 495 [NSDictionary 496 dictionaryWithObjectsAndKeys:[self localizedString:"summaryShrinkToFitTitle"], 497 NSPrintPanelAccessorySummaryItemNameKey, 498 [self summaryValueForCheckbox:mShrinkToFitCheckbox], 499 NSPrintPanelAccessorySummaryItemDescriptionKey, nil], 500 [NSDictionary 501 dictionaryWithObjectsAndKeys:[self localizedString:"summaryPrintBGColorsTitle"], 502 NSPrintPanelAccessorySummaryItemNameKey, 503 [self summaryValueForCheckbox:mPrintBGColorsCheckbox], 504 NSPrintPanelAccessorySummaryItemDescriptionKey, nil], 505 [NSDictionary 506 dictionaryWithObjectsAndKeys:[self localizedString:"summaryPrintBGImagesTitle"], 507 NSPrintPanelAccessorySummaryItemNameKey, 508 [self summaryValueForCheckbox:mPrintBGImagesCheckbox], 509 NSPrintPanelAccessorySummaryItemDescriptionKey, nil], 510 [NSDictionary dictionaryWithObjectsAndKeys:[self localizedString:"summaryHeaderTitle"], 511 NSPrintPanelAccessorySummaryItemNameKey, 512 [self headerSummaryValue], 513 NSPrintPanelAccessorySummaryItemDescriptionKey, 514 nil], 515 [NSDictionary dictionaryWithObjectsAndKeys:[self localizedString:"summaryFooterTitle"], 516 NSPrintPanelAccessorySummaryItemNameKey, 517 [self footerSummaryValue], 518 NSPrintPanelAccessorySummaryItemDescriptionKey, 519 nil], 520 nil]; 521} 522 523@end 524 525// Accessory controller 526 527@implementation PrintPanelAccessoryController 528 529- (id)initWithSettings:(nsIPrintSettings*)aSettings { 530 [super initWithNibName:nil bundle:nil]; 531 532 NSView* accView = [[PrintPanelAccessoryView alloc] initWithSettings:aSettings]; 533 [self setView:accView]; 534 [accView release]; 535 return self; 536} 537 538- (void)exportSettings { 539 return [(PrintPanelAccessoryView*)[self view] exportSettings]; 540} 541 542- (NSArray*)localizedSummaryItems { 543 return [(PrintPanelAccessoryView*)[self view] localizedSummaryItems]; 544} 545 546@end 547