source: trunk/src/helpers/cctl_tooltip.c@ 385

Last change on this file since 385 was 303, checked in by pr, 20 years ago

Fix warnings

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 68.7 KB
Line 
1
2/*
3 *@@sourcefile cctl_tooltip.c:
4 * implementation for the "tooltip" common control.
5 * See comctl.c for an overview.
6 *
7 * This has been extracted from comctl.c with V0.9.3 (2000-05-21) [umoeller].
8 *
9 * Note: Version numbering in this file relates to XWorkplace version
10 * numbering.
11 *
12 *@@header "helpers\comctl.h"
13 *@@added V0.9.3 (2000-05-21) [umoeller].
14 */
15
16/*
17 * Copyright (C) 1997-2005 Ulrich M”ller.
18 * This file is part of the "XWorkplace helpers" source package.
19 * This is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published
21 * by the Free Software Foundation, in version 2 as it comes in the
22 * "COPYING" file of the XWorkplace main distribution.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 */
28
29#define OS2EMX_PLAIN_CHAR
30 // this is needed for "os2emx.h"; if this is defined,
31 // emx will define PSZ as _signed_ char, otherwise
32 // as unsigned char
33
34#define INCL_DOSEXCEPTIONS
35#define INCL_DOSPROCESS
36#define INCL_DOSSEMAPHORES
37#define INCL_DOSERRORS
38
39#define INCL_WINWINDOWMGR
40#define INCL_WINFRAMEMGR
41#define INCL_WINMESSAGEMGR
42#define INCL_WININPUT
43#define INCL_WINPOINTERS
44#define INCL_WINTRACKRECT
45#define INCL_WINTIMER
46#define INCL_WINSYS
47
48#define INCL_WINRECTANGLES /// xxx temporary
49
50#define INCL_WINMENUS
51#define INCL_WINSTATICS
52#define INCL_WINBUTTONS
53#define INCL_WINSTDCNR
54
55#define INCL_GPIPRIMITIVES
56#define INCL_GPILOGCOLORTABLE
57#define INCL_GPILCIDS
58#define INCL_GPIPATHS
59#define INCL_GPIREGIONS
60#define INCL_GPIBITMAPS // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
61#include <os2.h>
62
63#include <stdlib.h>
64#include <stdio.h>
65#include <string.h>
66#include <setjmp.h> // needed for except.h
67#include <assert.h> // needed for except.h
68
69#include "setup.h" // code generation and debugging options
70
71#include "helpers\cnrh.h"
72#include "helpers\except.h" // exception handling
73#include "helpers\gpih.h"
74#include "helpers\linklist.h"
75#include "helpers\nls.h"
76#include "helpers\winh.h"
77
78#include "helpers\comctl.h"
79
80#pragma hdrstop
81
82/*
83 *@@category: Helpers\PM helpers\Window classes\Tooltips
84 * See cctl_tooltip.c.
85 */
86
87/* ******************************************************************
88 *
89 * Global variables
90 *
91 ********************************************************************/
92
93// linked list of all tools which were subclassed for tooltip
94HMTX G_hmtxSubclassedTools = NULLHANDLE;
95LINKLIST G_llSubclassedTools; // linked list of SUBCLASSEDTOOL items
96
97/* ******************************************************************
98 *
99 * "Tooltip" control
100 *
101 ********************************************************************/
102
103/*
104 *@@ ctlRegisterTooltip:
105 * this registers the Tooltip window class (ctl_fnwpTooltip)
106 * for an application. This is required before the tooltip
107 * control can be used.
108 *
109 *@@added V0.9.0 [umoeller]
110 */
111
112BOOL ctlRegisterTooltip(HAB hab)
113{
114 return WinRegisterClass(hab,
115 WC_CCTL_TOOLTIP,
116 ctl_fnwpTooltip,
117 CS_HITTEST, // class styles;
118 // CS_FRAME not working,
119 // CS_CLIPSIBLINGS not working
120 sizeof(PVOID) * 2); // addt'l bytes to reserve:
121 // one pointer for QWL_USER,
122 // one more for instance data
123}
124
125/* ******************************************************************
126 *
127 * Subclassing
128 *
129 ********************************************************************/
130
131/*
132 *@@ LockSubclassedTools:
133 * locks the global list of subclassed tools.
134 *
135 *@@added V0.9.12 (2001-04-28) [umoeller]
136 */
137
138STATIC BOOL LockSubclassedTools(VOID)
139{
140 if (!G_hmtxSubclassedTools)
141 {
142 // first call:
143
144 // initialize the list
145 lstInit(&G_llSubclassedTools,
146 TRUE); // auto-free
147
148 // create mutex and request it right away
149 return !DosCreateMutexSem(NULL,
150 &G_hmtxSubclassedTools,
151 0,
152 TRUE); // request!
153 }
154
155 return !DosRequestMutexSem(G_hmtxSubclassedTools, SEM_INDEFINITE_WAIT);
156}
157
158/*
159 *@@ UnlockSubclassedTools:
160 * unlocks the global list of subclassed tools.
161 *
162 *@@added V0.9.12 (2001-04-28) [umoeller]
163 *@@changed V0.9.12 (2001-05-03) [umoeller]: this did nothing... fixed
164 */
165
166STATIC VOID UnlockSubclassedTools(VOID)
167{
168 DosReleaseMutexSem(G_hmtxSubclassedTools); // was missing V0.9.12 (2001-05-03) [umoeller]
169}
170
171/*
172 *@@ SUBCLASSEDTOOL:
173 * structure created for each control which is
174 * subclassed by the tooltip control (ctl_fnwpTooltip).
175 *
176 *@@added V0.9.0 [umoeller]
177 */
178
179typedef struct _SUBCLASSEDTOOL
180{
181 HWND hwndTool;
182 PFNWP pfnwpOrig;
183 HWND hwndTooltip;
184 HAB hab;
185} SUBCLASSEDTOOL, *PSUBCLASSEDTOOL;
186
187/*
188 *@@ FindSubclassedTool:
189 * returns the SUBCLASSEDTOOL struct from the
190 * global list which matches hwndTool or NULL
191 * if not found.
192 *
193 * Preconditions: Caller must hold the subclassed
194 * tools mutex.
195 *
196 *@@added V0.9.12 (2001-04-28) [umoeller]
197 */
198
199STATIC PSUBCLASSEDTOOL FindSubclassedTool(HWND hwndTool)
200{
201 PLISTNODE pNode = lstQueryFirstNode(&G_llSubclassedTools);
202 while (pNode)
203 {
204 PSUBCLASSEDTOOL pstThis = (PSUBCLASSEDTOOL)pNode->pItemData;
205 if (pstThis->hwndTool == hwndTool)
206 return pstThis;
207
208 pNode = pNode->pNext;
209 }
210
211 return NULL;
212}
213
214/*
215 *@@ ctl_fnwpSubclassedTool:
216 * window procedure for tools which were subclassed
217 * to support tooltips.
218 *
219 *@@added V0.9.0 [umoeller]
220 *@@changed V0.9.12 (2001-04-28) [umoeller]: added mutex protection
221 *@@changed V1.0.2 (2003-07-26) [pr]: fixed tooltip moving with mouse from xcenters @@fixes 455
222 */
223
224MRESULT EXPENTRY ctl_fnwpSubclassedTool(HWND hwndTool, ULONG msg, MPARAM mp1, MPARAM mp2)
225{
226 MRESULT mrc = 0;
227
228 PFNWP pfnwpOrig = NULL;
229
230 if (LockSubclassedTools())
231 {
232 PSUBCLASSEDTOOL pst;
233
234 if (pst = FindSubclassedTool(hwndTool))
235 {
236 pfnwpOrig = pst->pfnwpOrig; // call default
237
238 switch (msg)
239 {
240 case WM_MOUSEMOVE:
241 case WM_BUTTON1DOWN:
242 case WM_BUTTON1UP:
243 case WM_BUTTON2DOWN:
244 case WM_BUTTON2UP:
245 case WM_BUTTON3DOWN:
246 case WM_BUTTON3UP:
247 case WM_MOUSELEAVE: // V1.0.2 (2003-07-26) [pr]: @@fixes 455
248 {
249 QMSG qmsg;
250 qmsg.hwnd = hwndTool;
251 qmsg.msg = msg;
252 qmsg.mp1 = mp1;
253 qmsg.mp2 = mp2;
254 // _Pmpf((__FUNCTION__ ": sending TTM_RELAYEVENT"));
255 WinSendMsg(pst->hwndTooltip,
256 TTM_RELAYEVENT,
257 (MPARAM)0,
258 (MPARAM)&qmsg);
259 }
260 break;
261
262 case WM_DESTROY:
263 lstRemoveItem(&G_llSubclassedTools, pst);
264 // this frees the item
265 break;
266 }
267 }
268
269 UnlockSubclassedTools();
270 }
271
272 if (pfnwpOrig)
273 mrc = pfnwpOrig(hwndTool, msg, mp1, mp2);
274
275 return mrc;
276}
277
278/*
279 *@@ SubclassTool:
280 * this gets called from ctl_fnwpTooltip if a control
281 * is to be subclassed to support mouse messaging
282 * (TTF_SUBCLASS flag).
283 *
284 * Preconditions: Caller must hold the subclassed
285 * tools mutex.
286 *
287 *@@added V0.9.0 [umoeller]
288 *@@changed V0.9.12 (2001-04-28) [umoeller]: renamed from SubclassToolForToolInfo
289 */
290
291STATIC BOOL SubclassTool(HWND hwndTooltip,
292 HWND hwndTool)
293{
294 BOOL brc = FALSE;
295
296 // make sure the tool is not on the list yet
297 // V0.9.12 (2001-04-28) [umoeller]
298 if (!FindSubclassedTool(hwndTool))
299 {
300 PFNWP pfnwpOrig;
301 if (pfnwpOrig = WinSubclassWindow(hwndTool,
302 ctl_fnwpSubclassedTool))
303 {
304 PSUBCLASSEDTOOL pst;
305 if (pst = (PSUBCLASSEDTOOL)malloc(sizeof(SUBCLASSEDTOOL)))
306 {
307 pst->pfnwpOrig = pfnwpOrig;
308 pst->hwndTooltip = hwndTooltip;
309 pst->hwndTool = hwndTool;
310 pst->hab = WinQueryAnchorBlock(hwndTool);
311
312 brc = !!lstAppendItem(&G_llSubclassedTools, pst);
313 }
314 }
315 }
316
317 return brc;
318}
319
320/*
321 *@@ UnSubclassTool:
322 * un-subclasses a tool previously subclassed by
323 * SubclassToolForToolinfo.
324 *
325 * Preconditions: Caller must hold the subclassed
326 * tools mutex.
327 *
328 *@@added V0.9.12 (2001-04-28) [umoeller]
329 */
330
331STATIC BOOL UnSubclassTool(HWND hwndTool)
332{
333 PSUBCLASSEDTOOL pst;
334 if (pst = FindSubclassedTool(hwndTool))
335 {
336 WinSubclassWindow(hwndTool,
337 pst->pfnwpOrig);
338 // orig winproc == un-subclass
339 return lstRemoveItem(&G_llSubclassedTools, pst);
340 // this frees the item
341 }
342
343 return FALSE;
344}
345
346/* ******************************************************************
347 *
348 * Implementation
349 *
350 ********************************************************************/
351
352/*
353 *@@ TOOLTIPDATA:
354 * private data structure stored in the window
355 * words of ctl_fnwpTooltip to hold information for
356 * a tooltip instance.
357 *
358 *@@added V0.9.0 [umoeller]
359 */
360
361typedef struct _TOOLTIPDATA
362{
363 HWND hwndOwner; // from WM_CREATE
364 HAB hab; // from WM_CREATE
365 USHORT usTooltipID; // from WM_CREATE
366
367 BOOL fIsActive; // TRUE per default; changed by TTM_ACTIVATE
368
369 ULONG idTimerInitial, // if != 0, "initial" timer is running
370 idTimerAutopop; // if != 0, "autopop" (hide) timer is running
371
372 ULONG ulTimeoutInitial, // "initial" timer timeout (ms)
373 ulTimeoutAutopop, // "autopop" (hide) timer timeout (ms)
374 ulTimeoutReshow; // "reshow" timer timeout (ms)
375
376 LINKLIST llTools; // linked list of TOOLINFO structures
377 // containing the tools
378 FONTMETRICS fontmetrics; // current font
379 LONG lForeColor, // current foreground color
380 lBackColor, // current background color
381 l3DHiColor, // 3D border light color
382 l3DLoColor; // 3D border dark color
383 PSZ pszPaintText; // text to paint (new buffer)
384
385 POINTL ptlPointerLast; // last mouse pointer position
386
387 PTOOLINFO ptiMouseOver; // tool info over which the mouse resides
388
389 BOOL fIsVisible; // TRUE if tooltip is visible
390
391 // CHAR szTextBuf[256]; // static buffer for copying/loading strings
392} TOOLTIPDATA, *PTOOLTIPDATA;
393
394// timer IDs
395#define TOOLTIP_ID_TIMER_INITIAL 1
396#define TOOLTIP_ID_TIMER_AUTOPOP 2
397
398// tooltip window border (free spaces)
399#define TOOLTIP_CX_BORDER 5
400#define TOOLTIP_CY_BORDER 3
401
402/*
403 *@@ UpdateTooltipPresColors:
404 * this gets called during WM_CREATE and WM_PRESPARAMCHANGED
405 * in ctl_fnwpTooltip to set the colors in TOOLTIPDATA according
406 * to the control's presparams.
407 */
408
409STATIC VOID UpdateTooltipPresColors(HWND hwndTooltip) // in: tooltip control
410{
411 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
412
413 // tooltip background color:
414 pttd->lBackColor = winhQueryPresColor(hwndTooltip,
415 PP_MENUBACKGROUNDCOLOR,
416 FALSE, // no inherit
417 SYSCLR_ENTRYFIELD);
418 // tooltip text color:
419 pttd->lForeColor = winhQueryPresColor(hwndTooltip,
420 PP_MENUFOREGROUNDCOLOR,
421 FALSE, // no inherit
422 SYSCLR_WINDOWTEXT);
423 // 3D border light color:
424 pttd->l3DHiColor = winhQueryPresColor(hwndTooltip,
425 PP_MENUHILITEFGNDCOLOR,
426 FALSE, // no inherit
427 SYSCLR_WINDOWTEXT);
428 // 3D border dark color:
429 pttd->l3DLoColor = winhQueryPresColor(hwndTooltip,
430 PP_MENUHILITEBGNDCOLOR,
431 FALSE, // no inherit
432 SYSCLR_WINDOWTEXT);
433}
434
435/*
436 *@@ TtmCreate:
437 * implementation for WM_CREATE in ctl_fnwpTooltip.
438 *
439 *@@added V0.9.13 (2001-06-21) [umoeller]
440 */
441
442STATIC MRESULT TtmCreate(HWND hwndTooltip,
443 MPARAM mp2)
444{
445 PTOOLTIPDATA pttd;
446 PCREATESTRUCT pcs = (PCREATESTRUCT)mp2;
447
448 // allocate and initialize tooltip data
449 if (pttd = (PTOOLTIPDATA)malloc(sizeof(TOOLTIPDATA)))
450 {
451 CHAR szFont[256];
452 memset(pttd, 0, sizeof(TOOLTIPDATA));
453 WinSetWindowPtr(hwndTooltip, 1, pttd);
454
455 pttd->hwndOwner = pcs->hwndOwner;
456 pttd->hab = WinQueryAnchorBlock(hwndTooltip);
457 pttd->usTooltipID = pcs->id;
458
459 pttd->fIsActive = TRUE;
460
461 // default timeouts
462 pttd->ulTimeoutInitial = 1000;
463 pttd->ulTimeoutAutopop = 5000;
464 pttd->ulTimeoutReshow = 500;
465
466 // get colors from presparams/syscolors
467 UpdateTooltipPresColors(hwndTooltip);
468
469 // check if font presparam set
470 if (WinQueryPresParam(hwndTooltip,
471 PP_FONTNAMESIZE, 0,
472 NULL,
473 sizeof(szFont),
474 szFont,
475 QPF_NOINHERIT)
476 == 0)
477 {
478 // no: set default font presparam
479 // (we never want the System Proportional font)
480 winhSetWindowFont(hwndTooltip,
481 NULL); // default (WarpSans or 8.Helv)
482 }
483
484 pttd->pszPaintText = strdup("undefined");
485
486 lstInit(&pttd->llTools, TRUE); // auto-free items
487
488 // override CREATESTRUCT
489 WinSetWindowPos(hwndTooltip,
490 HWND_TOP,
491 50, 50,
492 100, 100,
493 SWP_MOVE | SWP_SIZE | SWP_ZORDER | SWP_HIDE);
494
495 return (MPARAM)FALSE;
496 }
497
498 // malloc failed:
499 return (MPARAM)TRUE;
500}
501
502/*
503 *@@ TtmTimer:
504 * implementation for WM_TIMER in ctl_fnwpTooltip.
505 *
506 *@@added V0.9.13 (2001-06-21) [umoeller]
507 */
508
509STATIC BOOL TtmTimer(HWND hwndTooltip, MPARAM mp1)
510{
511 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
512 USHORT usTimer = SHORT1FROMMP(mp1);
513
514 switch (usTimer)
515 {
516 case TOOLTIP_ID_TIMER_INITIAL:
517 // _Pmpf(("WM_TIMER: Stopping initial timer: %d", usTimer));
518 // _Pmpf((__FUNCTION__ ": TOOLTIP_ID_TIMER_INITIAL"));
519 WinStopTimer(pttd->hab,
520 hwndTooltip,
521 usTimer);
522 pttd->idTimerInitial = 0;
523
524 if (pttd->fIsActive)
525 // show tooltip
526 WinPostMsg(hwndTooltip, TTM_SHOWTOOLTIPNOW, (MPARAM)TRUE, 0);
527 break;
528
529 case TOOLTIP_ID_TIMER_AUTOPOP:
530 // _Pmpf(("WM_TIMER: Stopping autopop timer: %d", usTimer));
531 WinStopTimer(pttd->hab,
532 hwndTooltip,
533 usTimer);
534 pttd->idTimerAutopop = 0;
535 WinPostMsg(hwndTooltip, TTM_SHOWTOOLTIPNOW, (MPARAM)FALSE, 0);
536 break;
537
538 default:
539 return FALSE;
540 } // end switch
541
542 return TRUE;
543}
544
545/*
546 *@@ TtmPaint:
547 * implementation for WM_PAINT in ctl_fnwpTooltip.
548 *
549 *@@added V0.9.1 (99-11-30) [umoeller]
550 */
551
552STATIC VOID TtmPaint(HWND hwndTooltip)
553{
554 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
555 HPS hps = WinBeginPaint(hwndTooltip, NULLHANDLE, NULL);
556 POINTL ptl = {0, 0};
557 RECTL rclWindow,
558 rclWindowBak;
559 ULONG ulStyle = WinQueryWindowULong(hwndTooltip, QWL_STYLE);
560
561 ULONG ulRounding = 0;
562 if (ulStyle & TTS_ROUNDED)
563 ulRounding = TT_ROUNDING;
564
565
566 gpihSwitchToRGB(hps);
567
568 // get tooltip size; this has been properly calculated
569 // by TTM_SHOWTOOLTIPNOW earlier
570 WinQueryWindowRect(hwndTooltip, &rclWindow);
571 memcpy(&rclWindowBak, &rclWindow, sizeof(RECTL));
572
573 if (ulStyle & TTS_SHADOW)
574 {
575 // if shadows are on, the window is too large;
576 // make rectl smaller for rest of paint
577 rclWindow.xRight -= TT_SHADOWOFS;
578 rclWindow.yBottom += TT_SHADOWOFS;
579 // restore old pattern
580 GpiSetPattern(hps, PATSYM_DEFAULT);
581 }
582
583 // draw shadow
584 if (ulStyle & TTS_SHADOW)
585 {
586 GpiSetColor(hps, CLR_BLACK);
587 GpiSetPattern(hps, PATSYM_HALFTONE);
588 ptl.x = rclWindowBak.xLeft + TT_SHADOWOFS;
589 ptl.y = rclWindowBak.yBottom;
590 GpiMove(hps, &ptl);
591 ptl.x = rclWindowBak.xRight - 1;
592 ptl.y = rclWindowBak.yTop - TT_SHADOWOFS;
593 GpiBox(hps,
594 DRO_FILL,
595 &ptl,
596 ulRounding, ulRounding);
597 // restore pattern
598 GpiSetPattern(hps, PATSYM_DEFAULT);
599 }
600
601 // draw "real" rectangle
602 ptl.x = rclWindow.xLeft;
603 ptl.y = rclWindow.yBottom;
604 GpiMove(hps, &ptl);
605 ptl.x = rclWindow.xRight - 1;
606 ptl.y = rclWindow.yTop - 1;
607 GpiSetColor(hps, pttd->lBackColor);
608 GpiBox(hps,
609 DRO_FILL,
610 &ptl,
611 6, 6);
612
613 GpiSetColor(hps, pttd->lForeColor);
614 GpiBox(hps,
615 DRO_OUTLINE,
616 &ptl,
617 ulRounding, ulRounding);
618
619 if (pttd->pszPaintText)
620 {
621 RECTL rclText;
622 GpiSetColor(hps, pttd->lForeColor);
623 GpiSetBackColor(hps, pttd->lBackColor);
624 rclText.xLeft = rclWindow.xLeft + TOOLTIP_CX_BORDER;
625 rclText.xRight = rclWindow.xRight - TOOLTIP_CX_BORDER;
626 rclText.yBottom = rclWindow.yBottom + TOOLTIP_CY_BORDER;
627 rclText.yTop = rclWindow.yTop - TOOLTIP_CY_BORDER;
628 winhDrawFormattedText(hps,
629 &rclText,
630 pttd->pszPaintText,
631 DT_LEFT | DT_TOP | DT_WORDBREAK);
632 }
633
634 WinEndPaint(hps);
635}
636
637/*
638 *@@ TtmDestroy:
639 * implementation for WM_DESTROY in ctl_fnwpTooltip.
640 *
641 *@@added V0.9.13 (2001-06-21) [umoeller]
642 */
643
644STATIC VOID TtmDestroy(HWND hwndTooltip)
645{
646 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
647 // stop timers
648 if (pttd->idTimerInitial)
649 WinStopTimer(pttd->hab,
650 hwndTooltip,
651 pttd->idTimerInitial);
652 if (pttd->idTimerAutopop)
653 WinStopTimer(pttd->hab,
654 hwndTooltip,
655 pttd->idTimerAutopop);
656 if (pttd->pszPaintText)
657 free(pttd->pszPaintText);
658
659 // un-subclass all tools that we subclassed
660 // V0.9.12 (2001-04-28) [umoeller]
661 if (LockSubclassedTools())
662 {
663 PLISTNODE pNode;
664 PSUBCLASSEDTOOL pst;
665 for (pNode = lstQueryFirstNode(&pttd->llTools);
666 pNode;
667 pNode = pNode->pNext)
668 {
669 PTOOLINFO pti = (PTOOLINFO)pNode->pItemData;
670 if (pst = FindSubclassedTool(pti->hwndTool))
671 UnSubclassTool(pti->hwndTool);
672 }
673
674 UnlockSubclassedTools();
675 }
676 // end V0.9.12 (2001-04-28) [umoeller]
677
678 lstClear(&pttd->llTools);
679
680 free(pttd);
681}
682
683/*
684 *@@ TtmAddTool:
685 * implementation for TTM_ADDTOOL in ctl_fnwpTooltip.
686 *
687 *@@added V0.9.13 (2001-06-21) [umoeller]
688 */
689
690STATIC MRESULT TtmAddTool(HWND hwndTooltip, MPARAM mp2)
691{
692 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
693 if (mp2)
694 {
695 PTOOLINFO ptiPassed = (PTOOLINFO)mp2,
696 ptiNew = (PTOOLINFO)malloc(sizeof(TOOLINFO));
697 if (ptiNew)
698 {
699 memcpy(ptiNew, ptiPassed, sizeof(TOOLINFO));
700 lstAppendItem(&pttd->llTools,
701 ptiNew);
702
703 if ( (ptiPassed->ulFlags & TTF_SUBCLASS)
704 && (LockSubclassedTools()) // V0.9.12 (2001-04-28) [umoeller]
705 )
706 {
707 // caller wants this tool to be subclassed:
708 // well, do it then
709 SubclassTool(hwndTooltip,
710 ptiPassed->hwndTool);
711
712 UnlockSubclassedTools();
713 }
714
715 return ((MPARAM)TRUE);
716 }
717 }
718
719 return (MPARAM)FALSE;
720}
721
722/*
723 *@@ TtmDelTool:
724 * implementation for TTM_DELTOOL in ctl_fnwpTooltip.
725 *
726 *@@added V0.9.13 (2001-06-21) [umoeller]
727 *@@changed V0.9.13 (2001-06-21) [umoeller]: fixed missing unlock
728 *@@changed V0.9.13 (2001-06-21) [umoeller]: fixed endless loop
729 */
730
731STATIC VOID TtmDelTool(HWND hwndTooltip, MPARAM mp2)
732{
733 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
734 PTOOLINFO ptiSearch;
735 if (ptiSearch = (PTOOLINFO)mp2)
736 {
737 PLISTNODE pToolNode = lstQueryFirstNode(&pttd->llTools);
738 while (pToolNode)
739 {
740 PTOOLINFO ptiThis = (PTOOLINFO)pToolNode->pItemData;
741 if ( (ptiThis->hwndToolOwner == ptiSearch->hwndToolOwner)
742 && (ptiThis->hwndTool == ptiSearch->hwndTool)
743 )
744 {
745 // found:
746
747 // V0.9.12 (2001-04-28) [umoeller]
748 // unsubclass if this was subclassed
749 if (ptiThis->ulFlags & TTF_SUBCLASS)
750 {
751 if (LockSubclassedTools())
752 {
753 UnSubclassTool(ptiSearch->hwndTool);
754
755 UnlockSubclassedTools();
756 // was missing V0.9.13 (2001-06-21) [umoeller]
757 }
758 }
759
760 // remove the tool from the list
761 lstRemoveNode(&pttd->llTools, pToolNode);
762
763 break;
764 }
765
766 pToolNode = pToolNode->pNext;
767 // fixed endless loop V0.9.13 (2001-06-21) [umoeller]
768 }
769 }
770}
771
772/*
773 *@@ TtmRelayEvent:
774 * implementation for TTM_RELAYEVENT in ctl_fnwpTooltip.
775 *
776 *@@added V0.9.13 (2001-06-21) [umoeller]
777 *@@changed V0.9.19 (2002-05-14) [umoeller]: fixed bad stop timer, thanks yuri
778 *@@changed V1.0.2 (2003-07-26) [pr]: fixed tooltip moving with mouse from xcenters @@fixes 455
779 */
780
781STATIC VOID TtmRelayEvent(HWND hwndTooltip, MPARAM mp2)
782{
783 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
784 PQMSG pqmsg = (PQMSG)mp2;
785 if (pqmsg)
786 {
787 POINTL ptlPointer;
788 PLISTNODE pToolNode;
789
790 // moved stop timer down V0.9.19 (2002-05-14) [umoeller]
791
792 WinQueryPointerPos(HWND_DESKTOP, &ptlPointer);
793
794 // find TOOLINFO from mouse position
795 pttd->ptiMouseOver = NULL;
796 pToolNode = lstQueryFirstNode(&pttd->llTools);
797 while (pToolNode)
798 {
799 PTOOLINFO pti = (PTOOLINFO)pToolNode->pItemData;
800 if (pti->hwndTool == pqmsg->hwnd)
801 {
802 // _Pmpf((__FUNCTION__ ": found tool"));
803 pttd->ptiMouseOver = pti;
804 break;
805 }
806 pToolNode = pToolNode->pNext;
807 }
808
809 if ( (ptlPointer.x != pttd->ptlPointerLast.x)
810 || (ptlPointer.y != pttd->ptlPointerLast.y)
811 || (pqmsg->msg == WM_BUTTON1DOWN)
812 || (pqmsg->msg == WM_BUTTON2DOWN)
813 || (pqmsg->msg == WM_BUTTON3DOWN)
814 || (pqmsg->msg == WM_MOUSELEAVE) // V1.0.2 (2003-07-26) [pr]: @@fixes 455
815 )
816 {
817 // mouse pos changed:
818 // moved stop timer here V0.9.19 (2002-05-14) [umoeller]
819 if (pttd->idTimerInitial)
820 {
821 // _Pmpf(("TTM_RELAYEVENT: Stopping timer: %d", pttd->idTimerInitial));
822 WinStopTimer(pttd->hab,
823 hwndTooltip,
824 TOOLTIP_ID_TIMER_INITIAL);
825 pttd->idTimerInitial = 0;
826 }
827
828 // hide tooltip
829 WinPostMsg(hwndTooltip,
830 TTM_SHOWTOOLTIPNOW,
831 (MPARAM)FALSE,
832 0);
833 memcpy(&pttd->ptlPointerLast, &ptlPointer, sizeof(POINTL));
834
835 // _Pmpf((__FUNCTION__ ": pttd->ptiMouseOver: 0x%lX", pttd->ptiMouseOver));
836 // _Pmpf((__FUNCTION__ ": pttd->fIsActive: 0x%lX", pttd->fIsActive));
837 if ( (pttd->ptiMouseOver)
838 && (pttd->fIsActive)
839 && (pqmsg->msg != WM_MOUSELEAVE) // V1.0.2 (2003-07-26) [pr]: @@fixes 455
840 )
841 {
842 // tool found and tooltip is activated:
843 pttd->idTimerInitial = WinStartTimer(pttd->hab,
844 hwndTooltip,
845 TOOLTIP_ID_TIMER_INITIAL,
846 pttd->ulTimeoutInitial);
847 // _Pmpf(("TTM_RELAYEVENT: Started timer: %d", pttd->idTimerInitial));
848 }
849 }
850 } // end if (pqmsg)
851}
852
853/*
854 *@@ TtmGetDelayTime:
855 * implementation for TTM_GETDELAYTIME in ctl_fnwpTooltip.
856 *
857 *@@added V0.9.13 (2001-06-21) [umoeller]
858 */
859
860STATIC MRESULT TtmGetDelayTime(HWND hwndTooltip, MPARAM mp1)
861{
862 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
863
864 switch ((ULONG)mp1)
865 {
866 case TTDT_AUTOPOP:
867 return (MRESULT)pttd->ulTimeoutAutopop;
868
869 case TTDT_INITIAL:
870 return (MRESULT)pttd->ulTimeoutInitial;
871
872 case TTDT_RESHOW:
873 return (MRESULT)pttd->ulTimeoutReshow;
874 }
875
876 return 0;
877}
878
879/*
880 *@@ TtmSetDelayTime:
881 * implementation for TTM_SETDELAYTIME in ctl_fnwpTooltip.
882 *
883 *@@added V0.9.13 (2001-06-21) [umoeller]
884 */
885
886STATIC VOID TtmSetDelayTime(HWND hwndTooltip, MPARAM mp1, MPARAM mp2)
887{
888 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
889 ULONG iDelay = (ULONG)mp2;
890 switch (SHORT1FROMMP(mp1))
891 {
892 case TTDT_AUTOMATIC:
893 pttd->ulTimeoutInitial = iDelay;
894 pttd->ulTimeoutAutopop = iDelay * 5;
895 pttd->ulTimeoutReshow = iDelay / 2;
896 break;
897
898 case TTDT_AUTOPOP:
899 pttd->ulTimeoutAutopop = iDelay;
900 break;
901
902 case TTDT_INITIAL:
903 pttd->ulTimeoutInitial = iDelay;
904 break;
905
906 case TTDT_RESHOW:
907 pttd->ulTimeoutReshow = iDelay;
908 break;
909 }
910}
911
912/*
913 *@@ TtmGetText:
914 * implementation for TTM_GETTEXT in ctl_fnwpTooltip.
915 *
916 *@@added V0.9.13 (2001-06-21) [umoeller]
917 */
918
919STATIC VOID TtmGetText(HWND hwndTooltip, MPARAM mp2)
920{
921 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
922 PTOOLINFO pti = (PTOOLINFO)mp2;
923
924 if (pti->pszText == PSZ_TEXTCALLBACK)
925 {
926 // TTN_NEEDTEXT notification desired:
927 // compose values for that msg
928 TOOLTIPTEXT ttt = {0};
929 ttt.hwndTooltip = hwndTooltip;
930 ttt.hwndTool = pti->hwndTool;
931 WinSendMsg(pti->hwndToolOwner,
932 WM_CONTROL,
933 MPFROM2SHORT(pttd->usTooltipID, // tooltip control wnd ID
934 TTN_NEEDTEXT),
935 &ttt);
936
937 // in case of error: set lpszText to NULL; this
938 // is not specified in the docs however.
939 pti->pszText = NULL;
940
941 switch (ttt.ulFormat)
942 {
943 case TTFMT_PSZ:
944 if (ttt.pszText)
945 pti->pszText = ttt.pszText;
946 break;
947
948 case TTFMT_STRINGRES:
949 // @@todo
950 break;
951 }
952 }
953}
954
955/*
956 *@@ TtmEnumTools:
957 * implementation for TTM_ENUMTOOLS in ctl_fnwpTooltip.
958 *
959 *@@added V0.9.13 (2001-06-21) [umoeller]
960 */
961
962STATIC MRESULT TtmEnumTools(HWND hwndTooltip, MPARAM mp1, MPARAM mp2)
963{
964 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
965 PTOOLINFO ptiTarget;
966 if (ptiTarget = (PTOOLINFO)mp2)
967 {
968 PTOOLINFO ptiFound = (PTOOLINFO)lstItemFromIndex(&pttd->llTools,
969 SHORT1FROMMP(mp1));
970 if (ptiFound)
971 {
972 memcpy(ptiTarget, ptiFound, sizeof(TOOLINFO));
973 return (MRESULT)TRUE;
974 }
975 }
976
977 return (MRESULT)FALSE;
978}
979
980/*
981 *@@ TtmGetCurrentTool:
982 * implementation for TTM_GETCURRENTTOOL in ctl_fnwpTooltip.
983 *
984 *@@added V0.9.13 (2001-06-21) [umoeller]
985 */
986
987STATIC MRESULT TtmGetCurrentTool(HWND hwndTooltip, MPARAM mp2)
988{
989 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
990 PTOOLINFO ptiTarget;
991 if (ptiTarget = (PTOOLINFO)mp2)
992 {
993 if (pttd->ptiMouseOver)
994 {
995 memcpy(ptiTarget, pttd->ptiMouseOver, sizeof(TOOLINFO));
996 return (MRESULT)TRUE;
997 }
998 }
999
1000 return (MRESULT)FALSE;
1001}
1002
1003/*
1004 *@@ TtmGetToolInfo:
1005 * implementation for TTM_GETTOOLINFO in ctl_fnwpTooltip.
1006 *
1007 *@@added V0.9.13 (2001-06-21) [umoeller]
1008 */
1009
1010STATIC MRESULT TtmGetToolInfo(HWND hwndTooltip, MPARAM mp2)
1011{
1012 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
1013 PTOOLINFO ptiSearch;
1014 if (ptiSearch = (PTOOLINFO)mp2)
1015 {
1016 PLISTNODE pToolNode = lstQueryFirstNode(&pttd->llTools);
1017 while (pToolNode)
1018 {
1019 PTOOLINFO ptiThis = (PTOOLINFO)pToolNode->pItemData;
1020 if ( (ptiThis->hwndToolOwner == ptiSearch->hwndToolOwner)
1021 && (ptiThis->hwndTool == ptiSearch->hwndTool)
1022 )
1023 {
1024 // found:
1025 memcpy(ptiSearch, ptiThis, sizeof(TOOLINFO));
1026 return (MPARAM)TRUE;
1027 }
1028 pToolNode = pToolNode->pNext;
1029 }
1030 }
1031
1032 return (MRESULT)FALSE;
1033}
1034
1035/*
1036 *@@ FormatTooltip:
1037 * formats the tooltip window according to
1038 * its text (which is assumed to be in
1039 * pttd->pszPaintText) and positions it
1040 * on the screen.
1041 *
1042 * This does not change the visibility
1043 * of the tooltip; if it is not yet visible,
1044 * you must show it afterwards.
1045 *
1046 *@@added V0.9.13 (2001-06-21) [umoeller]
1047 *@@changed V1.0.4 (2005-10-15) [bvl]: Add 1 pixel to width of DBCS tooltip @@fixes 676
1048 */
1049
1050STATIC VOID FormatTooltip(HWND hwndTooltip,
1051 PTOOLTIPDATA pttd,
1052 PPOINTL pptlPointer) // in: current pointer pos or NULL
1053{
1054 // find out how much space we need
1055 RECTL rcl = { 0, 0, 300, 1000 };
1056 POINTL ptlTooltip;
1057 LONG cx, cy;
1058 ULONG ulStyle = WinQueryWindowULong(hwndTooltip, QWL_STYLE);
1059
1060 HPS hps = WinGetPS(hwndTooltip);
1061
1062 winhDrawFormattedText(hps,
1063 &rcl,
1064 pttd->pszPaintText,
1065 DT_LEFT | DT_TOP | DT_WORDBREAK | DT_QUERYEXTENT);
1066 WinReleasePS(hps);
1067
1068 // calc width and height of tooltip
1069 // DBCS needs a extra pixel to show characters.
1070 cx = rcl.xRight + 2*TOOLTIP_CX_BORDER;
1071 if (nlsDBCS())
1072 cx++;
1073
1074 cy = (rcl.yTop - rcl.yBottom) + 2*TOOLTIP_CY_BORDER;
1075
1076 // calc x and y pos of tooltip:
1077
1078 // per default, use pointer pos
1079 ptlTooltip.x = pptlPointer->x - cx/2;
1080 ptlTooltip.y = pptlPointer->y - cy;
1081
1082 // do we need the tool's position?
1083 if ( pttd->ptiMouseOver->ulFlags
1084 & (TTF_CENTER_X_ON_TOOL | TTF_POS_Y_ABOVE_TOOL | TTF_POS_Y_BELOW_TOOL)
1085 )
1086 {
1087 // yes:
1088 SWP swpTool;
1089 POINTL ptlTool;
1090 WinQueryWindowPos(pttd->ptiMouseOver->hwndTool, &swpTool);
1091 ptlTool.x = swpTool.x;
1092 ptlTool.y = swpTool.y;
1093 // convert x, y to desktop points
1094 WinMapWindowPoints(WinQueryWindow(pttd->ptiMouseOver->hwndTool,
1095 QW_PARENT), // hwndFrom
1096 HWND_DESKTOP, // hwndTo
1097 &ptlTool,
1098 1);
1099
1100 // X
1101 if (pttd->ptiMouseOver->ulFlags & TTF_CENTER_X_ON_TOOL)
1102 // center X on tool:
1103 ptlTooltip.x = ptlTool.x + ((swpTool.cx - cx) / 2L);
1104
1105 // Y
1106 if (pttd->ptiMouseOver->ulFlags & TTF_POS_Y_ABOVE_TOOL)
1107 ptlTooltip.y = ptlTool.y + swpTool.cy;
1108 else if (pttd->ptiMouseOver->ulFlags & TTF_POS_Y_BELOW_TOOL)
1109 ptlTooltip.y = ptlTool.y - cy;
1110 }
1111
1112 // if "shy mouse" is enabled, make
1113 // sure the tool tip is not under the
1114 // mouse pointer
1115 if (ulStyle & TTF_SHYMOUSE)
1116 {
1117 // we need to subtract the current mouse
1118 // pointer's hot spot from the current
1119 // pointer position
1120 HPOINTER hptr = WinQueryPointer(HWND_DESKTOP);
1121 POINTERINFO pi;
1122 if (WinQueryPointerInfo(hptr, &pi))
1123 {
1124 // calc bottom edge of mouse pointer rect
1125 ULONG yBottomPointer = (pptlPointer->y - pi.yHotspot);
1126 // _Pmpf(("yHotspot: %d", pi.yHotspot));
1127 if ( (ptlTooltip.y + cy) // top edge of tool tip
1128 > yBottomPointer
1129 )
1130 {
1131 ptlTooltip.y = pptlPointer->y - cy - pi.yHotspot;
1132 }
1133 }
1134 }
1135
1136 // constrain to screen
1137 if (ptlTooltip.x < 0)
1138 ptlTooltip.x = 0;
1139 if (ptlTooltip.y < 0)
1140 ptlTooltip.y = 0;
1141 if (ptlTooltip.x + cx > G_cxScreen)
1142 ptlTooltip.x = G_cxScreen - cx;
1143 if (ptlTooltip.y + cy > G_cyScreen)
1144 ptlTooltip.y = G_cyScreen - cy;
1145
1146 // if shadow is enabled,
1147 // enlarge; the shadow might by
1148 // off-screen now, but that's OK
1149 if (ulStyle & TTS_SHADOW)
1150 {
1151 cx += TT_SHADOWOFS;
1152 cy += TT_SHADOWOFS;
1153 ptlTooltip.y -= TT_SHADOWOFS;
1154 }
1155
1156 // set tooltip position at the pos we calculated
1157 // and show tooltip
1158 WinSetWindowPos(hwndTooltip,
1159 HWND_TOP,
1160 ptlTooltip.x,
1161 ptlTooltip.y,
1162 cx,
1163 cy,
1164 SWP_MOVE | SWP_SIZE | SWP_ZORDER);
1165}
1166
1167/*
1168 *@@ TtmUpdateTipText:
1169 * implementation for TTM_UPDATETIPTEXT in ctl_fnwpTooltip.
1170 *
1171 *@@added V0.9.13 (2001-06-21) [umoeller]
1172 */
1173
1174STATIC VOID TtmUpdateTipText(HWND hwndTooltip,
1175 const char *pcszNewText)
1176{
1177 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
1178
1179 // has text really changed?
1180 if ( ( (pttd->pszPaintText)
1181 && (pcszNewText)
1182 && (strcmp(pttd->pszPaintText, pcszNewText))
1183 )
1184 || (pttd->pszPaintText && !pcszNewText)
1185 || (!pttd->pszPaintText && pcszNewText)
1186 )
1187 {
1188 // yes:
1189 if (pttd->pszPaintText)
1190 {
1191 free(pttd->pszPaintText);
1192 pttd->pszPaintText = NULL;
1193 }
1194
1195 if (pcszNewText)
1196 pttd->pszPaintText = strdup(pcszNewText);
1197
1198 if (pttd->fIsVisible)
1199 {
1200 // currently showing:
1201 // reformat
1202 POINTL ptlPointer;
1203 WinQueryPointerPos(HWND_DESKTOP, &ptlPointer);
1204 FormatTooltip(hwndTooltip,
1205 pttd,
1206 &ptlPointer);
1207 WinInvalidateRect(hwndTooltip, NULL, FALSE);
1208 }
1209 }
1210}
1211
1212/*
1213 *@@ TtmShowTooltip:
1214 * implementation for TTM_SHOW_TOOLTIP.
1215 *
1216 * Depending on fShow, this shows or hides the
1217 * tool tip at the position specified by the
1218 * current tool or the mouse pointer (specified
1219 * by the tool's style flags).
1220 *
1221 *@@added V0.9.1 (2000-02-04) [umoeller]
1222 */
1223
1224STATIC VOID TtmShowTooltip(HWND hwndTooltip,
1225 BOOL fShow) // if TRUE: show, else: HIDE
1226{
1227 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
1228 if (fShow)
1229 {
1230 /*
1231 * show tooltip::
1232 *
1233 */
1234
1235 POINTL ptlPointer;
1236 // HPS hps;
1237
1238 // free old text
1239 if (pttd->pszPaintText)
1240 {
1241 free(pttd->pszPaintText);
1242 pttd->pszPaintText = NULL;
1243 }
1244
1245 WinQueryPointerPos(HWND_DESKTOP, &ptlPointer);
1246
1247 if ( (ptlPointer.x == pttd->ptlPointerLast.x)
1248 && (ptlPointer.y == pttd->ptlPointerLast.y)
1249 )
1250 {
1251 // mouse not moved since timer was started:
1252 // find the current TOOLINFO
1253 if (pttd->ptiMouseOver)
1254 {
1255 TOOLINFO tiTemp;
1256 memcpy(&tiTemp, pttd->ptiMouseOver, sizeof(TOOLINFO));
1257 // get the text for the TOOLINFO
1258 WinSendMsg(hwndTooltip,
1259 TTM_GETTEXT,
1260 (MPARAM)0,
1261 (MPARAM)&tiTemp);
1262 if (tiTemp.pszText)
1263 pttd->pszPaintText = strdup(tiTemp.pszText);
1264 else
1265 pttd->pszPaintText = NULL;
1266 }
1267
1268 if (pttd->pszPaintText)
1269 {
1270 FormatTooltip(hwndTooltip,
1271 pttd,
1272 &ptlPointer);
1273
1274 // notify owner (TTN_SHOW)
1275 WinSendMsg(pttd->hwndOwner,
1276 WM_CONTROL,
1277 MPFROM2SHORT(pttd->usTooltipID, // tooltip control wnd ID
1278 TTN_SHOW),
1279 pttd->ptiMouseOver);
1280
1281 WinShowWindow(hwndTooltip, TRUE);
1282 pttd->fIsVisible = TRUE;
1283
1284 // start autopop timer
1285 pttd->idTimerAutopop = WinStartTimer(pttd->hab,
1286 hwndTooltip,
1287 TOOLTIP_ID_TIMER_AUTOPOP,
1288 pttd->ulTimeoutAutopop);
1289 } // end if (pttd->pszPaintText)
1290 } // end if ( (ptlPointer.x == pttd->ptlPointerLast.x)...
1291 } // end if (mp1)
1292 else
1293 {
1294 /*
1295 * hide tooltip::
1296 *
1297 */
1298
1299 if (pttd->fIsVisible)
1300 {
1301 // notify owner (TTN_POP)
1302 WinSendMsg(pttd->hwndOwner,
1303 WM_CONTROL,
1304 MPFROM2SHORT(pttd->usTooltipID, // tooltip control wnd ID
1305 TTN_POP),
1306 pttd->ptiMouseOver);
1307 WinShowWindow(hwndTooltip, FALSE);
1308 }
1309
1310 // stop autopop timer
1311 if (pttd->idTimerAutopop)
1312 {
1313 WinStopTimer(pttd->hab,
1314 hwndTooltip,
1315 TOOLTIP_ID_TIMER_AUTOPOP);
1316 pttd->idTimerAutopop = 0;
1317 }
1318 }
1319
1320 // store new visibility
1321 pttd->fIsVisible = fShow;
1322}
1323
1324/*
1325 *@@ ctl_fnwpTooltip:
1326 * window procedure for the "tooltip" control. This control is
1327 * largely source-code compatible to the Win32 version and has
1328 * been modelled according to the Win32 programmer's reference.
1329 *
1330 * A tooltip control is a small pop-up window that displays a
1331 * single line of descriptive text giving the purpose of "tools"
1332 * in an application.
1333 * A "tool" is either a window, such as a child window or control,
1334 * or an application-defined rectangular area within a window.
1335 * See the TTM_ADDTOOL message for details.
1336 *
1337 * To clarify: There is usually one tooltip control, which is hidden
1338 * most of the time, for many "tools" (parts of a visible window).
1339 * When the user puts the cursor on a tool and leaves it there for
1340 * approximately one-half second, the tooltip control is set up for
1341 * that tool and made visible. The tooltip control appears near the
1342 * pointer and disappears when the user clicks a mouse button or moves
1343 * the pointer off of the tool.
1344 *
1345 * The Win32 concept has been extended with this implementation to
1346 * display even longer texts, including line breaks. The tooltip
1347 * window is formatted accordingly.
1348 *
1349 * All default window styles are ignored when you create the tooltip,
1350 * but will rather be set by this window proc automatically. The
1351 * only valid window styles are the TTS_* flags (see notes below).
1352 *
1353 * Presentation parameters are fully supported.
1354 *
1355 * To create a tooltip control using comctl.c, use
1356 + ctlRegisterTooltip(hab);
1357 + hwndTooltip = WinCreateWindow(HWND_DESKTOP, // parent
1358 + WC_CCTL_TOOLTIP, // wnd class (comctl.h)
1359 + NULL, // window text
1360 + TTS_ALWAYSTIP, // window style, ignored except for TTS_* flags
1361 + 0, 0, 0, 0, // window pos and size, ignored
1362 + hwndOwner, // owner window -- important!
1363 + NULLHANDLE, // hwndInsertBehind, ignored
1364 + ulID, // window ID, optional
1365 + NULL, // control data
1366 + NULL); // presparams
1367 +
1368 * ctl_fnwpTooltip automatically sets the size, position, and visibility
1369 * of the tooltip control. The size of the tooltip window will vary
1370 * depending on the text to be displayed and on the font presentation
1371 * parameters, which are supported by this control (OS/2 only).
1372 *
1373 * Note: OS/2 normally does not destroy windows which are only owned by
1374 * another window. As a result, when the tooltip's owner is destroyed,
1375 * you must explicitly also destroy the tooltip window.
1376 *
1377 * Under both Win32 and OS/2, a tooltip control has two class-specific styles:
1378 *
1379 * -- TTS_ALWAYSTIP: the tooltip appears when the cursor is on a tool,
1380 * regardless of whether the tooltip control's owner window is active
1381 * or inactive. Without this style, the tooltip control appears when the
1382 * tool's owner window is active, but not when it is inactive.
1383 *
1384 * -- TTS_NOPREFIX: this prevents the system from stripping the ampersand (&)
1385 * character from a string. If a tooltip control does not have the TTS_NOPREFIX
1386 * style, the system automatically strips ampersand characters, allowing an
1387 * application to use the same string as both a menu item and tooltip text.
1388 *
1389 * The tooltip control can be (de)activated using the TTM_ACTIVATE message.
1390 *
1391 * To register tools with the tooltip control (to make it do anything
1392 * meaningful), send the TTM_ADDTOOL message to the tooltip control.
1393 *
1394 * This control supports the following presentation parameters (if the
1395 * PP_* value is not found, the SYSCLR_* system color is used):
1396 * -- PP_MENUBACKGROUNDCOLOR / SYSCLR_ENTRYFIELD: tooltip background color.
1397 * -- PP_MENUFOREGROUNDCOLOR / SYSCLR_WINDOWTEXT: tooltip text color.
1398 * -- PP_MENUHILITEFGNDCOLOR / SYSCLR_WINDOWTEXT: 3D border light color.
1399 * -- PP_MENUHILITEBGNDCOLOR / SYSCLR_WINDOWTEXT: 3D border dark color.
1400 * -- PP_FONTNAMESIZE: font to use for the tooltip. The default is "8.Helv"
1401 * on Warp 3 and "9.WarpSans" on Warp 4.
1402 *
1403 * So per default, if no presentation parameters are set, the control
1404 * paints the background like an entry field and the text and the border
1405 * in the same window text color (usually black).
1406 * The tooltip does _not_ inherit presentation parameters from the parent,
1407 * so unless you explicitly set presparams, the tooltip looks pretty much
1408 * like the Win32 counterpart (with the default system colors, black border
1409 * and text on yellow background).
1410 *
1411 *@@added V0.9.0 [umoeller]
1412 *@@changed V0.9.12 (2001-04-28) [umoeller]: various fixes WRT subclassing
1413 */
1414
1415MRESULT EXPENTRY ctl_fnwpTooltip(HWND hwndTooltip, ULONG msg, MPARAM mp1, MPARAM mp2)
1416{
1417 MRESULT mrc = 0;
1418
1419 TRY_LOUD(excpt1)
1420 {
1421 switch (msg)
1422 {
1423 /*
1424 * WM_CREATE:
1425 * this message is sent to the window procedure of
1426 * the window being created, thus offering it an
1427 * opportunity to initialize that window.
1428 *
1429 * The window procedure receives this after the window
1430 * is created but before the window becomes visible.
1431 */
1432
1433 case WM_CREATE:
1434 mrc = TtmCreate(hwndTooltip, mp2);
1435 break;
1436
1437 /*
1438 * WM_HITTEST:
1439 * since we have the CS_HITTEST class style set,
1440 * we get this message. We return HT_TRANSPARENT
1441 * always so the tooltip does not block mouse clicks
1442 * for the windows which lie below.
1443 */
1444
1445 case WM_HITTEST: // done
1446 mrc = (MPARAM)HT_TRANSPARENT;
1447 break;
1448
1449 /*
1450 * WM_TIMER:
1451 * posted when either the "initial" timer
1452 * or the "autopop" timer times out.
1453 * We'll show or hide the tooltip then.
1454 */
1455
1456 case WM_TIMER: // done
1457 if (!TtmTimer(hwndTooltip, mp1))
1458 mrc = WinDefWindowProc(hwndTooltip, msg, mp1, mp2);
1459 break;
1460
1461 /*
1462 * WM_PAINT:
1463 *
1464 */
1465
1466 case WM_PAINT: // done
1467 TtmPaint(hwndTooltip);
1468 break;
1469
1470 /*
1471 * WM_PRESPARAMCHANGED:
1472 * presentation parameter has changed.
1473 */
1474
1475 case WM_PRESPARAMCHANGED:
1476
1477 switch ((LONG)mp1) // pp index
1478 {
1479 case 0: // layout palette thing dropped
1480 case PP_MENUBACKGROUNDCOLOR:
1481 case PP_MENUFOREGROUNDCOLOR:
1482 case PP_MENUHILITEFGNDCOLOR:
1483 case PP_MENUHILITEBGNDCOLOR:
1484 // re-query our presparams
1485 UpdateTooltipPresColors(hwndTooltip);
1486 }
1487
1488 break;
1489
1490 /*
1491 * WM_SYSCOLORCHANGE:
1492 * system color has changed.
1493 */
1494
1495 case WM_SYSCOLORCHANGE:
1496 UpdateTooltipPresColors(hwndTooltip);
1497 break;
1498
1499 /*
1500 * WM_DESTROY:
1501 * clean up upon destruction.
1502 */
1503
1504 case WM_DESTROY:
1505 TtmDestroy(hwndTooltip);
1506 break;
1507
1508 /*
1509 *@@ TTM_ACTIVATE:
1510 * activates or deactives the tooltip control.
1511 *
1512 * Parameters:
1513 * -- BOOL mp1: if TRUE, activate, if FALSE, deactivate.
1514 * -- mp2: always 0.
1515 *
1516 * Return value: 0 always.
1517 */
1518
1519 case TTM_ACTIVATE: // done
1520 {
1521 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
1522 if (!(pttd->fIsActive = (BOOL)mp1))
1523 // disable: hide tooltip
1524 WinPostMsg(hwndTooltip, TTM_SHOWTOOLTIPNOW, (MPARAM)FALSE, 0);
1525 }
1526 break;
1527
1528 /*
1529 *@@ TTM_ADDTOOL:
1530 * registers a tool with a tooltip control.
1531 *
1532 * Parameters:
1533 * -- mp1: always 0.
1534 * -- PTOOLINFO mp2: information about the tool.
1535 *
1536 * Return value: TRUE if successful or FALSE otherwise.
1537 *
1538 * A tooltip control can support any number of tools. To
1539 * support a particular tool, you must register the tool
1540 * with the tooltip control by sending the TTM_ADDTOOL
1541 * message to the tooltip control. The message includes
1542 * the address of a TOOLINFO structure, which provides
1543 * information the tooltip control needs to display text
1544 * for the tool.
1545 *
1546 * A tooltip control supports tools implemented as windows
1547 * (such as child windows or control windows) and as
1548 * rectangular areas within a window's client area.
1549 *
1550 * -- When you add a tool implemented as a rectangular
1551 * area, the "hwndToolOwner" member of TOOLINFO must
1552 * specify the handle of the window that contains the
1553 * area, and the "rect" member must specify the client
1554 * coordinates of the area's bounding rectangle.
1555 *
1556 * -- When you add a tool implemented as a window, the
1557 * "hwndTool" member of TOOLINFO must contain the
1558 * window handle of the tool. hwndToolOwner should be
1559 * the owner of the tool.
1560 *
1561 * When you add a tool to a tooltip control, the "pszText"
1562 * member of the TOOLINFO structure must specify the string
1563 * to display for the tool. You can change the text any time
1564 * after adding the tool by using the TTM_UPDATETIPTEXT message.
1565 *
1566 * If you specify the PSZ_TEXTCALLBACK value in the pszText
1567 * member, whenever the tooltip control needs the text for the
1568 * tool, it sends a WM_CONTROL message to hwndToolOwner with
1569 * the TTN_NEEDTEXT notification code.
1570 *
1571 * The message includes the address of a TOOLTIPTEXT structure,
1572 * which contains the window handle of the tool. You can then
1573 * fill the TOOLTIPTEXT structure with the tool text.
1574 *
1575 * To retrieve the text for a tool, use the TTM_GETTEXT message.
1576 *
1577 *@@todo: add tool rectangles
1578 */
1579
1580 case TTM_ADDTOOL: // done
1581 mrc = TtmAddTool(hwndTooltip, mp2);
1582 break;
1583
1584 /*
1585 *@@ TTM_DELTOOL:
1586 * removes a tool from a tooltip control.
1587 *
1588 * Parameters:
1589 * -- mp1: always 0.
1590 * -- PTOOLINFO mp2: information about the tool;
1591 * all fields except cbSize, hwndToolOwner,
1592 * and hwndTool are ignored.
1593 *
1594 * Return value: 0 always.
1595 */
1596
1597 case TTM_DELTOOL: // done
1598 TtmDelTool(hwndTooltip, mp2);
1599 break;
1600
1601 /*
1602 *@@ TTM_NEWTOOLRECT:
1603 * sets a new bounding rectangle for a tool
1604 * already registered with a tooltip control.
1605 *
1606 * Parameters:
1607 * -- mp1: always 0.
1608 * -- PTOOLINFO mp2: information about the tool;
1609 * all fields except hwnd, uID, and rect
1610 * are ignored.
1611 *
1612 * Return value: 0 always.
1613 *
1614 * If an application includes a tool implemented as a rectangular
1615 * area and the size or position of the control changes, it can
1616 * use the TTM_NEWTOOLRECT message to report the change to the
1617 * tooltip control. An application does not need to report size
1618 * and position changes for a tool implemented as a window.
1619 * Reporting that is not necessary because the tooltip control
1620 * uses the window handle of a tool to determine if the cursor
1621 * is on the tool, not the tool's bounding rectangle.
1622 */
1623
1624 case TTM_NEWTOOLRECT:
1625
1626 break;
1627
1628 /*
1629 *@@ TTM_RELAYEVENT:
1630 * passes a mouse message to a tooltip control
1631 * for further processing.
1632 *
1633 * Parameters:
1634 * -- mp1: always 0.
1635 * -- PQMSG mp2: pointer to a QMSG struct (Win95: MSG struct)
1636 * containing a mouse message. Only the above messages
1637 * are processed.
1638 *
1639 * Return value: 0 always.
1640 *
1641 * A tooltip control needs to receive mouse messages to determine
1642 * when to display the tooltip window. Because mouse messages are
1643 * sent only to the window that contains the cursor, you must
1644 * use the TTM_RELAYEVENT message to relay mouse messages to the
1645 * tooltip control.
1646 *
1647 * If a tool is implemented as a rectangular area in an
1648 * application-defined window, the window procedure receives all
1649 * mouse messages and can relay the ones listed below to the
1650 * tooltip control, using this message.
1651 *
1652 * However, if a tool is implemented as a system-defined window,
1653 * the mouse messages are sent to that window and are not readily
1654 * available to the application. There are two ways you can have
1655 * the tooltip control notified of these mouse messages:
1656 *
1657 * -- You can specify the TTF_SUBCLASS flag when adding the tool
1658 * to the tooltip control. The tooltip control will then subclass
1659 * the tool window to get mouse-related messages, and you're off.
1660 *
1661 * -- You can implement a message hook for your process and check
1662 * for your tool windows there and send TTM_RELAYEVENT to the
1663 * tooltip control. In that case, you need to intercept the
1664 * messages specified below.
1665 *
1666 * When a tooltip control receives a relayed WM_MOUSEMOVE message,
1667 * it determines whether the cursor is in the bounding rectangle of
1668 * a tool. If the cursor is there, the tooltip control sets a timer.
1669 * At the end of the time-out duration, the tooltip control checks
1670 * the position of the cursor to see whether it has moved. If the
1671 * cursor has not, the tooltip control retrieves the text for the tool,
1672 * copies the text into the tooltip window, and shows the window.
1673 * The tooltip control continues to show the window until it receives
1674 * a relayed button-up or button-down message or until a relayed
1675 * WM_MOUSEMOVE message indicates that the cursor has moved outside
1676 * the bounding rectangle of the tool.
1677 *
1678 * For the timer descriptions, see TTM_SETDELAYTIME.
1679 *
1680 * When it is about to be displayed, a tootip control sends the
1681 * TTN_SHOW notification to the owner window. A tooltip control
1682 * sends the TTN_POP notification when it is about to be hidden.
1683 * Each notification is sent in the context of a WM_NOTIFY message
1684 * (OS/2: WM_CONTROL).
1685 *
1686 * Relevant messages:
1687 * -- WM_MOUSEMOVE
1688 * -- WM_BUTTON1DOWN (Win95: WM_LBUTTONDOWN)
1689 * -- WM_BUTTON1UP (Win95: WM_LBUTTONUP)
1690 * -- WM_BUTTON2DOWN (Win95: WM_RBUTTONDOWN)
1691 * -- WM_BUTTON2UP (Win95: WM_RBUTTONUP)
1692 * -- WM_BUTTON3DOWN (Win95: WM_MBUTTONDOWN)
1693 * -- WM_BUTTON3UP (Win95: WM_MBUTTONUP)
1694 */
1695
1696 case TTM_RELAYEVENT:
1697 TtmRelayEvent(hwndTooltip, mp2);
1698 break;
1699
1700 /*
1701 *@@ TTM_GETDELAYTIME:
1702 * returns the current value of the specified
1703 * timeout value. See TTM_SETDELAYTIME.
1704 *
1705 * Parameters:
1706 *
1707 * -- USHORT mp1: timer value to query. One of:
1708 * -- TTDT_AUTOPOP
1709 * -- TTDT_INITIAL
1710 * -- TTDT_RESHOW
1711 *
1712 * Returns: ULONG timeout value.
1713 *
1714 *@@added V0.9.12 (2001-04-28) [umoeller]
1715 */
1716
1717 case TTM_GETDELAYTIME:
1718 mrc = TtmGetDelayTime(hwndTooltip, mp1);
1719 break;
1720
1721 /*
1722 *@@ TTM_SETDELAYTIME:
1723 * overrides a few default timeout values for the
1724 * tooltip control.
1725 *
1726 * A tooltip control actually has three time-out durations
1727 * associated with it. The initial duration is the length
1728 * of time that the cursor must remain stationary within
1729 * the bounding rectangle of a tool before the tooltip window
1730 * is displayed. The reshow duration is the length of the delay
1731 * before subsequent tooltip windows are displayed when the
1732 * cursor moves from one tool to another. The autopopup duration
1733 * is the length of time that the tooltip window remains
1734 * displayed before it is hidden. That is, if the cursor remains
1735 * stationary within the bounding rectangle after the tooltip
1736 * window is displayed, the tooltip window is automatically
1737 * hidden at the end of the autopopup duration.
1738 *
1739 * You can adjust all of the time-out durations by using this
1740 * message.
1741 *
1742 * Parameters:
1743 * -- USHORT mp1: parameter selection. One of the following:
1744 * -- TTDT_AUTOMATIC: automatically calculates the initial,
1745 * reshow, and autopopup durations based on the value of mp2.
1746 * -- TTDT_AUTOPOP: sets the length of time before the tooltip
1747 * window is hidden if the cursor remains stationary
1748 * in the tool's bounding rectangle after the tooltip window
1749 * has appeared.
1750 * -- TTDT_INITIAL: sets the length of time that the cursor must
1751 * remain stationary within the bounding rectangle of
1752 * a tool before the tooltip window is displayed.
1753 * -- TTDT_RESHOW: sets the length of the delay before subsequent
1754 * tooltip windows are displayed when the cursor is moved
1755 * from one tool to another.
1756 * -- ULONG mp2: new duration, in milliseconds.
1757 *
1758 * Return value: 0 always.
1759 */
1760
1761 case TTM_SETDELAYTIME: // done
1762 TtmSetDelayTime(hwndTooltip, mp1, mp2);
1763 break;
1764
1765 /*
1766 *@@ TTM_GETTEXT:
1767 * retrieves the text for a tool in the tooltip control.
1768 *
1769 * Parameters:
1770 * -- mp1: always 0
1771 * -- PTOOLINFO mp2: pointer to a TOOLINFO structure.
1772 * hwndTool identifies a tool. If the tooltip control
1773 * includes the tool, the pszText member receives
1774 * the pointer to the string on output.
1775 *
1776 * Return value: 0 always.
1777 *
1778 * Additional note: On input, if TOOLINFO.lpszText == PSZ_TEXTCALLBACK,
1779 * this sends the TTN_NEEDTEXT notification to TOOLINFO.hwnd.
1780 *
1781 */
1782
1783 case TTM_GETTEXT: // done, I think
1784 TtmGetText(hwndTooltip, mp2);
1785 break;
1786
1787 /*
1788 *@@ TTM_UPDATETIPTEXT:
1789 * sets the current tooltip text explicitly,
1790 * no matter for what tool the tooltip is
1791 * currently being displayed. This might be
1792 * useful if you want to dynamically update
1793 * the tooltip display.
1794 *
1795 * If the tooltip is currently showing,
1796 * it is reformatted with the new text.
1797 *
1798 * Note that the change is not permanent;
1799 * if the mouse moves over a different
1800 * tool next, the change will be lost.
1801 *
1802 * Parameters:
1803 * -- PSZ mp1: new text to display.
1804 *
1805 *@@added V0.9.13 (2001-06-21) [umoeller]
1806 */
1807
1808 case TTM_UPDATETIPTEXT:
1809 TtmUpdateTipText(hwndTooltip, (PSZ)mp1);
1810 break;
1811
1812 /*
1813 *@@ TTM_HITTEST:
1814 * tests a point to determine whether it is within the
1815 * bounding rectangle of the specified tool and, if the
1816 * point is within, retrieves information about the tool.
1817 *
1818 * Parameters:
1819 * -- mp1: always 0.
1820 * -- PHITTESTINFO mp2: pointer to a TTHITTESTINFO structure.
1821 * When sending the message, the "hwnd" member must specify
1822 * the handle of a tool and the "pt" member must specify the
1823 * coordinates of a point. If the return value is TRUE, the
1824 * "ti" member receives information about the tool that
1825 * occupies the point.
1826 *
1827 * Return value: returns TRUE if the tool occupies the specified
1828 * point or FALSE otherwise.
1829 */
1830
1831 case TTM_HITTEST:
1832 break;
1833
1834 /*
1835 *@@ TTM_WINDOWFROMPOINT:
1836 * this message allows a subclass procedure to cause a tooltip
1837 * to display text for a window other than the one beneath the
1838 * mouse cursor.
1839 *
1840 * Parameters:
1841 * -- mp1: always 0.
1842 * -- mp2: PPOINTL lppt (Win95: (POINT FAR *)lppt):
1843 * pointer to a POINTL structure that defines the point to be checked.
1844 *
1845 * Return value: the handle to the window that contains the point, or
1846 * NULL if no window exists at the specified point.
1847 *
1848 * This message is intended to be processed by an application that
1849 * subclasses a tooltip. It is not intended to be sent by an
1850 * application. A tooltip sends this message to itself before
1851 * displaying the text for a window. By changing the coordinates
1852 * of the point specified by lppt, the subclass procedure can cause
1853 * the tooltip to display text for a window other than the one
1854 * beneath the mouse cursor.
1855 */
1856
1857 case TTM_WINDOWFROMPOINT:
1858 break;
1859
1860 /*
1861 *@@ TTM_ENUMTOOLS:
1862 * this message retrieves the information that a tooltip control
1863 * maintains about a certain tool.
1864 *
1865 * Parameters:
1866 * -- USHORT mp1: zero-based index of the tool for which to
1867 * retrieve information.
1868 * -- PTOOLINFO mp2: pointer to a TOOLINFO structure that
1869 * receives information about the tool. Before sending
1870 * this message, the cbSize member must specify the size
1871 * of the structure.
1872 *
1873 * Return value: TRUE if any tools are enumerated or FALSE otherwise.
1874 */
1875
1876 case TTM_ENUMTOOLS: // done
1877 mrc = TtmEnumTools(hwndTooltip, mp1, mp2);
1878 break;
1879
1880 /*
1881 *@@ TTM_GETCURRENTTOOL:
1882 * this message retrieves the information that the
1883 * tooltip control maintains for the _current_ tool;
1884 * that is, the one for which the tooltip control
1885 * is currently displaying text.
1886 *
1887 * Parameters:
1888 * -- mp1: always 0.
1889 * -- PTOOLINFO mp2: pointer to a TOOLINFO structure that receives
1890 * information about the current tool.
1891 *
1892 * Return value: TRUE if successful or FALSE otherwise.
1893 */
1894
1895 case TTM_GETCURRENTTOOL: // done
1896 mrc = TtmGetCurrentTool(hwndTooltip, mp2);
1897 break;
1898
1899 /*
1900 *@@ TTM_GETTOOLCOUNT:
1901 * returns the number of tools in the tooltip control.
1902 *
1903 * No parameters.
1904 */
1905
1906 case TTM_GETTOOLCOUNT: // done
1907 {
1908 PTOOLTIPDATA pttd = (PTOOLTIPDATA)WinQueryWindowPtr(hwndTooltip, 1);
1909 mrc = (MPARAM)lstCountItems(&pttd->llTools);
1910 }
1911 break;
1912
1913 /*
1914 *@@ TTM_GETTOOLINFO:
1915 * this message retrieves the information that a tooltip
1916 * control maintains about a tool.
1917 *
1918 * Parameters:
1919 * -- mp1: always 0.
1920 * -- PTOOLINFO mp2:
1921 * pointer to a TOOLINFO structure. When sending the message,
1922 * the hwnd and uId members identify a tool, and the cbSize
1923 * member must specify the size of the structure. If the
1924 * tooltip control includes the tool, the structure receives
1925 * information about the tool.
1926 *
1927 * Return value: TRUE if successful or FALSE otherwise.
1928 */
1929
1930 case TTM_GETTOOLINFO: // done
1931 mrc = TtmGetToolInfo(hwndTooltip, mp2);
1932 break;
1933
1934 /*
1935 *@@ TTM_SETTOOLINFO:
1936 * this message sets the information that a tooltip control
1937 * maintains for a tool.
1938 *
1939 * Parameters:
1940 * -- mp1: always 0.
1941 * -- PTOOLINFO mp2: pointer to a TOOLINFO structure that
1942 * specifies the information to set.
1943 *
1944 * Return value: 0 always.
1945 */
1946
1947 case TTM_SETTOOLINFO:
1948 break;
1949
1950 /*
1951 *@@ TTM_SHOWTOOLTIPNOW:
1952 * depending on BOOL mp1, shows or hides the tooltip.
1953 * This is required because we cannot show or hide
1954 * the window during processing of WM_MOUSEMOVE etc,
1955 * which will lead to strange PM hangs.
1956 *
1957 * This is not part of the Win95 message set but used
1958 * in the OS/2 implementation only. This calls
1959 * TtmShowTooltip in turn.
1960 */
1961
1962 case TTM_SHOWTOOLTIPNOW:
1963 TtmShowTooltip(hwndTooltip, (BOOL)mp1);
1964 break;
1965
1966 default:
1967 mrc = WinDefWindowProc(hwndTooltip, msg, mp1, mp2);
1968 }
1969 }
1970 CATCH(excpt1)
1971 {
1972 mrc = 0; // XWP V1.0.4 (2005-10-09) [pr]
1973 }
1974 END_CATCH();
1975
1976 return mrc;
1977}
1978
Note: See TracBrowser for help on using the repository browser.