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

Last change on this file since 373 was 373, checked in by pr, 17 years ago

Fix scroll bar bugs in textview control. Bug 1086.

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