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

Last change on this file since 9 was 8, checked in by umoeller, 25 years ago

Initial checkin of helpers code which used to be in WarpIN.

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