Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1354 lines
30 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. prntsave.c
  5. Abstract:
  6. Routines to print or save the incompatibility reports. Functions
  7. PrintReport and SaveReport are called when the user clicks
  8. Save As... or Print... buttons in the UI. We then present
  9. a common dialog box to the user and perform the operation.
  10. Author:
  11. Jim Schmidt (jimschm) 13-Mar-1997
  12. Revision History:
  13. --*/
  14. #include "pch.h"
  15. #include "uip.h"
  16. #include <commdlg.h>
  17. #include <winspool.h>
  18. #define DBG_PRINTSAVE "Print/Save"
  19. GROWBUFFER g_PunctTable = GROWBUF_INIT;
  20. VOID
  21. BuildPunctTable (
  22. VOID
  23. )
  24. {
  25. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  26. WORD cp;
  27. TCHAR cpString[16];
  28. PTSTR p;
  29. PTSTR q;
  30. MULTISZ_ENUM e;
  31. DWORD d;
  32. GetGlobalCodePage (&cp, NULL);
  33. wsprintf (cpString, TEXT("%u"), (UINT) cp);
  34. g_PunctTable.End = 0;
  35. if (InfFindFirstLine (g_Win95UpgInf, TEXT("Wrap Exceptions"), cpString, &is)) {
  36. p = InfGetMultiSzField (&is, 1);
  37. if (EnumFirstMultiSz (&e, p)) {
  38. do {
  39. p = (PTSTR) e.CurrentString;
  40. while (*p) {
  41. q = p;
  42. if (StringIPrefix (p, TEXT("0x"))) {
  43. d = _tcstoul (p + 2, &p, 16);
  44. } else {
  45. d = _tcstoul (p, &p, 10);
  46. }
  47. if (q == p) {
  48. break;
  49. }
  50. GrowBufAppendDword (&g_PunctTable, d);
  51. if (*p) {
  52. p = (PTSTR) SkipSpace (p);
  53. }
  54. if (*p == TEXT(',')) {
  55. p++;
  56. }
  57. }
  58. } while (EnumNextMultiSz (&e));
  59. }
  60. }
  61. GrowBufAppendDword (&g_PunctTable, 0);
  62. InfCleanUpInfStruct (&is);
  63. }
  64. VOID
  65. FreePunctTable (
  66. VOID
  67. )
  68. {
  69. FreeGrowBuffer (&g_PunctTable);
  70. }
  71. BOOL
  72. IsPunct (
  73. MBCHAR Char
  74. )
  75. {
  76. PDWORD p;
  77. if (_ismbcpunct (Char)) {
  78. return TRUE;
  79. }
  80. p = (PDWORD) g_PunctTable.Buf;
  81. if (p) {
  82. while (*p) {
  83. if (*p == Char) {
  84. return TRUE;
  85. }
  86. p++;
  87. }
  88. }
  89. return FALSE;
  90. }
  91. BOOL
  92. pGetSaveAsName (
  93. IN HWND ParentWnd,
  94. IN OUT PTSTR Buffer
  95. )
  96. /*++
  97. Routine Description:
  98. Calls common dialog box to obtain the file name in which to save
  99. the compatibility report text file.
  100. Arguments:
  101. ParentWnd - A handle to the parent of the save as dialog
  102. Buffer - A caller-supplied buffer. Supplies a file name to
  103. initialize the common dialog box with, and receives
  104. the file name the user chose to use.
  105. Return Value:
  106. TRUE if the user clicked OK, or FALSE if the user canceled or an
  107. error occurred.
  108. --*/
  109. {
  110. TCHAR CwdSave[MAX_TCHAR_PATH];
  111. OPENFILENAME ofn;
  112. BOOL SaveFlag;
  113. TCHAR Filter[512];
  114. PCTSTR FilterResStr;
  115. PTSTR p;
  116. TCHAR desktopFolder[MAX_TCHAR_PATH];
  117. LPITEMIDLIST pIDL;
  118. BOOL b = FALSE;
  119. #define MAX_EXT 3
  120. PCTSTR ext[MAX_EXT];
  121. INT i = 0;
  122. FilterResStr = GetStringResource (MSG_FILE_NAME_FILTER);
  123. if (FilterResStr) {
  124. StringCopy (Filter, FilterResStr);
  125. FreeStringResource (FilterResStr);
  126. } else {
  127. MYASSERT (FALSE);
  128. Filter[0] = 0;
  129. }
  130. for (p = Filter ; *p ; p = _tcsinc (p)) {
  131. if (*p == TEXT('|')) {
  132. *p = 0;
  133. }
  134. }
  135. for (p = Filter; *p; p = GetEndOfString (p) + 1) {
  136. if (i & 1) {
  137. //
  138. // skip the * in "*.ext"
  139. // if extension is "*.*" reduce that to an empty string (no extension)
  140. //
  141. MYASSERT (i / 2 < MAX_EXT);
  142. ext[i / 2] = _tcsinc (p);
  143. if (StringMatch (ext[i / 2], TEXT(".*"))) {
  144. ext[i / 2] = S_EMPTY;
  145. }
  146. }
  147. i++;
  148. }
  149. if (SHGetSpecialFolderLocation (ParentWnd, CSIDL_DESKTOP, &pIDL) == S_OK) {
  150. LPMALLOC pMalloc;
  151. b = SHGetPathFromIDList (pIDL, desktopFolder);
  152. if (SHGetMalloc (&pMalloc) == S_OK) {
  153. IMalloc_Free (pMalloc, pIDL);
  154. IMalloc_Release (pMalloc);
  155. }
  156. }
  157. if (!b) {
  158. desktopFolder[0] = 0;
  159. }
  160. // Initialize OPENFILENAME
  161. ZeroMemory (&ofn, sizeof(OPENFILENAME));
  162. ofn.lStructSize = sizeof(OPENFILENAME);
  163. ofn.hwndOwner = ParentWnd;
  164. ofn.lpstrFile = Buffer;
  165. ofn.lpstrFilter = Filter;
  166. ofn.nMaxFile = MAX_TCHAR_PATH;
  167. // Force to begin at Desktop
  168. // ofn.lpstrInitialDir = TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}");
  169. ofn.lpstrInitialDir = desktopFolder;
  170. ofn.Flags = OFN_NOCHANGEDIR | // leave the CWD unchanged
  171. OFN_EXPLORER |
  172. OFN_OVERWRITEPROMPT |
  173. OFN_HIDEREADONLY;
  174. // Let user select disk or directory
  175. GetCurrentDirectory (sizeof (CwdSave), CwdSave);
  176. SaveFlag = GetSaveFileName (&ofn);
  177. SetCurrentDirectory (CwdSave);
  178. //
  179. // if no extension provided, append the default one
  180. // this was selected by the user and returned in ofn.nFilterIndex
  181. //
  182. #define DEFAULT_EXTENSION TEXT(".htm")
  183. p = (PTSTR)GetFileNameFromPath (Buffer);
  184. if (!p) {
  185. p = Buffer;
  186. }
  187. if (!_tcschr (p, TEXT('.'))) {
  188. if (SizeOfString (Buffer) + sizeof (DEFAULT_EXTENSION) <= MAX_TCHAR_PATH * sizeof (TCHAR)) {
  189. if (ofn.nFilterIndex >= MAX_EXT + 1) {
  190. ofn.nFilterIndex = 1;
  191. }
  192. StringCat (p, ext[ofn.nFilterIndex - 1]);
  193. }
  194. }
  195. return SaveFlag;
  196. }
  197. UINT
  198. pCreateWordWrappedString (
  199. OUT PTSTR Buffer,
  200. IN PCTSTR Str,
  201. IN UINT FirstLineSize,
  202. IN UINT RestLineSize
  203. )
  204. /*++
  205. Routine Description:
  206. Converts a string to a series of lines, where no line is bigger
  207. than LineSize. If a buffer is not supplied, this function estimates
  208. the number of bytes needed.
  209. If the code page is a far-east code page, then lines are broken at any
  210. multi-byte character, as well as at the spaces.
  211. Arguments:
  212. Buffer - If non-NULL, supplies address of buffer big enough to hold
  213. enlarged, wrapped string. If NULL, parameter is ignored.
  214. Str - Supplies the string that needs to be wrapped.
  215. FirstLineSize - Specifies the max size the first line can be.
  216. RestLineSize - Specifies the max size the remaining lines can be.
  217. Return Value:
  218. The size of the string copied to Buffer including the terminating NULL,
  219. or the size of the needed buffer if Buffer is NULL.
  220. --*/
  221. {
  222. PCTSTR p, Start;
  223. UINT Col;
  224. PCTSTR LastSpace;
  225. CHARTYPE c;
  226. UINT Size;
  227. BOOL PrevCharMb;
  228. UINT LineSize;
  229. LineSize = FirstLineSize;
  230. p = Str;
  231. if (!p)
  232. return 0;
  233. Size = SizeOfString(Str);
  234. if (Buffer) {
  235. *Buffer = 0;
  236. }
  237. while (*p) {
  238. // Beginning of line
  239. Col = 0;
  240. LastSpace = NULL;
  241. Start = p;
  242. PrevCharMb = FALSE;
  243. do {
  244. // Is this a hard-coded line break?
  245. c = _tcsnextc (p);
  246. if (c == TEXT('\r') || c == TEXT('\n')) {
  247. LastSpace = p;
  248. p = _tcsinc (p);
  249. if (c == TEXT('\r') && _tcsnextc (p) == TEXT('\n')) {
  250. p = _tcsinc (p);
  251. } else {
  252. Size += sizeof (TCHAR);
  253. }
  254. c = TEXT('\n');
  255. break;
  256. }
  257. else if (_istspace (c)) {
  258. LastSpace = p;
  259. }
  260. else if (IsLeadByte (*p)) {
  261. // MB chars are usually two cols wide
  262. Col++;
  263. if (PrevCharMb) {
  264. //
  265. // If this char is not punctuation, then we can
  266. // break here
  267. //
  268. if (!IsPunct (c)) {
  269. LastSpace = p;
  270. }
  271. }
  272. PrevCharMb = TRUE;
  273. }
  274. else {
  275. if (PrevCharMb) {
  276. LastSpace = p;
  277. }
  278. PrevCharMb = FALSE;
  279. }
  280. // Continue until line gets too long
  281. Col++;
  282. p = _tcsinc (p);
  283. } while (*p && Col < LineSize);
  284. // If no more text, or line that has no space needs to be broken
  285. if (!(*p) || (c != TEXT('\n') && !LastSpace)) {
  286. LastSpace = p;
  287. }
  288. if (Buffer) {
  289. StringCopyAB (Buffer, Start, LastSpace);
  290. Buffer = GetEndOfString (Buffer);
  291. }
  292. if (*p && c != TEXT('\n')) {
  293. p = LastSpace;
  294. Size += sizeof (TCHAR) * 2;
  295. }
  296. // remove space at start of wrapped line
  297. while (_tcsnextc (p) == TEXT(' ')) {
  298. Size -= sizeof (TCHAR);
  299. p = _tcsinc (p);
  300. }
  301. if (Buffer && *p) {
  302. Buffer = _tcsappend (Buffer, TEXT("\r\n"));
  303. }
  304. LineSize = RestLineSize;
  305. }
  306. return Size;
  307. }
  308. PCTSTR
  309. CreateIndentedString (
  310. IN PCTSTR UnwrappedStr,
  311. IN UINT Indent,
  312. IN INT HangingIndent,
  313. IN UINT LineLen
  314. )
  315. /*++
  316. Routine Description:
  317. Takes an unwrapped string, word-wraps it (via pCreateWordWrappedString),
  318. inserts spaces before each line, optionally skipping the first line.
  319. If the code page is a far-east code page, then lines are broken at any
  320. multi-byte character, as well as at the spaces.
  321. Arguments:
  322. UnwrappedStr - A pointer to the string that is to be word-wrapped and
  323. adjusted with space inserts.
  324. Indent - The number of spaces to insert before each line.
  325. HangingIndent - The adjustment made to the indent after the first line
  326. LineLen - The maximum line size. Spaces must always be smaller
  327. than LineLen (and should be considerably smaller).
  328. FirstLine - If TRUE, the first line is indented. If FALSE, the
  329. first line is skipped ("hanging indent").
  330. Return Value:
  331. A pointer to the indented string, or NULL if MemAlloc failed. The
  332. caller must free the string with MemFree.
  333. --*/
  334. {
  335. UINT Size;
  336. UINT Count;
  337. PCTSTR p, q;
  338. PTSTR d;
  339. PTSTR Dest;
  340. PTSTR Str;
  341. UINT FirstLineLen;
  342. UINT RestLineLen;
  343. UINT FirstLineIndent;
  344. UINT RestLineIndent;
  345. UINT RealIndent;
  346. if (!UnwrappedStr) {
  347. return NULL;
  348. }
  349. MYASSERT ((INT)Indent + HangingIndent >= 0);
  350. FirstLineIndent = Indent;
  351. RestLineIndent = Indent + HangingIndent;
  352. MYASSERT (LineLen > FirstLineIndent);
  353. MYASSERT (LineLen > RestLineIndent);
  354. FirstLineLen = LineLen - FirstLineIndent;
  355. RestLineLen = LineLen - RestLineIndent;
  356. //
  357. // Estimate line size, then do the wrapping
  358. //
  359. Str = (PTSTR) MemAlloc (
  360. g_hHeap,
  361. 0,
  362. pCreateWordWrappedString (
  363. NULL,
  364. UnwrappedStr,
  365. FirstLineLen,
  366. RestLineLen
  367. )
  368. );
  369. if (!Str) {
  370. return NULL;
  371. }
  372. pCreateWordWrappedString (
  373. Str,
  374. UnwrappedStr,
  375. FirstLineLen,
  376. RestLineLen
  377. );
  378. if (!FirstLineIndent && !RestLineIndent) {
  379. return Str;
  380. }
  381. //
  382. // Count number of lines
  383. //
  384. for (Count = 1, p = Str ; *p ; p = _tcsinc (p)) {
  385. if (*p == TEXT('\n')) {
  386. Count++;
  387. }
  388. }
  389. //
  390. // Allocate a new buffer that is big enough for all the indented text
  391. //
  392. Size = max (FirstLineIndent, RestLineIndent) * Count + SizeOfString (Str);
  393. Dest = MemAlloc (g_hHeap, 0, Size);
  394. if (Dest) {
  395. *Dest = 0;
  396. //
  397. // Indent each line
  398. //
  399. p = Str;
  400. d = Dest;
  401. RealIndent = FirstLineIndent;
  402. while (*p) {
  403. for (Count = 0 ; Count < RealIndent ; Count++) {
  404. *d++ = TEXT(' ');
  405. }
  406. q = _tcschr (p, TEXT('\n'));
  407. if (!q) {
  408. q = GetEndOfString (p);
  409. } else {
  410. q = _tcsinc (q);
  411. }
  412. StringCopyAB (d, p, q);
  413. d = GetEndOfString (d);
  414. p = q;
  415. RealIndent = RestLineIndent;
  416. }
  417. }
  418. MemFree (g_hHeap, 0, Str);
  419. return Dest;
  420. }
  421. BOOL
  422. pSaveReportToDisk (
  423. IN HWND Parent,
  424. IN PCTSTR FileSpec,
  425. IN BOOL Html,
  426. IN DWORD MinLevel
  427. )
  428. {
  429. HANDLE File;
  430. BOOL b;
  431. PCTSTR Msg;
  432. //
  433. // Create the report file
  434. //
  435. File = CreateFile (
  436. FileSpec,
  437. GENERIC_WRITE,
  438. 0,
  439. NULL,
  440. CREATE_ALWAYS,
  441. FILE_ATTRIBUTE_NORMAL,
  442. NULL
  443. );
  444. if (File == INVALID_HANDLE_VALUE) {
  445. LOG ((LOG_ERROR, "Error while saving report to %s", FileSpec));
  446. return FALSE;
  447. }
  448. //
  449. // Save the report text to disk
  450. //
  451. b = FALSE;
  452. Msg = CreateReportText (Html, 70, MinLevel, FALSE);
  453. if (Msg) {
  454. b = WriteFileString (File, Msg);
  455. }
  456. FreeReportText();
  457. //
  458. // Close the file and alert the user to save errors!
  459. //
  460. CloseHandle (File);
  461. return b;
  462. }
  463. BOOL
  464. SaveReport (
  465. IN HWND Parent, OPTIONAL
  466. IN PCTSTR Path OPTIONAL
  467. )
  468. /*++
  469. Routine Description:
  470. Obtains a file name from the user via common Save As dialog,
  471. creates the file and writes an incompatibility list to disk.
  472. Arguments:
  473. Parent - The parent of the common dialog box. Optional If this is NULL,
  474. no UI is displayed.
  475. Path - The path to save to. If this is NULL, parent must be specified.
  476. Return Value:
  477. TRUE if the report was saved, or FALSE if the user canceled the
  478. operation or the save failed. The user is alerted when the
  479. save fails.
  480. --*/
  481. {
  482. TCHAR Buffer[MAX_TCHAR_PATH + 4];
  483. PCTSTR Str;
  484. BOOL b;
  485. DWORD attributes;
  486. PTSTR p;
  487. BOOL saved = FALSE;
  488. BOOL bSaveBothFormats = FALSE;
  489. MYASSERT(Parent != NULL || Path != NULL);
  490. //
  491. // Obtain a path, or use caller-supplied path
  492. //
  493. if (Path) {
  494. attributes = GetFileAttributes(Path);
  495. }
  496. if (!Path || attributes != 0xFFFFFFFF && (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
  497. if (Path) {
  498. StringCopy(Buffer,Path);
  499. AppendPathWack(Buffer);
  500. bSaveBothFormats = TRUE;
  501. } else {
  502. Buffer[0] = 0;
  503. }
  504. Str = GetStringResource (MSG_DEFAULT_REPORT_FILE);
  505. MYASSERT (Str);
  506. if (!Str) {
  507. return FALSE;
  508. }
  509. StringCat (Buffer, Str);
  510. FreeStringResource (Str);
  511. } else {
  512. StringCopy(Buffer,Path);
  513. }
  514. if (Parent) {
  515. if (!pGetSaveAsName (Parent, Buffer)) {
  516. return FALSE;
  517. }
  518. }
  519. p = _tcsrchr (Buffer, TEXT('.'));
  520. if (!p || _tcschr (p, TEXT('\\'))) {
  521. p = GetEndOfString (Buffer);
  522. }
  523. //
  524. // Save as text, if extension is .txt
  525. //
  526. if (StringIMatch (p, TEXT(".txt"))) {
  527. b = pSaveReportToDisk (Parent, Buffer, FALSE, REPORTLEVEL_VERBOSE);
  528. if (Parent) {
  529. saved = b;
  530. }
  531. } else {
  532. b = TRUE;
  533. }
  534. //
  535. // Save as HTML unless user chose to save as .txt
  536. //
  537. if (b && !saved) {
  538. if (!Parent && p) {
  539. StringCopy (p, TEXT(".htm"));
  540. }
  541. b = pSaveReportToDisk (Parent, Buffer, TRUE, REPORTLEVEL_VERBOSE);
  542. }
  543. if (bSaveBothFormats) {
  544. StringCopy (p, TEXT(".txt"));
  545. b = pSaveReportToDisk (Parent, Buffer, FALSE, REPORTLEVEL_VERBOSE);
  546. }
  547. if (!b) {
  548. if (Parent) {
  549. ResourceMessageBox (Parent, MSG_CANT_SAVE, MB_OK, NULL);
  550. }
  551. }
  552. return TRUE;
  553. }
  554. VOID
  555. pFreePrintMem (
  556. IN OUT PRINTDLG *ppd
  557. )
  558. /*++
  559. Routine Description:
  560. Frees all memory associated with PRINTDLG structure.
  561. Arguments:
  562. ppd - Pointer to PRINTDLG structure.
  563. Return Value:
  564. none
  565. --*/
  566. {
  567. if (ppd->hDevMode) {
  568. GlobalFree (ppd->hDevMode);
  569. ppd->hDevMode = NULL;
  570. }
  571. if(ppd->hDevNames) {
  572. GlobalFree (ppd->hDevNames);
  573. ppd->hDevNames = NULL;
  574. }
  575. }
  576. VOID
  577. pInitPrintDlgStruct (
  578. OUT PRINTDLG *ppd,
  579. IN HWND Parent,
  580. IN DWORD Flags
  581. )
  582. /*++
  583. Routine Description:
  584. Initializes PRINTDLG structure, setting the owner window and
  585. print dialog flags.
  586. Arguments:
  587. ppd - Pointer to PRINTDLG structure to be initialized
  588. Parent - Handle to parent window for the dialog
  589. Flags - PrintDlg flags (PD_*)
  590. Return Value:
  591. none (structure is initialized)
  592. --*/
  593. {
  594. ZeroMemory (ppd, sizeof (PRINTDLG));
  595. ppd->lStructSize = sizeof (PRINTDLG);
  596. ppd->hwndOwner = Parent;
  597. ppd->Flags = Flags;
  598. ppd->hInstance = g_hInst;
  599. }
  600. HDC
  601. pGetPrintDC (
  602. IN HWND Parent
  603. )
  604. /*++
  605. Routine Description:
  606. Displays common dialog box to the user, and if the user chooses
  607. a printer, returns a device context handle.
  608. Arguments:
  609. Parent - The parent of the common dialog to be displayed
  610. Return Value:
  611. A handle to a device context of the chosen printer, or NULL if the
  612. user canceled printing.
  613. --*/
  614. {
  615. PRINTDLG pd;
  616. pInitPrintDlgStruct (
  617. &pd,
  618. Parent,
  619. PD_ALLPAGES|PD_NOPAGENUMS|PD_NOSELECTION|PD_RETURNDC
  620. );
  621. if (PrintDlg (&pd)) {
  622. pFreePrintMem (&pd);
  623. return pd.hDC;
  624. }
  625. return NULL;
  626. }
  627. typedef struct {
  628. HDC hdc;
  629. INT Page;
  630. INT Line;
  631. RECT HeaderRect; // in logical units
  632. RECT PrintableRect; // in logical units
  633. TEXTMETRIC tm;
  634. INT LineHeight;
  635. INT TotalLines; // printable height/line height
  636. INT TotalCols; // printable width/char width
  637. HFONT FontHandle;
  638. BOOL PageActive;
  639. } PRINT_POSITION, *PPRINT_POSITION;
  640. PCTSTR
  641. pDrawLineText (
  642. IN OUT PPRINT_POSITION PrintPos,
  643. IN PCTSTR Text,
  644. IN DWORD Flags,
  645. IN BOOL Header
  646. )
  647. /*++
  648. Routine Description:
  649. Draws a single line of text on a printer device context and
  650. returns a pointer to the next line or nul terminator.
  651. Arguments:
  652. PrintPos - A pointer to the current PRINT_POSITION structure
  653. that gives page position settings.
  654. Text - A pointer to the text string containing the line.
  655. Flags - Additional DrawText flags (DT_LEFT, DT_CENTER,
  656. DT_RIGHT and/or DT_RTLREADING)
  657. Header - TRUE if text should be written to header, or
  658. FALSE if it should be written to the current line
  659. Return Value:
  660. A pointer to the next line within the string, a pointer to the
  661. nul terminator, or NULL if an error occurred.
  662. --*/
  663. {
  664. RECT rect;
  665. PCTSTR p;
  666. CHARTYPE ch;
  667. if (Header) {
  668. CopyMemory (&rect, &PrintPos->HeaderRect, sizeof (RECT));
  669. } else {
  670. CopyMemory (&rect, &PrintPos->PrintableRect, sizeof (RECT));
  671. rect.top += PrintPos->LineHeight * PrintPos->Line;
  672. }
  673. Flags = Flags & (DT_CENTER|DT_LEFT|DT_RIGHT|DT_RTLREADING);
  674. Flags |= DT_TOP|DT_EDITCONTROL|DT_NOPREFIX|DT_EXTERNALLEADING;
  675. for (ch = 0, p = Text ; *p ; p = _tcsinc (p)) {
  676. ch = _tcsnextc (p);
  677. if (ch == TEXT('\n') || ch == TEXT('\r')) {
  678. break;
  679. }
  680. }
  681. if (p != Text) {
  682. if (!DrawText (PrintPos->hdc, Text, p - Text, &rect, Flags)) {
  683. LOG ((LOG_ERROR, "Failure while sending text to printer."));
  684. return NULL;
  685. }
  686. }
  687. // Skip past line break
  688. if (ch == TEXT('\r')) {
  689. p = _tcsinc (p);
  690. ch = _tcsnextc (p);
  691. }
  692. if (ch == TEXT('\n')) {
  693. p = _tcsinc (p);
  694. }
  695. return p;
  696. }
  697. BOOL
  698. pPrintString (
  699. IN OUT PPRINT_POSITION PrintPos,
  700. IN PCTSTR MultiLineString
  701. )
  702. /*++
  703. Routine Description:
  704. Dumps a multi-line string to the printer. If necessary, the
  705. string may be printed on a new page. This function tries to
  706. eliminate widows and orphans by printing the entire string
  707. on the same page if possible.
  708. Arguments:
  709. PrintPos - The current position information, describing
  710. the printer device context, page number, line
  711. number and metrics.
  712. MultiLineString - A pointer to the string to print.
  713. Return Value:
  714. TRUE if printing was successful, or FALSE if an error occurrred.
  715. --*/
  716. {
  717. INT LineCount;
  718. PCTSTR p;
  719. PCTSTR Str;
  720. PCTSTR Args[1];
  721. TCHAR Buffer[32];
  722. CHARTYPE ch;
  723. HDC hdc;
  724. hdc = PrintPos->hdc;
  725. //
  726. // Count lines in MultiLineString
  727. //
  728. ch = TEXT('\n');
  729. for (LineCount = 0, p = MultiLineString ; *p ; p = _tcsinc (p)) {
  730. ch = _tcsnextc (p);
  731. if (ch == TEXT('\n')) {
  732. LineCount++;
  733. }
  734. }
  735. if (ch != TEXT('\n')) {
  736. LineCount++;
  737. }
  738. //
  739. // Widow/orphan suppression: If all lines do not fit on
  740. // the page, and we are more than half way down the page,
  741. // roll to the next page.
  742. //
  743. if (PrintPos->Line + LineCount > PrintPos->TotalLines) {
  744. if (PrintPos->Line > PrintPos->TotalLines / 2) {
  745. // Move to next page
  746. EndPage (hdc);
  747. PrintPos->PageActive = FALSE;
  748. PrintPos->Page++;
  749. PrintPos->Line = 0;
  750. }
  751. }
  752. //
  753. // Send each line in MultiLineString
  754. //
  755. while (*MultiLineString) {
  756. //
  757. // Draw header if necessary
  758. //
  759. if (!PrintPos->Line) {
  760. StartPage (hdc);
  761. PrintPos->PageActive = TRUE;
  762. SetMapMode (hdc, MM_TWIPS);
  763. SelectObject (hdc, PrintPos->FontHandle);
  764. SetBkMode (hdc, TRANSPARENT);
  765. //Rectangle (hdc, PrintPos->HeaderRect.left, PrintPos->HeaderRect.top, PrintPos->HeaderRect.right, PrintPos->HeaderRect.bottom);
  766. //Rectangle (hdc, PrintPos->PrintableRect.left, PrintPos->PrintableRect.top, PrintPos->PrintableRect.right, PrintPos->PrintableRect.bottom);
  767. wsprintf (Buffer, TEXT("%u"), PrintPos->Page);
  768. Args[0] = Buffer;
  769. // Left side
  770. Str = ParseMessageID (
  771. MSG_REPORT_HEADER_LEFT,
  772. Args
  773. );
  774. if (Str && *Str) {
  775. p = pDrawLineText (PrintPos, Str, DT_LEFT, TRUE);
  776. if (!p) {
  777. return FALSE;
  778. }
  779. }
  780. // Center
  781. Str = ParseMessageID (
  782. MSG_REPORT_HEADER_CENTER,
  783. Args
  784. );
  785. if (Str && *Str) {
  786. p = pDrawLineText (PrintPos, Str, DT_CENTER, TRUE);
  787. if (!p) {
  788. return FALSE;
  789. }
  790. }
  791. // Right side
  792. Str = ParseMessageID (
  793. MSG_REPORT_HEADER_RIGHT,
  794. Args
  795. );
  796. if (Str && *Str) {
  797. p = pDrawLineText (PrintPos, Str, DT_RIGHT, TRUE);
  798. if (!p) {
  799. return FALSE;
  800. }
  801. }
  802. }
  803. //
  804. // Draw line
  805. //
  806. MultiLineString = pDrawLineText (
  807. PrintPos,
  808. MultiLineString,
  809. DT_LEFT,
  810. FALSE
  811. );
  812. if (!MultiLineString) {
  813. return FALSE;
  814. }
  815. PrintPos->Line++;
  816. if (PrintPos->Line >= PrintPos->TotalLines) {
  817. EndPage (hdc);
  818. PrintPos->PageActive = FALSE;
  819. PrintPos->Page++;
  820. PrintPos->Line = 0;
  821. }
  822. }
  823. return TRUE;
  824. }
  825. VOID
  826. pCalculatePageMetrics (
  827. IN OUT PPRINT_POSITION PrintPos
  828. )
  829. /*++
  830. Routine Description:
  831. Calculates all the page metrics (margins, header position,
  832. line count, col count, etc). Positions are in TWIPS and
  833. counts are in characters or lines.
  834. Arguments:
  835. PrintPos - Pointer to PRINT_POSITION structure which gives
  836. the printer device context. Structure receives
  837. metrics.
  838. Return Value:
  839. none
  840. --*/
  841. {
  842. INT WidthPixels, HeightPixels;
  843. INT DpiX, DpiY;
  844. INT UnprintableLeftPixels, UnprintableTopPixels;
  845. POINT TempPoint;
  846. HDC hdc;
  847. hdc = PrintPos->hdc;
  848. //
  849. // Make no assumptions about hdc
  850. //
  851. SetMapMode (hdc, MM_TWIPS);
  852. SelectObject (hdc, PrintPos->FontHandle);
  853. GetTextMetrics (hdc, &PrintPos->tm);
  854. //
  855. // Get device dimensions
  856. //
  857. DpiX = GetDeviceCaps (hdc, LOGPIXELSX);
  858. DpiY = GetDeviceCaps (hdc, LOGPIXELSY);
  859. UnprintableLeftPixels = GetDeviceCaps (hdc, PHYSICALOFFSETX);
  860. UnprintableTopPixels = GetDeviceCaps (hdc, PHYSICALOFFSETY);
  861. WidthPixels = GetDeviceCaps (hdc, PHYSICALWIDTH);
  862. HeightPixels = GetDeviceCaps (hdc, PHYSICALHEIGHT);
  863. // Calulate 3/4 inch left/right margins
  864. PrintPos->HeaderRect.left = (DpiX * 3 / 4) - UnprintableLeftPixels;
  865. PrintPos->HeaderRect.right = WidthPixels - (DpiX * 3 / 4) - UnprintableLeftPixels;
  866. // Calculate 1/2 inch top margin for header
  867. PrintPos->HeaderRect.top = (DpiY / 2) - UnprintableTopPixels;
  868. PrintPos->HeaderRect.bottom = DpiY - UnprintableTopPixels;
  869. // Convert pixels (device units) into logical units
  870. DPtoLP (hdc, (LPPOINT) (&PrintPos->HeaderRect), 2);
  871. // Copy header's left & right margins to printable rect
  872. // Copy header's bottom margin to printable rect's top margin
  873. PrintPos->PrintableRect.left = PrintPos->HeaderRect.left;
  874. PrintPos->PrintableRect.right = PrintPos->HeaderRect.right;
  875. PrintPos->PrintableRect.top = PrintPos->HeaderRect.bottom;
  876. // Calculate printable rect's bottom margin (3/4 inch)
  877. TempPoint.x = 0;
  878. TempPoint.y = HeightPixels - (DpiY * 3 / 4) - UnprintableTopPixels;
  879. DPtoLP (hdc, &TempPoint, 1);
  880. PrintPos->PrintableRect.bottom = TempPoint.y;
  881. PrintPos->LineHeight = -(PrintPos->tm.tmHeight + PrintPos->tm.tmInternalLeading + PrintPos->tm.tmExternalLeading);
  882. MYASSERT (PrintPos->LineHeight);
  883. PrintPos->TotalLines = (PrintPos->PrintableRect.bottom - PrintPos->PrintableRect.top) / PrintPos->LineHeight;
  884. PrintPos->TotalCols = (PrintPos->PrintableRect.right - PrintPos->PrintableRect.left) / PrintPos->tm.tmAveCharWidth;
  885. }
  886. BOOL
  887. PrintReport (
  888. IN HWND Parent,
  889. IN DWORD Level
  890. )
  891. /*++
  892. Routine Description:
  893. Obtains a printer from the user via common Print dialog,
  894. starts the print job and sends an incompatibility list to
  895. one or more pages.
  896. Arguments:
  897. Parent - A handle to the parent of the print dialog
  898. Return Value:
  899. TRUE if printing completed, or FALSE if it was canceled or an
  900. error occurred.
  901. --*/
  902. {
  903. HDC hdc;
  904. PRINT_POSITION pp;
  905. LOGFONT Font;
  906. BOOL b;
  907. DOCINFO di;
  908. INT JobId;
  909. PCTSTR Msg;
  910. HANDLE DefaultUiFont;
  911. hdc = pGetPrintDC (Parent);
  912. if (!hdc) {
  913. return FALSE; // user canceled print dialog
  914. }
  915. if (!BeginMessageProcessing()) {
  916. // unexpected out-of-memory
  917. DeleteDC (hdc);
  918. return FALSE;
  919. }
  920. //
  921. // Initialize PRINT_POSITION
  922. //
  923. b = TRUE;
  924. TurnOnWaitCursor();
  925. ZeroMemory (&pp, sizeof (pp));
  926. pp.hdc = hdc;
  927. pp.Page = 1;
  928. //
  929. // Start doc
  930. //
  931. ZeroMemory (&di, sizeof (di));
  932. di.cbSize = sizeof (di);
  933. di.lpszDocName = GetStringResource (MSG_REPORT_DOC_NAME);
  934. MYASSERT (di.lpszDocName);
  935. if (di.lpszDocName) {
  936. JobId = StartDoc (hdc, &di);
  937. if (!JobId) {
  938. LOG ((LOG_ERROR, "Cannot start print job."));
  939. ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL);
  940. b = FALSE;
  941. }
  942. } else {
  943. //
  944. // not enough memory
  945. //
  946. JobId = 0;
  947. b = FALSE;
  948. }
  949. if (b) {
  950. //
  951. // Create font
  952. //
  953. ZeroMemory (&Font, sizeof (Font));
  954. DefaultUiFont = (HFONT) GetStockObject (DEFAULT_GUI_FONT);
  955. if (DefaultUiFont) {
  956. GetObject (DefaultUiFont, sizeof (Font), &Font);
  957. Font.lfHeight = 12 * 20; // height in TWIPS (1/20 of a point)
  958. Font.lfWeight = FW_NORMAL;
  959. Font.lfOutPrecision = OUT_TT_PRECIS;
  960. Font.lfPitchAndFamily = FIXED_PITCH|FF_MODERN;
  961. pp.FontHandle = CreateFontIndirect (&Font);
  962. if (!pp.FontHandle) {
  963. LOG ((LOG_ERROR, "Cannot create font for print operation."));
  964. //
  965. // deferred this call to the end
  966. //
  967. //ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL);
  968. b = FALSE;
  969. }
  970. } else {
  971. b = FALSE;
  972. }
  973. }
  974. if (b) {
  975. //
  976. // Create page metrics
  977. //
  978. pCalculatePageMetrics (&pp);
  979. DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: LineHeight=%i", pp.LineHeight));
  980. DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: TotalLines=%i", pp.TotalLines));
  981. DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: TotalCols=%i", pp.TotalCols));
  982. DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: Header rect: (%i, %i)-(%i, %i)", pp.HeaderRect.left, pp.HeaderRect.top, pp.HeaderRect.right, pp.HeaderRect.bottom));
  983. DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: Printable rect: (%i, %i)-(%i, %i)", pp.PrintableRect.left, pp.PrintableRect.top, pp.PrintableRect.right, pp.PrintableRect.bottom));
  984. //
  985. // Print the report
  986. //
  987. Msg = CreateReportText (FALSE, pp.TotalCols, Level, FALSE);
  988. if (Msg) {
  989. b = pPrintString (&pp, Msg);
  990. }
  991. FreeReportText();
  992. }
  993. if (JobId) {
  994. if (b) {
  995. if (pp.PageActive) {
  996. EndPage (hdc);
  997. }
  998. EndDoc (hdc);
  999. } else {
  1000. AbortDoc (hdc);
  1001. }
  1002. }
  1003. DeleteDC (hdc);
  1004. if (pp.FontHandle) {
  1005. DeleteObject (pp.FontHandle);
  1006. }
  1007. TurnOffWaitCursor();
  1008. if (!b) {
  1009. ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL);
  1010. }
  1011. EndMessageProcessing();
  1012. return b;
  1013. }