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

Last change on this file since 98 was 98, checked in by umoeller, 24 years ago

Misc updates.

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