source: trunk/src/helpers/textview.c@ 47

Last change on this file since 47 was 47, checked in by umoeller, 24 years ago

misc changes

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 127.9 KB
Line 
1
2/*
3 *@@sourcefile textview.c:
4 * all-new XTextView control as well as device-independent
5 * text formatting and printing. Whoa.
6 *
7 * <B>Text view control</B>
8 *
9 * This is a read-only control to display any given text
10 * (PSZ) in any given font. As opposed to a multi-line entry
11 * field (MLE), this can handle multiple fonts and character
12 * and paragraph formatting. Also, this thing sets its scroll
13 * bars right, which is one of the most annoying bugs in the
14 * MLE control.
15 *
16 * This is currently in the process of turning into a full-fledged
17 * "rich text" control. For example, the WarpIN "Readme" pages
18 * use this control.
19 *
20 * This is all new with V0.9.1. Great changes have been made
21 * with V0.9.3.
22 *
23 * To use the text view control, you must call txvRegisterTextView
24 * in your application first. This registers the WC_XTEXTVIEW
25 * window class with PM.
26 *
27 * Then create your XTextView using WinCreateWindow. The
28 * XTextView control has an XTEXTVIEWCDATA control data
29 * structure which optionally can be passed to WinCreateWindow
30 * like this:
31 *
32 + XTEXTVIEWCDATA xtxvCData;
33 + memset(&xtxvCData, 0, sizeof(xtxvCData));
34 + xtxvCData.cbData = sizeof(xtxvCData);
35 + xtxvCData.flStyle = XTXF_VSCROLL;
36 + xtxvCData.ulXBorder = 20;
37 + xtxvCData.ulYBorder = 20;
38 + G_hwndProcView = WinCreateWindow(hwndClient, // parent
39 + WC_XTEXTVIEW, // class
40 + "", // title, always ignored
41 + WS_VISIBLE, // style flags
42 + 0, 0, 100, 100, // pos and size
43 + hwndClient, // owner
44 + HWND_TOP, // z-order
45 + ID_PROCINFO, // win ID
46 + &xtxvCData, // control data
47 + 0); // presparams
48 +
49 * <B>Setting the text to be displayed</B>
50 *
51 * The text to be displayed must be passed to the control using
52 * the standard WinSetWindowText function (upon which PM sends a
53 * WM_SETWINDOWPARMS message to the control), which is then
54 * automatically formatted and painted.
55 *
56 * However, since the XTextView control is extremely capable,
57 * a few things need to be noted:
58 *
59 * -- The text view assumes that lines terminate with \n ONLY.
60 * The \r char is used for soft line breaks (start new line
61 * in the same paragraph, similar to the HTML BR tag). If
62 * you give the control the usual OS/2 \r\n sequence, you
63 * get large spacings. Use txvStripLinefeeds to strip the
64 * \r characters before setting the text.
65 *
66 * In short, to give the control any text, do this:
67 +
68 + PSZ psz = ... // whatever, load string
69 + txvStripLinefeeds(&psz); // reallocates
70 + WinSetWindowText(hwndTextView, psz);
71 *
72 * -- The control uses the \xFF (255) character internally as
73 * an escape code for formatting commands. See "Escape codes"
74 * below. If your text contains this character, you should
75 * overwrite all occurences with spaces, or they will be
76 * considered an escape code, which will cause problems.
77 *
78 * -- If you don't care about learning all the escape codes,
79 * you can automatically have HTML code converted to the
80 * XTextView format using txvConvertFromHTML, which will
81 * automatically insert all the codes right from plain
82 * HTML. In the above code, use txvConvertFromHTML instead
83 * of txvStripLinefeeds.
84 *
85 * <B>Code page support</B>
86 *
87 * The XTextView control assumes that the text given to it uses
88 * the same codepage as the message queue (thread) on which
89 * the control is running. So if you need codepage support,
90 * issue WinSetCp before creating the text view control.
91 *
92 * <B>Text formatting</B>
93 *
94 * The XTextView control has a default paragraph format which
95 * determines how text is formatted. If you don't change this
96 * format, the control performs no word-wrapping and displays
97 * all text "as is", that is, practically no formatting is
98 * performed.
99 *
100 * You can change the default paragraph format by sending
101 * TXM_SETPARFORMAT to the control. This takes a XFMTPARAGRAPH
102 * structure for input.
103 *
104 * To quickly enable word-wrapping only, we have the extra
105 * TXM_SETWORDWRAP message. This changes the word-wrapping flag
106 * in the default paragraph format only so you don't have to
107 * mess with all the rest.
108 *
109 * The XTextView control is extremely fast in formatting. It
110 * does pre-calculations once so that resizing the text
111 * window does not perform a full reformat, but a quick
112 * format based on the pre-calculations.
113 *
114 * Presently, formatting is done synchronously. It is planned
115 * to put formatting into a separate thread. Performance is
116 * acceptable already now unless very large texts (> 200 KB)
117 * are formatted (tested on a PII-400 machine).
118 *
119 * <B>Presentation Parameters</B>
120 *
121 * The XTextView control recognizes the following presentation
122 * parameters:
123 *
124 * -- PP_BACKGROUNDCOLOR; if not set, SYSCLR_DIALOGBACKGROUND
125 * (per default gray) is used.
126 *
127 * -- PP_FOREGROUNDCOLOR: if not set, SYSCLR_WINDOWSTATICTEXT
128 * (per default blue) is used to signify that the text
129 * cannot be worked on.
130 *
131 * -- PP_FONTNAMESIZE: default font. This is the system font,
132 * if not set.
133 *
134 * This implies that fonts and colors can be dropped on the
135 * control in the normal way. Font changes will cause a reformat.
136 *
137 * Changing those presentation parameters is equivalent to
138 * changing the corresponding fields in the default paragraph
139 * format using TXM_SETPARFORMAT.
140 *
141 * <B>Escape codes</B>
142 *
143 * All XTextView escape codes start with a \xFF (255) character,
144 * followed by at least one more character. The escape sequences
145 * are variable in length and can have parameters. For details,
146 * see textview.h where all these are listed.
147 *
148 * Escape codes are evaluated by txvFormatText during formatting.
149 *
150 * If you choose to give the text view control a text which
151 * contains escape codes, you better make sure that you get the
152 * exact codes right, or the text view control can crash. The
153 * control has been optimized for speed, so no checking is done
154 * on escape sequences.
155 *
156 * <B>Device-independent text formatting</B>
157 *
158 * If the features of the XTextView control satisfy your needs,
159 * there's not much to worry about. However, if you're interested
160 * in formatting the text yourself, here's more:
161 *
162 * This file has the txvFormatText function, which is capable
163 * of formatting an input string into any HPS. This works for
164 * windows (used by the text view control) and printers (used
165 * by txvPrint). Word-wrapping is supported. This is used by
166 * the XTextView control internally whenever (re)formatting is
167 * needed: either when the text is set or the formatting parameters
168 * (fonts, margins, etc.) have changed.
169 *
170 * These functions are designed to be used in a two-step process:
171 * first format the text (using txvFormatText), then paint it
172 * (using txvPaintText) for viewing or printing.
173 * This speeds up painting dramatically, because formatting
174 * might take some time.
175 *
176 * Note: Version numbering in this file relates to XWorkplace version
177 * numbering.
178 *
179 *@@header "helpers\textview.h"
180 *
181 *@@added V0.9.1 (2000-02-13) [umoeller]
182 */
183
184/*
185 * Copyright (C) 2000 Ulrich M”ller.
186 * This program is part of the XWorkplace package.
187 * This program is free software; you can redistribute it and/or modify
188 * it under the terms of the GNU General Public License as published by
189 * the Free Software Foundation, in version 2 as it comes in the COPYING
190 * file of the XWorkplace main distribution.
191 * This program is distributed in the hope that it will be useful,
192 * but WITHOUT ANY WARRANTY; without even the implied warranty of
193 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
194 * GNU General Public License for more details.
195 */
196
197#define OS2EMX_PLAIN_CHAR
198 // this is needed for "os2emx.h"; if this is defined,
199 // emx will define PSZ as _signed_ char, otherwise
200 // as unsigned char
201
202#define OS2EMX_PLAIN_CHAR
203 // this is needed for "os2emx.h"; if this is defined,
204 // emx will define PSZ as _signed_ char, otherwise
205 // as unsigned char
206
207#define INCL_WINWINDOWMGR
208#define INCL_WINFRAMEMGR
209#define INCL_WINMESSAGEMGR
210#define INCL_WININPUT
211#define INCL_WINRECTANGLES
212#define INCL_WINPOINTERS
213#define INCL_WINSYS
214#define INCL_WINSCROLLBARS
215#define INCL_WINSTDFONT
216#define INCL_WINCOUNTRY
217
218#define INCL_DEV
219#define INCL_SPL
220#define INCL_SPLDOSPRINT
221
222#define INCL_GPIPRIMITIVES
223#define INCL_GPILCIDS
224#define INCL_GPILOGCOLORTABLE
225#define INCL_GPITRANSFORMS
226#define INCL_GPIREGIONS
227
228#define INCL_ERRORS
229#include <os2.h>
230
231#include <stdlib.h>
232#include <stdio.h>
233#include <string.h>
234
235#include "setup.h" // code generation and debugging options
236
237#include "helpers\comctl.h"
238#include "helpers\gpih.h"
239#include "helpers\linklist.h"
240#include "helpers\stringh.h"
241#include "helpers\winh.h"
242#include "helpers\xstring.h" // extended string helpers
243
244#include "helpers\textview.h"
245
246#pragma hdrstop
247
248/*
249 *@@category: Helpers\PM helpers\Window classes\XTextView control
250 * See textview.c.
251 */
252
253/* ******************************************************************
254 *
255 * Device-independent functions
256 *
257 ********************************************************************/
258
259/*
260 *@@ txvInitFormat:
261 *
262 */
263
264VOID txvInitFormat(PXFORMATDATA pxfd)
265{
266 memset(pxfd, 0, sizeof(XFORMATDATA));
267 lstInit(&pxfd->llRectangles,
268 TRUE); // auto-free items
269 lstInit(&pxfd->llWords,
270 TRUE); // auto-free items
271 xstrInit(&pxfd->strViewText, 0);
272}
273
274/*
275 *@@ SetSubFont:
276 *
277 *@@added V0.9.3 (2000-05-06) [umoeller]
278 */
279
280VOID SetSubFont(HPS hps,
281 PXFMTFONT pFont,
282 ULONG ulPointSize,
283 PSZ pszFaceName,
284 ULONG flFormat)
285{
286 CHAR ac[256];
287 ULONG ul;
288 POINTL ptlStart = {0, 0},
289 aptl[257];
290
291 if (pFont->lcid)
292 {
293 // font already loaded:
294 if (GpiQueryCharSet(hps) == pFont->lcid)
295 // font currently selected:
296 GpiSetCharSet(hps, LCID_DEFAULT);
297 GpiDeleteSetId(hps, pFont->lcid);
298
299 }
300
301 if (pszFaceName)
302 pFont->lcid = gpihFindFont(hps,
303 ulPointSize,
304 TRUE, // family, not face name
305 pszFaceName,
306 flFormat,
307 &pFont->FontMetrics);
308 else
309 pFont->lcid = LCID_DEFAULT; // 0
310
311 GpiSetCharSet(hps, pFont->lcid);
312 if (pFont->FontMetrics.fsDefn & FM_DEFN_OUTLINE)
313 // is outline font:
314 gpihSetPointSize(hps, ulPointSize);
315
316 for (ul = 0;
317 ul < 256;
318 ul++)
319 ac[ul] = ul;
320
321 GpiQueryCharStringPosAt(hps,
322 &ptlStart,
323 0,
324 254, // starting at one
325 ac + 1, // starting at one
326 NULL,
327 aptl);
328 // now compute width of every char
329 for (ul = 1;
330 ul < 256;
331 ul++)
332 {
333 pFont->alCX[ul] = aptl[ul+1].x - aptl[ul].x;
334 }
335}
336
337/*
338 *@@ txvSetFormatFont:
339 * creates logical fonts from the specified
340 * font information.
341 *
342 *@@added V0.9.3 (2000-05-06) [umoeller]
343 */
344
345VOID txvSetFormatFont(HPS hps, // in: HPS to select default font into
346 PXFMTCHARACTER pxfmtc, // in/out: format data
347 ULONG ulPointSize, // in: font point size (e.g. 12) or 0
348 PSZ pszFaceName) // in: font face name (e.g. "Courier") or NULL
349{
350 pxfmtc->lPointSize = ulPointSize;
351
352 // regular
353 SetSubFont(hps,
354 &pxfmtc->fntRegular,
355 ulPointSize,
356 pszFaceName,
357 0);
358
359 // bold
360 SetSubFont(hps,
361 &pxfmtc->fntBold,
362 ulPointSize,
363 pszFaceName,
364 FATTR_SEL_BOLD);
365
366 // italics
367 SetSubFont(hps,
368 &pxfmtc->fntItalics,
369 ulPointSize,
370 pszFaceName,
371 FATTR_SEL_ITALIC);
372
373 // bold italics
374 SetSubFont(hps,
375 &pxfmtc->fntBoldItalics,
376 ulPointSize,
377 pszFaceName,
378 FATTR_SEL_BOLD | FATTR_SEL_ITALIC);
379}
380
381/*
382 *@@ AppendCharNoCheck:
383 *
384 *@@added V0.9.3 (2000-05-07) [umoeller]
385 */
386
387VOID AppendCharNoCheck(char **ppszNew,
388 PULONG pcbNew,
389 char **ppTarget,
390 char c)
391{
392 ULONG cbSizeThis = *ppTarget - *ppszNew;
393 if (cbSizeThis >= *pcbNew)
394 {
395 // more mem needed:
396 *pcbNew += 10000;
397 *ppszNew = (PSZ)realloc(*ppszNew, *pcbNew);
398 // if first call, pszNew is NULL, and realloc
399 // behaves just like malloc
400 // adjust target, because ptr might have changed
401 *ppTarget = *ppszNew + cbSizeThis;
402 }
403
404 **ppTarget = c;
405 (*ppTarget)++;
406}
407
408/*
409 *@@ txvStripLinefeeds:
410 * this removes all linefeeds (\r) from
411 * the specified string to prepare it
412 * for display with the text view control.
413 *
414 * This also replaces tabs (\t) with ulTabSize spaces.
415 *
416 * The buffer gets reallocated by this function, so it
417 * must be free()'able.
418 *
419 *@@added V0.9.3 (2000-05-07) [umoeller]
420 */
421
422VOID txvStripLinefeeds(char **ppszText,
423 ULONG ulTabSize)
424{
425 PSZ pSource = *ppszText;
426 ULONG cbNew = 1000;
427 PSZ pszNew = (PSZ)malloc(cbNew);
428 PSZ pTarget = pszNew;
429
430 while (*pSource)
431 {
432 if (*pSource == '\r')
433 pSource++;
434 else if (*pSource == '\t')
435 {
436 ULONG ul;
437 for (ul = 0;
438 ul < ulTabSize;
439 ul++)
440 AppendCharNoCheck(&pszNew,
441 &cbNew,
442 &pTarget,
443 ' ');
444
445 // skip the tab
446 pSource++;
447 }
448 else
449 AppendCharNoCheck(&pszNew,
450 &cbNew,
451 &pTarget,
452 *pSource++);
453 }
454 AppendCharNoCheck(&pszNew,
455 &cbNew,
456 &pTarget,
457 '\n');
458 AppendCharNoCheck(&pszNew,
459 &cbNew,
460 &pTarget,
461 0);
462
463 free(*ppszText);
464 *ppszText = pszNew;
465}
466
467/* ******************************************************************
468 *
469 * Device-independent text formatting
470 *
471 ********************************************************************/
472
473/*
474 *@@ strhFindEOL2:
475 * finds the end of a line.
476 *
477 * An "end of line" means the next \r, \n, or \0 character
478 * after *ppszSearchIn.
479 *
480 * This returns the pointer to that exact character, which
481 * can be equal or higher than *ppszSearchIn.
482 * This should never return NULL because at some point,
483 * there will be a null byte in your string (unless you have
484 * a heap problem).
485 *
486 * If the EOL character is not null (\0), *ppszSearchIN is
487 * advanced to the first character of the _next_ line. This
488 * can be the EOL pointer plus one if you have a UNIX-style
489 * string (\n only at the end of each line) or EOL + 2 for
490 * DOS and OS/2-style EOLs (which have \r\n at the end of
491 * each line).
492 *
493 *@added V0.9.3 (2000-05-06) [umoeller]
494 */
495
496PSZ strhFindEOL2(PSZ *ppszSearchIn, // in: where to search
497 PULONG pulOffset) // out: offset (ptr can be NULL)
498{
499 PSZ pThis = *ppszSearchIn,
500 prc = NULL;
501 while (TRUE)
502 {
503 if ( (*pThis == '\r') || (*pThis == '\n') || (*pThis == 0) )
504 {
505 prc = pThis;
506 break;
507 }
508 pThis++;
509 }
510
511 // before modifying pointer, store offset
512 if (pulOffset)
513 *pulOffset = prc - *ppszSearchIn;
514
515 if (*prc == 0)
516 {
517 // null byte (end of string):
518 *ppszSearchIn = prc;
519 }
520 else
521 {
522 // not null byte (end of string):
523 // skip following newline characters
524 if (*prc == '\r')
525 {
526 if ( *(prc+1) == '\n')
527 // we have a \r char next,
528 // that's the DOS and OS/2 format (\r\n):
529 // skip that too
530 *ppszSearchIn = prc + 2;
531 else
532 *ppszSearchIn = prc + 1;
533 }
534 else if (*prc == '\n')
535 // UNIX format (used by HTML formatter also):
536 *ppszSearchIn = prc + 1;
537 }
538
539 // now:
540 // 1) prc points to the \r, \n, or \0 character (EOL)
541 // 2) *ppszSearchIn has been advanced to the first character
542 // of the next line or points to the \0 character
543
544 return (prc);
545}
546
547/* #define TXVFRECTF_EMPTY 0x0001
548#define TXVFRECTF_PARAGRAPHDONE 0x0002
549#define TXVFRECTF_WORDSLEFT 0x0004
550#define TXVFRECTF_STOPPEDONESCAPE 0x0008
551#define TXVFRECTF_ENDOFTEXT 0x0010
552 */
553/*
554 *@@ FORMATLINEBUF:
555 * worker structure to store various data
556 * in txvFormatText in between txvCreateWord
557 * calls. This has been created for speed
558 * so we don't have to pass all these on
559 * the stack all the time.
560 *
561 *@@added V0.9.3 (2000-05-06) [umoeller]
562 */
563
564typedef struct _FORMATLINEBUF
565{
566 PSZ pLastChar; // ptr to null terminator in text
567
568 // formatting data; this is set by txvFormatText according
569 // to escape characters and read by txvCreateRectangle
570 XFMTPARAGRAPH fmtp;
571 PXFMTCHARACTER pfmtc; // pointer to character formatting data
572 PXFMTFONT pfmtf; // pointer to font to use
573
574 BOOL fPre,
575 fBold,
576 fItalics;
577
578 // current anchor
579 USHORT usCurrentAnchor;
580 // this is > 0 if we're currently in an anchor block
581
582 // data copied to TXVWORD
583 LONG lcid;
584 LONG lPointSize;
585 ULONG flOptions; // any combination of CHS_UNDERSCORE and CHS_STRIKEOUT
586
587 // counters, ...
588 LONG lXCurrent; // current X position while adding words to rectangle
589} FORMATLINEBUF, *PFORMATLINEBUF;
590
591/*
592 *@@ txvCreateWord:
593 *
594 * -- If the word ends with one or several spaces,
595 * ppStartOfWord is set to the beginning of the
596 * next word (non-space character).
597 * pWord->ulFlags is set to 0.
598 *
599 * -- If the word ends with an escape character,
600 * ppStartOfWord is set to point to the escape,
601 * which must be handled by the caller.
602 * pWord->ulFlags is set to TXVWORDF_GLUEWITHNEXT.
603 *
604 * -- If the word ends with a \n or \r,
605 * ppStartOfWord is set to the beginning of the
606 * next line (first char after \n or \r). This
607 * may be another \n or \r, but the first one
608 * is skipped.
609 * pWord->ulFlags is set to TXVWORDF_LINEBREAK or
610 * TXVWORDF_LINEFEED.
611 *
612 *@@added V0.9.3 (2000-05-14) [umoeller]
613 */
614
615PTXVWORD txvCreateWord(HPS hps,
616 PSZ *ppStartOfWord,
617 PFORMATLINEBUF pflbuf)
618{
619 PTXVWORD pWord = NULL;
620
621 // find next word:
622 if (**ppStartOfWord)
623 {
624 PSZ pWordStart = *ppStartOfWord,
625 pWordEnd = NULL;
626 PSZ pCheck = *ppStartOfWord;
627 ULONG cChars = 0,
628 cCheck = 0;
629
630 pWord = (PTXVWORD)malloc(sizeof(TXVWORD));
631 memset(pWord, 0, sizeof(TXVWORD));
632 // this includes fIsEscapeSequence = FALSE;
633 pWord->pStart = pWordStart;
634
635 // initially, this has pWordStart pointing
636 // to *ppStartOfWord. If a word is found,
637 // pWordStart is set to the first char of
638 // the word and pWordEnd receives the
639 // pointer to the first character after the word (probably space)
640 if (strhGetWord(&pWordStart, // in/out
641 pflbuf->pLastChar,
642 " ",
643 "\x0d\x0a \xFF", // in: end chars; includes our escape!
644 &pWordEnd)) // out: first char after word
645 {
646 // whoa, found a word:
647 while (*pWordEnd == ' ')
648 pWordEnd++;
649
650 cChars = (pWordEnd - *ppStartOfWord);
651 }
652
653 if (cChars)
654 {
655 POINTL aptlText[TXTBOX_COUNT];
656 // cChars is != 0 if strhGetWord succeeded AND the
657 // line is not empty, so go on
658 cCheck = cChars;
659
660 // advance input pointer
661 *ppStartOfWord = pWordEnd;
662
663 GpiQueryTextBox(hps,
664 // no. of chars since start of word:
665 cChars,
666 // first char:
667 pCheck,
668 TXTBOX_COUNT,
669 (PPOINTL)&aptlText);
670
671 pWord->cChars = cChars;
672 pWord->ulFlags = 0;
673
674 if (cChars)
675 pWord->ulCXWithSpaces = aptlText[TXTBOX_TOPRIGHT].x;
676 else
677 pWord->ulCXWithSpaces = 0;
678
679 pWord->ulCY = aptlText[TXTBOX_TOPRIGHT].y
680 - aptlText[TXTBOX_BOTTOMRIGHT].y;
681 // store base line ofs; aptlText[TXTBOX_BOTTOMRIGHT].y is negative
682 // if the string has any characters drawn below the base line, e.g.
683 // for the "g" and "y" characters
684 pWord->ulBaseLineOfs = -aptlText[TXTBOX_BOTTOMRIGHT].y;
685 }
686 else
687 {
688 // no word found or empty line:
689 pWord->ulCY = pflbuf->pfmtf->FontMetrics.lMaxBaselineExt;
690 }
691
692 switch (**ppStartOfWord)
693 {
694 case TXVESC_CHAR: // '\xFF':
695 pWord->ulFlags = TXVWORDF_GLUEWITHNEXT;
696 break;
697
698 case '\n':
699 pWord->ulFlags = TXVWORDF_LINEBREAK;
700 (*ppStartOfWord)++; // skip \n
701 break;
702
703 case '\r':
704 pWord->ulFlags = TXVWORDF_LINEFEED;
705 (*ppStartOfWord)++; // skip \r
706 break;
707 }
708
709 pWord->lcid = pflbuf->pfmtf->lcid;
710 pWord->lPointSize = pflbuf->lPointSize;
711 pWord->flOptions = pflbuf->flOptions;
712
713 pWord->usAnchor = pflbuf->usCurrentAnchor; // 0 if none
714 }
715
716 return (pWord);
717}
718
719/*
720 *@@ ProcessEscapes:
721 * gets called when txvFormatText stops on an
722 * escape character (\xFF). This evaluates the
723 * escape sequence, reacts accordingly, and
724 * advances *ppCurrent to after the escape
725 * sequence so that regular processing can
726 * continue.
727 *
728 * There are two types of escape sequences:
729 *
730 * -- Those which are only relevant during word processing,
731 * such as character formatting attributes (bold, italics,
732 * font, size, ...). Those affect the TXVWORD structure
733 * directly and are thus never evaluated in step 2,
734 * rectangles correlation.
735 *
736 * -- Those which affect spacings, margins, etc. (paragraph
737 * formatting). These need to be re-evaluated even during
738 * "quick" format, without words being recalculated, because
739 * those spacings affect the output rectangles.
740 *
741 * If one of those sequences is encountered, this function
742 * appends a special TXVWORD structure to XFORMATDATA.llWords.
743 *
744 *@@added V0.9.3 (2000-05-07) [umoeller]
745 */
746
747PTXVWORD ProcessEscapes(char **ppCurrent, // in/out: current position; initially points to esc char
748 PXFORMATDATA pxfd, // in/out: formatting data
749 PFORMATLINEBUF pflbuf, // in/out: formatting buffer
750 BOOL fWordsProcessed) // FALSE during step 1 (words processing),
751 // TRUE during step 2 (rectangles correlation)
752{
753 PTXVWORD pEscapeWord = NULL;
754
755 // this is set to TRUE either above or by txvCreateRectangle if
756 // an escape character was found; txvCreateRectangle
757 // then sets pCurrent to the escape character (\xFF)
758 CHAR cCode1 = *((*ppCurrent)+1);
759 CHAR cCode2 = *((*ppCurrent)+2);
760 ULONG ulSkip = 3; // per default, skip \xFF plus two
761 CHAR szDecimal[10];
762 LONG lDecimal;
763
764 BOOL fCreateWord = FALSE,
765 fPaintEscapeWord = FALSE;
766
767 switch (cCode1)
768 {
769 case 1: // change font:
770 // three decimals follow specifying the font
771 memcpy(szDecimal, (*ppCurrent)+2, 3);
772 szDecimal[3] = 0;
773 lDecimal = atoi(szDecimal);
774 if (lDecimal == 0)
775 pflbuf->pfmtc = &pxfd->fmtcStandard;
776 else if (lDecimal == 1)
777 pflbuf->pfmtc = &pxfd->fmtcCode;
778 ulSkip = 5;
779 break;
780
781 case 2: // B or /B
782 if (cCode2 == 1)
783 pflbuf->fBold = TRUE;
784 else
785 pflbuf->fBold = FALSE;
786 break;
787
788 case 3: // I or /I
789 if (cCode2 == 1)
790 pflbuf->fItalics = TRUE;
791 else
792 pflbuf->fItalics = FALSE;
793 break;
794
795 case 4: // U or /U
796 if (cCode2 == 1)
797 pflbuf->flOptions |= CHS_UNDERSCORE;
798 else
799 pflbuf->flOptions &= ~CHS_UNDERSCORE;
800 break;
801
802 case 5: // STRIKE or /STRIKE
803 if (cCode2 == 1)
804 pflbuf->flOptions |= CHS_STRIKEOUT;
805 else
806 pflbuf->flOptions &= ~CHS_STRIKEOUT;
807 break;
808
809 case 6: // A or /A HREF= (link)
810 {
811 // four characters with hex anchor index (>=1)
812 // or "####"
813 if ( *( (*ppCurrent)+2 )
814 == '#'
815 )
816 pflbuf->usCurrentAnchor = 0;
817 else
818 {
819 PSZ pEnd;
820 memcpy(szDecimal, (*ppCurrent)+2, 4);
821 szDecimal[4] = 0;
822 lDecimal = strtol(szDecimal, &pEnd, 16);
823 pflbuf->usCurrentAnchor = lDecimal;
824 }
825
826 ulSkip = 6;
827 break; }
828
829 case 7: // A NAME= (anchor name)
830 {
831 // this is variable in length and terminated with
832 // another 0xFF char; we completely ignore this
833 // here and just skip the anchor name, this is
834 // only used with TXM_JUMPTOANCHORNAME, which then
835 // searches the buffer
836 PSZ pEnd = strchr((*ppCurrent)+2, 0xFF);
837 if (pEnd)
838 {
839 ulSkip = pEnd - *ppCurrent + 1;
840 // store this with the other words so we can
841 // find this word later
842 fCreateWord = TRUE;
843 // and store this with the rectangles
844 fPaintEscapeWord = TRUE;
845 }
846 break; }
847
848 case 0x10: // relative point size in percent
849 // three characters follow specifying the
850 // percentage
851 memcpy(szDecimal, (*ppCurrent)+2, 3);
852 szDecimal[3] = 0;
853 lDecimal = atoi(szDecimal);
854
855 pflbuf->lPointSize = pflbuf->pfmtc->lPointSize * lDecimal / 100;
856 ulSkip = 5;
857 break;
858
859 case 0x20: // left margin changed:
860 memcpy(szDecimal, (*ppCurrent)+2, 4); // four decimals xxxx
861 szDecimal[4] = 0;
862 lDecimal = atoi(szDecimal);
863
864 // this is based on the current average font width, so
865 // find this:
866 pflbuf->fmtp.lLeftMargin = (lDecimal
867 * pflbuf->lPointSize);
868 ulSkip = 6;
869 fCreateWord = TRUE; // for rectangle correlation
870 break;
871
872 case 0x21: // first line margin changed:
873 memcpy(szDecimal, (*ppCurrent)+2, 4); // +xxx, -xxx
874 szDecimal[4] = 0;
875 lDecimal = atoi(szDecimal);
876
877 // this is based on the current average font width, so
878 // find this:
879 pflbuf->fmtp.lFirstLineMargin = (lDecimal
880 * pflbuf->lPointSize);
881 ulSkip = 6;
882 fCreateWord = TRUE; // for rectangle correlation
883 break;
884
885 case 0x22: // tab: forward current X to left margin
886 pflbuf->lXCurrent = pflbuf->fmtp.lLeftMargin;
887
888 ulSkip = 2;
889 fCreateWord = TRUE; // for rectangle correlation
890 break;
891
892 case 0x23: // marker: store this in output, this needs
893 // to be painted
894 fCreateWord = TRUE;
895 fPaintEscapeWord = TRUE;
896 ulSkip = 3;
897 break;
898
899 case 0x30: // spacing before paragraph:
900 // four chars follow with either "####" or decimal spacing
901 memcpy(szDecimal, (*ppCurrent)+2, 4);
902 szDecimal[4] = 0;
903 if (memcmp(szDecimal, "####", 4) == 0)
904 // reset to default:
905 pflbuf->fmtp.lSpaceBefore = pxfd->fmtpStandard.lSpaceBefore;
906 else
907 {
908 lDecimal = atoi(szDecimal);
909 pflbuf->fmtp.lSpaceBefore = lDecimal;
910 }
911 ulSkip = 6;
912 fCreateWord = TRUE; // for rectangle correlation
913 break;
914
915 case 0x31: // spacing before paragraph:
916 // four chars follow with either "####" or decimal spacing
917 memcpy(szDecimal, (*ppCurrent)+2, 4);
918 szDecimal[4] = 0;
919 if (memcmp(szDecimal, "####", 4) == 0)
920 // reset to default:
921 pflbuf->fmtp.lSpaceAfter = pxfd->fmtpStandard.lSpaceAfter;
922 else
923 {
924 lDecimal = atoi(szDecimal);
925 pflbuf->fmtp.lSpaceAfter = lDecimal;
926 }
927 ulSkip = 6;
928 fCreateWord = TRUE; // for rectangle correlation
929 break;
930
931 case 0x32: // word-wrapping:
932 // here follows a single char being "0" or "1"
933 if ( *((*ppCurrent) + 2) == '0')
934 pflbuf->fmtp.fWordWrap = FALSE;
935 else
936 pflbuf->fmtp.fWordWrap = TRUE;
937 fCreateWord = TRUE; // for rectangle correlation
938 }
939
940 if (fCreateWord) // append for rectangle correlation?
941 if (!fWordsProcessed) // are we processing words still (step 1)?
942 {
943 // yes: append to list for rectangle correlation later
944 pEscapeWord = (PTXVWORD)malloc(sizeof(TXVWORD));
945 memset(pEscapeWord, 0, sizeof(TXVWORD));
946 // mark as escape sequence
947 pEscapeWord->pStart = *ppCurrent;
948 pEscapeWord->cChars = ulSkip;
949 pEscapeWord->cEscapeCode = *(*ppCurrent + 1);
950 pEscapeWord->fPaintEscapeWord = fPaintEscapeWord;
951 pEscapeWord->usAnchor = pflbuf->usCurrentAnchor; // 0 if none
952 if (fPaintEscapeWord)
953 {
954 pEscapeWord->lX = pflbuf->lXCurrent;
955 pEscapeWord->lcid = pflbuf->pfmtf->lcid;
956 pEscapeWord->lPointSize = pflbuf->lPointSize;
957 pEscapeWord->flOptions = pflbuf->flOptions;
958 }
959 lstAppendItem(&pxfd->llWords, pEscapeWord);
960 }
961
962 if (!fWordsProcessed)
963 // if we're still processing words, advance
964 // current pointer by the escape length
965 *ppCurrent += ulSkip;
966
967 return (pEscapeWord);
968}
969
970/*
971 *@@ txvFormatText:
972 * this is the core function to text formatting, which
973 * must be done before the text can be painted into an
974 * HPS. See the top of textview.c for details.
975 *
976 * Even though this function does not seem to have a
977 * lot of parameters, it is extremely powerful. This
978 * function handles paragraph and character formatting
979 * automatically. See XFMTPARAGRAPH and XFMTCHARACTER
980 * for possible formatting attributes, which are part
981 * of the XFORMATDATA structure passed to this function.
982 *
983 * "Formatting" means splitting up any zero-terminated
984 * string (XFORMATDATA.pszViewText) into a possibly
985 * large list of TXVRECTANGLE structures, which each
986 * hold a rectangle to be painted. This allows for
987 * extremely fast painting.
988 *
989 * Each TXVRECTANGLE in turn holds several "words" to
990 * be painted. A word consists of a TXVWORD structure
991 * and is normally a sequence of characters between
992 * spaces, \n and \r characters. As an exception, if
993 * escape sequences come up, such a "word" is split up
994 * into several words because character formatting
995 * (font, size, ...) is done on a per-word basis when painting.
996 *
997 * This approach allows for quicker word-wrapping when only
998 * the output (paint) rectangle is changed because we don't
999 * have to re-calculate all the character widths (TXVWORD) once we
1000 * got the words. Instead, when re-formatting, we just recompose
1001 * the rectangles based on the words we calculated already.
1002 * Of course, when character widths change (e.g. because
1003 * fonts are changed), everything has to be redone.
1004 *
1005 * Processing depends on the current formatting settings
1006 * of the XFORMATDATA structure passed to this func and
1007 * can become quite complicated:
1008 *
1009 * -- In the simplest possible formatting mode, that is, if
1010 * word wrapping is disabled, each such TXVRECTANGLE
1011 * structure will hold one paragraph from the text
1012 * (that is, the text between two \n chars).
1013 *
1014 * -- If word wrapping is enabled, each paragraph in the text
1015 * can consist of several such rectangles if the paragraph
1016 * does not fit into one line. In that case, we create
1017 * one XFMTRECTANGLE for each line which is needed to
1018 * display the paragraph word-wrapped.
1019 *
1020 * This uses an XFORMATDATA structure for input and output
1021 * (besides the other parameters).
1022 *
1023 * On input, specify the following:
1024 *
1025 * -- hps: window or printer HPS. This is used for
1026 * formatting only, but nothing is painted.
1027 *
1028 * -- XFORMATDATA.pszViewText: the text to be formatted.
1029 * This must follow certain conventions; the \xFF,
1030 * \r, and \n characters have a special meaning.
1031 * See the top of textview.c for details.
1032 *
1033 * -- XFORMATDATA.fmtpStandard, fmtcStandard, fmtcCode:
1034 * paragraph and character formatting attributes.
1035 * For the simplest possible formatting, memset
1036 * all these to 0. Word-wrapping depends on
1037 * the paragraph formats.
1038 *
1039 * -- prclView: rectangle for which formatting should take
1040 * place. When this is called for a screen window,
1041 * this should be the visible area of the window
1042 * (WinQueryWindowRect).
1043 * When this is called with a printer PS, this should
1044 * be the size of a printer page.
1045 *
1046 * This function updates the following:
1047 *
1048 * -- XFORMATDATA.llWords: list of TXVWORD structures,
1049 * holding all the "words" in the text as described
1050 * above. This list can grow very long, but only needs
1051 * to be recalculated when fonts change.
1052 *
1053 * -- XFORMATDATA.llRectangles: list of TXVRECTANGLE
1054 * structures, correlating the words on the words list
1055 * to paint rectangles.
1056 *
1057 * -- XFORMATDATA.ulViewportCX, ulViewportCY: total width
1058 * and height of the "viewport", i.e. the total space
1059 * needed to display the text (in pels). This might
1060 * be smaller, the same, or larger than prclView,
1061 * depending on whether the text fits into prclView.
1062 *
1063 * When displaying text, you should display scroll bars
1064 * if the viewport is larger than the window (prclView).
1065 *
1066 * When printing, if the viewport is larger than the
1067 * printer page (prclView), you will need to call
1068 * txvPaintText several times for each page.
1069 *
1070 * All coordinates are in world space (PU_PELS).
1071 *
1072 *@@changed V0.9.3 (2000-05-06) [umoeller]: largely rewritten; now handling paragraph and character formats
1073 *@@todo TXVWORDF_GLUEWITHNEXT
1074 */
1075
1076VOID txvFormatText(HPS hps, // in: HPS whose font is used for
1077 // calculating text dimensions
1078 PXFORMATDATA pxfd, // in: formatting data
1079 PRECTL prclView, // in: rectangle to format for (window or printer page)
1080 BOOL fFullRecalc) // in: re-calculate word list too? (must be TRUE on the first call)
1081{
1082 /* ULONG ulWinCX = (prclView->xRight - prclView->xLeft),
1083 ulWinCY = (prclView->yTop - prclView->yBottom); */
1084
1085 lstClear(&pxfd->llRectangles);
1086 if (fFullRecalc)
1087 lstClear(&pxfd->llWords);
1088
1089 pxfd->ulViewportCX = 0;
1090 pxfd->ulViewportCY = 0;
1091
1092 if (pxfd->strViewText.cbAllocated)
1093 {
1094 ULONG ulTextLen = pxfd->strViewText.ulLength;
1095
1096 FORMATLINEBUF flbuf;
1097 LONG lcidLast = -99,
1098 lPointSizeLast = -99;
1099
1100 memset(&flbuf, 0, sizeof(flbuf));
1101 // copy default paragraph formatting
1102 memcpy(&flbuf.fmtp, &pxfd->fmtpStandard, sizeof(flbuf.fmtp));
1103 // set font
1104 flbuf.pfmtc = &pxfd->fmtcStandard;
1105 flbuf.lPointSize = pxfd->fmtcStandard.lPointSize;
1106 flbuf.pLastChar = pxfd->strViewText.psz + ulTextLen;
1107
1108 if (ulTextLen)
1109 {
1110 ULONG cWords = 0;
1111
1112 if (fFullRecalc)
1113 {
1114 /*
1115 * step 1: create words
1116 *
1117 */
1118
1119 PSZ pCurrent = pxfd->strViewText.psz;
1120
1121 // loop until null terminator
1122 while (*pCurrent)
1123 {
1124 PTXVWORD pWord;
1125
1126 if (flbuf.fBold)
1127 {
1128 if (flbuf.fItalics)
1129 flbuf.pfmtf = &flbuf.pfmtc->fntBoldItalics;
1130 else
1131 flbuf.pfmtf = &flbuf.pfmtc->fntBold;
1132 }
1133 else
1134 if (flbuf.fItalics)
1135 flbuf.pfmtf = &flbuf.pfmtc->fntItalics;
1136 else
1137 flbuf.pfmtf = &flbuf.pfmtc->fntRegular;
1138
1139 // set font for subsequent calculations,
1140 // if changed (this includes the first call)
1141 if (lcidLast != flbuf.pfmtf->lcid)
1142 {
1143 GpiSetCharSet(hps, flbuf.pfmtf->lcid);
1144 lcidLast = flbuf.pfmtf->lcid;
1145 // force recalc of point size
1146 lPointSizeLast = -99;
1147 }
1148
1149 if (lPointSizeLast != flbuf.lPointSize)
1150 {
1151 if (flbuf.pfmtf->FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1152 // is outline font:
1153 gpihSetPointSize(hps, flbuf.lPointSize);
1154 lPointSizeLast = flbuf.lPointSize;
1155 }
1156
1157 pWord = txvCreateWord(hps,
1158 &pCurrent, // advanced to next word
1159 &flbuf);
1160 if (pWord)
1161 {
1162 lstAppendItem(&pxfd->llWords, pWord);
1163
1164 /* {
1165 CHAR szWord[3000];
1166 strhncpy0(szWord, pWord->pStart, min(pWord->cChars, sizeof(szWord)));
1167 _Pmpf(("Found word '%s'", szWord));
1168 } */
1169
1170 cWords++;
1171
1172 while (*pCurrent == TXVESC_CHAR) // '\xFF')
1173 {
1174 // handle escapes;
1175 // this advances pCurrent depending on the
1176 // escape sequence length and might append
1177 // another "word" for the escape sequence
1178 // if it's relevant for rectangle correlation
1179 ProcessEscapes(&pCurrent,
1180 pxfd,
1181 &flbuf,
1182 FALSE); // fWordsProcessed
1183 }
1184 }
1185 else
1186 break;
1187 }
1188 } // end if (fFullRecalc)
1189 else
1190 cWords = lstCountItems(&pxfd->llWords);
1191
1192 /*
1193 * step 2: create rectangles
1194 *
1195 */
1196
1197 if (cWords)
1198 {
1199 PLISTNODE pWordNode = lstQueryFirstNode(&pxfd->llWords);
1200
1201 LONG lCurrentYTop = prclView->yTop,
1202 lOrigYTop = lCurrentYTop;
1203
1204 BOOL fRects2Go = TRUE;
1205
1206 // space before paragraph; this is reset
1207 // to 0 if we start a new rectangle for
1208 // the same paragraph
1209 ULONG ulYPre = flbuf.fmtp.lSpaceBefore;
1210
1211 // rectangles loop
1212 while (fRects2Go)
1213 {
1214 BOOL fWords2Go = TRUE;
1215 ULONG ulWordsInThisRect = 0;
1216
1217 // maximum height of words in this rect
1218 ULONG lWordsMaxCY = 0;
1219
1220 // start a new rectangle:
1221 PTXVRECTANGLE pRect = (PTXVRECTANGLE)malloc(sizeof(TXVRECTANGLE));
1222 lstInit(&pRect->llWords,
1223 FALSE); // no auto-free; the words are stored in the main
1224 // list also, which is freed
1225 // rectangle's xLeft;
1226 // xRight will be set when we're done with this rectangle
1227 pRect->rcl.xLeft = prclView->xLeft + flbuf.fmtp.lLeftMargin;
1228 if (ulYPre)
1229 // starting new paragraph:
1230 // add first-line offset also
1231 pRect->rcl.xLeft += flbuf.fmtp.lFirstLineMargin;
1232
1233 // current X pos: start with left of rectangle
1234 flbuf.lXCurrent = pRect->rcl.xLeft;
1235
1236 // max baseline ofs: set to 0, this will be raised
1237 pRect->ulMaxBaseLineOfs = 0;
1238
1239 // words-per-rectangle loop;
1240 // we keep adding words to the rectangle until
1241 // a) words no longer fit and word-wrapping is on;
1242 // b) a newline or line feed is found;
1243 // c) the last word has been reached;
1244 while (fWords2Go)
1245 {
1246 PTXVWORD pWordThis = (PTXVWORD)pWordNode->pItemData;
1247/*
1248 #define TXVWORDF_GLUEWITHNEXT 1 // escape
1249 #define TXVWORDF_LINEBREAK 2 // \n
1250 #define TXVWORDF_LINEFEED 4 // \r
1251*/
1252 BOOL fNextWord = FALSE;
1253
1254 if (pWordThis->cEscapeCode)
1255 {
1256 // pseudo-word for escape sequence:
1257 // process...
1258 ProcessEscapes((PSZ*)&pWordThis->pStart,
1259 pxfd,
1260 &flbuf,
1261 TRUE);
1262
1263 // append this sequence only if it's needed
1264 // for painting (list markers etc.)
1265 if (pWordThis->fPaintEscapeWord)
1266 {
1267 pWordThis->lX = flbuf.lXCurrent;
1268 pWordThis->pvRectangle = (PVOID)pRect;
1269 lstAppendItem(&pRect->llWords, pWordThis);
1270 ulWordsInThisRect++;
1271 }
1272
1273 fNextWord = TRUE;
1274 }
1275 else
1276 {
1277 BOOL fWordWrapped = FALSE;
1278
1279 // not escape sequence, but real word: format...
1280 // is word-wrapping on?
1281 if (flbuf.fmtp.fWordWrap)
1282 {
1283 // yes: check if the word still fits
1284 if ( (flbuf.lXCurrent + pWordThis->ulCXWithSpaces // ###
1285 > prclView->xRight)
1286 // > ulWinCX)
1287 // but always add the first word in the rectangle,
1288 // because otherwise we get infinite loops
1289 && (ulWordsInThisRect > 0)
1290 )
1291 // no:
1292 fWordWrapped = TRUE;
1293 }
1294
1295 if (fWordWrapped)
1296 // start a new rectangle with the current word:
1297 fWords2Go = FALSE;
1298 // and do _not_ advance to the next word,
1299 // but start with this word for the next
1300 // rectangle...
1301 else
1302 {
1303 // add this word to the rectangle:
1304
1305 // store current X pos in word
1306 pWordThis->lX = flbuf.lXCurrent;
1307
1308 // increase current X pos by word width
1309 flbuf.lXCurrent += pWordThis->ulCXWithSpaces;
1310
1311 // store word in rectangle
1312 pWordThis->pvRectangle = (PVOID)pRect;
1313 lstAppendItem(&pRect->llWords, pWordThis);
1314 // ### memory leak right here!!!
1315 ulWordsInThisRect++;
1316
1317 // store highest word width found for this rect
1318 if (pWordThis->ulCY > lWordsMaxCY)
1319 lWordsMaxCY = pWordThis->ulCY;
1320
1321 // store highest base line ofs found for this rect
1322 if (pWordThis->ulBaseLineOfs > pRect->ulMaxBaseLineOfs)
1323 pRect->ulMaxBaseLineOfs = pWordThis->ulBaseLineOfs;
1324
1325 // go for next word in any case
1326 fNextWord = TRUE;
1327 } // end if (!fBreakThisWord)
1328
1329 // now check: add more words to this rectangle?
1330 if ( (pWordThis->ulFlags == TXVWORDF_LINEBREAK)
1331 // no if linebreak found
1332 || (pWordThis->ulFlags == TXVWORDF_LINEFEED)
1333 // no if linefeed found
1334 || (!fWords2Go)
1335 // no if we're out of words or
1336 // word-break was forced
1337 )
1338 {
1339 // no: finish up this rectangle...
1340
1341 // xLeft has been set on top
1342 pRect->rcl.xRight = flbuf.lXCurrent;
1343 pRect->rcl.yTop = lCurrentYTop - ulYPre;
1344 pRect->rcl.yBottom = pRect->rcl.yTop - lWordsMaxCY;
1345
1346 // decrease current y top for next line
1347 lCurrentYTop = pRect->rcl.yBottom;
1348 if (!fRects2Go)
1349 // we're done completely:
1350 // add another one
1351 lCurrentYTop -= lWordsMaxCY;
1352
1353 if (fWordWrapped)
1354 // starting with wrapped word in next line:
1355 ulYPre = 0;
1356 else
1357 if (pWordThis->ulFlags == TXVWORDF_LINEFEED)
1358 ulYPre = 0;
1359 else if (pWordThis->ulFlags == TXVWORDF_LINEBREAK)
1360 {
1361 // line break:
1362 // set y-pre for next loop
1363 ulYPre = flbuf.fmtp.lSpaceBefore;
1364 // and add paragraph post-y
1365 lCurrentYTop -= flbuf.fmtp.lSpaceAfter;
1366 }
1367
1368 // update x extents
1369 if (pRect->rcl.xRight > pxfd->ulViewportCX)
1370 pxfd->ulViewportCX = pRect->rcl.xRight;
1371
1372 // and quit the inner loop
1373 fWords2Go = FALSE;
1374 } // end finish up rectangle
1375 } // end else if (pWordThis->fIsEscapeSequence)
1376
1377 if (fNextWord)
1378 {
1379 pWordNode = pWordNode->pNext;
1380 if (!pWordNode)
1381 {
1382 // no more to go:
1383 // quit
1384 fWords2Go = FALSE;
1385 fRects2Go = FALSE;
1386 }
1387 }
1388 } // end while (fWords2Go)
1389
1390 // store rectangle
1391 lstAppendItem(&pxfd->llRectangles, pRect);
1392 }
1393
1394 // lCurrentYTop now has the bottommost point we've used;
1395 // store this as viewport (this might be negative)
1396 pxfd->ulViewportCY = lOrigYTop - lCurrentYTop;
1397 }
1398 }
1399 }
1400}
1401
1402/* ******************************************************************
1403 *
1404 * Device-independent text painting
1405 *
1406 ********************************************************************/
1407
1408/*
1409 *@@ DrawListMarker:
1410 *
1411 *@@added V0.9.3 (2000-05-17) [umoeller]
1412 */
1413
1414VOID DrawListMarker(HPS hps,
1415 PRECTL prclLine, // current line rectangle
1416 PTXVWORD pWordThis, // current word
1417 LONG lViewXOfs) // in: x offset to paint; 0 means rightmost
1418{
1419 POINTL ptl;
1420
1421 ULONG ulBulletSize = pWordThis->lPointSize * 2 / 3; // 2/3 of point size
1422
1423 ARCPARAMS arcp = {1, 1, 0, 0};
1424
1425 // pWordThis->pStart points to the \xFF character;
1426 // next is the "marker" escape (\x23),
1427 // next is the marker type
1428 CHAR cBulletType = *((pWordThis->pStart) + 2) ;
1429
1430 switch (cBulletType)
1431 {
1432 case 2: // square (filled box)
1433 ptl.x = pWordThis->lX - lViewXOfs;
1434 // center bullet vertically
1435 ptl.y = prclLine->yBottom
1436 + ( (prclLine->yTop - prclLine->yBottom) // height
1437 - ulBulletSize
1438 ) / 2;
1439
1440 GpiMove(hps, &ptl);
1441 ptl.x += ulBulletSize;
1442 ptl.y += ulBulletSize;
1443 GpiBox(hps, DRO_FILL, &ptl, 0, 0);
1444 break;
1445
1446 default: // case 1: // disc (filled circle)
1447 ptl.x = pWordThis->lX - lViewXOfs;
1448 // center bullet vertically;
1449 // the arc is drawn with the current position in its center
1450 ptl.y = prclLine->yBottom
1451 + ( (prclLine->yTop - prclLine->yBottom) // height
1452 / 2
1453 );
1454
1455 GpiSetArcParams(hps, &arcp);
1456 GpiMove(hps, &ptl);
1457 GpiFullArc(hps,
1458 (cBulletType == 3)
1459 ? DRO_OUTLINE
1460 : DRO_FILL,
1461 MAKEFIXED(ulBulletSize / 2, // radius!
1462 0));
1463 break;
1464
1465 }
1466}
1467
1468/*
1469 *@@ txvPaintText:
1470 * device-independent function for painting.
1471 * This can only be called after the text has
1472 * been formatted (using txvFormatText).
1473 *
1474 * This only paints rectangles which are within
1475 * prcl2Paint.
1476 *
1477 * -- For WM_PAINT, set this to the
1478 * update rectangle, and set fPaintHalfLines
1479 * to TRUE.
1480 *
1481 * -- For printing, set this to the page rectangle,
1482 * and set fPaintHalfLines to FALSE.
1483 *
1484 * All coordinates are in world space (PU_PELS).
1485 *
1486 *@@changed V0.9.3 (2000-05-05) [umoeller]: fixed wrong visible lines calculations; great speedup painting!
1487 *@@changed V0.9.3 (2000-05-06) [umoeller]: now using gpihCharStringPosAt
1488 */
1489
1490BOOL txvPaintText(HAB hab,
1491 HPS hps, // in: window or printer PS
1492 PXFORMATDATA pxfd,
1493 PRECTL prcl2Paint, // in: invalid rectangle to be drawn,
1494 // can be NULL to paint all
1495 LONG lViewXOfs, // in: x offset to paint; 0 means rightmost
1496 PULONG pulViewYOfs, // in: y offset to paint; 0 means _top_most;
1497 // out: y offset which should be passed to next call
1498 // (if TRUE is returned and fPaintHalfLines == FALSE)
1499 BOOL fPaintHalfLines, // in: if FALSE, lines which do not fully fit on
1500 // the page are dropped (useful for printing)
1501 PULONG pulLineIndex) // in: line to start painting with;
1502 // out: next line to paint, if any
1503 // (if TRUE is returned and fPaintHalfLines == FALSE)
1504{
1505 BOOL brc = FALSE,
1506 fAnyLinesPainted = FALSE;
1507 ULONG ulCurrentLineIndex = *pulLineIndex;
1508 // LONG lViewYOfsSaved = *pulViewYOfs;
1509 PLISTNODE pRectNode = lstNodeFromIndex(&pxfd->llRectangles,
1510 ulCurrentLineIndex);
1511
1512 LONG lcidLast = -99;
1513 LONG lPointSizeLast = -99;
1514
1515 while (pRectNode)
1516 {
1517 PTXVRECTANGLE pLineRcl = (PTXVRECTANGLE)pRectNode->pItemData;
1518 BOOL fPaintThis = FALSE;
1519
1520 // compose rectangle to draw for this line
1521 RECTL rclLine;
1522 rclLine.xLeft = pLineRcl->rcl.xLeft - lViewXOfs;
1523 rclLine.xRight = pLineRcl->rcl.xRight - lViewXOfs;
1524 rclLine.yBottom = pLineRcl->rcl.yBottom + *pulViewYOfs;
1525 rclLine.yTop = pLineRcl->rcl.yTop + *pulViewYOfs;
1526
1527 /* if (pmpf)
1528 {
1529 CHAR szTemp[100];
1530 ULONG cb = min(pLineRcl->cLineChars, 99);
1531 strhncpy0(szTemp, pLineRcl->pStartOfLine, cb);
1532
1533 _Pmpf(("Checking line %d: '%s'",
1534 ulCurrentLineIndex,
1535 szTemp));
1536
1537 _Pmpf((" (yB stored %d -> in HPS %d against win yB %d)",
1538 pLineRcl->rcl.yBottom,
1539 rclLine.yBottom,
1540 prcl2Paint->yBottom));
1541 } */
1542
1543 if (prcl2Paint == NULL)
1544 // draw all:
1545 fPaintThis = TRUE;
1546 else
1547 {
1548 BOOL fBottomInPaint = ( (rclLine.yBottom >= prcl2Paint->yBottom)
1549 && (rclLine.yBottom <= prcl2Paint->yTop)
1550 );
1551 BOOL fTopInPaint = ( (rclLine.yTop >= prcl2Paint->yBottom)
1552 && (rclLine.yTop <= prcl2Paint->yTop)
1553 );
1554
1555 if ((fBottomInPaint) && (fTopInPaint))
1556 // both in update rect:
1557 fPaintThis = TRUE;
1558 else
1559 if (fPaintHalfLines)
1560 {
1561 if ((fBottomInPaint) || (fTopInPaint))
1562 // only one in update rect:
1563 fPaintThis = TRUE;
1564 else
1565 // now, for very small update rectangles,
1566 // especially with slow scrolling,
1567 // we can have the case that the paint rectangle
1568 // is only a few pixels high so that the top of
1569 // the line is above the repaint, and the bottom
1570 // of the line is below it!
1571 if ( (rclLine.yTop >= prcl2Paint->yTop)
1572 && (rclLine.yBottom <= prcl2Paint->yBottom)
1573 )
1574 fPaintThis = TRUE;
1575 }
1576 }
1577
1578 if (fPaintThis)
1579 {
1580 // rectangle invalid: paint this rectangle
1581 // by going thru the member words
1582 PLISTNODE pWordNode = lstQueryFirstNode(&pLineRcl->llWords);
1583
1584 POINTL ptlStart;
1585
1586 while (pWordNode)
1587 {
1588 PTXVWORD pWordThis = (PTXVWORD)pWordNode->pItemData;
1589 ULONG flOptions = pWordThis->flOptions;
1590
1591 if (pWordThis->usAnchor)
1592 flOptions |= CHS_UNDERSCORE;
1593
1594 // x start: this word's X coordinate
1595 ptlStart.x = pWordThis->lX - lViewXOfs;
1596 // y start: bottom line of rectangle plus highest
1597 // base line offset found in all words (format step 2)
1598 ptlStart.y = rclLine.yBottom + pLineRcl->ulMaxBaseLineOfs;
1599 // pWordThis->ulBaseLineOfs;
1600
1601 // set font for subsequent calculations,
1602 // if changed (this includes the first call)
1603 if (lcidLast != pWordThis->lcid)
1604 {
1605 GpiSetCharSet(hps, pWordThis->lcid);
1606 lcidLast = pWordThis->lcid;
1607 // force recalc of point size
1608 lPointSizeLast = -99;
1609 }
1610
1611 if (lPointSizeLast != pWordThis->lPointSize)
1612 {
1613 if (pWordThis->lPointSize)
1614 // is outline font:
1615 gpihSetPointSize(hps, pWordThis->lPointSize);
1616 lPointSizeLast = pWordThis->lPointSize;
1617 }
1618
1619 if (!pWordThis->cEscapeCode)
1620 // regular word:
1621 gpihCharStringPosAt(hps,
1622 &ptlStart,
1623 &rclLine,
1624 flOptions,
1625 pWordThis->cChars,
1626 (PSZ)pWordThis->pStart);
1627 else
1628 {
1629 // check escape code
1630 switch (pWordThis->cEscapeCode)
1631 {
1632 case 0x23:
1633 // escape to be painted:
1634 DrawListMarker(hps,
1635 &rclLine,
1636 pWordThis,
1637 lViewXOfs);
1638 break;
1639 }
1640 }
1641
1642 // ptlStart.x += pWordThis->ulCXWithSpaces;
1643
1644 fAnyLinesPainted = TRUE;
1645 pWordNode = pWordNode->pNext;
1646 }
1647
1648 /* {
1649 LONG lColor = GpiQueryColor(hps);
1650 POINTL ptl2;
1651 GpiSetColor(hps, RGBCOL_RED);
1652 ptl2.x = rclLine.xLeft;
1653 ptl2.y = rclLine.yBottom;
1654 GpiMove(hps, &ptl2);
1655 ptl2.x = rclLine.xRight;
1656 ptl2.y = rclLine.yTop;
1657 GpiBox(hps,
1658 DRO_OUTLINE,
1659 &ptl2,
1660 0, 0);
1661 GpiSetColor(hps, lColor);
1662 } */
1663
1664 }
1665 else
1666 {
1667 // this line is no longer fully visible:
1668
1669 if (fAnyLinesPainted)
1670 {
1671 // we had painted lines already:
1672 // this means that all the following lines are
1673 // too far below the window, so quit
1674 /* if (pmpf)
1675 _Pmpf(("Quitting with line %d (xL = %d yB = %d)",
1676 ulCurrentLineIndex, rclLine.xLeft, rclLine.yBottom)); */
1677
1678 *pulLineIndex = ulCurrentLineIndex;
1679 if (pRectNode->pNext)
1680 {
1681 // another line to paint:
1682 PTXVRECTANGLE pLineRcl2 = (PTXVRECTANGLE)pRectNode->pNext->pItemData;
1683 // return TRUE
1684 brc = TRUE;
1685 // and set *pulViewYOfs to the top of
1686 // the next line, which wasn't visible
1687 // on the page any more
1688 *pulViewYOfs = pLineRcl2->rcl.yTop + *pulViewYOfs;
1689 }
1690 break;
1691 }
1692 // else no lines painted yet:
1693 // go for next node, because we're still above the visible window
1694 }
1695
1696 // next line
1697 pRectNode = pRectNode->pNext;
1698 // raise index to return
1699 ulCurrentLineIndex++;
1700 }
1701
1702 if (!fAnyLinesPainted)
1703 brc = FALSE;
1704
1705 return (brc);
1706}
1707
1708/*
1709 *@@ txvFindWordFromPoint:
1710 * returns the list node of the word under the
1711 * given point. The list node is from the global
1712 * words list in pxfd.
1713 *
1714 *@@added V0.9.3 (2000-05-18) [umoeller]
1715 */
1716
1717PLISTNODE txvFindWordFromPoint(PXFORMATDATA pxfd,
1718 PPOINTL pptl)
1719{
1720 PLISTNODE pWordNodeFound = NULL;
1721
1722 PLISTNODE pRectangleNode = lstQueryFirstNode(&pxfd->llRectangles);
1723 while ((pRectangleNode) && (!pWordNodeFound))
1724 {
1725 PTXVRECTANGLE prclThis = (PTXVRECTANGLE)pRectangleNode->pItemData;
1726 if ( (pptl->x >= prclThis->rcl.xLeft)
1727 && (pptl->x <= prclThis->rcl.xRight)
1728 && (pptl->y >= prclThis->rcl.yBottom)
1729 && (pptl->y <= prclThis->rcl.yTop)
1730 )
1731 {
1732 // cool, we found the rectangle:
1733 // now go thru the words in this rectangle
1734 PLISTNODE pWordNode = lstQueryFirstNode(&prclThis->llWords);
1735 while (pWordNode)
1736 {
1737 PTXVWORD pWordThis = (PTXVWORD)pWordNode->pItemData;
1738
1739 if ( (pptl->x >= pWordThis->lX)
1740 && (pptl->x <= pWordThis->lX + pWordThis->ulCXWithSpaces)
1741 )
1742 {
1743 pWordNodeFound = pWordNode;
1744 break;
1745 }
1746 pWordNode = pWordNode->pNext;
1747 }
1748 }
1749 pRectangleNode = pRectangleNode->pNext;
1750 }
1751
1752 return (pWordNodeFound);
1753}
1754
1755/*
1756 *@@ txvFindWordFromAnchor:
1757 * returns the list node from the global words list
1758 * BEFORE the word which represents the escape sequence
1759 * containing the specified anchor name.
1760 *
1761 *@@added V0.9.4 (2000-06-12) [umoeller]
1762 */
1763
1764PLISTNODE txvFindWordFromAnchor(PXFORMATDATA pxfd,
1765 const char *pszAnchorName)
1766{
1767 PLISTNODE pNodeFound = NULL;
1768
1769 ULONG cbAnchorName = strlen(pszAnchorName);
1770
1771 PLISTNODE pWordNode = lstQueryFirstNode(&pxfd->llWords);
1772 while ((pWordNode) && (!pNodeFound))
1773 {
1774 PTXVWORD pWordThis = (PTXVWORD)pWordNode->pItemData;
1775 if (pWordThis->cEscapeCode == 7)
1776 {
1777 // this word is an anchor escape sequence:
1778 if (strnicmp(pszAnchorName, (pWordThis->pStart + 2), cbAnchorName) == 0)
1779 {
1780 // matches: check length
1781 if (*(pWordThis->pStart + 2 + cbAnchorName) == (char)0xFF)
1782 // OK:
1783 pNodeFound = pWordNode;
1784 }
1785 }
1786
1787 pWordNode = pWordNode ->pNext;
1788 }
1789
1790 if (pNodeFound)
1791 {
1792 // anchor found:
1793 // go backwords in word list until we find a "real" word
1794 // which is no escape sequence
1795 while (pNodeFound)
1796 {
1797 PTXVWORD pWordThis = (PTXVWORD)pNodeFound->pItemData;
1798 if (pWordThis->cEscapeCode)
1799 pNodeFound = pNodeFound->pPrevious;
1800 else
1801 break;
1802 }
1803 }
1804
1805 return (pNodeFound);
1806}
1807
1808/* ******************************************************************
1809 *
1810 * Window-dependent functions
1811 *
1812 ********************************************************************/
1813
1814/*
1815 *@@ TEXTVIEWWINDATA:
1816 * view control-internal structure, stored in
1817 * QWL_USER at fnwpTextView.
1818 * This is device-dependent on the text view
1819 * window.
1820 */
1821
1822typedef struct _TEXTVIEWWINDATA
1823{
1824 HAB hab; // anchor block (for speed)
1825
1826 HDC hdc;
1827 HPS hps;
1828
1829 LONG lBackColor,
1830 lForeColor;
1831
1832 XTEXTVIEWCDATA cdata; // control data, as passed to WM_CREATE
1833
1834 XFORMATDATA xfd;
1835
1836 HWND hwndVScroll, // vertical scroll bar
1837 hwndHScroll; // horizontal scroll bar
1838
1839 BOOL fVScrollVisible, // TRUE if vscroll is currently used
1840 fHScrollVisible; // TRUE if hscroll is currently used
1841
1842 RECTL rclViewReal, // window rect as returned by WinQueryWindowRect
1843 // (top right point is inclusive!!)
1844 rclViewPaint, // same as rclViewReal, but excluding scroll bars
1845 rclViewText; // same as rclViewPaint, but excluding cdata borders
1846
1847 ULONG ulViewXOfs, // pixels that we have scrolled to the RIGHT; 0 means very left
1848 ulViewYOfs; // pixels that we have scrolled to the BOTTOM; 0 means very top
1849
1850 BOOL fAcceptsPresParamsNow; // TRUE after first WM_PAINT
1851
1852 // anchor clicking
1853 PLISTNODE pWordNodeFirstInAnchor; // points to first word which belongs to anchor
1854 USHORT usLastAnchorClicked; // last anchor which was clicked (1-0xFFFF)
1855} TEXTVIEWWINDATA, *PTEXTVIEWWINDATA;
1856
1857#define ID_VSCROLL 100
1858#define ID_HSCROLL 101
1859
1860/*
1861 *@@ UpdateTextViewPresData:
1862 * called from WM_CREATE and WM_PRESPARAMCHANGED
1863 * in fnwpTextView to update the TEXTVIEWWINDATA
1864 * from the window's presparams. This calls
1865 * txvSetDefaultFormat in turn.
1866 */
1867
1868VOID UpdateTextViewPresData(HWND hwndTextView,
1869 PTEXTVIEWWINDATA ptxvd)
1870{
1871 PSZ pszFont;
1872 ptxvd->lBackColor = winhQueryPresColor(hwndTextView,
1873 PP_BACKGROUNDCOLOR,
1874 FALSE, // no inherit
1875 SYSCLR_DIALOGBACKGROUND);
1876 ptxvd->lForeColor = winhQueryPresColor(hwndTextView,
1877 PP_FOREGROUNDCOLOR,
1878 FALSE, // no inherit
1879 SYSCLR_WINDOWSTATICTEXT);
1880
1881 if ((pszFont = winhQueryWindowFont(hwndTextView)))
1882 {
1883 ULONG ulSize;
1884 PSZ pszFaceName;
1885 // _Pmpf(("font: %s", pszFont));
1886 if (gpihSplitPresFont(pszFont,
1887 &ulSize,
1888 &pszFaceName))
1889 {
1890 txvSetFormatFont(ptxvd->hps,
1891 &ptxvd->xfd.fmtcStandard,
1892 ulSize,
1893 pszFaceName);
1894 }
1895 free(pszFont);
1896 }
1897}
1898
1899/*
1900 *@@ AdjustViewRects:
1901 * updates the internal size-dependent structures
1902 * and positions the scroll bars.
1903 *
1904 * This is device-dependent for the text view
1905 * control and must be called before FormatText2Screen
1906 * so that the view rectangles get calculated right.
1907 *
1908 * Required input in TEXTVIEWWINDATA:
1909 *
1910 * -- rclViewReal: the actual window dimensions.
1911 *
1912 * -- cdata: control data.
1913 *
1914 * Output from this function in TEXTVIEWWINDATA:
1915 *
1916 * -- rclViewPaint: the paint subrectangle (which
1917 * is rclViewReal minus scrollbars, if any).
1918 *
1919 * -- rclViewText: the text subrectangle (which
1920 * is rclViewPaint minus borders).
1921 */
1922
1923VOID AdjustViewRects(HWND hwndTextView,
1924 PTEXTVIEWWINDATA ptxvd)
1925{
1926 ULONG ulScrollCX = WinQuerySysValue(HWND_DESKTOP, SV_CXVSCROLL),
1927 ulScrollCY = WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL),
1928 ulOfs;
1929
1930 // calculate rclViewPaint:
1931 // 1) left
1932 ptxvd->rclViewPaint.xLeft = ptxvd->rclViewReal.xLeft;
1933 // 2) bottom
1934 ptxvd->rclViewPaint.yBottom = ptxvd->rclViewReal.yBottom;
1935 if (ptxvd->fHScrollVisible)
1936 // if we have a horizontal scroll bar at the bottom,
1937 // raise bottom by its height
1938 ptxvd->rclViewPaint.yBottom += ulScrollCY;
1939 // 3) right
1940 ptxvd->rclViewPaint.xRight = ptxvd->rclViewReal.xRight;
1941 if (ptxvd->fVScrollVisible)
1942 // if we have a vertical scroll bar at the right,
1943 // subtract its width from the right
1944 ptxvd->rclViewPaint.xRight -= ulScrollCX;
1945 ptxvd->rclViewPaint.yTop = ptxvd->rclViewReal.yTop;
1946
1947 // calculate rclViewText from that
1948 ptxvd->rclViewText.xLeft = ptxvd->rclViewPaint.xLeft + ptxvd->cdata.ulXBorder;
1949 ptxvd->rclViewText.yBottom = ptxvd->rclViewPaint.yBottom + ptxvd->cdata.ulYBorder;
1950 ptxvd->rclViewText.xRight = ptxvd->rclViewPaint.xRight - ptxvd->cdata.ulXBorder;
1951 ptxvd->rclViewText.yTop = ptxvd->rclViewPaint.yTop - ptxvd->cdata.ulXBorder;
1952
1953 // now reposition scroll bars; their sizes may change
1954 // if either the vertical or horizontal scroll bar has
1955 // popped up or been hidden
1956 if (ptxvd->cdata.flStyle & XTXF_VSCROLL)
1957 {
1958 // vertical scroll bar enabled:
1959 ulOfs = 0;
1960 if (ptxvd->fHScrollVisible)
1961 ulOfs = ulScrollCX;
1962 WinSetWindowPos(ptxvd->hwndVScroll,
1963 HWND_TOP,
1964 ptxvd->rclViewReal.xRight - ulScrollCX,
1965 ulOfs, // y
1966 ulScrollCX, // cx
1967 ptxvd->rclViewReal.yTop - ulOfs, // cy
1968 SWP_MOVE | SWP_SIZE);
1969 }
1970
1971 if (ptxvd->cdata.flStyle & XTXF_HSCROLL)
1972 {
1973 ulOfs = 0;
1974 if (ptxvd->fVScrollVisible)
1975 ulOfs = ulScrollCX;
1976 WinSetWindowPos(ptxvd->hwndHScroll,
1977 HWND_TOP,
1978 0,
1979 0,
1980 ptxvd->rclViewReal.xRight - ulOfs, // cx
1981 ulScrollCY, // cy
1982 SWP_MOVE | SWP_SIZE);
1983 }
1984}
1985
1986/*
1987 *@@ FormatText2Screen:
1988 * device-dependent version of text formatting
1989 * for the text view window. This calls txvFormatText
1990 * in turn and updates the view's scroll bars.
1991 *
1992 *@@changed V0.9.3 (2000-05-05) [umoeller]: fixed buggy vertical scroll bars
1993 */
1994
1995VOID FormatText2Screen(HWND hwndTextView,
1996 PTEXTVIEWWINDATA ptxvd,
1997 BOOL fAlreadyRecursing, // in: set this to FALSE when calling
1998 BOOL fFullRecalc)
1999{
2000 ULONG ulWinCX,
2001 ulWinCY;
2002
2003 // call device-independent formatter with the
2004 // window presentation space
2005 txvFormatText(ptxvd->hps,
2006 &ptxvd->xfd,
2007 &ptxvd->rclViewText,
2008 fFullRecalc);
2009
2010 ulWinCY = (ptxvd->rclViewText.yTop - ptxvd->rclViewText.yBottom);
2011
2012 if (ptxvd->ulViewYOfs < 0)
2013 ptxvd->ulViewYOfs = 0;
2014 if (ptxvd->ulViewYOfs > ((LONG)ptxvd->xfd.ulViewportCY - ulWinCY))
2015 ptxvd->ulViewYOfs = (LONG)ptxvd->xfd.ulViewportCY - ulWinCY;
2016
2017 // vertical scroll bar enabled at all?
2018 if (ptxvd->cdata.flStyle & XTXF_VSCROLL)
2019 {
2020 BOOL fEnabled = winhUpdateScrollBar(ptxvd->hwndVScroll,
2021 ulWinCY,
2022 ptxvd->xfd.ulViewportCY,
2023 ptxvd->ulViewYOfs,
2024 (ptxvd->cdata.flStyle & XTXF_AUTOVHIDE));
2025 // is auto-hide on?
2026 if (ptxvd->cdata.flStyle & XTXF_AUTOVHIDE)
2027 {
2028 // yes, auto-hide on: did visibility change?
2029 if (fEnabled != ptxvd->fVScrollVisible)
2030 // visibility changed:
2031 // if we're not already recursing,
2032 // force calling ourselves again
2033 if (!fAlreadyRecursing)
2034 {
2035 ptxvd->fVScrollVisible = fEnabled;
2036 AdjustViewRects(hwndTextView,
2037 ptxvd);
2038 FormatText2Screen(hwndTextView,
2039 ptxvd,
2040 TRUE, // fAlreadyRecursing
2041 FALSE); // quick format
2042 }
2043 }
2044 }
2045
2046 ulWinCX = (ptxvd->rclViewText.xRight - ptxvd->rclViewText.xLeft);
2047
2048 // horizontal scroll bar enabled at all?
2049 if (ptxvd->cdata.flStyle & XTXF_HSCROLL)
2050 {
2051 BOOL fEnabled = winhUpdateScrollBar(ptxvd->hwndHScroll,
2052 ulWinCX,
2053 ptxvd->xfd.ulViewportCX,
2054 ptxvd->ulViewXOfs,
2055 (ptxvd->cdata.flStyle & XTXF_AUTOHHIDE));
2056 // is auto-hide on?
2057 if (ptxvd->cdata.flStyle & XTXF_AUTOHHIDE)
2058 {
2059 // yes, auto-hide on: did visibility change?
2060 if (fEnabled != ptxvd->fHScrollVisible)
2061 // visibility changed:
2062 // if we're not already recursing,
2063 // force calling ourselves again (at the bottom)
2064 if (!fAlreadyRecursing)
2065 {
2066 ptxvd->fHScrollVisible = fEnabled;
2067 AdjustViewRects(hwndTextView,
2068 ptxvd);
2069 }
2070 }
2071 }
2072
2073 WinInvalidateRect(hwndTextView, NULL, FALSE);
2074}
2075
2076/*
2077 *@@ PaintViewText2Screen:
2078 * device-dependent version of text painting
2079 * for the text view window. This calls txvPaintText
2080 * in turn and updates the view's scroll bars.
2081 */
2082
2083VOID PaintViewText2Screen(PTEXTVIEWWINDATA ptxvd,
2084 PRECTL prcl2Paint) // in: invalid rectangle, can be NULL == paint all
2085{
2086 ULONG ulLineIndex = 0;
2087 ULONG ulYOfs = ptxvd->ulViewYOfs;
2088 txvPaintText(ptxvd->hab,
2089 ptxvd->hps, // paint PS: screen
2090 &ptxvd->xfd, // formatting data
2091 prcl2Paint, // update rectangle given to us
2092 ptxvd->ulViewXOfs, // current X scrolling offset
2093 &ulYOfs, // current Y scrolling offset
2094 TRUE, // draw even partly visible lines
2095 &ulLineIndex);
2096}
2097
2098/*
2099 *@@ PaintViewFocus:
2100 * paint a focus rectangle.
2101 */
2102
2103VOID PaintViewFocus(HPS hps,
2104 PTEXTVIEWWINDATA ptxvd,
2105 BOOL fFocus)
2106{
2107 POINTL ptl;
2108 HRGN hrgn;
2109 GpiSetClipRegion(hps,
2110 NULLHANDLE,
2111 &hrgn);
2112 GpiSetColor(hps,
2113 (fFocus)
2114 ? WinQuerySysColor(HWND_DESKTOP, SYSCLR_HILITEBACKGROUND, 0)
2115 : ptxvd->lBackColor);
2116 GpiSetLineType(hps, LINETYPE_DOT);
2117 ptl.x = ptxvd->rclViewPaint.xLeft;
2118 ptl.y = ptxvd->rclViewPaint.yBottom;
2119 GpiMove(hps, &ptl);
2120 ptl.x = ptxvd->rclViewPaint.xRight - 1;
2121 ptl.y = ptxvd->rclViewPaint.yTop - 1;
2122 GpiBox(hps,
2123 DRO_OUTLINE,
2124 &ptl,
2125 0, 0);
2126}
2127
2128/*
2129 *@@ RepaintWord:
2130 *
2131 *@@added V0.9.3 (2000-05-18) [umoeller]
2132 */
2133
2134VOID RepaintWord(PTEXTVIEWWINDATA ptxvd,
2135 PTXVWORD pWordThis,
2136 LONG lColor)
2137{
2138 POINTL ptlStart;
2139 ULONG flOptions = pWordThis->flOptions;
2140 PTXVRECTANGLE pLineRcl = (PTXVRECTANGLE)pWordThis->pvRectangle;
2141
2142 RECTL rclLine;
2143 rclLine.xLeft = pLineRcl->rcl.xLeft - ptxvd->ulViewXOfs;
2144 rclLine.xRight = pLineRcl->rcl.xRight - ptxvd->ulViewXOfs;
2145 rclLine.yBottom = pLineRcl->rcl.yBottom + ptxvd->ulViewYOfs;
2146 rclLine.yTop = pLineRcl->rcl.yTop + ptxvd->ulViewYOfs;
2147
2148 if (pWordThis->usAnchor)
2149 flOptions |= CHS_UNDERSCORE;
2150
2151 // x start: this word's X coordinate
2152 ptlStart.x = pWordThis->lX - ptxvd->ulViewXOfs;
2153 // y start: bottom line of rectangle plus highest
2154 // base line offset found in all words (format step 2)
2155 ptlStart.y = rclLine.yBottom + pLineRcl->ulMaxBaseLineOfs;
2156 // pWordThis->ulBaseLineOfs;
2157
2158 GpiSetCharSet(ptxvd->hps, pWordThis->lcid);
2159 if (pWordThis->lPointSize)
2160 // is outline font:
2161 gpihSetPointSize(ptxvd->hps, pWordThis->lPointSize);
2162
2163 GpiSetColor(ptxvd->hps,
2164 lColor);
2165
2166 if (!pWordThis->cEscapeCode)
2167 gpihCharStringPosAt(ptxvd->hps,
2168 &ptlStart,
2169 &rclLine,
2170 flOptions,
2171 pWordThis->cChars,
2172 (PSZ)pWordThis->pStart);
2173 else
2174 // escape to be painted:
2175 DrawListMarker(ptxvd->hps,
2176 &rclLine,
2177 pWordThis,
2178 ptxvd->ulViewXOfs);
2179}
2180
2181/*
2182 *@@ RepaintAnchor:
2183 *
2184 *@@added V0.9.3 (2000-05-18) [umoeller]
2185 */
2186
2187VOID RepaintAnchor(PTEXTVIEWWINDATA ptxvd,
2188 LONG lColor)
2189{
2190 PLISTNODE pNode = ptxvd->pWordNodeFirstInAnchor;
2191 USHORT usAnchor = 0;
2192 while (pNode)
2193 {
2194 PTXVWORD pWordThis = (PTXVWORD)pNode->pItemData;
2195 if (usAnchor == 0)
2196 // first loop:
2197 usAnchor = pWordThis->usAnchor;
2198 else
2199 if (pWordThis->usAnchor != usAnchor)
2200 // first word with different anchor:
2201 break;
2202
2203 RepaintWord(ptxvd,
2204 pWordThis,
2205 lColor);
2206 pNode = pNode->pNext;
2207 }
2208}
2209
2210/*
2211 *@@ fnwpTextView:
2212 * window procedure for the text view control. This is
2213 * registered with the WC_XTEXTVIEW class in txvRegisterTextView.
2214 * We have a TEXTVIEWWINDATA structure in QWL_USER where we
2215 * store all information we need.
2216 *
2217 * The text view control is not a subclassed whatever control,
2218 * but a control implemented from scratch. As a result, we
2219 * had to implement all messages which are usually recognized
2220 * by a control. In detail, we have:
2221 *
2222 * -- WM_WINDOWPOSCHANGED: if the control is resized, the
2223 * text is reformatted and the scroll bars are readjusted.
2224 * See AdjustViewRects and txvFormatText.
2225 *
2226 * -- WM_PRESPARAMCHANGED: if fonts or colors are dropped
2227 * on the control, we reformat the text also.
2228 *
2229 * -- WM_HSCROLL and WM_VSCROLL: this calls winhHandleScrollMsg
2230 * to scroll the window contents.
2231 *
2232 * -- WM_BUTTON1DOWN: this sets the focus to the control.
2233 *
2234 * -- WM_SETFOCUS: if we receive the focus, we draw a fine
2235 * dotted line in the "selection" color around the text
2236 * window.
2237 *
2238 * -- WM_CHAR: if we have the focus, the user can move the
2239 * visible part within the viewport using the usual
2240 * cursor and HOME/END keys.
2241 *
2242 * -- WM_MOUSEMOVE: this sends WM_CONTROLPOINTER to the
2243 * owner so the owner can change the mouse pointer.
2244 *
2245 * <B>Painting</B>
2246 *
2247 * The text view control creates a micro presentation space
2248 * from the window's device context upon WM_CREATE, which is
2249 * stored in TEXTVIEWWINDATA. We do not use WinBeginPaint in
2250 * WM_PAINT, but only the PS we created ourselves. This saves
2251 * us from resetting and researching all the fonts etc., which
2252 * should be speedier.
2253 *
2254 *@@changed V0.9.3 (2000-05-05) [umoeller]: removed TXM_NEWTEXT; now supporting WinSetWindowText
2255 *@@changed V0.9.3 (2000-05-07) [umoeller]: crashed if create param was NULL; fixed
2256 */
2257
2258MRESULT EXPENTRY fnwpTextView(HWND hwndTextView, ULONG msg, MPARAM mp1, MPARAM mp2)
2259{
2260 MRESULT mrc = 0;
2261
2262 PTEXTVIEWWINDATA ptxvd = (PTEXTVIEWWINDATA)WinQueryWindowPtr(hwndTextView, QWL_USER);
2263
2264 switch (msg)
2265 {
2266 /*
2267 * WM_CREATE:
2268 *
2269 */
2270
2271 case WM_CREATE:
2272 {
2273 PXTEXTVIEWCDATA pcd = (PXTEXTVIEWCDATA)mp1;
2274 // can be NULL
2275 PCREATESTRUCT pcs = (PCREATESTRUCT)mp2;
2276 SBCDATA sbcd;
2277
2278 mrc = (MPARAM)TRUE; // error
2279
2280 // allocate TEXTVIEWWINDATA for QWL_USER
2281 ptxvd = (PTEXTVIEWWINDATA)malloc(sizeof(TEXTVIEWWINDATA));
2282 if (ptxvd)
2283 {
2284 SIZEL szlPage = {0, 0};
2285 BOOL fShow = FALSE;
2286 // LONG lcid = 0;
2287
2288 // query message queue
2289 HMQ hmq = WinQueryWindowULong(hwndTextView, QWL_HMQ);
2290 // get codepage of message queue
2291 ULONG ulCodepage = WinQueryCp(hmq);
2292
2293 memset(ptxvd, 0, sizeof(TEXTVIEWWINDATA));
2294 WinSetWindowPtr(hwndTextView, QWL_USER, ptxvd);
2295
2296 ptxvd->hab = WinQueryAnchorBlock(hwndTextView);
2297
2298 ptxvd->hdc = WinOpenWindowDC(hwndTextView);
2299 ptxvd->hps = GpiCreatePS(ptxvd->hab,
2300 ptxvd->hdc,
2301 &szlPage, // use same page size as device
2302 PU_PELS | GPIT_MICRO | GPIA_ASSOC);
2303
2304 gpihSwitchToRGB(ptxvd->hps);
2305
2306 // set codepage; GPI defaults this to
2307 // the process codepage
2308 GpiSetCp(ptxvd->hps, ulCodepage);
2309
2310 txvInitFormat(&ptxvd->xfd);
2311
2312 // copy control data, if present
2313 if (pcd)
2314 memcpy(&ptxvd->cdata, pcd, pcd->cbData);
2315
2316 // check values which might cause null divisions
2317 if (ptxvd->cdata.ulVScrollLineUnit == 0)
2318 ptxvd->cdata.ulVScrollLineUnit = 15;
2319 if (ptxvd->cdata.ulHScrollLineUnit == 0)
2320 ptxvd->cdata.ulHScrollLineUnit = 15;
2321
2322 ptxvd->fAcceptsPresParamsNow = FALSE;
2323
2324 // copy window dimensions from CREATESTRUCT
2325 ptxvd->rclViewReal.xLeft = 0;
2326 ptxvd->rclViewReal.yBottom = 0;
2327 ptxvd->rclViewReal.xRight = pcs->cx;
2328 ptxvd->rclViewReal.yTop = pcs->cy;
2329
2330 sbcd.cb = sizeof(SBCDATA);
2331 sbcd.sHilite = 0;
2332 sbcd.posFirst = 0;
2333 sbcd.posLast = 100;
2334 sbcd.posThumb = 30;
2335 sbcd.cVisible = 50;
2336 sbcd.cTotal = 50;
2337
2338 ptxvd->hwndVScroll = WinCreateWindow(hwndTextView,
2339 WC_SCROLLBAR,
2340 "",
2341 SBS_VERT | SBS_THUMBSIZE | WS_VISIBLE,
2342 10, 10,
2343 20, 100,
2344 hwndTextView, // owner
2345 HWND_TOP,
2346 ID_VSCROLL,
2347 &sbcd,
2348 0);
2349 fShow = ((ptxvd->cdata.flStyle & XTXF_VSCROLL) != 0);
2350 WinShowWindow(ptxvd->hwndVScroll, fShow);
2351 ptxvd->fVScrollVisible = fShow;
2352
2353 ptxvd->hwndHScroll = WinCreateWindow(hwndTextView,
2354 WC_SCROLLBAR,
2355 "",
2356 SBS_THUMBSIZE | WS_VISIBLE,
2357 10, 10,
2358 20, 100,
2359 hwndTextView, // owner
2360 HWND_TOP,
2361 ID_HSCROLL,
2362 &sbcd,
2363 0);
2364 fShow = ((ptxvd->cdata.flStyle & XTXF_HSCROLL) != 0);
2365 WinShowWindow(ptxvd->hwndHScroll, fShow);
2366 ptxvd->fHScrollVisible = fShow;
2367
2368 // set "code" format
2369 txvSetFormatFont(ptxvd->hps,
2370 &ptxvd->xfd.fmtcCode,
2371 6,
2372 "System VIO");
2373
2374 // get colors from presparams/syscolors
2375 UpdateTextViewPresData(hwndTextView, ptxvd);
2376
2377 AdjustViewRects(hwndTextView,
2378 ptxvd);
2379
2380 mrc = (MPARAM)FALSE; // OK
2381 }
2382 break; }
2383
2384 /*
2385 * WM_SETWINDOWPARAMS:
2386 * this message sets the window parameters,
2387 * most importantly, the window text.
2388 *
2389 * This updates the control.
2390 */
2391
2392 case WM_SETWINDOWPARAMS:
2393 {
2394 WNDPARAMS *pwndParams = (WNDPARAMS *)mp1;
2395 if (pwndParams)
2396 {
2397 if (pwndParams->fsStatus & WPM_TEXT)
2398 {
2399 xstrcpy(&ptxvd->xfd.strViewText,
2400 pwndParams->pszText,
2401 0);
2402 ptxvd->ulViewXOfs = 0;
2403 ptxvd->ulViewYOfs = 0;
2404 /* ptxvd->fVScrollVisible = FALSE;
2405 ptxvd->fHScrollVisible = FALSE; */
2406 AdjustViewRects(hwndTextView,
2407 ptxvd);
2408 FormatText2Screen(hwndTextView,
2409 ptxvd,
2410 FALSE,
2411 TRUE); // full format
2412 }
2413 }
2414 break; }
2415
2416 /*
2417 * WM_WINDOWPOSCHANGED:
2418 *
2419 */
2420
2421 case WM_WINDOWPOSCHANGED:
2422 {
2423 // this msg is passed two SWP structs:
2424 // one for the old, one for the new data
2425 // (from PM docs)
2426 PSWP pswpNew = (PSWP)(mp1);
2427 // PSWP pswpOld = pswpNew + 1;
2428
2429 // resizing?
2430 if (pswpNew->fl & SWP_SIZE)
2431 {
2432 if (ptxvd)
2433 {
2434 WinQueryWindowRect(hwndTextView,
2435 &ptxvd->rclViewReal);
2436 AdjustViewRects(hwndTextView,
2437 ptxvd);
2438 FormatText2Screen(hwndTextView,
2439 ptxvd,
2440 FALSE,
2441 FALSE); // quick format
2442 }
2443 }
2444 break; }
2445
2446 /*
2447 * WM_PAINT:
2448 *
2449 */
2450
2451 case WM_PAINT:
2452 {
2453 HRGN hrgnOldClip;
2454
2455 /* HPS hps = WinBeginPaint(hwndTextView,
2456 ptxvd->hps,
2457 &rcl2Paint); // store invalid rectangle here
2458 */
2459
2460 if (ptxvd)
2461 {
2462 RECTL rclClip;
2463 RECTL rcl2Update;
2464
2465 // get update rectangle
2466 WinQueryUpdateRect(hwndTextView,
2467 &rcl2Update);
2468 // since we're not using WinBeginPaint,
2469 // we must validate the update region,
2470 // or we'll get bombed with WM_PAINT msgs
2471 WinValidateRect(hwndTextView,
2472 NULL,
2473 FALSE);
2474
2475 // reset clip region to "all"
2476 GpiSetClipRegion(ptxvd->hps,
2477 NULLHANDLE,
2478 &hrgnOldClip); // out: old clip region
2479 // reduce clip region to update rectangle
2480 GpiIntersectClipRectangle(ptxvd->hps,
2481 &rcl2Update); // exclusive
2482
2483 // draw little box at the bottom right
2484 // (in between scroll bars) if we have
2485 // both vertical and horizontal scroll bars
2486 if ( (ptxvd->cdata.flStyle & (XTXF_VSCROLL | XTXF_HSCROLL))
2487 == (XTXF_VSCROLL | XTXF_HSCROLL)
2488 && (ptxvd->fVScrollVisible)
2489 && (ptxvd->fHScrollVisible)
2490 )
2491 {
2492 RECTL rclBox;
2493 rclBox.xLeft = ptxvd->rclViewPaint.xRight;
2494 rclBox.yBottom = 0;
2495 rclBox.xRight = rclBox.xLeft + WinQuerySysValue(HWND_DESKTOP, SV_CXVSCROLL);
2496 rclBox.yTop = WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL);
2497 WinFillRect(ptxvd->hps,
2498 &rclBox,
2499 WinQuerySysColor(HWND_DESKTOP,
2500 SYSCLR_DIALOGBACKGROUND,
2501 0));
2502 }
2503
2504 // paint "view paint" rectangle white;
2505 // this can be larger than "view text"
2506 WinFillRect(ptxvd->hps,
2507 &ptxvd->rclViewPaint, // exclusive
2508 ptxvd->lBackColor);
2509
2510 // now reduce clipping rectangle to "view text" rectangle
2511 rclClip.xLeft = ptxvd->rclViewText.xLeft;
2512 rclClip.xRight = ptxvd->rclViewText.xRight - 1;
2513 rclClip.yBottom = ptxvd->rclViewText.yBottom;
2514 rclClip.yTop = ptxvd->rclViewText.yTop - 1;
2515 GpiIntersectClipRectangle(ptxvd->hps,
2516 &rclClip); // exclusive
2517 // finally, draw text lines in invalid rectangle;
2518 // this subfunction is smart enough to redraw only
2519 // the lines which intersect with rcl2Update
2520 GpiSetColor(ptxvd->hps, ptxvd->lForeColor);
2521 PaintViewText2Screen(ptxvd,
2522 &rcl2Update);
2523
2524 if (WinQueryFocus(HWND_DESKTOP) == hwndTextView)
2525 {
2526 // we have the focus:
2527 // reset clip region to "all"
2528 GpiSetClipRegion(ptxvd->hps,
2529 NULLHANDLE,
2530 &hrgnOldClip); // out: old clip region
2531 PaintViewFocus(ptxvd->hps,
2532 ptxvd,
2533 TRUE);
2534 }
2535
2536 ptxvd->fAcceptsPresParamsNow = TRUE;
2537 }
2538
2539 // WinEndPaint(hps);
2540 break; }
2541
2542 /*
2543 * WM_PRESPARAMCHANGED:
2544 *
2545 * Changing the color or font settings
2546 * is equivalent to changing the default
2547 * paragraph format. See TXM_SETFORMAT.
2548 */
2549
2550 case WM_PRESPARAMCHANGED:
2551 mrc = WinDefWindowProc(hwndTextView, msg, mp1, mp2);
2552 if (ptxvd)
2553 {
2554 LONG lPPIndex = (LONG)mp1;
2555 switch (lPPIndex)
2556 {
2557 case 0: // layout palette thing dropped
2558 case PP_BACKGROUNDCOLOR:
2559 case PP_FOREGROUNDCOLOR:
2560 case PP_FONTNAMESIZE:
2561 // re-query our presparams
2562 UpdateTextViewPresData(hwndTextView, ptxvd);
2563 }
2564
2565 if (ptxvd->fAcceptsPresParamsNow)
2566 FormatText2Screen(hwndTextView,
2567 ptxvd,
2568 FALSE,
2569 TRUE); // full reformat
2570 }
2571 break;
2572
2573 /*
2574 * WM_VSCROLL:
2575 *
2576 */
2577
2578 case WM_VSCROLL:
2579 {
2580 if (ptxvd->fVScrollVisible)
2581 {
2582 winhHandleScrollMsg(hwndTextView,
2583 ptxvd->hwndVScroll,
2584 &ptxvd->ulViewYOfs,
2585 &ptxvd->rclViewText,
2586 ptxvd->xfd.ulViewportCY,
2587 ptxvd->cdata.ulVScrollLineUnit,
2588 msg,
2589 mp2);
2590 }
2591 break; }
2592
2593 /*
2594 * WM_HSCROLL:
2595 *
2596 */
2597
2598 case WM_HSCROLL:
2599 {
2600 if (ptxvd->fHScrollVisible)
2601 {
2602 winhHandleScrollMsg(hwndTextView,
2603 ptxvd->hwndHScroll,
2604 &ptxvd->ulViewXOfs,
2605 &ptxvd->rclViewText,
2606 ptxvd->xfd.ulViewportCX,
2607 ptxvd->cdata.ulHScrollLineUnit,
2608 msg,
2609 mp2);
2610 }
2611 break; }
2612
2613 /*
2614 * WM_SETFOCUS:
2615 *
2616 */
2617
2618 case WM_SETFOCUS:
2619 {
2620 HPS hps = WinGetPS(hwndTextView);
2621 gpihSwitchToRGB(hps);
2622 PaintViewFocus(hps,
2623 ptxvd,
2624 (mp2 != 0));
2625 WinReleasePS(hps);
2626 break; }
2627
2628 /*
2629 * WM_MOUSEMOVE:
2630 * send WM_CONTROLPOINTER to owner.
2631 */
2632
2633 case WM_MOUSEMOVE:
2634 {
2635 HWND hwndOwner = WinQueryWindow(hwndTextView, QW_OWNER);
2636 if (hwndOwner)
2637 {
2638 HPOINTER hptrSet
2639 = (HPOINTER)WinSendMsg(hwndOwner,
2640 WM_CONTROLPOINTER,
2641 (MPARAM)(LONG)WinQueryWindowUShort(hwndTextView,
2642 QWS_ID),
2643 (MPARAM)WinQuerySysPointer(HWND_DESKTOP,
2644 SPTR_ARROW,
2645 FALSE));
2646 WinSetPointer(HWND_DESKTOP, hptrSet);
2647 }
2648 break; }
2649
2650 /*
2651 * WM_BUTTON1DOWN:
2652 *
2653 */
2654
2655 case WM_BUTTON1DOWN:
2656 {
2657 POINTL ptlPos;
2658 PLISTNODE pWordNodeClicked = NULL;
2659
2660 ptlPos.x = SHORT1FROMMP(mp1) + ptxvd->ulViewXOfs;
2661 ptlPos.y = SHORT2FROMMP(mp1) - ptxvd->ulViewYOfs;
2662
2663 if (hwndTextView != WinQueryFocus(HWND_DESKTOP))
2664 WinSetFocus(HWND_DESKTOP, hwndTextView);
2665
2666 ptxvd->usLastAnchorClicked = 0;
2667
2668 pWordNodeClicked = txvFindWordFromPoint(&ptxvd->xfd,
2669 &ptlPos);
2670
2671 if (pWordNodeClicked)
2672 {
2673 PTXVWORD pWordClicked = (PTXVWORD)pWordNodeClicked->pItemData;
2674
2675 // store anchor (can be 0)
2676 ptxvd->usLastAnchorClicked = pWordClicked->usAnchor;
2677
2678 if (pWordClicked->usAnchor)
2679 {
2680 // word has an anchor:
2681 PLISTNODE pNode = pWordNodeClicked;
2682
2683 // reset first word of anchor
2684 ptxvd->pWordNodeFirstInAnchor = NULL;
2685
2686 // go back to find the first word which has this anchor,
2687 // because we need to repaint them all
2688 while (pNode)
2689 {
2690 PTXVWORD pWordThis = (PTXVWORD)pNode->pItemData;
2691 if (pWordThis->usAnchor == pWordClicked->usAnchor)
2692 {
2693 // still has same anchor:
2694 // go for previous
2695 ptxvd->pWordNodeFirstInAnchor = pNode;
2696 pNode = pNode->pPrevious;
2697 }
2698 else
2699 // different anchor:
2700 // pNodeFirst points to first node with same anchor now
2701 break;
2702 }
2703
2704 RepaintAnchor(ptxvd,
2705 RGBCOL_RED);
2706 }
2707 }
2708
2709 WinSetCapture(HWND_DESKTOP, hwndTextView);
2710 mrc = (MPARAM)TRUE;
2711 break; }
2712
2713 /*
2714 * WM_BUTTON1UP:
2715 *
2716 */
2717
2718 case WM_BUTTON1UP:
2719 {
2720 POINTL ptlPos;
2721 // PTXVWORD pWordClicked = NULL;
2722 HWND hwndOwner = NULLHANDLE;
2723
2724 ptlPos.x = SHORT1FROMMP(mp1) + ptxvd->ulViewXOfs;
2725 ptlPos.y = SHORT2FROMMP(mp1) - ptxvd->ulViewYOfs;
2726 WinSetCapture(HWND_DESKTOP, NULLHANDLE);
2727
2728 if (ptxvd->usLastAnchorClicked)
2729 {
2730 RepaintAnchor(ptxvd,
2731 ptxvd->lForeColor);
2732
2733 // nofify owner
2734 hwndOwner = WinQueryWindow(hwndTextView, QW_OWNER);
2735 if (hwndOwner)
2736 WinPostMsg(hwndOwner,
2737 WM_CONTROL,
2738 MPFROM2SHORT(WinQueryWindowUShort(hwndTextView,
2739 QWS_ID),
2740 TXVN_LINK),
2741 (MPARAM)(ULONG)(ptxvd->usLastAnchorClicked));
2742 }
2743
2744 mrc = (MPARAM)TRUE;
2745 break; }
2746
2747 /*
2748 * WM_CHAR:
2749 *
2750 */
2751
2752 case WM_CHAR:
2753 {
2754 BOOL fDefProc = TRUE;
2755 USHORT usFlags = SHORT1FROMMP(mp1);
2756 // USHORT usch = SHORT1FROMMP(mp2);
2757 USHORT usvk = SHORT2FROMMP(mp2);
2758
2759 if (usFlags & KC_VIRTUALKEY)
2760 {
2761 ULONG ulMsg = 0;
2762 USHORT usID = ID_VSCROLL;
2763 SHORT sPos = 0;
2764 SHORT usCmd = 0;
2765 fDefProc = FALSE;
2766
2767 switch (usvk)
2768 {
2769 case VK_UP:
2770 ulMsg = WM_VSCROLL;
2771 usCmd = SB_LINEUP;
2772 break;
2773
2774 case VK_DOWN:
2775 ulMsg = WM_VSCROLL;
2776 usCmd = SB_LINEDOWN;
2777 break;
2778
2779 case VK_RIGHT:
2780 ulMsg = WM_HSCROLL;
2781 usCmd = SB_LINERIGHT;
2782 break;
2783
2784 case VK_LEFT:
2785 ulMsg = WM_HSCROLL;
2786 usCmd = SB_LINELEFT;
2787 break;
2788
2789 case VK_PAGEUP:
2790 ulMsg = WM_VSCROLL;
2791 if (usFlags & KC_CTRL)
2792 {
2793 sPos = 0;
2794 usCmd = SB_SLIDERPOSITION;
2795 }
2796 else
2797 usCmd = SB_PAGEUP;
2798 break;
2799
2800 case VK_PAGEDOWN:
2801 ulMsg = WM_VSCROLL;
2802 if (usFlags & KC_CTRL)
2803 {
2804 sPos = ptxvd->xfd.ulViewportCY;
2805 usCmd = SB_SLIDERPOSITION;
2806 }
2807 else
2808 usCmd = SB_PAGEDOWN;
2809 break;
2810
2811 case VK_HOME:
2812 if (usFlags & KC_CTRL)
2813 // vertical:
2814 ulMsg = WM_VSCROLL;
2815 else
2816 ulMsg = WM_HSCROLL;
2817
2818 sPos = 0;
2819 usCmd = SB_SLIDERPOSITION;
2820 break;
2821
2822 case VK_END:
2823 if (usFlags & KC_CTRL)
2824 {
2825 // vertical:
2826 ulMsg = WM_VSCROLL;
2827 sPos = ptxvd->xfd.ulViewportCY;
2828 }
2829 else
2830 {
2831 ulMsg = WM_HSCROLL;
2832 sPos = ptxvd->xfd.ulViewportCX;
2833 }
2834
2835 usCmd = SB_SLIDERPOSITION;
2836 break;
2837
2838 default:
2839 // other:
2840 fDefProc = TRUE;
2841 }
2842
2843 if ( ((usFlags & KC_KEYUP) == 0)
2844 && (ulMsg)
2845 )
2846 WinSendMsg(hwndTextView,
2847 ulMsg,
2848 MPFROMSHORT(usID),
2849 MPFROM2SHORT(sPos,
2850 usCmd));
2851 }
2852
2853 if (fDefProc)
2854 mrc = WinDefWindowProc(hwndTextView, msg, mp1, mp2);
2855 // sends to owner
2856 else
2857 mrc = (MPARAM)TRUE;
2858 break; }
2859
2860 /*
2861 *@@ TXM_QUERYPARFORMAT:
2862 * this msg can be sent to the text view control
2863 * to retrieve the paragraph format with the
2864 * index specified in mp1.
2865 *
2866 * Parameters:
2867 * -- ULONG mp1: index of format to query.
2868 * Must be 0 currently for the standard
2869 * paragraph format.
2870 * -- PXFMTPARAGRAPH mp2: pointer to buffer
2871 * which is to receive the formatting
2872 * data.
2873 *
2874 * Returns TRUE if copying was successful.
2875 *
2876 *@@added V0.9.3 (2000-05-06) [umoeller]
2877 */
2878
2879 case TXM_QUERYPARFORMAT:
2880 {
2881 PXFMTPARAGRAPH pFmt = NULL; // source
2882
2883 mrc = (MPARAM)FALSE;
2884
2885 if (mp1 == 0)
2886 pFmt = &ptxvd->xfd.fmtpStandard;
2887 /* else if ((ULONG)mp1 == 1)
2888 pFmt = &ptxvd->xfd.fmtpCode; */
2889
2890 if ((pFmt) && (mp2))
2891 {
2892 memcpy(mp2, pFmt, sizeof(XFMTPARAGRAPH));
2893 mrc = (MPARAM)TRUE;
2894 }
2895 break; }
2896
2897 /*
2898 *@@ TXM_SETPARFORMAT:
2899 * reverse to TXM_QUERYPARFORMAT, this sets a
2900 * paragraph format (line spacings, margins
2901 * and such).
2902 *
2903 * Parameters:
2904 * -- ULONG mp1: index of format to set.
2905 * Must be 0 currently for the standard
2906 * paragraph format.
2907 * -- PXFMTPARAGRAPH mp2: pointer to buffer
2908 * from which to copy formatting data.
2909 * If this pointer is NULL, the format
2910 * is reset to the default.
2911 *
2912 * This reformats the control.
2913 *
2914 *@@added V0.9.3 (2000-05-06) [umoeller]
2915 */
2916
2917 case TXM_SETPARFORMAT:
2918 {
2919 PXFMTPARAGRAPH pFmt = NULL; // target
2920
2921 mrc = (MPARAM)FALSE;
2922
2923 if (mp1 == 0)
2924 pFmt = &ptxvd->xfd.fmtpStandard;
2925 /* else if ((ULONG)mp1 == 1)
2926 pFmt = &ptxvd->xfd.fmtpCode; */
2927
2928 if (pFmt)
2929 {
2930 if (mp2)
2931 // copy
2932 memcpy(pFmt, mp2, sizeof(XFMTPARAGRAPH));
2933 else
2934 // default:
2935 memset(pFmt, 0, sizeof(XFMTPARAGRAPH));
2936
2937 FormatText2Screen(hwndTextView,
2938 ptxvd,
2939 FALSE,
2940 TRUE); // full reformat
2941
2942 mrc = (MPARAM)TRUE;
2943 }
2944 break; }
2945
2946 /*
2947 *@@ TXM_SETWORDWRAP:
2948 * this text view control msg quickly changes
2949 * the word-wrapping style of the default
2950 * paragraph formatting.
2951 * (BOOL)mp1 determines whether word wrapping
2952 * should be turned on or off.
2953 */
2954
2955 case TXM_SETWORDWRAP:
2956 {
2957 BOOL ulOldFlFormat = ptxvd->xfd.fmtpStandard.fWordWrap;
2958 ptxvd->xfd.fmtpStandard.fWordWrap = (BOOL)mp1;
2959 if (ptxvd->xfd.fmtpStandard.fWordWrap != ulOldFlFormat)
2960 FormatText2Screen(hwndTextView,
2961 ptxvd,
2962 FALSE,
2963 FALSE); // quick format
2964 break; }
2965
2966 /*
2967 *@@ TXM_QUERYCDATA:
2968 * copies the current XTEXTVIEWCDATA
2969 * into the specified buffer. This must
2970 * be sent to the control.
2971 *
2972 * Parameters:
2973 * -- PXTEXTVIEWCDATA mp1: target buffer.
2974 * Before calling this, you MUST specify
2975 * XTEXTVIEWCDATA.cbData.
2976 */
2977
2978 case TXM_QUERYCDATA:
2979 if (mp1)
2980 {
2981 PXTEXTVIEWCDATA pTarget = (PXTEXTVIEWCDATA)mp1;
2982 memcpy(pTarget, &ptxvd->cdata, pTarget->cbData);
2983 }
2984 break;
2985
2986 /*
2987 *@@ TXM_SETCDATA:
2988 * updates the current XTEXTVIEWCDATA
2989 * with the data from the specified buffer.
2990 * This must be sent to the control.
2991 *
2992 * Parameters:
2993 * -- PXTEXTVIEWCDATA mp1: source buffer.
2994 * Before calling this, you MUST specify
2995 * XTEXTVIEWCDATA.cbData.
2996 */
2997
2998 case TXM_SETCDATA:
2999 if (mp1)
3000 {
3001 PXTEXTVIEWCDATA pSource = (PXTEXTVIEWCDATA)mp1;
3002 memcpy(&ptxvd->cdata, pSource, pSource->cbData);
3003 }
3004 break;
3005
3006 /*
3007 *@@ TXM_JUMPTOANCHORNAME:
3008 * scrolls the XTextView control contents so that
3009 * the text marked with the specified anchor name
3010 * (TXVESC_ANCHORNAME escape) appears at the top
3011 * of the control.
3012 *
3013 * This must be sent, not posted to the control
3014 *
3015 * Parameters:
3016 * -- PSZ mp1: anchor name (e.g. "anchor1").
3017 *
3018 *@@added V0.9.4 (2000-06-12) [umoeller]
3019 */
3020
3021 case TXM_JUMPTOANCHORNAME:
3022 {
3023 if (mp1)
3024 {
3025 PLISTNODE pWordNode = txvFindWordFromAnchor(&ptxvd->xfd,
3026 (const char*)mp1);
3027 if (pWordNode)
3028 {
3029 // found:
3030 PTXVWORD pWord = (PTXVWORD)pWordNode->pItemData;
3031 if (pWord)
3032 {
3033 PTXVRECTANGLE pRect = (PTXVRECTANGLE)pWord->pvRectangle;
3034 ULONG ulWinCY = (ptxvd->rclViewText.yTop - ptxvd->rclViewText.yBottom);
3035
3036 // now we need to scroll the window so that this rectangle is on top.
3037 // Since rectangles start out with the height of the window (e.g. +768)
3038 // and then have lower y coordinates down to way in the negatives,
3039 // to get the y offset, we must...
3040 ptxvd->ulViewYOfs = (-pRect->rcl.yTop) - ulWinCY;
3041
3042 if (ptxvd->ulViewYOfs < 0)
3043 ptxvd->ulViewYOfs = 0;
3044 if (ptxvd->ulViewYOfs > ((LONG)ptxvd->xfd.ulViewportCY - ulWinCY))
3045 ptxvd->ulViewYOfs = (LONG)ptxvd->xfd.ulViewportCY - ulWinCY;
3046
3047 // vertical scroll bar enabled at all?
3048 if (ptxvd->cdata.flStyle & XTXF_VSCROLL)
3049 {
3050 BOOL fEnabled = winhUpdateScrollBar(ptxvd->hwndVScroll,
3051 ulWinCY,
3052 ptxvd->xfd.ulViewportCY,
3053 ptxvd->ulViewYOfs,
3054 (ptxvd->cdata.flStyle & XTXF_AUTOVHIDE));
3055 WinInvalidateRect(hwndTextView, NULL, FALSE);
3056 }
3057 }
3058 }
3059 }
3060 break; }
3061
3062 /*
3063 * WM_DESTROY:
3064 * clean up.
3065 */
3066
3067 case WM_DESTROY:
3068 xstrClear(&ptxvd->xfd.strViewText);
3069 lstClear(&ptxvd->xfd.llRectangles);
3070 lstClear(&ptxvd->xfd.llWords);
3071 free(ptxvd);
3072 GpiDestroyPS(ptxvd->hps);
3073 mrc = WinDefWindowProc(hwndTextView, msg, mp1, mp2);
3074 break;
3075
3076 default:
3077 mrc = WinDefWindowProc(hwndTextView, msg, mp1, mp2);
3078 }
3079
3080 return (mrc);
3081}
3082
3083/*
3084 *@@ txvRegisterTextView:
3085 * registers the Text View class with PM. Required
3086 * before the text view control can be used.
3087 */
3088
3089BOOL txvRegisterTextView(HAB hab)
3090{
3091 return (WinRegisterClass(hab,
3092 WC_XTEXTVIEW,
3093 fnwpTextView,
3094 0,
3095 sizeof(PVOID))); // QWL_USER
3096}
3097
3098/*
3099 *@@ txvReplaceWithTextView:
3100 * replaces any window with a text view control.
3101 * You must call txvRegisterTextView beforehand.
3102 *
3103 *@@added V0.9.1 (2000-02-13) [umoeller]
3104 */
3105
3106HWND txvReplaceWithTextView(HWND hwndParentAndOwner,
3107 USHORT usID,
3108 ULONG flWinStyle,
3109 ULONG flStyle,
3110 USHORT usBorder)
3111{
3112 HWND hwndMLE = WinWindowFromID(hwndParentAndOwner, usID),
3113 hwndTextView = NULLHANDLE;
3114 if (hwndMLE)
3115 {
3116 ULONG ul,
3117 // attrFound,
3118 abValue[32];
3119 SWP swpMLE;
3120 XTEXTVIEWCDATA xtxCData;
3121 PSZ pszFont = winhQueryWindowFont(hwndMLE);
3122 LONG lBackClr = -1,
3123 lForeClr = -1;
3124
3125 if ((ul = WinQueryPresParam(hwndMLE,
3126 PP_BACKGROUNDCOLOR,
3127 0,
3128 NULL,
3129 (ULONG)sizeof(abValue),
3130 (PVOID)&abValue,
3131 QPF_NOINHERIT)))
3132 lBackClr = abValue[0];
3133
3134 if ((ul = WinQueryPresParam(hwndMLE,
3135 PP_FOREGROUNDCOLOR,
3136 0,
3137 NULL,
3138 (ULONG)sizeof(abValue),
3139 (PVOID)&abValue,
3140 QPF_NOINHERIT)))
3141 lForeClr = abValue[0];
3142
3143 WinQueryWindowPos(hwndMLE, &swpMLE);
3144
3145 WinDestroyWindow(hwndMLE);
3146 memset(&xtxCData, 0, sizeof(xtxCData));
3147 xtxCData.cbData = sizeof(xtxCData);
3148 xtxCData.flStyle = flStyle;
3149 xtxCData.ulXBorder = usBorder;
3150 xtxCData.ulYBorder = usBorder;
3151 hwndTextView = WinCreateWindow(hwndParentAndOwner,
3152 WC_XTEXTVIEW,
3153 "",
3154 flWinStyle,
3155 swpMLE.x,
3156 swpMLE.y,
3157 swpMLE.cx,
3158 swpMLE.cy,
3159 hwndParentAndOwner,
3160 HWND_TOP,
3161 usID,
3162 &xtxCData,
3163 0);
3164 if (pszFont)
3165 {
3166 winhSetWindowFont(hwndTextView, pszFont);
3167 free(pszFont);
3168 }
3169
3170 if (lBackClr != -1)
3171 WinSetPresParam(hwndTextView,
3172 PP_BACKGROUNDCOLOR,
3173 sizeof(ULONG),
3174 &lBackClr);
3175 if (lForeClr != -1)
3176 WinSetPresParam(hwndTextView,
3177 PP_FOREGROUNDCOLOR,
3178 sizeof(ULONG),
3179 &lForeClr);
3180 }
3181 return (hwndTextView);
3182}
3183
3184/* ******************************************************************
3185 *
3186 * Printer-dependent functions
3187 *
3188 ********************************************************************/
3189
3190/*
3191 *@@ prthQueryQueues:
3192 * returns a buffer containing all print queues
3193 * on the system.
3194 *
3195 * This is usually the first step before printing.
3196 * After calling this function, show a dlg to the
3197 * user, allow him to select the printer queue
3198 * to be used. This can then be passed to
3199 * prthCreatePrinterDC.
3200 *
3201 * Use prthFreeBuf to free the returned buffer.
3202 */
3203
3204PRQINFO3* prthEnumQueues(PULONG pulReturned) // out: no. of queues found
3205{
3206 SPLERR rc;
3207 ULONG cTotal;
3208 ULONG cbNeeded = 0;
3209 PRQINFO3 *pprq3 = NULL;
3210
3211 // count queues & get number of bytes needed for buffer
3212 rc = SplEnumQueue(NULL, // default computer
3213 3, // detail level
3214 NULL, // pbuf
3215 0L, // cbBuf
3216 pulReturned, // out: entries returned
3217 &cTotal, // out: total entries available
3218 &cbNeeded,
3219 NULL); // reserved
3220
3221 if (cbNeeded)
3222 {
3223 pprq3 = (PRQINFO3*)malloc(cbNeeded);
3224 if (pprq3)
3225 {
3226 // enum the queues
3227 rc = SplEnumQueue(NULL,
3228 3,
3229 pprq3,
3230 cbNeeded,
3231 pulReturned,
3232 &cTotal,
3233 &cbNeeded,
3234 NULL);
3235 }
3236 }
3237
3238 return (pprq3);
3239}
3240
3241/*
3242 *@@ prthFreeBuf:
3243 *
3244 */
3245
3246VOID prthFreeBuf(PVOID pprq3)
3247{
3248 if (pprq3)
3249 free(pprq3);
3250}
3251
3252/*
3253 *@@ prthCreatePrinterDC:
3254 * creates a device context for the printer
3255 * specified by the given printer queue.
3256 *
3257 * As a nifty feature, this returns printer
3258 * device resolution automatically in the
3259 * specified buffer.
3260 *
3261 * Returns NULLHANDLE (== DEV_ERROR) on errors.
3262 *
3263 * Use DevCloseDC to destroy the DC.
3264 *
3265 * Based on print sample by Peter Fitzsimmons, Fri 95-09-29 02:47:16am.
3266 */
3267
3268HDC prthCreatePrinterDC(HAB hab,
3269 PRQINFO3 *pprq3,
3270 PLONG palRes) // out: 2 longs holding horizontal and vertical
3271 // printer resolution in pels per inch
3272{
3273 HDC hdc = NULLHANDLE;
3274 DEVOPENSTRUC dos;
3275 PSZ p;
3276
3277 memset(&dos, 0, sizeof(dos));
3278 p = strrchr(pprq3->pszDriverName, '.');
3279 if (p)
3280 *p = 0; // del everything after '.'
3281
3282 dos.pszLogAddress = pprq3->pszName;
3283 dos.pszDriverName = pprq3->pszDriverName;
3284 dos.pdriv = pprq3->pDriverData;
3285 dos.pszDataType = "PM_Q_STD";
3286 hdc = DevOpenDC(hab,
3287 OD_QUEUED,
3288 "*",
3289 4L, // count of items in next param
3290 (PDEVOPENDATA)&dos,
3291 0); // compatible DC
3292
3293 if (hdc)
3294 DevQueryCaps(hdc,
3295 CAPS_HORIZONTAL_FONT_RES,
3296 2,
3297 palRes); // buffer
3298
3299 return (hdc);
3300}
3301
3302/*
3303 *@@ prthQueryForms:
3304 * returns a buffer containing all forms
3305 * supported by the specified printer DC.
3306 *
3307 * Use prthFreeBuf to free the returned
3308 * buffer.
3309 *
3310 * HCINFO uses different model spaces for
3311 * the returned info. See PMREF for details.
3312 */
3313
3314HCINFO* prthQueryForms(HDC hdc,
3315 PULONG pulCount)
3316{
3317 HCINFO *pahci = NULL;
3318
3319 LONG cForms;
3320
3321 // get form count
3322 cForms = DevQueryHardcopyCaps(hdc, 0L, 0L, NULL); // phci);
3323 if (cForms)
3324 {
3325 pahci = (HCINFO*)malloc(cForms * sizeof(HCINFO));
3326 if (pahci)
3327 {
3328 *pulCount = DevQueryHardcopyCaps(hdc, 0, cForms, pahci);
3329 }
3330 }
3331
3332 return (pahci);
3333}
3334
3335/*
3336 *@@ prthCreatePS:
3337 * creates a "normal" presentation space from the specified
3338 * printer device context (which can be opened thru
3339 * prthCreatePrinterDC).
3340 *
3341 * Returns NULLHANDLE on errors.
3342 *
3343 * Based on print sample by Peter Fitzsimmons, Fri 95-09-29 02:47:16am.
3344 */
3345
3346HPS prthCreatePS(HAB hab, // in: anchor block
3347 HDC hdc, // in: printer device context
3348 ULONG ulUnits) // in: one of:
3349 // -- PU_PELS
3350 // -- PU_LOMETRIC
3351 // -- PU_HIMETRIC
3352 // -- PU_LOENGLISH
3353 // -- PU_HIENGLISH
3354 // -- PU_TWIPS
3355{
3356 SIZEL sizel;
3357
3358 sizel.cx = 0;
3359 sizel.cy = 0;
3360 return (GpiCreatePS(hab,
3361 hdc,
3362 &sizel,
3363 ulUnits | GPIA_ASSOC | GPIT_NORMAL));
3364}
3365
3366/*
3367 *@@ prthStartDoc:
3368 * calls DevEscape with DEVESC_STARTDOC.
3369 * This must be called before any painting
3370 * into the HDC's HPS. Any GPI calls made
3371 * before this are ignored.
3372 *
3373 * pszDocTitle appears in the spooler.
3374 */
3375
3376VOID prthStartDoc(HDC hdc,
3377 PSZ pszDocTitle)
3378{
3379 DevEscape(hdc,
3380 DEVESC_STARTDOC,
3381 strlen(pszDocTitle),
3382 pszDocTitle,
3383 0L,
3384 0L);
3385}
3386
3387/*
3388 *@@ prthNextPage:
3389 * calls DevEscape with DEVESC_NEWFRAME.
3390 * Signals when an application has finished writing to a page and wants to
3391 * start a new page. It is similar to GpiErase processing for a screen device
3392 * context, and causes a reset of the attributes. This escape is used with a
3393 * printer device to advance to a new page.
3394 */
3395
3396VOID prthNextPage(HDC hdc)
3397{
3398 DevEscape(hdc,
3399 DEVESC_NEWFRAME,
3400 0,
3401 0,
3402 0,
3403 0);
3404}
3405
3406/*
3407 *@@ prthEndDoc:
3408 * calls DevEscape with DEVESC_ENDDOC
3409 * and disassociates the HPS from the HDC.
3410 * Call this right before doing
3411 + GpiDestroyPS(hps);
3412 + DevCloseDC(hdc);
3413 */
3414
3415VOID prthEndDoc(HDC hdc,
3416 HPS hps)
3417{
3418 DevEscape(hdc, DEVESC_ENDDOC, 0L, 0L, 0, NULL);
3419 GpiAssociate(hps, NULLHANDLE);
3420}
3421
3422/*
3423 *@@ txvPrint:
3424 * this does the actual printing.
3425 */
3426
3427BOOL txvPrint(HAB hab,
3428 HDC hdc, // in: printer device context
3429 HPS hps, // in: printer presentation space (using PU_PELS)
3430 PSZ pszViewText, // in: text to print
3431 ULONG ulSize, // in: default font point size
3432 PSZ pszFaceName, // in: default font face name
3433 HCINFO *phci, // in: hardcopy form to use
3434 PSZ pszDocTitle, // in: document title (appears in spooler)
3435 FNPRINTCALLBACK *pfnCallback)
3436{
3437 RECTL rclPageDevice,
3438 rclPageWorld;
3439 XFORMATDATA xfd;
3440 BOOL fAnotherPage = FALSE;
3441 ULONG ulCurrentLineIndex = 0,
3442 ulCurrentPage = 1;
3443 ULONG ulCurrentYOfs = 0;
3444
3445 /* MATRIXLF matlf;
3446 POINTL ptlCenter;
3447 FIXED scalars[2]; */
3448
3449 // important: we must do a STARTDOC before we use the printer HPS.
3450 prthStartDoc(hdc,
3451 pszDocTitle);
3452
3453 // the PS is in TWIPS, but our world coordinate
3454 // space is in pels, so we need to transform
3455 /* GpiQueryViewingTransformMatrix(hps,
3456 1L,
3457 &matlf);
3458 ptlCenter.x = 0;
3459 ptlCenter.y = 0;
3460 scalars[0] = MAKEFIXED(2,0);
3461 scalars[1] = MAKEFIXED(3,0);
3462
3463 GpiScale (hps,
3464 &matlf,
3465 TRANSFORM_REPLACE,
3466 scalars,
3467 &ptlCenter); */
3468
3469 // initialize format with font from window
3470 txvInitFormat(&xfd);
3471
3472 /* txvSetFormatFont(hps,
3473 &xfd,
3474 ulSize,
3475 pszFaceName); */
3476
3477 // use text from window
3478 xstrcpy(&xfd.strViewText, pszViewText, 0);
3479
3480 // setup page
3481 GpiQueryPageViewport(hps,
3482 &rclPageDevice);
3483 // this is in device units; convert this
3484 // to the world coordinate space of the printer PS
3485 memcpy(&rclPageWorld, &rclPageDevice, sizeof(RECTL));
3486 GpiConvert(hps,
3487 CVTC_DEVICE, // source
3488 CVTC_WORLD,
3489 2, // 2 points, it's a rectangle
3490 (PPOINTL)&rclPageWorld);
3491
3492 // left and bottom margins are in millimeters...
3493 /* rclPage.xLeft = 100; // ###
3494 rclPage.yBottom = 100;
3495 rclPage.xRight = rclPage.xLeft + phci->xPels;
3496 rclPage.yTop = rclPage.yBottom + phci->yPels; */
3497
3498 txvFormatText(hps,
3499 &xfd, // in: ptxvd->rclViewText
3500 &rclPageWorld,
3501 TRUE);
3502
3503 do
3504 {
3505 _Pmpf(("---- printing page %d",
3506 ulCurrentPage));
3507
3508 fAnotherPage = txvPaintText(hab,
3509 hps,
3510 &xfd,
3511 &rclPageWorld,
3512 0,
3513 &ulCurrentYOfs,
3514 FALSE, // draw only fully visible lines
3515 &ulCurrentLineIndex); // in/out: line to start with
3516 if (fAnotherPage)
3517 {
3518 prthNextPage(hdc);
3519
3520 if (pfnCallback(ulCurrentPage++, 0) == FALSE)
3521 fAnotherPage = FALSE;
3522 }
3523 } while (fAnotherPage);
3524
3525 prthEndDoc(hdc, hps);
3526
3527 return (TRUE);
3528}
3529
3530/*
3531 *@@ txvPrintWindow:
3532 * one-shot function which prints the contents
3533 * of the specified XTextView control to the
3534 * default printer, using the default form.
3535 *
3536 * Returns a nonzero value upon errors.
3537 *
3538 * Based on print sample by Peter Fitzsimmons, Fri 95-09-29 02:47:16am.
3539 */
3540
3541int txvPrintWindow(HWND hwndTextView,
3542 PSZ pszDocTitle, // in: document title (appears in spooler)
3543 FNPRINTCALLBACK *pfnCallback)
3544{
3545 int irc = 0;
3546
3547 PTEXTVIEWWINDATA ptxvd = (PTEXTVIEWWINDATA)WinQueryWindowPtr(hwndTextView, QWL_USER);
3548
3549 if (!ptxvd)
3550 irc = 1;
3551 else
3552 {
3553 ULONG cReturned = 0;
3554 PRQINFO3 *pprq3 = prthEnumQueues(&cReturned);
3555 HDC hdc = NULLHANDLE;
3556 LONG caps[2];
3557
3558 // find default queue
3559 if (pprq3)
3560 {
3561 ULONG i;
3562 // search for default queue;
3563 for (i = 0; i < cReturned; i++)
3564 if (pprq3[i].fsType & PRQ3_TYPE_APPDEFAULT)
3565 {
3566 hdc = prthCreatePrinterDC(ptxvd->hab,
3567 &pprq3[i],
3568 caps);
3569
3570 break;
3571 }
3572 prthFreeBuf(pprq3);
3573 }
3574
3575 if (!hdc)
3576 irc = 2;
3577 else
3578 {
3579 // OK, we got a printer DC:
3580 HPS hps;
3581 ULONG cForms = 0;
3582 HCINFO *pahci,
3583 *phciSelected = 0;
3584
3585 // find default form
3586 pahci = prthQueryForms(hdc,
3587 &cForms);
3588 if (pahci)
3589 {
3590 HCINFO *phciThis = pahci;
3591 ULONG i;
3592 for (i = 0;
3593 i < cForms;
3594 i++, phciThis++)
3595 {
3596 if (phciThis->flAttributes & HCAPS_CURRENT)
3597 {
3598 phciSelected = phciThis;
3599 }
3600 }
3601 }
3602
3603 if (!phciSelected)
3604 irc = 3;
3605 else
3606 {
3607 // create printer PS
3608 hps = prthCreatePS(ptxvd->hab,
3609 hdc,
3610 PU_PELS);
3611
3612 if (hps == GPI_ERROR)
3613 irc = 4;
3614 else
3615 {
3616 PSZ pszFont;
3617 ULONG ulSize = 0;
3618 PSZ pszFaceName = 0;
3619
3620 if ((pszFont = winhQueryWindowFont(hwndTextView)))
3621 gpihSplitPresFont(pszFont,
3622 &ulSize,
3623 &pszFaceName);
3624 txvPrint(ptxvd->hab,
3625 hdc,
3626 hps,
3627 ptxvd->xfd.strViewText.psz,
3628 ulSize,
3629 pszFaceName,
3630 phciSelected,
3631 pszDocTitle,
3632 pfnCallback);
3633
3634 if (pszFont)
3635 free(pszFont);
3636
3637 GpiDestroyPS(hps);
3638 }
3639 }
3640 DevCloseDC(hdc);
3641 }
3642 }
3643
3644 return (irc);
3645}
3646
3647
Note: See TracBrowser for help on using the repository browser.