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

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

Mistc. updates for WarpIN, new features.

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