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

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

Misc changes.

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