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

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

Bart's fix for bug 676

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