source: trunk/src/helpers/cctl_splitwin.c@ 229

Last change on this file since 229 was 229, checked in by umoeller, 23 years ago

Sources as of 1.0.0.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 37.2 KB
Line 
1
2/*
3 *@@sourcefile cctl.splitwin.c:
4 * implementation for split windows.
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-2000 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\Split windows
83 * See cctl_splitwin.c.
84 */
85
86/* ******************************************************************
87 *
88 * Split windows
89 *
90 ********************************************************************/
91
92/*
93 *@@ PaintSplitWindow:
94 * implementation for WM_PAINT in ctl_fnwpSplitWindow.
95 *
96 *@@added V0.9.1 (2000-02-05) [umoeller]
97 *@@changed V1.0.0 (2002-08-24) [umoeller]: added SBCF_3DEXPLORERSTYLE
98 */
99
100STATIC VOID PaintSplitWindow(HWND hwndSplit)
101{
102 HPS hps;
103
104 if (hps = WinBeginPaint(hwndSplit, (HPS)0, NULL))
105 {
106 HWND hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
107 PSPLITBARDATA pData = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar,
108 QWL_USER);
109
110 if ((pData->hwndLinked1) && (pData->hwndLinked2))
111 {
112 SWP swp;
113
114 // switch to RGB mode
115 gpihSwitchToRGB(hps);
116
117 // "3D-sunk" style?
118 if (pData->sbcd.ulCreateFlags & SBCF_3DEXPLORERSTYLE)
119 {
120 // this style is new with V1.0.0 (2002-08-24) [umoeller];
121 // simulate the Warp 4 entry field margins around the
122 // right control only, but leave the left control flat.
123 RECTL rcl;
124 WinQueryWindowPos(pData->hwndLinked2, &swp);
125 rcl.xLeft = swp.x - 2;
126 rcl.yBottom = swp.y - 2;
127 rcl.xRight = swp.x + swp.cx + 1;
128 rcl.yTop = swp.y + swp.cy + 1;
129 gpihDraw3DFrame2(hps,
130 &rcl,
131 1,
132 RGBCOL_BLACK,
133 pData->lcol3DLight);
134 gpihDraw3DFrame2(hps,
135 &rcl,
136 1,
137 RGBCOL_BLACK,
138 pData->lcolInactiveBorder);
139 }
140 else if (pData->sbcd.ulCreateFlags & SBCF_3DSUNK)
141 {
142 // yes: draw sunk frame around split windows
143 POINTL ptl1;
144
145 WinQueryWindowPos(pData->hwndLinked1, &swp);
146 GpiSetColor(hps, pData->lcol3DDark);
147 ptl1.x = swp.x - 1;
148 ptl1.y = swp.y - 1;
149 GpiMove(hps, &ptl1);
150 ptl1.y = swp.y + swp.cy;
151 GpiLine(hps, &ptl1);
152 ptl1.x = swp.x + swp.cx;
153 GpiLine(hps, &ptl1);
154 GpiSetColor(hps, pData->lcol3DLight);
155 ptl1.y = swp.y - 1;
156 GpiLine(hps, &ptl1);
157 ptl1.x = swp.x - 1;
158 GpiLine(hps, &ptl1);
159
160 WinQueryWindowPos(pData->hwndLinked2, &swp);
161 GpiSetColor(hps, pData->lcol3DDark);
162 ptl1.x = swp.x - 1;
163 ptl1.y = swp.y - 1;
164 GpiMove(hps, &ptl1);
165 ptl1.y = swp.y + swp.cy;
166 GpiLine(hps, &ptl1);
167 ptl1.x = swp.x + swp.cx;
168 GpiLine(hps, &ptl1);
169 GpiSetColor(hps, pData->lcol3DLight);
170 ptl1.y = swp.y - 1;
171 GpiLine(hps, &ptl1);
172 ptl1.x = swp.x - 1;
173 GpiLine(hps, &ptl1);
174 }
175 }
176
177 WinEndPaint(hps);
178 }
179}
180
181/*
182 *@@ ctl_fnwpSplitWindow:
183 * this is the window procedure of the "invisible"
184 * split window. One of these windows is created
185 * for each split view and has exactly three children:
186 *
187 * 1) the split bar (ctl_fnwpSplitBar);
188 *
189 * 2) the left or bottom window linked to the split bar;
190 *
191 * 3) the right or top window linked to the split bar.
192 *
193 * See ctlCreateSplitWindow for more info and a detailed
194 * window hierarchy.
195 *
196 * This stand-alone window procedure must be registered.
197 * ctlCreateSplitWindow does this for you.
198 *
199 *@@added V0.9.0 [umoeller]
200 */
201
202MRESULT EXPENTRY ctl_fnwpSplitWindow(HWND hwndSplit, ULONG msg, MPARAM mp1, MPARAM mp2)
203{
204 MRESULT mrc = (MRESULT)0;
205
206 switch(msg)
207 {
208 /*
209 * WM_WINDOWPOSCHANGED:
210 *
211 */
212
213 case WM_WINDOWPOSCHANGED:
214 // this msg is passed two SWP structs:
215 // one for the old, one for the new data
216 // (from PM docs)
217 if (((PSWP)mp1)->fl & SWP_SIZE)
218 // _Pmpf(("ctl_fnwpSplitWindow, WM_WINDOWPOSCHANGED"));
219 ctlUpdateSplitWindow(hwndSplit);
220
221 mrc = WinDefWindowProc(hwndSplit, msg, mp1, mp2);
222 break;
223
224 /*
225 * WM_PAINT:
226 *
227 */
228
229 case WM_PAINT:
230 PaintSplitWindow(hwndSplit);
231 break;
232
233 /*
234 *@@ SPLM_SETLINKS:
235 * this specifies the windows which the
236 * split window will link. This updates
237 * the internal SPLITBARDATA and changes
238 * the parents of the two subwindows to
239 * the split window.
240 *
241 * Parameters:
242 * -- HWND mp1: left or bottom subwindow
243 * -- HWND mp2: right or top subwindow
244 */
245
246 case SPLM_SETLINKS:
247 {
248 HWND hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
249 PSPLITBARDATA pData;
250 if (pData = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar,
251 QWL_USER))
252 {
253 pData->hwndLinked1 = (HWND)mp1;
254 pData->hwndLinked2 = (HWND)mp2;
255 // change parents of the windows to link
256 WinSetParent(pData->hwndLinked1, hwndSplit,
257 FALSE); // no redraw
258 WinSetParent(pData->hwndLinked2, hwndSplit,
259 FALSE); // no redraw
260 }
261 }
262 break;
263
264 default:
265 mrc = WinDefWindowProc(hwndSplit, msg, mp1, mp2);
266 }
267
268 return mrc;
269}
270
271/*
272 *@@ TrackSplitBar:
273 * implementation for WM_BUTTON1DOWN/WM_BUTTON2DOWN in ctl_fnwpSplitBar.
274 *
275 *@@added V0.9.1 (2000-02-05) [umoeller]
276 */
277
278STATIC VOID TrackSplitBar(HWND hwndBar,
279 PSPLITBARDATA pData)
280{
281 TRACKINFO track;
282 RECTL rclBar;
283
284 track.cxBorder = 2;
285 track.cyBorder = 2;
286 track.cxGrid = 1;
287 track.cyGrid = 1;
288 track.cxKeyboard = 8;
289 track.cyKeyboard = 8;
290
291 WinQueryWindowRect(hwndBar, &rclBar);
292 WinMapWindowPoints(hwndBar,
293 HWND_DESKTOP, // map to screen coords.
294 (PPOINTL)&rclBar,
295 2); // 2 points == rectangle
296
297 // limit tracking space to parent window;
298 // this is either the client or another split window
299 WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
300 &track.rclBoundary);
301 WinMapWindowPoints(pData->sbcd.hwndParentAndOwner,
302 HWND_DESKTOP, // map to screen coords.
303 (PPOINTL)&track.rclBoundary,
304 2); // 2 points, since we have a RECTL
305
306 // decrease tracking limits by what was
307 // specified by the caller
308 if (pData->sbcd.ulCreateFlags & SBCF_HORIZONTAL)
309 {
310 // horizontal split bar
311 track.rclBoundary.yBottom += pData->sbcd.ulLeftOrBottomLimit;
312 track.rclBoundary.yTop -= pData->sbcd.ulRightOrTopLimit;
313 track.rclBoundary.xLeft = rclBar.xLeft;
314 track.rclBoundary.xRight = rclBar.xRight;
315 }
316 else
317 {
318 // vertical split bar
319 track.rclBoundary.xLeft += pData->sbcd.ulLeftOrBottomLimit;
320 track.rclBoundary.xRight -= pData->sbcd.ulRightOrTopLimit;
321 track.rclBoundary.yBottom = rclBar.yBottom;
322 track.rclBoundary.yTop = rclBar.yTop;
323 }
324
325 // initial tracking rectangle = split bar
326 memcpy(&track.rclTrack, &rclBar, sizeof(rclBar));
327
328 track.ptlMinTrackSize.x =
329 track.ptlMaxTrackSize.x = track.rclTrack.xRight
330 - track.rclTrack.xLeft; // width of status bar
331 track.ptlMinTrackSize.y =
332 track.ptlMaxTrackSize.y = track.rclTrack.yTop
333 - track.rclTrack.yBottom; // height of status bar
334
335 track.fs = TF_MOVE | TF_ALLINBOUNDARY;
336
337 // now do the tracking: this is a modal
338 // operation and returns only after the
339 // mouse has been released
340 if (WinTrackRect(HWND_DESKTOP, 0, &track))
341 {
342 // OK, successfully moved: reposition the
343 // windows which are linked to the split bar
344
345 if (pData->sbcd.ulCreateFlags & SBCF_HORIZONTAL)
346 {
347 // horizontal split bar
348 ULONG ulNewYPos = track.rclTrack.yBottom
349 - track.rclBoundary.yBottom;
350 // add the limit specified by the caller
351 ulNewYPos += pData->sbcd.ulLeftOrBottomLimit;
352
353 if (pData->sbcd.ulCreateFlags & SBCF_PERCENTAGE)
354 {
355 // status bar pos is determined by
356 // a percentage:
357 // well, we'll need a new percentage then
358 RECTL rclClient;
359 WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
360 &rclClient);
361 pData->sbcd.lPos = ulNewYPos
362 * 100 / (rclClient.yTop - rclClient.yBottom);
363 }
364 else
365 // absolute split bar positioning:
366 if (pData->sbcd.lPos > 0)
367 // from bottom: easy
368 pData->sbcd.lPos = ulNewYPos;
369 else
370 {
371 // negative -> from top:
372 RECTL rclClient;
373 WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
374 &rclClient);
375 // calc new negative
376 pData->sbcd.lPos = ulNewYPos - rclClient.yTop;
377 }
378 }
379 else
380 {
381 // vertical split bar:
382 ULONG ulNewXPos = track.rclTrack.xLeft
383 - track.rclBoundary.xLeft;
384 // add the limit specified by the caller
385 ulNewXPos += pData->sbcd.ulLeftOrBottomLimit;
386
387 if (pData->sbcd.ulCreateFlags & SBCF_PERCENTAGE)
388 {
389 // status bar pos is determined by
390 // a percentage:
391 // well, we'll need a new percentage then
392 RECTL rclClient;
393 WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
394 &rclClient);
395 pData->sbcd.lPos = ulNewXPos
396 * 100 / (rclClient.xRight - rclClient.xLeft);
397 }
398 else
399 // absolute split bar positioning:
400 if (pData->sbcd.lPos > 0)
401 // from left: easy
402 pData->sbcd.lPos = ulNewXPos;
403 else
404 {
405 // negative -> from right:
406 RECTL rclClient;
407 WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
408 &rclClient);
409 // calc new negative
410 pData->sbcd.lPos = ulNewXPos - rclClient.xRight;
411 }
412 }
413
414 // update parent (== "split window")
415 ctlUpdateSplitWindow(WinQueryWindow(hwndBar, QW_PARENT));
416 }
417}
418
419/*
420 *@@ PaintSplitBar:
421 * implementation for WM_PAINT in ctl_fnwpSplitBar.
422 *
423 *@@added V0.9.1 (2000-02-05) [umoeller]
424 *@@changed V0.9.1 (2000-02-05) [umoeller]: fixed paint errors with sunken 3D style
425 */
426
427STATIC VOID PaintSplitBar(HWND hwndBar,
428 PSPLITBARDATA pData)
429{
430 HPS hps;
431 RECTL rcl,
432 rclBar;
433 POINTL ptl1;
434 if (hps = WinBeginPaint(hwndBar, NULLHANDLE, &rcl))
435 {
436 WinQueryWindowRect(hwndBar, &rclBar);
437 // switch to RGB mode
438 GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
439
440 WinFillRect(hps,
441 &rclBar,
442 pData->lcolInactiveBorder);
443
444 if (!(pData->sbcd.ulCreateFlags & (SBCF_3DSUNK | SBCF_3DEXPLORERSTYLE)))
445 // V1.0.0 (2002-08-31) [umoeller]
446 {
447 GpiSetColor(hps, pData->lcol3DLight);
448 // draw left border (bottom to up)
449 ptl1.x = 0;
450 ptl1.y = 0;
451 GpiMove(hps, &ptl1);
452 ptl1.y = (rclBar.yTop - rclBar.yBottom) - 1;
453 if (pData->sbcd.ulCreateFlags & SBCF_VERTICAL)
454 // vertical:
455 GpiLine(hps, &ptl1);
456 else
457 GpiMove(hps, &ptl1);
458
459 // draw top border (to right)
460 ptl1.x = (rclBar.xRight - rclBar.xLeft) - 1;
461 if (pData->sbcd.ulCreateFlags & SBCF_VERTICAL)
462 // vertical:
463 GpiMove(hps, &ptl1);
464 else
465 GpiLine(hps, &ptl1);
466
467 GpiSetColor(hps, pData->lcol3DDark);
468 // draw right border (to bottom)
469 ptl1.y = 0;
470 if (pData->sbcd.ulCreateFlags & SBCF_VERTICAL)
471 // vertical:
472 GpiLine(hps, &ptl1);
473 else
474 GpiMove(hps, &ptl1);
475
476 if (!(pData->sbcd.ulCreateFlags & SBCF_VERTICAL))
477 {
478 // horizontal:
479 // draw bottom border
480 ptl1.x = 0;
481 GpiLine(hps, &ptl1);
482 }
483 }
484
485 WinEndPaint(hps);
486 }
487}
488
489/*
490 *@@ ctl_fnwpSplitBar:
491 * window procedure for all split bars, horizontal and vertical.
492 * This paints the split bar and handles dragging the split bar
493 * and repositioning the "linked" windows afterwards.
494 *
495 * This is not a stand-alone window procedure, but must only
496 * be used with ctlCreateSplitWindow, which creates and subclasses
497 * a static control as necessary.
498 *
499 * See ctlCreateSplitWindow for more info and a detailed
500 * window hierarchy.
501 *
502 *@@added V0.9.0 [umoeller]
503 *@@changed V0.9.1 (99-12-07): fixed memory leak
504 *@@changed V0.9.9 (2001-02-01) [lafaix]: added MB2 drag
505 *@@changed V0.9.14 (2001-08-21) [umoeller]: SBCF_MOVEABLE was always ignored, fixed
506 */
507
508MRESULT EXPENTRY ctl_fnwpSplitBar(HWND hwndBar, ULONG msg, MPARAM mp1, MPARAM mp2)
509{
510 PSPLITBARDATA pData = (PSPLITBARDATA)WinQueryWindowULong(hwndBar, QWL_USER);
511
512 PFNWP OldStaticProc = NULL;
513
514 MRESULT mrc = NULL;
515
516 if (pData)
517 {
518 OldStaticProc = pData->OldStaticProc;
519
520 switch(msg)
521 {
522 /*
523 * WM_MOUSEMOVE:
524 * change mouse pointer when over split bar.
525 */
526
527 case WM_MOUSEMOVE:
528 if (pData->sbcd.ulCreateFlags & SBCF_MOVEABLE)
529 // V0.9.14 (2001-08-21) [umoeller]
530 // split bar is moveable:
531 pData->hptrOld = WinSetPointer(HWND_DESKTOP, pData->hptrMove);
532 else
533 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
534 break;
535
536 /*
537 * WM_BUTTON1DOWN:
538 * this will start moving the split bar.
539 * We use WinTrackRect for this.
540 */
541
542 case WM_BUTTON1DOWN:
543 case WM_BUTTON2DOWN:
544 if (pData->sbcd.ulCreateFlags & SBCF_MOVEABLE)
545 // V0.9.14 (2001-08-21) [umoeller]
546 // split bar is moveable:
547 TrackSplitBar(hwndBar, pData);
548 else
549 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
550 break;
551
552 /*
553 * WM_PAINT:
554 * paint the split bar
555 */
556
557 case WM_PAINT:
558 PaintSplitBar(hwndBar, pData);
559 break;
560
561 case WM_DESTROY:
562 free (pData); // added V0.9.1 (99-12-07)
563 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
564 break;
565
566 default:
567 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
568 }
569 }
570 return mrc;
571}
572
573/*
574 *@@ ctlCreateSplitWindow:
575 * this creates a new split window view according to
576 * the given SPLITBARCDATA structure. A split view
577 * links two existing windows together with a split
578 * bar in between, which can be moved with the mouse.
579 *
580 * Split windows are used for example in XWorkplace
581 * for the WPS class list object and in WarpIN for
582 * the database.
583 *
584 * After the split view has been created using this
585 * function, all window resizing/positioning is
586 * automatically performed, that is:
587 *
588 * a) if the parent window of the split view (e.g.
589 * the frame) is resized, the subwindows are
590 * adjusted;
591 *
592 * b) if the split bar is moved, the linked windows
593 * are adjusted also.
594 *
595 * <B>Creating a split view</B>
596 *
597 * To create a split view, you need to perform the
598 * following steps:
599 *
600 * 1) Create two windows that shall be separated by
601 * a split bar. These can be _any_ PM windows:
602 * a simple control window (e.g. a container),
603 * a frame you have created, or even a dialog
604 * which has been loaded using WinLoadDlg.
605 *
606 * Note: With frame windows, make sure they have the
607 * FCF_NOBYTEALIGN flag set, or they'll look funny.
608 *
609 * 2) Fill a SPLITBARCDATA structure with the necessary
610 * data for the split view (see comctl.h for details).
611 *
612 * 3) Call this function with that structure, which creates
613 * and returns the "split window", the invisible parent
614 * of the windows to be split.
615 * See the window hierarchy below.
616 *
617 * 4) Send a SPLM_SETLINKS message to the split window
618 * (returned by this function), with the two windows
619 * to be linked in mp1 and mp2. This has been implemented
620 * using a message so that you can re-link windows later
621 * at any time. (This is how the XWorkplace "WPS Class List"
622 * window does it when the settings notebook is changed.)
623 *
624 * Sending this message will change parentship (not ownership)
625 * of those two windows to the invisible split window.
626 *
627 * 5) Position the split window (which is returned from this
628 * function) within your existing windows using
629 * WinSetWindowPos(..., SWP_SIZE), which will automatically
630 * reposition the two linked subwindows of the split window.
631 * This works because the window procedure of the split
632 * window (ctl_fnwpSplitWindow) handles WM_WINDOWPOSCHANGED to
633 * recalculate the positions of the child windows.
634 *
635 * This function returns the HWND of the "split window".
636 * The handle of the split _bar_, if you absolutely need it,
637 * can be found using
638 + WinWindowFromID(hwndWhatThisReturns, ID_SPLITBAR).
639 *
640 * After SPLM_SETLINKS, the following window hierarchy
641 * is established (parentship):
642 *
643 + SPLITBARCDATA.hwndClient (whatever you have specified;
644 + | e.g. FID_CLIENT of your frame window.
645 + | You must intercept WM_SIZE and
646 + | call WinSetWindowPos on the "split window".)
647 + |
648 + +-- hwndReturn ("split window" created and returned by this
649 + | function; uses ctl_fnwpSplitWindow)
650 + |
651 + +-- hwndLink1 with SPLM_SETLINKS (parent changed)
652 + | |
653 + | +-- ... (your child windows remain untouched)
654 + |
655 + +-- ID_SPLITBAR (uses ctl_fnwpSplitBar)
656 + |
657 + +-- hwndLink2 with SPLM_SETLINKS (parent changed)
658 + |
659 + +-- ... (your child windows remain untouched)
660 *
661 * Note that only parentship of hwndSplit1 and hwndSplit2
662 * is changed. Ownership remains untouched. So you can specify
663 * any window as the owner of hwndLink1/2 so that messages
664 * can be forwarded properly.
665 *
666 * After the above hierarchy has been established, there are
667 * two situations where the "linked" windows will be repositioned
668 * and/or resized:
669 *
670 * 1) ctl_fnwpSplitBar will automatically resize and reposition
671 * the left and right "linked" windows if the user moves the
672 * split bar.
673 *
674 * 2) If the "split window" itself receives WM_WINDOWPOSCHANGED,
675 * e.g. because WinSetWindowPos has been invoked on the
676 * split window (which you should do when WM_SIZE is received
677 * by your parent window), the "linked" windows will
678 * automatically be repositioned and resized. This is done
679 * in ctl_fnwpSplitWindow.
680 *
681 * As a result, you must implement the following in your window
682 * procedures for the windows passed to this function:
683 *
684 * 1) The window procedure of SPLITBARCDATA.hwndClient (the
685 * parent of the split window) must intercept WM_SIZE and
686 * do a WinSetWindowPos on the split window. This will
687 * readjust the split bar and SPLITBARCDATA.hwndLink1/2.
688 *
689 * 2) The two linked windows should in turn handle WM_SIZE
690 * correctly because whenever the split window is resized,
691 * it invokes a WinSetWindowPos on SPLITBARCDATA.hwndLink1/2
692 * in turn.
693 *
694 * If you're using a single standard control as your subwindow
695 * (e.g. a container), this is no problem. However, if you
696 * specify frame windows, you might want to reposition
697 * the controls in those windows in turn.
698 *
699 * Note that dialog windows do not receive WM_SIZE;
700 * you'll need to handle WM_WINDOWPOSCHANGED instead.
701 *
702 * 3) Your window procedure should forward WM_SYSCOMMAND to
703 * the owner (top) window so that Alt-F4 etc. hotkeys
704 * still work.
705 *
706 * If you wish to <B>nest split windows,</B> you can do so by
707 * specifying the "split window" (the HWND which is returned
708 * by this function) as the "hwndLink1/2" to another call
709 * of this function. This way you can create a complex
710 * split window hierarchy (similar to what Netscape does
711 * with the FRAMESET tag). So to create a split view like
712 * this:
713 *
714 + +---------+------------+
715 + | | |
716 + | | 2 |
717 + | | |
718 + | 1 +------------+
719 + | | |
720 + | | 3 |
721 + | | |
722 + +---------+------------+
723 *
724 * First invoke ctlCreateSplitWindow to link "2" and "3"
725 * together (creating a horizontal split bar), which returns
726 * a "split window"; then link "1" and that split window
727 * together again.
728 *
729 *@@added V0.9.0 [umoeller]
730 */
731
732HWND ctlCreateSplitWindow(HAB hab,
733 PSPLITBARCDATA psbcd) // in: split window control data
734{
735 HWND hwndSplit = NULLHANDLE,
736 hwndBar = NULLHANDLE;
737 static s_Registered = FALSE;
738
739 if (psbcd)
740 {
741 // register "split window" class
742 if (!s_Registered)
743 {
744 WinRegisterClass(hab,
745 WC_SPLITWINDOW,
746 ctl_fnwpSplitWindow,
747 CS_SIZEREDRAW | CS_SYNCPAINT,
748 0); // additional bytes to reserve
749 s_Registered = TRUE;
750 }
751
752 if ( (hwndSplit = WinCreateWindow(psbcd->hwndParentAndOwner, // parent
753 WC_SPLITWINDOW,
754 "",
755 WS_VISIBLE,
756 0, 0, 10, 10,
757 psbcd->hwndParentAndOwner, // owner
758 HWND_TOP,
759 psbcd->ulSplitWindowID,
760 NULL,
761 NULL))
762 && (hwndBar = WinCreateWindow(psbcd->hwndParentAndOwner, // parent
763 WC_STATIC,
764 "",
765 WS_VISIBLE // wnd style
766 | SS_TEXT,
767 0, 0, 10, 10,
768 psbcd->hwndParentAndOwner, // owner
769 HWND_TOP,
770 ID_SPLITBAR, // win ID
771 NULL, // cdata
772 NULL)) // presparams
773 )
774 {
775 // create SPLITBARDATA to store in split bar's QWL_USER
776 PSPLITBARDATA pData;
777 if (pData = (PSPLITBARDATA)malloc(sizeof(SPLITBARDATA)))
778 {
779 // set parent for split bar
780 WinSetParent(hwndBar, hwndSplit, FALSE);
781
782 memset(pData, 0, sizeof(SPLITBARDATA));
783
784 // copy control data
785 memcpy(&(pData->sbcd), psbcd, sizeof(SPLITBARCDATA));
786
787 // subclass static control to make it a split bar
788 pData->OldStaticProc = WinSubclassWindow(hwndBar, ctl_fnwpSplitBar);
789 pData->hptrOld = NULLHANDLE;
790 pData->hptrMove = WinQuerySysPointer(HWND_DESKTOP,
791 (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
792 ? SPTR_SIZENS
793 : SPTR_SIZEWE,
794 FALSE); // don't make copy
795 pData->fCaptured = FALSE;
796 pData->hwndLinked1 =
797 pData->hwndLinked2 = NULLHANDLE;
798
799 // caching these colors now V1.0.0 (2002-08-21) [umoeller]
800 pData->lcol3DDark = WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONDARK, 0);
801 pData->lcol3DLight = WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONLIGHT, 0);
802 pData->lcolInactiveBorder = WinQuerySysColor(HWND_DESKTOP, SYSCLR_INACTIVEBORDER, 0);
803
804 WinSetWindowULong(hwndBar, QWL_USER, (ULONG)pData);
805 }
806 }
807 }
808
809 return hwndSplit;
810}
811
812/*
813 *@@ ctlUpdateSplitWindow:
814 * this function takes a split window as input and
815 * repositions all its children (the split bars
816 * and the other subwindows which are separated by the
817 * split bars) according to the parent.
818 *
819 * This function gets called from ctl_fnwpSplitWindow
820 * when WM_SIZE is received. Normally, you won't have
821 * to call this function independently; you should do
822 * a WinSetWindowPos on the split window instead.
823 *
824 *@@added V0.9.0 [umoeller]
825 *@@changed V1.0.0 (2002-08-24) [umoeller]: added support for SBCF_3DEXPLORERSTYLE
826 */
827
828BOOL ctlUpdateSplitWindow(HWND hwndSplit)
829{
830 BOOL brc = FALSE;
831 HWND hwndSplitBar;
832 PSPLITBARDATA psbd;
833
834 if ( (hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR))
835 && (psbd = (PSPLITBARDATA)WinQueryWindowPtr(hwndSplitBar, QWL_USER))
836 )
837 {
838 PSPLITBARCDATA psbcd = &(psbd->sbcd);
839 RECTL rclSplit,
840 rclBar;
841 LONG l3DOfsLink1 = 0,
842 l3DOfsLink2 = 0;
843
844 // _Pmpf(("Entering ctlUpdateSplitWindow for hwndSplit 0x%lX", hwndSplit));
845
846 // query the rectangle of the split window's parent;
847 // this is either the client or another split window
848 WinQueryWindowRect(hwndSplit, &rclSplit);
849
850 /* _Pmpf((" rect: %d, %d, %d, %d",
851 rclSplit.xLeft,
852 rclSplit.yBottom,
853 rclSplit.xRight - rclSplit.xLeft,
854 rclSplit.yTop - rclSplit.yBottom)); */
855
856 // update split bar
857 if (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
858 {
859 // _Pmpf((" Calc horizontal"));
860 // horizontal split bar:
861 if (psbcd->ulCreateFlags & SBCF_PERCENTAGE)
862 // take height of client and apply percentage
863 rclBar.yBottom = (rclSplit.yTop - rclSplit.yBottom)
864 * psbcd->lPos
865 / 100;
866 else
867 if (psbcd->lPos > 0)
868 // offset from bottom:
869 rclBar.yBottom = psbcd->lPos;
870 else
871 // offset from right:
872 rclBar.yBottom = (rclSplit.yTop - rclSplit.yBottom)
873 + psbcd->lPos; // which is negative
874
875 rclBar.yTop = rclBar.yBottom + WinQuerySysValue(HWND_DESKTOP,
876 SV_CXSIZEBORDER);
877 rclBar.xLeft = 0;
878 // take width of client
879 rclBar.xRight = (rclSplit.xRight - rclSplit.xLeft);
880 }
881 else
882 {
883 // _Pmpf((" Calc vertical"));
884 // vertical split bar:
885 if (psbcd->ulCreateFlags & SBCF_PERCENTAGE)
886 // take width of client and apply percentage
887 rclBar.xLeft = (rclSplit.xRight - rclSplit.xLeft)
888 * psbcd->lPos
889 / 100;
890 else
891 if (psbcd->lPos > 0)
892 // offset from left:
893 rclBar.xLeft = psbcd->lPos;
894 else
895 // offset from right:
896 rclBar.xLeft = (rclSplit.xRight - rclSplit.xLeft)
897 + psbcd->lPos; // which is negative
898
899 rclBar.xRight = rclBar.xLeft + WinQuerySysValue(HWND_DESKTOP,
900 SV_CXSIZEBORDER);
901 rclBar.yBottom = 0;
902 // take height of client
903 rclBar.yTop = (rclSplit.yTop - rclSplit.yBottom);
904 }
905
906 // reposition split bar
907 brc = WinSetWindowPos(hwndSplitBar,
908 HWND_TOP,
909 rclBar.xLeft,
910 rclBar.yBottom,
911 rclBar.xRight - rclBar.xLeft,
912 rclBar.yTop - rclBar.yBottom,
913 SWP_MOVE | SWP_SIZE);
914
915 /* _Pmpf((" Set splitbar hwnd %lX to %d, %d, %d, %d; rc: %d",
916 hwndSplitBar,
917 rclBar.xLeft,
918 rclBar.yBottom,
919 rclBar.xRight - rclBar.xLeft,
920 rclBar.yTop - rclBar.yBottom,
921 brc)); */
922
923 // reposition left/bottom window of split bar
924 if (psbcd->ulCreateFlags & SBCF_3DEXPLORERSTYLE)
925 {
926 l3DOfsLink2 = 2;
927 }
928 else if (psbcd->ulCreateFlags & SBCF_3DSUNK)
929 {
930 l3DOfsLink1 = 1;
931 l3DOfsLink2 = 1;
932 }
933 // else 0
934
935 // now reposition the linked windows
936 if (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
937 {
938 // horizontal:
939 // reposition bottom window of split bar
940 WinSetWindowPos(psbd->hwndLinked1,
941 HWND_TOP,
942 l3DOfsLink1,
943 l3DOfsLink1,
944 rclBar.xRight - rclBar.xLeft - 2 * l3DOfsLink1,
945 rclBar.yBottom - 2 * l3DOfsLink1,
946 // the window rect is non-inclusive
947 SWP_MOVE | SWP_SIZE);
948
949 // reposition top window of split bar
950 WinSetWindowPos(psbd->hwndLinked2,
951 HWND_TOP,
952 l3DOfsLink2,
953 rclBar.yTop + l3DOfsLink2,
954 // the window rect is non-inclusive
955 rclBar.xRight - rclBar.xLeft - 2 * l3DOfsLink2,
956 rclSplit.yTop - rclBar.yTop - 2 * l3DOfsLink2,
957 SWP_MOVE | SWP_SIZE);
958 }
959 else
960 {
961 // vertical:
962 // reposition left window of split bar
963 WinSetWindowPos(psbd->hwndLinked1,
964 HWND_TOP,
965 l3DOfsLink1,
966 l3DOfsLink1,
967 rclBar.xLeft - 2 * l3DOfsLink1,
968 // the window rect is non-inclusive
969 rclBar.yTop - rclBar.yBottom - 2 * l3DOfsLink1,
970 SWP_MOVE | SWP_SIZE);
971
972 // reposition right window of split bar
973 WinSetWindowPos(psbd->hwndLinked2,
974 HWND_TOP,
975 rclBar.xRight + l3DOfsLink2,
976 // the window rect is non-inclusive
977 l3DOfsLink2,
978 rclSplit.xRight - rclBar.xRight - 2 * l3DOfsLink2,
979 rclBar.yTop - rclBar.yBottom - 2 * l3DOfsLink2,
980 SWP_MOVE | SWP_SIZE);
981 }
982
983 // repaint split window (3D frame)
984 WinInvalidateRect(hwndSplit,
985 NULL, // all
986 FALSE); // don't repaint children
987 }
988
989 return brc;
990}
991
992/*
993 *@@ ctlQuerySplitPos:
994 * this returns the position of the split bar
995 * (the child control of hwndSplit; see ctlCreateSplitWindow
996 * for the window hierarchy).
997 *
998 * The meaning of the return value depends on what you
999 * specified with ulCreateFlags in the SPLITBARCDATA
1000 * structure passed to ctlCreateSplitWindow, that is,
1001 * it can be a percentage or an offset from the left
1002 * or from the right of the split window.
1003 *
1004 * This returns NULL upon errors.
1005 *
1006 *@@added V0.9.0 [umoeller]
1007 */
1008
1009LONG ctlQuerySplitPos(HWND hwndSplit)
1010{
1011 // the split bar data is stored in QWL_USER of the
1012 // split bar (not the split window)
1013 HWND hwndSplitBar;
1014 PSPLITBARDATA psbd;
1015 if ( (hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR))
1016 && (psbd = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar, QWL_USER))
1017 )
1018 return psbd->sbcd.lPos;
1019
1020 return 0;
1021}
1022
1023
Note: See TracBrowser for help on using the repository browser.