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

Last change on this file since 164 was 142, 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: 37.0 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
99static VOID 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
253static VOID 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
402static VOID 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 *@@changed V0.9.14 (2001-08-21) [umoeller]: SBCF_MOVEABLE was always ignored, fixed
491 */
492
493MRESULT EXPENTRY ctl_fnwpSplitBar(HWND hwndBar, ULONG msg, MPARAM mp1, MPARAM mp2)
494{
495 PSPLITBARDATA pData = (PSPLITBARDATA)WinQueryWindowULong(hwndBar, QWL_USER);
496
497 PFNWP OldStaticProc = NULL;
498
499 MRESULT mrc = NULL;
500
501 if (pData)
502 {
503 OldStaticProc = pData->OldStaticProc;
504
505 switch(msg)
506 {
507 /*
508 * WM_MOUSEMOVE:
509 * change mouse pointer when over split bar.
510 */
511
512 case WM_MOUSEMOVE:
513 if (pData->sbcd.ulCreateFlags & SBCF_MOVEABLE)
514 // V0.9.14 (2001-08-21) [umoeller]
515 // split bar is moveable:
516 pData->hptrOld = WinSetPointer(HWND_DESKTOP, pData->hptrMove);
517 else
518 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
519 break;
520
521 /*
522 * WM_BUTTON1DOWN:
523 * this will start moving the split bar.
524 * We use WinTrackRect for this.
525 */
526
527 case WM_BUTTON1DOWN:
528 case WM_BUTTON2DOWN:
529 if (pData->sbcd.ulCreateFlags & SBCF_MOVEABLE)
530 // V0.9.14 (2001-08-21) [umoeller]
531 // split bar is moveable:
532 TrackSplitBar(hwndBar, pData);
533 else
534 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
535 break;
536
537 /*
538 * WM_PAINT:
539 * paint the split bar
540 */
541
542 case WM_PAINT:
543 PaintSplitBar(hwndBar, pData);
544 break;
545
546 case WM_DESTROY:
547 free (pData); // added V0.9.1 (99-12-07)
548 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
549 break;
550
551 default:
552 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
553 }
554 }
555 return (mrc);
556}
557
558/*
559 *@@ ctlCreateSplitWindow:
560 * this creates a new split window view according to
561 * the given SPLITBARCDATA structure. A split view
562 * links two existing windows together with a split
563 * bar in between, which can be moved with the mouse.
564 *
565 * Split windows are used for example in XWorkplace
566 * for the WPS class list object and in WarpIN for
567 * the database.
568 *
569 * After the split view has been created using this
570 * function, all window resizing/positioning is
571 * automatically performed, that is:
572 *
573 * a) if the parent window of the split view (e.g.
574 * the frame) is resized, the subwindows are
575 * adjusted;
576 *
577 * b) if the split bar is moved, the linked windows
578 * are adjusted also.
579 *
580 * <B>Creating a split view</B>
581 *
582 * To create a split view, you need to perform the
583 * following steps:
584 *
585 * 1) Create two windows that shall be separated by
586 * a split bar. These can be _any_ PM windows:
587 * a simple control window (e.g. a container),
588 * a frame you have created, or even a dialog
589 * which has been loaded using WinLoadDlg.
590 *
591 * Note: With frame windows, make sure they have the
592 * FCF_NOBYTEALIGN flag set, or they'll look funny.
593 *
594 * 2) Fill a SPLITBARCDATA structure with the necessary
595 * data for the split view (see comctl.h for details).
596 *
597 * 3) Call this function with that structure, which creates
598 * and returns the "split window", the invisible parent
599 * of the windows to be split.
600 * See the window hierarchy below.
601 *
602 * 4) Send a SPLM_SETLINKS message to the split window
603 * (returned by this function), with the two windows
604 * to be linked in mp1 and mp2. This has been implemented
605 * using a message so that you can re-link windows later
606 * at any time. (This is how the XWorkplace "WPS Class List"
607 * window does it when the settings notebook is changed.)
608 *
609 * Sending this message will change parentship (not ownership)
610 * of those two windows to the invisible split window.
611 *
612 * 5) Position the split window (which is returned from this
613 * function) within your existing windows using
614 * WinSetWindowPos(..., SWP_SIZE), which will automatically
615 * reposition the two linked subwindows of the split window.
616 * This works because the window procedure of the split
617 * window (ctl_fnwpSplitWindow) handles WM_WINDOWPOSCHANGED to
618 * recalculate the positions of the child windows.
619 *
620 * This function returns the HWND of the "split window".
621 * The handle of the split _bar_, if you absolutely need it,
622 * can be found using
623 + WinWindowFromID(hwndWhatThisReturns, ID_SPLITBAR).
624 *
625 * After SPLM_SETLINKS, the following window hierarchy
626 * is established (parentship):
627 *
628 + SPLITBARCDATA.hwndClient (whatever you have specified;
629 + | e.g. FID_CLIENT of your frame window.
630 + | You must intercept WM_SIZE and
631 + | call WinSetWindowPos on the "split window".)
632 + |
633 + +-- hwndReturn ("split window" created and returned by this
634 + | function; uses ctl_fnwpSplitWindow)
635 + |
636 + +-- hwndLink1 with SPLM_SETLINKS (parent changed)
637 + | |
638 + | +-- ... (your child windows remain untouched)
639 + |
640 + +-- ID_SPLITBAR (uses ctl_fnwpSplitBar)
641 + |
642 + +-- hwndLink2 with SPLM_SETLINKS (parent changed)
643 + |
644 + +-- ... (your child windows remain untouched)
645 *
646 * Note that only parentship of hwndSplit1 and hwndSplit2
647 * is changed. Ownership remains untouched. So you can specify
648 * any window as the owner of hwndLink1/2 so that messages
649 * can be forwarded properly.
650 *
651 * After the above hierarchy has been established, there are
652 * two situations where the "linked" windows will be repositioned
653 * and/or resized:
654 *
655 * 1) ctl_fnwpSplitBar will automatically resize and reposition
656 * the left and right "linked" windows if the user moves the
657 * split bar.
658 *
659 * 2) If the "split window" itself receives WM_WINDOWPOSCHANGED,
660 * e.g. because WinSetWindowPos has been invoked on the
661 * split window (which you should do when WM_SIZE is received
662 * by your parent window), the "linked" windows will
663 * automatically be repositioned and resized. This is done
664 * in ctl_fnwpSplitWindow.
665 *
666 * As a result, you must implement the following in your window
667 * procedures for the windows passed to this function:
668 *
669 * 1) The window procedure of SPLITBARCDATA.hwndClient (the
670 * parent of the split window) must intercept WM_SIZE and
671 * do a WinSetWindowPos on the split window. This will
672 * readjust the split bar and SPLITBARCDATA.hwndLink1/2.
673 *
674 * 2) The two linked windows should in turn handle WM_SIZE
675 * correctly because whenever the split window is resized,
676 * it invokes a WinSetWindowPos on SPLITBARCDATA.hwndLink1/2
677 * in turn.
678 *
679 * If you're using a single standard control as your subwindow
680 * (e.g. a container), this is no problem. However, if you
681 * specify frame windows, you might want to reposition
682 * the controls in those windows in turn.
683 *
684 * Note that dialog windows do not receive WM_SIZE;
685 * you'll need to handle WM_WINDOWPOSCHANGED instead.
686 *
687 * 3) Your window procedure should forward WM_SYSCOMMAND to
688 * the owner (top) window so that Alt-F4 etc. hotkeys
689 * still work.
690 *
691 * If you wish to <B>nest split windows,</B> you can do so by
692 * specifying the "split window" (the HWND which is returned
693 * by this function) as the "hwndLink1/2" to another call
694 * of this function. This way you can create a complex
695 * split window hierarchy (similar to what Netscape does
696 * with the FRAMESET tag). So to create a split view like
697 * this:
698 *
699 + +---------+------------+
700 + | | |
701 + | | 2 |
702 + | | |
703 + | 1 +------------+
704 + | | |
705 + | | 3 |
706 + | | |
707 + +---------+------------+
708 *
709 * First invoke ctlCreateSplitWindow to link "2" and "3"
710 * together (creating a horizontal split bar), which returns
711 * a "split window"; then link "1" and that split window
712 * together again.
713 *
714 *@@added V0.9.0 [umoeller]
715 */
716
717HWND ctlCreateSplitWindow(HAB hab,
718 PSPLITBARCDATA psbcd) // in: split window control data
719{
720 HWND hwndSplit = NULLHANDLE,
721 hwndBar = NULLHANDLE;
722 static s_Registered = FALSE;
723
724 if (psbcd)
725 {
726 // register "split window" class
727 if (!s_Registered)
728 {
729 WinRegisterClass(hab,
730 WC_SPLITWINDOW,
731 ctl_fnwpSplitWindow,
732 CS_SIZEREDRAW | CS_SYNCPAINT,
733 0); // additional bytes to reserve
734 s_Registered = TRUE;
735 }
736
737 hwndSplit = WinCreateWindow(psbcd->hwndParentAndOwner, // parent
738 WC_SPLITWINDOW,
739 "",
740 WS_VISIBLE,
741 0, 0, 10, 10,
742 psbcd->hwndParentAndOwner, // owner
743 HWND_TOP,
744 psbcd->ulSplitWindowID,
745 NULL,
746 NULL);
747 if (hwndSplit)
748 {
749 hwndBar = WinCreateWindow(psbcd->hwndParentAndOwner, // parent
750 WC_STATIC,
751 "",
752 WS_VISIBLE // wnd style
753 | SS_TEXT,
754 0, 0, 10, 10,
755 psbcd->hwndParentAndOwner, // owner
756 HWND_TOP,
757 ID_SPLITBAR, // win ID
758 NULL, // cdata
759 NULL // presparams
760 );
761 if (hwndBar)
762 {
763 // create SPLITBARDATA to store in split bar's QWL_USER
764 PSPLITBARDATA pData = (PSPLITBARDATA)malloc(sizeof(SPLITBARDATA));
765 if (pData)
766 {
767 // set parent for split bar
768 WinSetParent(hwndBar, hwndSplit, FALSE);
769
770 memset(pData, 0, sizeof(SPLITBARDATA));
771
772 // copy control data
773 memcpy(&(pData->sbcd), psbcd, sizeof(SPLITBARCDATA));
774 // set other data
775 /* WinQueryWindowRect(hwndBar, &(pData->rclBar));
776 (pData->rclBar.xRight)--;
777 (pData->rclBar.yTop)--; */
778 // subclass static control to make it a split bar
779 pData->OldStaticProc = WinSubclassWindow(hwndBar, ctl_fnwpSplitBar);
780 pData->hptrOld = NULLHANDLE;
781 pData->hptrMove = WinQuerySysPointer(HWND_DESKTOP,
782 (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
783 ? SPTR_SIZENS
784 : SPTR_SIZEWE,
785 FALSE); // don't make copy
786 pData->fCaptured = FALSE;
787 pData->hwndLinked1 =
788 pData->hwndLinked2 = NULLHANDLE;
789
790 WinSetWindowULong(hwndBar, QWL_USER, (ULONG)pData);
791 }
792 }
793 }
794 }
795
796 return (hwndSplit);
797}
798
799/*
800 *@@ ctlUpdateSplitWindow:
801 * this function takes a split window as input and
802 * repositions all its children (the split bars
803 * and the other subwindows which are separated by the
804 * split bars) according to the parent.
805 *
806 * This function gets called from ctl_fnwpSplitWindow
807 * when WM_SIZE is received. Normally, you won't have
808 * to call this function independently; you should do
809 * a WinSetWindowPos on the split window instead.
810 *
811 *@@added V0.9.0 [umoeller]
812 */
813
814BOOL ctlUpdateSplitWindow(HWND hwndSplit)
815{
816 BOOL brc = FALSE;
817 HWND hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
818
819 if (hwndSplitBar)
820 {
821 PSPLITBARDATA psbd = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar, QWL_USER);
822
823 if (psbd)
824 {
825 PSPLITBARCDATA psbcd = &(psbd->sbcd);
826 RECTL rclSplit,
827 rclBar;
828 ULONG ul3DOfs = 0;
829
830 // _Pmpf(("Entering ctlUpdateSplitWindow for hwndSplit 0x%lX", hwndSplit));
831
832 // query the rectangle of the split window's parent;
833 // this is either the client or another split window
834 WinQueryWindowRect(hwndSplit, &rclSplit);
835
836 /* _Pmpf((" rect: %d, %d, %d, %d",
837 rclSplit.xLeft,
838 rclSplit.yBottom,
839 rclSplit.xRight - rclSplit.xLeft,
840 rclSplit.yTop - rclSplit.yBottom)); */
841
842 // set anti-recursion flag;
843 // this is neccessary, because ctl_fnwpSplitWindow
844 // calls this func again when
845 // WM_WINDOWPOSCHANGED comes in
846 // psbd->fNoAdjust = TRUE;
847 // set split window to the same
848 /* WinSetWindowPos(hwndSplit, HWND_TOP,
849 rclSplit.xLeft,
850 rclSplit.yBottom,
851 rclSplit.xRight - rclSplit.xLeft,
852 rclSplit.yTop - rclSplit.yBottom,
853 SWP_MOVE | SWP_SIZE); */
854 // psbd->fNoAdjust = FALSE;
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_3DSUNK)
925 ul3DOfs = 1;
926 // else 0
927
928 // now reposition the linked windows
929 if (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
930 {
931 // horizontal:
932 // reposition bottom window of split bar
933 WinSetWindowPos(psbd->hwndLinked1,
934 HWND_TOP,
935 ul3DOfs,
936 ul3DOfs,
937 rclBar.xRight - rclBar.xLeft - ul3DOfs*2,
938 rclBar.yBottom - ul3DOfs*2, // the window rect is non-inclusive
939 SWP_MOVE | SWP_SIZE);
940
941 // reposition top window of split bar
942 WinSetWindowPos(psbd->hwndLinked2,
943 HWND_TOP,
944 ul3DOfs,
945 rclBar.yTop + ul3DOfs, // the window rect is non-inclusive
946 rclBar.xRight - rclBar.xLeft - ul3DOfs*2,
947 rclSplit.yTop - rclBar.yTop - ul3DOfs*2,
948 SWP_MOVE | SWP_SIZE);
949 }
950 else
951 {
952 // vertical:
953 // reposition left window of split bar
954 WinSetWindowPos(psbd->hwndLinked1,
955 HWND_TOP,
956 ul3DOfs,
957 ul3DOfs,
958 rclBar.xLeft - ul3DOfs*2, // the window rect is non-inclusive
959 rclBar.yTop - rclBar.yBottom - ul3DOfs*2,
960 SWP_MOVE | SWP_SIZE);
961
962 // reposition right window of split bar
963 WinSetWindowPos(psbd->hwndLinked2,
964 HWND_TOP,
965 rclBar.xRight + ul3DOfs, // the window rect is non-inclusive
966 ul3DOfs,
967 rclSplit.xRight - rclBar.xRight - ul3DOfs*2,
968 rclBar.yTop - rclBar.yBottom - ul3DOfs*2,
969 SWP_MOVE | SWP_SIZE);
970 }
971
972 // repaint split window (3D frame)
973 WinInvalidateRect(hwndSplit,
974 NULL, // all
975 FALSE); // don't repaint children
976 }
977 }
978
979 return (brc);
980}
981
982/*
983 *@@ ctlQuerySplitPos:
984 * this returns the position of the split bar
985 * (the child control of hwndSplit; see ctlCreateSplitWindow
986 * for the window hierarchy).
987 *
988 * The meaning of the return value depends on what you
989 * specified with ulCreateFlags in the SPLITBARCDATA
990 * structure passed to ctlCreateSplitWindow, that is,
991 * it can be a percentage or an offset from the left
992 * or from the right of the split window.
993 *
994 * This returns NULL upon errors.
995 *
996 *@@added V0.9.0 [umoeller]
997 */
998
999LONG ctlQuerySplitPos(HWND hwndSplit)
1000{
1001 ULONG lrc = 0;
1002
1003 // the split bar data is stored in QWL_USER of the
1004 // split bar (not the split window)
1005 HWND hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
1006 if (hwndSplitBar)
1007 {
1008 PSPLITBARDATA psbd = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar, QWL_USER);
1009 if (psbd)
1010 lrc = psbd->sbcd.lPos;
1011 }
1012
1013 return (lrc);
1014}
1015
1016
Note: See TracBrowser for help on using the repository browser.