source: trunk/src/helpers/textview.c

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

Allow copy to clipboard from textview control. Bug 1116.

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