embedding/components/printingui/src/win/nsPrintDialogUtil.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:5090e3a202c0
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 /* -------------------------------------------------------------------
7 To Build This:
8
9 You need to add this to the the makefile.win in mozilla/content/base/src:
10
11 .\$(OBJDIR)\nsFlyOwnPrintDialog.obj \
12
13
14 And this to the makefile.win in mozilla/content/build:
15
16 WIN_LIBS= \
17 winspool.lib \
18 comctl32.lib \
19 comdlg32.lib
20
21 ---------------------------------------------------------------------- */
22
23 #define NOMINMAX 1
24
25 #include "plstr.h"
26 #include <windows.h>
27 #include <tchar.h>
28
29 #include <unknwn.h>
30 #include <commdlg.h>
31
32 #include "nsIWebBrowserPrint.h"
33 #include "nsString.h"
34 #include "nsIServiceManager.h"
35 #include "nsReadableUtils.h"
36 #include "nsIPrintSettings.h"
37 #include "nsIPrintSettingsWin.h"
38 #include "nsIPrintOptions.h"
39
40 #include "nsRect.h"
41
42 #include "nsIPrefService.h"
43 #include "nsIPrefBranch.h"
44
45 #include "nsCRT.h"
46 #include "prenv.h" /* for PR_GetEnv */
47
48 #include <windows.h>
49 #include <winspool.h>
50
51 // For Localization
52 #include "nsIStringBundle.h"
53
54 // For NS_CopyUnicodeToNative
55 #include "nsNativeCharsetUtils.h"
56
57 // This is for extending the dialog
58 #include <dlgs.h>
59
60 // Default labels for the radio buttons
61 static const char* kAsLaidOutOnScreenStr = "As &laid out on the screen";
62 static const char* kTheSelectedFrameStr = "The selected &frame";
63 static const char* kEachFrameSeparately = "&Each frame separately";
64
65
66 //-----------------------------------------------
67 // Global Data
68 //-----------------------------------------------
69 // Identifies which new radio btn was cliked on
70 static UINT gFrameSelectedRadioBtn = 0;
71
72 // Indicates whether the native print dialog was successfully extended
73 static bool gDialogWasExtended = false;
74
75 #define PRINTDLG_PROPERTIES "chrome://global/locale/printdialog.properties"
76
77 static HWND gParentWnd = nullptr;
78
79 //******************************************************
80 // Define native paper sizes
81 //******************************************************
82 typedef struct {
83 short mPaperSize; // native enum
84 double mWidth;
85 double mHeight;
86 bool mIsInches;
87 } NativePaperSizes;
88
89 // There are around 40 default print sizes defined by Windows
90 const NativePaperSizes kPaperSizes[] = {
91 {DMPAPER_LETTER, 8.5, 11.0, true},
92 {DMPAPER_LEGAL, 8.5, 14.0, true},
93 {DMPAPER_A4, 210.0, 297.0, false},
94 {DMPAPER_TABLOID, 11.0, 17.0, true},
95 {DMPAPER_LEDGER, 17.0, 11.0, true},
96 {DMPAPER_STATEMENT, 5.5, 8.5, true},
97 {DMPAPER_EXECUTIVE, 7.25, 10.5, true},
98 {DMPAPER_A3, 297.0, 420.0, false},
99 {DMPAPER_A5, 148.0, 210.0, false},
100 {DMPAPER_CSHEET, 17.0, 22.0, true},
101 {DMPAPER_DSHEET, 22.0, 34.0, true},
102 {DMPAPER_ESHEET, 34.0, 44.0, true},
103 {DMPAPER_LETTERSMALL, 8.5, 11.0, true},
104 {DMPAPER_A4SMALL, 210.0, 297.0, false},
105 {DMPAPER_B4, 250.0, 354.0, false},
106 {DMPAPER_B5, 182.0, 257.0, false},
107 {DMPAPER_FOLIO, 8.5, 13.0, true},
108 {DMPAPER_QUARTO, 215.0, 275.0, false},
109 {DMPAPER_10X14, 10.0, 14.0, true},
110 {DMPAPER_11X17, 11.0, 17.0, true},
111 {DMPAPER_NOTE, 8.5, 11.0, true},
112 {DMPAPER_ENV_9, 3.875, 8.875, true},
113 {DMPAPER_ENV_10, 40.125, 9.5, true},
114 {DMPAPER_ENV_11, 4.5, 10.375, true},
115 {DMPAPER_ENV_12, 4.75, 11.0, true},
116 {DMPAPER_ENV_14, 5.0, 11.5, true},
117 {DMPAPER_ENV_DL, 110.0, 220.0, false},
118 {DMPAPER_ENV_C5, 162.0, 229.0, false},
119 {DMPAPER_ENV_C3, 324.0, 458.0, false},
120 {DMPAPER_ENV_C4, 229.0, 324.0, false},
121 {DMPAPER_ENV_C6, 114.0, 162.0, false},
122 {DMPAPER_ENV_C65, 114.0, 229.0, false},
123 {DMPAPER_ENV_B4, 250.0, 353.0, false},
124 {DMPAPER_ENV_B5, 176.0, 250.0, false},
125 {DMPAPER_ENV_B6, 176.0, 125.0, false},
126 {DMPAPER_ENV_ITALY, 110.0, 230.0, false},
127 {DMPAPER_ENV_MONARCH, 3.875, 7.5, true},
128 {DMPAPER_ENV_PERSONAL, 3.625, 6.5, true},
129 {DMPAPER_FANFOLD_US, 14.875, 11.0, true},
130 {DMPAPER_FANFOLD_STD_GERMAN, 8.5, 12.0, true},
131 {DMPAPER_FANFOLD_LGL_GERMAN, 8.5, 13.0, true},
132 };
133 const int32_t kNumPaperSizes = 41;
134
135 //----------------------------------------------------------------------------------
136 // Map an incoming size to a Windows Native enum in the DevMode
137 static void
138 MapPaperSizeToNativeEnum(LPDEVMODEW aDevMode,
139 int16_t aType,
140 double aW,
141 double aH)
142 {
143
144 #ifdef DEBUG_rods
145 BOOL doingOrientation = aDevMode->dmFields & DM_ORIENTATION;
146 BOOL doingPaperSize = aDevMode->dmFields & DM_PAPERSIZE;
147 BOOL doingPaperLength = aDevMode->dmFields & DM_PAPERLENGTH;
148 BOOL doingPaperWidth = aDevMode->dmFields & DM_PAPERWIDTH;
149 #endif
150
151 const double kThreshold = 0.05;
152 for (int32_t i=0;i<kNumPaperSizes;i++) {
153 double width = kPaperSizes[i].mWidth;
154 double height = kPaperSizes[i].mHeight;
155 if (aW < width+kThreshold && aW > width-kThreshold &&
156 aH < height+kThreshold && aH > height-kThreshold) {
157 aDevMode->dmPaperSize = kPaperSizes[i].mPaperSize;
158 aDevMode->dmFields &= ~DM_PAPERLENGTH;
159 aDevMode->dmFields &= ~DM_PAPERWIDTH;
160 aDevMode->dmFields |= DM_PAPERSIZE;
161 return;
162 }
163 }
164
165 short width = 0;
166 short height = 0;
167 if (aType == nsIPrintSettings::kPaperSizeInches) {
168 width = short(NS_TWIPS_TO_MILLIMETERS(NS_INCHES_TO_TWIPS(float(aW))) / 10);
169 height = short(NS_TWIPS_TO_MILLIMETERS(NS_INCHES_TO_TWIPS(float(aH))) / 10);
170
171 } else if (aType == nsIPrintSettings::kPaperSizeMillimeters) {
172 width = short(aW / 10.0);
173 height = short(aH / 10.0);
174 } else {
175 return; // don't set anything
176 }
177
178 // width and height is in
179 aDevMode->dmPaperSize = 0;
180 aDevMode->dmPaperWidth = width;
181 aDevMode->dmPaperLength = height;
182
183 aDevMode->dmFields |= DM_PAPERSIZE;
184 aDevMode->dmFields |= DM_PAPERLENGTH;
185 aDevMode->dmFields |= DM_PAPERWIDTH;
186 }
187
188 //----------------------------------------------------------------------------------
189 // Setup Paper Size & Orientation options into the DevMode
190 //
191 static void
192 SetupDevModeFromSettings(LPDEVMODEW aDevMode, nsIPrintSettings* aPrintSettings)
193 {
194 // Setup paper size
195 if (aPrintSettings) {
196 int16_t type;
197 aPrintSettings->GetPaperSizeType(&type);
198 if (type == nsIPrintSettings::kPaperSizeNativeData) {
199 int16_t paperEnum;
200 aPrintSettings->GetPaperData(&paperEnum);
201 aDevMode->dmPaperSize = paperEnum;
202 aDevMode->dmFields &= ~DM_PAPERLENGTH;
203 aDevMode->dmFields &= ~DM_PAPERWIDTH;
204 aDevMode->dmFields |= DM_PAPERSIZE;
205 } else {
206 int16_t unit;
207 double width, height;
208 aPrintSettings->GetPaperSizeUnit(&unit);
209 aPrintSettings->GetPaperWidth(&width);
210 aPrintSettings->GetPaperHeight(&height);
211 MapPaperSizeToNativeEnum(aDevMode, unit, width, height);
212 }
213
214 // Setup Orientation
215 int32_t orientation;
216 aPrintSettings->GetOrientation(&orientation);
217 aDevMode->dmOrientation = orientation == nsIPrintSettings::kPortraitOrientation?DMORIENT_PORTRAIT:DMORIENT_LANDSCAPE;
218 aDevMode->dmFields |= DM_ORIENTATION;
219
220 // Setup Number of Copies
221 int32_t copies;
222 aPrintSettings->GetNumCopies(&copies);
223 aDevMode->dmCopies = copies;
224 aDevMode->dmFields |= DM_COPIES;
225
226 }
227
228 }
229
230 //----------------------------------------------------------------------------------
231 // Helper Function - Free and reallocate the string
232 static nsresult
233 SetPrintSettingsFromDevMode(nsIPrintSettings* aPrintSettings,
234 LPDEVMODEW aDevMode)
235 {
236 if (aPrintSettings == nullptr) {
237 return NS_ERROR_FAILURE;
238 }
239
240 aPrintSettings->SetIsInitializedFromPrinter(true);
241 if (aDevMode->dmFields & DM_ORIENTATION) {
242 int32_t orientation = aDevMode->dmOrientation == DMORIENT_PORTRAIT?
243 nsIPrintSettings::kPortraitOrientation:nsIPrintSettings::kLandscapeOrientation;
244 aPrintSettings->SetOrientation(orientation);
245 }
246
247 // Setup Number of Copies
248 if (aDevMode->dmFields & DM_COPIES) {
249 aPrintSettings->SetNumCopies(int32_t(aDevMode->dmCopies));
250 }
251
252 // Scaling
253 // Since we do the scaling, grab their value and reset back to 100
254 if (aDevMode->dmFields & DM_SCALE) {
255 double origScale = 1.0;
256 aPrintSettings->GetScaling(&origScale);
257 double scale = double(aDevMode->dmScale) / 100.0f;
258 if (origScale == 1.0 || scale != 1.0) {
259 aPrintSettings->SetScaling(scale);
260 }
261 aDevMode->dmScale = 100;
262 // To turn this on you must change where the mPrt->mShrinkToFit is being set in the DocumentViewer
263 //aPrintSettings->SetShrinkToFit(false);
264 }
265
266 if (aDevMode->dmFields & DM_PAPERSIZE) {
267 aPrintSettings->SetPaperSizeType(nsIPrintSettings::kPaperSizeNativeData);
268 aPrintSettings->SetPaperData(aDevMode->dmPaperSize);
269 for (int32_t i=0;i<kNumPaperSizes;i++) {
270 if (kPaperSizes[i].mPaperSize == aDevMode->dmPaperSize) {
271 aPrintSettings->SetPaperSizeUnit(kPaperSizes[i].mIsInches?nsIPrintSettings::kPaperSizeInches:nsIPrintSettings::kPaperSizeMillimeters);
272 break;
273 }
274 }
275
276 } else if (aDevMode->dmFields & DM_PAPERLENGTH && aDevMode->dmFields & DM_PAPERWIDTH) {
277 bool found = false;
278 for (int32_t i=0;i<kNumPaperSizes;i++) {
279 if (kPaperSizes[i].mPaperSize == aDevMode->dmPaperSize) {
280 aPrintSettings->SetPaperSizeType(nsIPrintSettings::kPaperSizeDefined);
281 aPrintSettings->SetPaperWidth(kPaperSizes[i].mWidth);
282 aPrintSettings->SetPaperHeight(kPaperSizes[i].mHeight);
283 aPrintSettings->SetPaperSizeUnit(kPaperSizes[i].mIsInches?nsIPrintSettings::kPaperSizeInches:nsIPrintSettings::kPaperSizeMillimeters);
284 found = true;
285 break;
286 }
287 }
288 if (!found) {
289 return NS_ERROR_FAILURE;
290 }
291 } else {
292 return NS_ERROR_FAILURE;
293 }
294 return NS_OK;
295 }
296
297 //----------------------------------------------------------------------------------
298 // Return localized bundle for resource strings
299 static nsresult
300 GetLocalizedBundle(const char * aPropFileName, nsIStringBundle** aStrBundle)
301 {
302 NS_ENSURE_ARG_POINTER(aPropFileName);
303 NS_ENSURE_ARG_POINTER(aStrBundle);
304
305 nsresult rv;
306 nsCOMPtr<nsIStringBundle> bundle;
307
308
309 // Create bundle
310 nsCOMPtr<nsIStringBundleService> stringService =
311 do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
312 if (NS_SUCCEEDED(rv) && stringService) {
313 rv = stringService->CreateBundle(aPropFileName, aStrBundle);
314 }
315
316 return rv;
317 }
318
319 //--------------------------------------------------------
320 // Return localized string
321 static nsresult
322 GetLocalizedString(nsIStringBundle* aStrBundle, const char* aKey, nsString& oVal)
323 {
324 NS_ENSURE_ARG_POINTER(aStrBundle);
325 NS_ENSURE_ARG_POINTER(aKey);
326
327 // Determine default label from string bundle
328 nsXPIDLString valUni;
329 nsAutoString key;
330 key.AssignWithConversion(aKey);
331 nsresult rv = aStrBundle->GetStringFromName(key.get(), getter_Copies(valUni));
332 if (NS_SUCCEEDED(rv) && valUni) {
333 oVal.Assign(valUni);
334 } else {
335 oVal.Truncate();
336 }
337 return rv;
338 }
339
340 //--------------------------------------------------------
341 // Set a multi-byte string in the control
342 static void SetTextOnWnd(HWND aControl, const nsString& aStr)
343 {
344 nsAutoCString text;
345 if (NS_SUCCEEDED(NS_CopyUnicodeToNative(aStr, text))) {
346 ::SetWindowText(aControl, text.get());
347 }
348 }
349
350 //--------------------------------------------------------
351 // Will get the control and localized string by "key"
352 static void SetText(HWND aParent,
353 UINT aId,
354 nsIStringBundle* aStrBundle,
355 const char* aKey)
356 {
357 HWND wnd = GetDlgItem (aParent, aId);
358 if (!wnd) {
359 return;
360 }
361 nsAutoString str;
362 nsresult rv = GetLocalizedString(aStrBundle, aKey, str);
363 if (NS_SUCCEEDED(rv)) {
364 SetTextOnWnd(wnd, str);
365 }
366 }
367
368 //--------------------------------------------------------
369 static void SetRadio(HWND aParent,
370 UINT aId,
371 bool aIsSet,
372 bool isEnabled = true)
373 {
374 HWND wnd = ::GetDlgItem (aParent, aId);
375 if (!wnd) {
376 return;
377 }
378 if (!isEnabled) {
379 ::EnableWindow(wnd, FALSE);
380 return;
381 }
382 ::EnableWindow(wnd, TRUE);
383 ::SendMessage(wnd, BM_SETCHECK, (WPARAM)aIsSet, (LPARAM)0);
384 }
385
386 //--------------------------------------------------------
387 static void SetRadioOfGroup(HWND aDlg, int aRadId)
388 {
389 int radioIds[] = {rad4, rad5, rad6};
390 int numRads = 3;
391
392 for (int i=0;i<numRads;i++) {
393 HWND radWnd = ::GetDlgItem(aDlg, radioIds[i]);
394 if (radWnd != nullptr) {
395 ::SendMessage(radWnd, BM_SETCHECK, (WPARAM)(radioIds[i] == aRadId), (LPARAM)0);
396 }
397 }
398 }
399
400 //--------------------------------------------------------
401 typedef struct {
402 const char * mKeyStr;
403 long mKeyId;
404 } PropKeyInfo;
405
406 // These are the control ids used in the dialog and
407 // defined by MS-Windows in commdlg.h
408 static PropKeyInfo gAllPropKeys[] = {
409 {"printFramesTitleWindows", grp3},
410 {"asLaidOutWindows", rad4},
411 {"selectedFrameWindows", rad5},
412 {"separateFramesWindows", rad6},
413 {nullptr, 0}};
414
415 //--------------------------------------------------------
416 //--------------------------------------------------------
417 //--------------------------------------------------------
418 //--------------------------------------------------------
419 // Get the absolute coords of the child windows relative
420 // to its parent window
421 static void GetLocalRect(HWND aWnd, RECT& aRect, HWND aParent)
422 {
423 ::GetWindowRect(aWnd, &aRect);
424
425 // MapWindowPoints converts screen coordinates to client coordinates.
426 // It works correctly in both left-to-right and right-to-left windows.
427 ::MapWindowPoints(nullptr, aParent, (LPPOINT)&aRect, 2);
428 }
429
430 //--------------------------------------------------------
431 // Show or Hide the control
432 static void Show(HWND aWnd, bool bState)
433 {
434 if (aWnd) {
435 ::ShowWindow(aWnd, bState?SW_SHOW:SW_HIDE);
436 }
437 }
438
439 //--------------------------------------------------------
440 // Create a child window "control"
441 static HWND CreateControl(LPCTSTR aType,
442 DWORD aStyle,
443 HINSTANCE aHInst,
444 HWND aHdlg,
445 int aId,
446 const nsAString& aStr,
447 const nsIntRect& aRect)
448 {
449 nsAutoCString str;
450 if (NS_FAILED(NS_CopyUnicodeToNative(aStr, str)))
451 return nullptr;
452
453 HWND hWnd = ::CreateWindow (aType, str.get(),
454 WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | aStyle,
455 aRect.x, aRect.y, aRect.width, aRect.height,
456 (HWND)aHdlg, (HMENU)(intptr_t)aId,
457 aHInst, nullptr);
458 if (hWnd == nullptr) return nullptr;
459
460 // get the native font for the dialog and
461 // set it into the new control
462 HFONT hFont = (HFONT)::SendMessage(aHdlg, WM_GETFONT, (WPARAM)0, (LPARAM)0);
463 if (hFont != nullptr) {
464 ::SendMessage(hWnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0);
465 }
466 return hWnd;
467 }
468
469 //--------------------------------------------------------
470 // Create a Radio Button
471 static HWND CreateRadioBtn(HINSTANCE aHInst,
472 HWND aHdlg,
473 int aId,
474 const char* aStr,
475 const nsIntRect& aRect)
476 {
477 nsString cStr;
478 cStr.AssignWithConversion(aStr);
479 return CreateControl("BUTTON", BS_RADIOBUTTON, aHInst, aHdlg, aId, cStr, aRect);
480 }
481
482 //--------------------------------------------------------
483 // Create a Group Box
484 static HWND CreateGroupBox(HINSTANCE aHInst,
485 HWND aHdlg,
486 int aId,
487 const nsAString& aStr,
488 const nsIntRect& aRect)
489 {
490 return CreateControl("BUTTON", BS_GROUPBOX, aHInst, aHdlg, aId, aStr, aRect);
491 }
492
493 //--------------------------------------------------------
494 // Localizes and initializes the radio buttons and group
495 static void InitializeExtendedDialog(HWND hdlg, int16_t aHowToEnableFrameUI)
496 {
497 NS_ABORT_IF_FALSE(aHowToEnableFrameUI != nsIPrintSettings::kFrameEnableNone,
498 "should not be called");
499
500 // Localize the new controls in the print dialog
501 nsCOMPtr<nsIStringBundle> strBundle;
502 if (NS_SUCCEEDED(GetLocalizedBundle(PRINTDLG_PROPERTIES, getter_AddRefs(strBundle)))) {
503 int32_t i = 0;
504 while (gAllPropKeys[i].mKeyStr != nullptr) {
505 SetText(hdlg, gAllPropKeys[i].mKeyId, strBundle, gAllPropKeys[i].mKeyStr);
506 i++;
507 }
508 }
509
510 // Set up radio buttons
511 if (aHowToEnableFrameUI == nsIPrintSettings::kFrameEnableAll) {
512 SetRadio(hdlg, rad4, false);
513 SetRadio(hdlg, rad5, true);
514 SetRadio(hdlg, rad6, false);
515 // set default so user doesn't have to actually press on it
516 gFrameSelectedRadioBtn = rad5;
517
518 } else { // nsIPrintSettings::kFrameEnableAsIsAndEach
519 SetRadio(hdlg, rad4, false);
520 SetRadio(hdlg, rad5, false, false);
521 SetRadio(hdlg, rad6, true);
522 // set default so user doesn't have to actually press on it
523 gFrameSelectedRadioBtn = rad6;
524 }
525 }
526
527
528 //--------------------------------------------------------
529 // Special Hook Procedure for handling the print dialog messages
530 static UINT CALLBACK PrintHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
531 {
532
533 if (uiMsg == WM_COMMAND) {
534 UINT id = LOWORD(wParam);
535 if (id == rad4 || id == rad5 || id == rad6) {
536 gFrameSelectedRadioBtn = id;
537 SetRadioOfGroup(hdlg, id);
538 }
539
540 } else if (uiMsg == WM_INITDIALOG) {
541 PRINTDLG * printDlg = (PRINTDLG *)lParam;
542 if (printDlg == nullptr) return 0L;
543
544 int16_t howToEnableFrameUI = (int16_t)printDlg->lCustData;
545 // don't add frame options if they would be disabled anyway
546 // because there are no frames
547 if (howToEnableFrameUI == nsIPrintSettings::kFrameEnableNone)
548 return TRUE;
549
550 HINSTANCE hInst = (HINSTANCE)::GetWindowLongPtr(hdlg, GWLP_HINSTANCE);
551 if (hInst == nullptr) return 0L;
552
553 // Start by getting the local rects of several of the controls
554 // so we can calculate where the new controls are
555 HWND wnd = ::GetDlgItem(hdlg, grp1);
556 if (wnd == nullptr) return 0L;
557 RECT dlgRect;
558 GetLocalRect(wnd, dlgRect, hdlg);
559
560 wnd = ::GetDlgItem(hdlg, rad1); // this is the top control "All"
561 if (wnd == nullptr) return 0L;
562 RECT rad1Rect;
563 GetLocalRect(wnd, rad1Rect, hdlg);
564
565 wnd = ::GetDlgItem(hdlg, rad2); // this is the bottom control "Selection"
566 if (wnd == nullptr) return 0L;
567 RECT rad2Rect;
568 GetLocalRect(wnd, rad2Rect, hdlg);
569
570 wnd = ::GetDlgItem(hdlg, rad3); // this is the middle control "Pages"
571 if (wnd == nullptr) return 0L;
572 RECT rad3Rect;
573 GetLocalRect(wnd, rad3Rect, hdlg);
574
575 HWND okWnd = ::GetDlgItem(hdlg, IDOK);
576 if (okWnd == nullptr) return 0L;
577 RECT okRect;
578 GetLocalRect(okWnd, okRect, hdlg);
579
580 wnd = ::GetDlgItem(hdlg, grp4); // this is the "Print range" groupbox
581 if (wnd == nullptr) return 0L;
582 RECT prtRect;
583 GetLocalRect(wnd, prtRect, hdlg);
584
585
586 // calculate various different "gaps" for layout purposes
587
588 int rbGap = rad3Rect.top - rad1Rect.bottom; // gap between radiobtns
589 int grpBotGap = dlgRect.bottom - rad2Rect.bottom; // gap from bottom rb to bottom of grpbox
590 int grpGap = dlgRect.top - prtRect.bottom ; // gap between group boxes
591 int top = dlgRect.bottom + grpGap;
592 int radHgt = rad1Rect.bottom - rad1Rect.top + 1; // top of new group box
593 int y = top+(rad1Rect.top-dlgRect.top); // starting pos of first radio
594 int rbWidth = dlgRect.right - rad1Rect.left - 5; // measure from rb left to the edge of the groupbox
595 // (5 is arbitrary)
596 nsIntRect rect;
597
598 // Create and position the radio buttons
599 //
600 // If any one control cannot be created then
601 // hide the others and bail out
602 //
603 rect.SetRect(rad1Rect.left, y, rbWidth,radHgt);
604 HWND rad4Wnd = CreateRadioBtn(hInst, hdlg, rad4, kAsLaidOutOnScreenStr, rect);
605 if (rad4Wnd == nullptr) return 0L;
606 y += radHgt + rbGap;
607
608 rect.SetRect(rad1Rect.left, y, rbWidth, radHgt);
609 HWND rad5Wnd = CreateRadioBtn(hInst, hdlg, rad5, kTheSelectedFrameStr, rect);
610 if (rad5Wnd == nullptr) {
611 Show(rad4Wnd, FALSE); // hide
612 return 0L;
613 }
614 y += radHgt + rbGap;
615
616 rect.SetRect(rad1Rect.left, y, rbWidth, radHgt);
617 HWND rad6Wnd = CreateRadioBtn(hInst, hdlg, rad6, kEachFrameSeparately, rect);
618 if (rad6Wnd == nullptr) {
619 Show(rad4Wnd, FALSE); // hide
620 Show(rad5Wnd, FALSE); // hide
621 return 0L;
622 }
623 y += radHgt + grpBotGap;
624
625 // Create and position the group box
626 rect.SetRect (dlgRect.left, top, dlgRect.right-dlgRect.left+1, y-top+1);
627 HWND grpBoxWnd = CreateGroupBox(hInst, hdlg, grp3, NS_LITERAL_STRING("Print Frame"), rect);
628 if (grpBoxWnd == nullptr) {
629 Show(rad4Wnd, FALSE); // hide
630 Show(rad5Wnd, FALSE); // hide
631 Show(rad6Wnd, FALSE); // hide
632 return 0L;
633 }
634
635 // Here we figure out the old height of the dlg
636 // then figure its gap from the old grpbx to the bottom
637 // then size the dlg
638 RECT pr, cr;
639 ::GetWindowRect(hdlg, &pr);
640 ::GetClientRect(hdlg, &cr);
641
642 int dlgHgt = (cr.bottom - cr.top) + 1;
643 int bottomGap = dlgHgt - okRect.bottom;
644 pr.bottom += (dlgRect.bottom-dlgRect.top) + grpGap + 1 - (dlgHgt-dlgRect.bottom) + bottomGap;
645
646 ::SetWindowPos(hdlg, nullptr, pr.left, pr.top, pr.right-pr.left+1, pr.bottom-pr.top+1,
647 SWP_NOMOVE|SWP_NOREDRAW|SWP_NOZORDER);
648
649 // figure out the new height of the dialog
650 ::GetClientRect(hdlg, &cr);
651 dlgHgt = (cr.bottom - cr.top) + 1;
652
653 // Reposition the OK and Cancel btns
654 int okHgt = okRect.bottom - okRect.top + 1;
655 ::SetWindowPos(okWnd, nullptr, okRect.left, dlgHgt-bottomGap-okHgt, 0, 0,
656 SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER);
657
658 HWND cancelWnd = ::GetDlgItem(hdlg, IDCANCEL);
659 if (cancelWnd == nullptr) return 0L;
660
661 RECT cancelRect;
662 GetLocalRect(cancelWnd, cancelRect, hdlg);
663 int cancelHgt = cancelRect.bottom - cancelRect.top + 1;
664 ::SetWindowPos(cancelWnd, nullptr, cancelRect.left, dlgHgt-bottomGap-cancelHgt, 0, 0,
665 SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER);
666
667 // localize and initialize the groupbox and radiobuttons
668 InitializeExtendedDialog(hdlg, howToEnableFrameUI);
669
670 // Looks like we were able to extend the dialog
671 gDialogWasExtended = true;
672 return TRUE;
673 }
674 return 0L;
675 }
676
677 //----------------------------------------------------------------------------------
678 // Returns a Global Moveable Memory Handle to a DevMode
679 // from the Printer by the name of aPrintName
680 //
681 // NOTE:
682 // This function assumes that aPrintName has already been converted from
683 // unicode
684 //
685 static HGLOBAL CreateGlobalDevModeAndInit(const nsXPIDLString& aPrintName, nsIPrintSettings* aPS)
686 {
687 HGLOBAL hGlobalDevMode = nullptr;
688
689 HANDLE hPrinter = nullptr;
690 // const cast kludge for silly Win32 api's
691 LPWSTR printName = const_cast<wchar_t*>(static_cast<const wchar_t*>(aPrintName.get()));
692 BOOL status = ::OpenPrinterW(printName, &hPrinter, nullptr);
693 if (status) {
694
695 LPDEVMODEW pNewDevMode;
696 DWORD dwNeeded, dwRet;
697
698 // Get the buffer size
699 dwNeeded = ::DocumentPropertiesW(gParentWnd, hPrinter, printName, nullptr, nullptr, 0);
700 if (dwNeeded == 0) {
701 return nullptr;
702 }
703
704 // Allocate a buffer of the correct size.
705 pNewDevMode = (LPDEVMODEW)::HeapAlloc (::GetProcessHeap(), HEAP_ZERO_MEMORY, dwNeeded);
706 if (!pNewDevMode) return nullptr;
707
708 hGlobalDevMode = (HGLOBAL)::GlobalAlloc(GHND, dwNeeded);
709 if (!hGlobalDevMode) {
710 ::HeapFree(::GetProcessHeap(), 0, pNewDevMode);
711 return nullptr;
712 }
713
714 dwRet = ::DocumentPropertiesW(gParentWnd, hPrinter, printName, pNewDevMode, nullptr, DM_OUT_BUFFER);
715
716 if (dwRet != IDOK) {
717 ::HeapFree(::GetProcessHeap(), 0, pNewDevMode);
718 ::GlobalFree(hGlobalDevMode);
719 ::ClosePrinter(hPrinter);
720 return nullptr;
721 }
722
723 // Lock memory and copy contents from DEVMODE (current printer)
724 // to Global Memory DEVMODE
725 LPDEVMODEW devMode = (DEVMODEW *)::GlobalLock(hGlobalDevMode);
726 if (devMode) {
727 memcpy(devMode, pNewDevMode, dwNeeded);
728 // Initialize values from the PrintSettings
729 SetupDevModeFromSettings(devMode, aPS);
730
731 // Sets back the changes we made to the DevMode into the Printer Driver
732 dwRet = ::DocumentPropertiesW(gParentWnd, hPrinter, printName, devMode, devMode, DM_IN_BUFFER | DM_OUT_BUFFER);
733 if (dwRet != IDOK) {
734 ::GlobalUnlock(hGlobalDevMode);
735 ::GlobalFree(hGlobalDevMode);
736 ::HeapFree(::GetProcessHeap(), 0, pNewDevMode);
737 ::ClosePrinter(hPrinter);
738 return nullptr;
739 }
740
741 ::GlobalUnlock(hGlobalDevMode);
742 } else {
743 ::GlobalFree(hGlobalDevMode);
744 hGlobalDevMode = nullptr;
745 }
746
747 ::HeapFree(::GetProcessHeap(), 0, pNewDevMode);
748
749 ::ClosePrinter(hPrinter);
750
751 } else {
752 return nullptr;
753 }
754
755 return hGlobalDevMode;
756 }
757
758 //------------------------------------------------------------------
759 // helper
760 static void GetDefaultPrinterNameFromGlobalPrinters(nsXPIDLString &printerName)
761 {
762 nsCOMPtr<nsIPrinterEnumerator> prtEnum = do_GetService("@mozilla.org/gfx/printerenumerator;1");
763 if (prtEnum) {
764 prtEnum->GetDefaultPrinterName(getter_Copies(printerName));
765 }
766 }
767
768 // Determine whether we have a completely native dialog
769 // or whether we cshould extend it
770 static bool ShouldExtendPrintDialog()
771 {
772 nsresult rv;
773 nsCOMPtr<nsIPrefService> prefs =
774 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
775 NS_ENSURE_SUCCESS(rv, true);
776 nsCOMPtr<nsIPrefBranch> prefBranch;
777 rv = prefs->GetBranch(nullptr, getter_AddRefs(prefBranch));
778 NS_ENSURE_SUCCESS(rv, true);
779
780 bool result;
781 rv = prefBranch->GetBoolPref("print.extend_native_print_dialog", &result);
782 NS_ENSURE_SUCCESS(rv, true);
783 return result;
784 }
785
786 //------------------------------------------------------------------
787 // Displays the native Print Dialog
788 static nsresult
789 ShowNativePrintDialog(HWND aHWnd,
790 nsIPrintSettings* aPrintSettings)
791 {
792 //NS_ENSURE_ARG_POINTER(aHWnd);
793 NS_ENSURE_ARG_POINTER(aPrintSettings);
794
795 gDialogWasExtended = false;
796
797 HGLOBAL hGlobalDevMode = nullptr;
798 HGLOBAL hDevNames = nullptr;
799
800 // Get the Print Name to be used
801 nsXPIDLString printerName;
802 aPrintSettings->GetPrinterName(getter_Copies(printerName));
803
804 // If there is no name then use the default printer
805 if (printerName.IsEmpty()) {
806 GetDefaultPrinterNameFromGlobalPrinters(printerName);
807 } else {
808 HANDLE hPrinter = nullptr;
809 if(!::OpenPrinterW(const_cast<wchar_t*>(static_cast<const wchar_t*>(printerName.get())), &hPrinter, nullptr)) {
810 // If the last used printer is not found, we should use default printer.
811 GetDefaultPrinterNameFromGlobalPrinters(printerName);
812 } else {
813 ::ClosePrinter(hPrinter);
814 }
815 }
816
817 // Now create a DEVNAMES struct so the the dialog is initialized correctly.
818
819 uint32_t len = printerName.Length();
820 hDevNames = (HGLOBAL)::GlobalAlloc(GHND, sizeof(wchar_t) * (len + 1) +
821 sizeof(DEVNAMES));
822 if (!hDevNames) {
823 return NS_ERROR_OUT_OF_MEMORY;
824 }
825
826 DEVNAMES* pDevNames = (DEVNAMES*)::GlobalLock(hDevNames);
827 if (!pDevNames) {
828 ::GlobalFree(hDevNames);
829 return NS_ERROR_FAILURE;
830 }
831 pDevNames->wDriverOffset = sizeof(DEVNAMES)/sizeof(wchar_t);
832 pDevNames->wDeviceOffset = sizeof(DEVNAMES)/sizeof(wchar_t);
833 pDevNames->wOutputOffset = sizeof(DEVNAMES)/sizeof(wchar_t)+len;
834 pDevNames->wDefault = 0;
835
836 memcpy(pDevNames+1, printerName, (len + 1) * sizeof(wchar_t));
837 ::GlobalUnlock(hDevNames);
838
839 // Create a Moveable Memory Object that holds a new DevMode
840 // from the Printer Name
841 // The PRINTDLG.hDevMode requires that it be a moveable memory object
842 // NOTE: We only need to free hGlobalDevMode when the dialog is cancelled
843 // When the user prints, it comes back in the printdlg struct and
844 // is used and cleaned up later
845 hGlobalDevMode = CreateGlobalDevModeAndInit(printerName, aPrintSettings);
846
847 // Prepare to Display the Print Dialog
848 PRINTDLGW prntdlg;
849 memset(&prntdlg, 0, sizeof(PRINTDLGW));
850
851 prntdlg.lStructSize = sizeof(prntdlg);
852 prntdlg.hwndOwner = aHWnd;
853 prntdlg.hDevMode = hGlobalDevMode;
854 prntdlg.hDevNames = hDevNames;
855 prntdlg.hDC = nullptr;
856 prntdlg.Flags = PD_ALLPAGES | PD_RETURNIC |
857 PD_USEDEVMODECOPIESANDCOLLATE | PD_COLLATE;
858
859 // if there is a current selection then enable the "Selection" radio button
860 int16_t howToEnableFrameUI = nsIPrintSettings::kFrameEnableNone;
861 bool isOn;
862 aPrintSettings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, &isOn);
863 if (!isOn) {
864 prntdlg.Flags |= PD_NOSELECTION;
865 }
866 aPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI);
867
868 int32_t pg = 1;
869 aPrintSettings->GetStartPageRange(&pg);
870 prntdlg.nFromPage = pg;
871
872 aPrintSettings->GetEndPageRange(&pg);
873 prntdlg.nToPage = pg;
874
875 prntdlg.nMinPage = 1;
876 prntdlg.nMaxPage = 0xFFFF;
877 prntdlg.nCopies = 1;
878 prntdlg.lpfnSetupHook = nullptr;
879 prntdlg.lpSetupTemplateName = nullptr;
880 prntdlg.hPrintTemplate = nullptr;
881 prntdlg.hSetupTemplate = nullptr;
882
883 prntdlg.hInstance = nullptr;
884 prntdlg.lpPrintTemplateName = nullptr;
885
886 if (!ShouldExtendPrintDialog()) {
887 prntdlg.lCustData = 0;
888 prntdlg.lpfnPrintHook = nullptr;
889 } else {
890 // Set up print dialog "hook" procedure for extending the dialog
891 prntdlg.lCustData = (DWORD)howToEnableFrameUI;
892 prntdlg.lpfnPrintHook = (LPPRINTHOOKPROC)PrintHookProc;
893 prntdlg.Flags |= PD_ENABLEPRINTHOOK;
894 }
895
896 BOOL result = ::PrintDlgW(&prntdlg);
897
898 if (TRUE == result) {
899 // check to make sure we don't have any nullptr pointers
900 NS_ENSURE_TRUE(aPrintSettings && prntdlg.hDevMode, NS_ERROR_FAILURE);
901
902 if (prntdlg.hDevNames == nullptr) {
903 ::GlobalFree(hGlobalDevMode);
904 return NS_ERROR_FAILURE;
905 }
906 // Lock the deviceNames and check for nullptr
907 DEVNAMES *devnames = (DEVNAMES *)::GlobalLock(prntdlg.hDevNames);
908 if (devnames == nullptr) {
909 ::GlobalFree(hGlobalDevMode);
910 return NS_ERROR_FAILURE;
911 }
912
913 char16_t* device = &(((char16_t *)devnames)[devnames->wDeviceOffset]);
914 char16_t* driver = &(((char16_t *)devnames)[devnames->wDriverOffset]);
915
916 // Check to see if the "Print To File" control is checked
917 // then take the name from devNames and set it in the PrintSettings
918 //
919 // NOTE:
920 // As per Microsoft SDK documentation the returned value offset from
921 // devnames->wOutputOffset is either "FILE:" or nullptr
922 // if the "Print To File" checkbox is checked it MUST be "FILE:"
923 // We assert as an extra safety check.
924 if (prntdlg.Flags & PD_PRINTTOFILE) {
925 char16ptr_t fileName = &(((wchar_t *)devnames)[devnames->wOutputOffset]);
926 NS_ASSERTION(wcscmp(fileName, L"FILE:") == 0, "FileName must be `FILE:`");
927 aPrintSettings->SetToFileName(fileName);
928 aPrintSettings->SetPrintToFile(true);
929 } else {
930 // clear "print to file" info
931 aPrintSettings->SetPrintToFile(false);
932 aPrintSettings->SetToFileName(nullptr);
933 }
934
935 nsCOMPtr<nsIPrintSettingsWin> psWin(do_QueryInterface(aPrintSettings));
936 if (!psWin) {
937 ::GlobalFree(hGlobalDevMode);
938 return NS_ERROR_FAILURE;
939 }
940
941 // Setup local Data members
942 psWin->SetDeviceName(device);
943 psWin->SetDriverName(driver);
944
945 #if defined(DEBUG_rods) || defined(DEBUG_dcone)
946 wprintf(L"printer: driver %s, device %s flags: %d\n", driver, device, prntdlg.Flags);
947 #endif
948 // fill the print options with the info from the dialog
949
950 aPrintSettings->SetPrinterName(device);
951
952 if (prntdlg.Flags & PD_SELECTION) {
953 aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeSelection);
954
955 } else if (prntdlg.Flags & PD_PAGENUMS) {
956 aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeSpecifiedPageRange);
957 aPrintSettings->SetStartPageRange(prntdlg.nFromPage);
958 aPrintSettings->SetEndPageRange(prntdlg.nToPage);
959
960 } else { // (prntdlg.Flags & PD_ALLPAGES)
961 aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeAllPages);
962 }
963
964 if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) {
965 // make sure the dialog got extended
966 if (gDialogWasExtended) {
967 // check to see about the frame radio buttons
968 switch (gFrameSelectedRadioBtn) {
969 case rad4:
970 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
971 break;
972 case rad5:
973 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kSelectedFrame);
974 break;
975 case rad6:
976 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kEachFrameSep);
977 break;
978 } // switch
979 } else {
980 // if it didn't get extended then have it default to printing
981 // each frame separately
982 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kEachFrameSep);
983 }
984 } else {
985 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kNoFrames);
986 }
987 // Unlock DeviceNames
988 ::GlobalUnlock(prntdlg.hDevNames);
989
990 // Transfer the settings from the native data to the PrintSettings
991 LPDEVMODEW devMode = (LPDEVMODEW)::GlobalLock(prntdlg.hDevMode);
992 if (devMode == nullptr) {
993 ::GlobalFree(hGlobalDevMode);
994 return NS_ERROR_FAILURE;
995 }
996 psWin->SetDevMode(devMode); // copies DevMode
997 SetPrintSettingsFromDevMode(aPrintSettings, devMode);
998 ::GlobalUnlock(prntdlg.hDevMode);
999
1000 #if defined(DEBUG_rods) || defined(DEBUG_dcone)
1001 bool printSelection = prntdlg.Flags & PD_SELECTION;
1002 bool printAllPages = prntdlg.Flags & PD_ALLPAGES;
1003 bool printNumPages = prntdlg.Flags & PD_PAGENUMS;
1004 int32_t fromPageNum = 0;
1005 int32_t toPageNum = 0;
1006
1007 if (printNumPages) {
1008 fromPageNum = prntdlg.nFromPage;
1009 toPageNum = prntdlg.nToPage;
1010 }
1011 if (printSelection) {
1012 printf("Printing the selection\n");
1013
1014 } else if (printAllPages) {
1015 printf("Printing all the pages\n");
1016
1017 } else {
1018 printf("Printing from page no. %d to %d\n", fromPageNum, toPageNum);
1019 }
1020 #endif
1021
1022 } else {
1023 ::SetFocus(aHWnd);
1024 aPrintSettings->SetIsCancelled(true);
1025 if (hGlobalDevMode) ::GlobalFree(hGlobalDevMode);
1026 return NS_ERROR_ABORT;
1027 }
1028
1029 return NS_OK;
1030 }
1031
1032 //------------------------------------------------------------------
1033 static void
1034 PrepareForPrintDialog(nsIWebBrowserPrint* aWebBrowserPrint, nsIPrintSettings* aPS)
1035 {
1036 NS_ASSERTION(aWebBrowserPrint, "Can't be null");
1037 NS_ASSERTION(aPS, "Can't be null");
1038
1039 bool isFramesetDocument;
1040 bool isFramesetFrameSelected;
1041 bool isIFrameSelected;
1042 bool isRangeSelection;
1043
1044 aWebBrowserPrint->GetIsFramesetDocument(&isFramesetDocument);
1045 aWebBrowserPrint->GetIsFramesetFrameSelected(&isFramesetFrameSelected);
1046 aWebBrowserPrint->GetIsIFrameSelected(&isIFrameSelected);
1047 aWebBrowserPrint->GetIsRangeSelection(&isRangeSelection);
1048
1049 // Setup print options for UI
1050 if (isFramesetDocument) {
1051 if (isFramesetFrameSelected) {
1052 aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAll);
1053 } else {
1054 aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAsIsAndEach);
1055 }
1056 } else {
1057 aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableNone);
1058 }
1059
1060 // Now determine how to set up the Frame print UI
1061 aPS->SetPrintOptions(nsIPrintSettings::kEnableSelectionRB, isRangeSelection || isIFrameSelected);
1062
1063 }
1064
1065 //----------------------------------------------------------------------------------
1066 //-- Show Print Dialog
1067 //----------------------------------------------------------------------------------
1068 nsresult NativeShowPrintDialog(HWND aHWnd,
1069 nsIWebBrowserPrint* aWebBrowserPrint,
1070 nsIPrintSettings* aPrintSettings)
1071 {
1072 PrepareForPrintDialog(aWebBrowserPrint, aPrintSettings);
1073
1074 nsresult rv = ShowNativePrintDialog(aHWnd, aPrintSettings);
1075 if (aHWnd) {
1076 ::DestroyWindow(aHWnd);
1077 }
1078
1079 return rv;
1080 }
1081

mercurial