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

Last change on this file since 204 was 201, checked in by umoeller, 23 years ago

Major work on textview control and dialogs.

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