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

Last change on this file since 45 was 21, checked in by umoeller, 25 years ago

Final changes for 0.9.7, i hope...

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 36.2 KB
Line 
1
2/*
3 *@@sourcefile cctl.splitwin.c:
4 * implementation for split windows.
5 * See comctl.c for an overview.
6 *
7 * This has been extracted from comctl.c with V0.9.3 (2000-05-21) [umoeller].
8 *
9 * Note: Version numbering in this file relates to XWorkplace version
10 * numbering.
11 *
12 *@@header "helpers\comctl.h"
13 *@@added V0.9.3 (2000-05-21) [umoeller].
14 */
15
16/*
17 * Copyright (C) 1997-2000 Ulrich M”ller.
18 * This file is part of the "XWorkplace helpers" source package.
19 * This is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published
21 * by the Free Software Foundation, in version 2 as it comes in the
22 * "COPYING" file of the XWorkplace main distribution.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 */
28
29#define OS2EMX_PLAIN_CHAR
30 // this is needed for "os2emx.h"; if this is defined,
31 // emx will define PSZ as _signed_ char, otherwise
32 // as unsigned char
33
34#define INCL_DOSEXCEPTIONS
35#define INCL_DOSPROCESS
36#define INCL_DOSSEMAPHORES
37#define INCL_DOSERRORS
38
39#define INCL_WINWINDOWMGR
40#define INCL_WINFRAMEMGR
41#define INCL_WINMESSAGEMGR
42#define INCL_WININPUT
43#define INCL_WINPOINTERS
44#define INCL_WINTRACKRECT
45#define INCL_WINTIMER
46#define INCL_WINSYS
47
48#define INCL_WINRECTANGLES /// xxx temporary
49
50#define INCL_WINMENUS
51#define INCL_WINSTATICS
52#define INCL_WINBUTTONS
53#define INCL_WINSTDCNR
54
55#define INCL_GPIPRIMITIVES
56#define INCL_GPILOGCOLORTABLE
57#define INCL_GPILCIDS
58#define INCL_GPIPATHS
59#define INCL_GPIREGIONS
60#define INCL_GPIBITMAPS // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
61#include <os2.h>
62
63#include <stdlib.h>
64#include <stdio.h>
65#include <string.h>
66#include <setjmp.h> // needed for except.h
67#include <assert.h> // needed for except.h
68
69#include "setup.h" // code generation and debugging options
70
71#include "helpers\cnrh.h"
72#include "helpers\except.h" // exception handling
73#include "helpers\gpih.h"
74#include "helpers\linklist.h"
75#include "helpers\winh.h"
76
77#include "helpers\comctl.h"
78
79#pragma hdrstop
80
81/*
82 *@@category: Helpers\PM helpers\Window classes\Split windows
83 * See cctl_splitwin.c.
84 */
85
86/* ******************************************************************
87 *
88 * Split windows
89 *
90 ********************************************************************/
91
92/*
93 *@@ PaintSplitWindow:
94 * implementation for WM_PAINT in ctl_fnwpSplitWindow.
95 *
96 *@@added V0.9.1 (2000-02-05) [umoeller]
97 */
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 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 */
490
491MRESULT EXPENTRY ctl_fnwpSplitBar(HWND hwndBar, ULONG msg, MPARAM mp1, MPARAM mp2)
492{
493 PSPLITBARDATA pData = (PSPLITBARDATA)WinQueryWindowULong(hwndBar, QWL_USER);
494
495 PFNWP OldStaticProc = NULL;
496
497 MRESULT mrc = NULL;
498
499 if (pData)
500 {
501 OldStaticProc = pData->OldStaticProc;
502
503 switch(msg)
504 {
505 /*
506 * WM_MOUSEMOVE:
507 * change mouse pointer when over split bar.
508 */
509
510 case WM_MOUSEMOVE:
511 pData->hptrOld = WinSetPointer(HWND_DESKTOP, pData->hptrMove);
512 break;
513
514 /*
515 * WM_BUTTON1DOWN:
516 * this will start moving the split bar.
517 * We use WinTrackRect for this.
518 */
519
520 case WM_BUTTON1DOWN:
521 TrackSplitBar(hwndBar, pData);
522 break;
523
524 /*
525 * WM_PAINT:
526 * paint the split bar
527 */
528
529 case WM_PAINT:
530 PaintSplitBar(hwndBar, pData);
531 break;
532
533 case WM_DESTROY:
534 free (pData); // added V0.9.1 (99-12-07)
535 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
536 break;
537
538 default:
539 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
540 }
541 }
542 return (mrc);
543}
544
545/*
546 *@@ ctlCreateSplitWindow:
547 * this creates a new split window view according to
548 * the given SPLITBARCDATA structure. A split view
549 * links two existing windows together with a split
550 * bar in between, which can be moved with the mouse.
551 *
552 * Split windows are used for example in XWorkplace
553 * for the WPS class list object and in WarpIN for
554 * the database.
555 *
556 * After the split view has been created using this
557 * function, all window resizing/positioning is
558 * automatically performed, that is:
559 *
560 * a) if the parent window of the split view (e.g.
561 * the frame) is resized, the subwindows are
562 * adjusted;
563 *
564 * b) if the split bar is moved, the linked windows
565 * are adjusted also.
566 *
567 * <B>Creating a split view</B>
568 *
569 * To create a split view, you need to perform the
570 * following steps:
571 *
572 * 1) Create two windows that shall be separated by
573 * a split bar. These can be _any_ PM windows:
574 * a simple control window (e.g. a container),
575 * a frame you have created, or even a dialog
576 * which has been loaded using WinLoadDlg.
577 *
578 * Note: With frame windows, make sure they have the
579 * FCF_NOBYTEALIGN flag set, or they'll look funny.
580 *
581 * 2) Fill a SPLITBARCDATA structure with the necessary
582 * data for the split view (see comctl.h for details).
583 *
584 * 3) Call this function with that structure, which creates
585 * and returns the "split window", the invisible parent
586 * of the windows to be split.
587 * See the window hierarchy below.
588 *
589 * 4) Send a SPLM_SETLINKS message to the split window
590 * (returned by this function), with the two windows
591 * to be linked in mp1 and mp2. This has been implemented
592 * using a message so that you can re-link windows later
593 * at any time. (This is how the XWorkplace "WPS Class List"
594 * window does it when the settings notebook is changed.)
595 *
596 * Sending this message will change parentship (not ownership)
597 * of those two windows to the invisible split window.
598 *
599 * 5) Position the split window (which is returned from this
600 * function) within your existing windows using
601 * WinSetWindowPos(..., SWP_SIZE), which will automatically
602 * reposition the two linked subwindows of the split window.
603 * This works because the window procedure of the split
604 * window (ctl_fnwpSplitWindow) handles WM_WINDOWPOSCHANGED to
605 * recalculate the positions of the child windows.
606 *
607 * This function returns the HWND of the "split window".
608 * The handle of the split _bar_, if you absolutely need it,
609 * can be found using
610 + WinWindowFromID(hwndWhatThisReturns, ID_SPLITBAR).
611 *
612 * After SPLM_SETLINKS, the following window hierarchy
613 * is established (parentship):
614 *
615 + SPLITBARCDATA.hwndClient (whatever you have specified;
616 + | e.g. FID_CLIENT of your frame window.
617 + | You must intercept WM_SIZE and
618 + | call WinSetWindowPos on the "split window".)
619 + |
620 + +-- hwndReturn ("split window" created and returned by this
621 + | function; uses ctl_fnwpSplitWindow)
622 + |
623 + +-- hwndLink1 with SPLM_SETLINKS (parent changed)
624 + | |
625 + | +-- ... (your child windows remain untouched)
626 + |
627 + +-- ID_SPLITBAR (uses ctl_fnwpSplitBar)
628 + |
629 + +-- hwndLink2 with SPLM_SETLINKS (parent changed)
630 + |
631 + +-- ... (your child windows remain untouched)
632 *
633 * Note that only parentship of hwndSplit1 and hwndSplit2
634 * is changed. Ownership remains untouched. So you can specify
635 * any window as the owner of hwndLink1/2 so that messages
636 * can be forwarded properly.
637 *
638 * After the above hierarchy has been established, there are
639 * two situations where the "linked" windows will be repositioned
640 * and/or resized:
641 *
642 * 1) ctl_fnwpSplitBar will automatically resize and reposition
643 * the left and right "linked" windows if the user moves the
644 * split bar.
645 *
646 * 2) If the "split window" itself receives WM_WINDOWPOSCHANGED,
647 * e.g. because WinSetWindowPos has been invoked on the
648 * split window (which you should do when WM_SIZE is received
649 * by your parent window), the "linked" windows will
650 * automatically be repositioned and resized. This is done
651 * in ctl_fnwpSplitWindow.
652 *
653 * As a result, you must implement the following in your window
654 * procedures for the windows passed to this function:
655 *
656 * 1) The window procedure of SPLITBARCDATA.hwndClient (the
657 * parent of the split window) must intercept WM_SIZE and
658 * do a WinSetWindowPos on the split window. This will
659 * readjust the split bar and SPLITBARCDATA.hwndLink1/2.
660 *
661 * 2) The two linked windows should in turn handle WM_SIZE
662 * correctly because whenever the split window is resized,
663 * it invokes a WinSetWindowPos on SPLITBARCDATA.hwndLink1/2
664 * in turn.
665 *
666 * If you're using a single standard control as your subwindow
667 * (e.g. a container), this is no problem. However, if you
668 * specify frame windows, you might want to reposition
669 * the controls in those windows in turn.
670 *
671 * Note that dialog windows do not receive WM_SIZE;
672 * you'll need to handle WM_WINDOWPOSCHANGED instead.
673 *
674 * 3) Your window procedure should forward WM_SYSCOMMAND to
675 * the owner (top) window so that Alt-F4 etc. hotkeys
676 * still work.
677 *
678 * If you wish to <B>nest split windows,</B> you can do so by
679 * specifying the "split window" (the HWND which is returned
680 * by this function) as the "hwndLink1/2" to another call
681 * of this function. This way you can create a complex
682 * split window hierarchy (similar to what Netscape does
683 * with the FRAMESET tag). So to create a split view like
684 * this:
685 *
686 + +---------+------------+
687 + | | |
688 + | | 2 |
689 + | | |
690 + | 1 +------------+
691 + | | |
692 + | | 3 |
693 + | | |
694 + +---------+------------+
695 *
696 * First invoke ctlCreateSplitWindow to link "2" and "3"
697 * together (creating a horizontal split bar), which returns
698 * a "split window"; then link "1" and that split window
699 * together again.
700 *
701 *@@added V0.9.0 [umoeller]
702 */
703
704HWND ctlCreateSplitWindow(HAB hab,
705 PSPLITBARCDATA psbcd) // in: split window control data
706{
707 HWND hwndSplit = NULLHANDLE,
708 hwndBar = NULLHANDLE;
709
710 if (psbcd)
711 {
712 // register "split window" class
713 WinRegisterClass(hab,
714 WC_SPLITWINDOW,
715 ctl_fnwpSplitWindow,
716 CS_SIZEREDRAW | CS_SYNCPAINT,
717 0); // additional bytes to reserve
718
719 hwndSplit = WinCreateWindow(psbcd->hwndParentAndOwner, // parent
720 WC_SPLITWINDOW,
721 "",
722 WS_VISIBLE,
723 0, 0, 10, 10,
724 psbcd->hwndParentAndOwner, // owner
725 HWND_TOP,
726 psbcd->ulSplitWindowID,
727 NULL,
728 NULL);
729 if (hwndSplit)
730 {
731 hwndBar = WinCreateWindow(psbcd->hwndParentAndOwner, // parent
732 WC_STATIC,
733 "",
734 WS_VISIBLE // wnd style
735 | SS_TEXT,
736 0, 0, 10, 10,
737 psbcd->hwndParentAndOwner, // owner
738 HWND_TOP,
739 ID_SPLITBAR, // win ID
740 NULL, // cdata
741 NULL // presparams
742 );
743 if (hwndBar)
744 {
745 // create SPLITBARDATA to store in split bar's QWL_USER
746 PSPLITBARDATA pData = (PSPLITBARDATA)malloc(sizeof(SPLITBARDATA));
747 if (pData)
748 {
749 // set parent for split bar
750 WinSetParent(hwndBar, hwndSplit, FALSE);
751
752 memset(pData, 0, sizeof(SPLITBARDATA));
753
754 // copy control data
755 memcpy(&(pData->sbcd), psbcd, sizeof(SPLITBARCDATA));
756 // set other data
757 /* WinQueryWindowRect(hwndBar, &(pData->rclBar));
758 (pData->rclBar.xRight)--;
759 (pData->rclBar.yTop)--; */
760 // subclass static control to make it a split bar
761 pData->OldStaticProc = WinSubclassWindow(hwndBar, ctl_fnwpSplitBar);
762 pData->hptrOld = NULLHANDLE;
763 pData->hptrMove = WinQuerySysPointer(HWND_DESKTOP,
764 (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
765 ? SPTR_SIZENS
766 : SPTR_SIZEWE,
767 FALSE); // don't make copy
768 pData->fCaptured = FALSE;
769 pData->hwndLinked1 =
770 pData->hwndLinked2 = NULLHANDLE;
771
772 WinSetWindowULong(hwndBar, QWL_USER, (ULONG)pData);
773 }
774 }
775 }
776 }
777
778 return (hwndSplit);
779}
780
781/*
782 *@@ ctlUpdateSplitWindow:
783 * this function takes a split window as input and
784 * repositions all its children (the split bars
785 * and the other subwindows which are separated by the
786 * split bars) according to the parent.
787 *
788 * This function gets called from ctl_fnwpSplitWindow
789 * when WM_SIZE is received. Normally, you won't have
790 * to call this function independently; you should do
791 * a WinSetWindowPos on the split window instead.
792 *
793 *@@added V0.9.0 [umoeller]
794 */
795
796BOOL ctlUpdateSplitWindow(HWND hwndSplit)
797{
798 BOOL brc = FALSE;
799 HWND hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
800
801 if (hwndSplitBar)
802 {
803 PSPLITBARDATA psbd = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar, QWL_USER);
804
805 if (psbd)
806 {
807 PSPLITBARCDATA psbcd = &(psbd->sbcd);
808 RECTL rclSplit,
809 rclBar;
810 ULONG ul3DOfs = 0;
811
812 // _Pmpf(("Entering ctlUpdateSplitWindow for hwndSplit 0x%lX", hwndSplit));
813
814 // query the rectangle of the split window's parent;
815 // this is either the client or another split window
816 WinQueryWindowRect(hwndSplit, &rclSplit);
817
818 /* _Pmpf((" rect: %d, %d, %d, %d",
819 rclSplit.xLeft,
820 rclSplit.yBottom,
821 rclSplit.xRight - rclSplit.xLeft,
822 rclSplit.yTop - rclSplit.yBottom)); */
823
824 // set anti-recursion flag;
825 // this is neccessary, because ctl_fnwpSplitWindow
826 // calls this func again when
827 // WM_WINDOWPOSCHANGED comes in
828 // psbd->fNoAdjust = TRUE;
829 // set split window to the same
830 /* WinSetWindowPos(hwndSplit, HWND_TOP,
831 rclSplit.xLeft,
832 rclSplit.yBottom,
833 rclSplit.xRight - rclSplit.xLeft,
834 rclSplit.yTop - rclSplit.yBottom,
835 SWP_MOVE | SWP_SIZE); */
836 // psbd->fNoAdjust = FALSE;
837
838 // update split bar
839 if (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
840 {
841 // _Pmpf((" Calc horizontal"));
842 // horizontal split bar:
843 if (psbcd->ulCreateFlags & SBCF_PERCENTAGE)
844 // take height of client and apply percentage
845 rclBar.yBottom = (rclSplit.yTop - rclSplit.yBottom)
846 * psbcd->lPos
847 / 100;
848 else
849 if (psbcd->lPos > 0)
850 // offset from bottom:
851 rclBar.yBottom = psbcd->lPos;
852 else
853 // offset from right:
854 rclBar.yBottom = (rclSplit.yTop - rclSplit.yBottom)
855 + psbcd->lPos; // which is negative
856
857 rclBar.yTop = rclBar.yBottom + WinQuerySysValue(HWND_DESKTOP,
858 SV_CXSIZEBORDER);
859 rclBar.xLeft = 0;
860 // take width of client
861 rclBar.xRight = (rclSplit.xRight - rclSplit.xLeft);
862 }
863 else
864 {
865 // _Pmpf((" Calc vertical"));
866 // vertical split bar:
867 if (psbcd->ulCreateFlags & SBCF_PERCENTAGE)
868 // take width of client and apply percentage
869 rclBar.xLeft = (rclSplit.xRight - rclSplit.xLeft)
870 * psbcd->lPos
871 / 100;
872 else
873 if (psbcd->lPos > 0)
874 // offset from left:
875 rclBar.xLeft = psbcd->lPos;
876 else
877 // offset from right:
878 rclBar.xLeft = (rclSplit.xRight - rclSplit.xLeft)
879 + psbcd->lPos; // which is negative
880
881 rclBar.xRight = rclBar.xLeft + WinQuerySysValue(HWND_DESKTOP,
882 SV_CXSIZEBORDER);
883 rclBar.yBottom = 0;
884 // take height of client
885 rclBar.yTop = (rclSplit.yTop - rclSplit.yBottom);
886 }
887
888 // reposition split bar
889 brc = WinSetWindowPos(hwndSplitBar,
890 HWND_TOP,
891 rclBar.xLeft,
892 rclBar.yBottom,
893 rclBar.xRight - rclBar.xLeft,
894 rclBar.yTop - rclBar.yBottom,
895 SWP_MOVE | SWP_SIZE);
896
897 /* _Pmpf((" Set splitbar hwnd %lX to %d, %d, %d, %d; rc: %d",
898 hwndSplitBar,
899 rclBar.xLeft,
900 rclBar.yBottom,
901 rclBar.xRight - rclBar.xLeft,
902 rclBar.yTop - rclBar.yBottom,
903 brc)); */
904
905 // reposition left/bottom window of split bar
906 if (psbcd->ulCreateFlags & SBCF_3DSUNK)
907 ul3DOfs = 1;
908 // else 0
909
910 // now reposition the linked windows
911 if (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
912 {
913 // horizontal:
914 // reposition bottom window of split bar
915 WinSetWindowPos(psbd->hwndLinked1,
916 HWND_TOP,
917 ul3DOfs,
918 ul3DOfs,
919 rclBar.xRight - rclBar.xLeft - ul3DOfs*2,
920 rclBar.yBottom - ul3DOfs*2, // the window rect is non-inclusive
921 SWP_MOVE | SWP_SIZE);
922
923 // reposition top window of split bar
924 WinSetWindowPos(psbd->hwndLinked2,
925 HWND_TOP,
926 ul3DOfs,
927 rclBar.yTop + ul3DOfs, // the window rect is non-inclusive
928 rclBar.xRight - rclBar.xLeft - ul3DOfs*2,
929 rclSplit.yTop - rclBar.yTop - ul3DOfs*2,
930 SWP_MOVE | SWP_SIZE);
931 }
932 else
933 {
934 // vertical:
935 // reposition left window of split bar
936 WinSetWindowPos(psbd->hwndLinked1,
937 HWND_TOP,
938 ul3DOfs,
939 ul3DOfs,
940 rclBar.xLeft - ul3DOfs*2, // the window rect is non-inclusive
941 rclBar.yTop - rclBar.yBottom - ul3DOfs*2,
942 SWP_MOVE | SWP_SIZE);
943
944 // reposition right window of split bar
945 WinSetWindowPos(psbd->hwndLinked2,
946 HWND_TOP,
947 rclBar.xRight + ul3DOfs, // the window rect is non-inclusive
948 ul3DOfs,
949 rclSplit.xRight - rclBar.xRight - ul3DOfs*2,
950 rclBar.yTop - rclBar.yBottom - ul3DOfs*2,
951 SWP_MOVE | SWP_SIZE);
952 }
953
954 // repaint split window (3D frame)
955 WinInvalidateRect(hwndSplit,
956 NULL, // all
957 FALSE); // don't repaint children
958 }
959 }
960
961 return (brc);
962}
963
964/*
965 *@@ ctlQuerySplitPos:
966 * this returns the position of the split bar
967 * (the child control of hwndSplit; see ctlCreateSplitWindow
968 * for the window hierarchy).
969 *
970 * The meaning of the return value depends on what you
971 * specified with ulCreateFlags in the SPLITBARCDATA
972 * structure passed to ctlCreateSplitWindow, that is,
973 * it can be a percentage or an offset from the left
974 * or from the right of the split window.
975 *
976 * This returns NULL upon errors.
977 *
978 *@@added V0.9.0 [umoeller]
979 */
980
981LONG ctlQuerySplitPos(HWND hwndSplit)
982{
983 ULONG lrc = 0;
984
985 // the split bar data is stored in QWL_USER of the
986 // split bar (not the split window)
987 HWND hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
988 if (hwndSplitBar)
989 {
990 PSPLITBARDATA psbd = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar, QWL_USER);
991 if (psbd)
992 lrc = psbd->sbcd.lPos;
993 }
994
995 return (lrc);
996}
997
998
Note: See TracBrowser for help on using the repository browser.