source: branches/branch-1-0/src/helpers/cctl_splitwin.c

Last change on this file was 313, checked in by pr, 19 years ago

Fix split window cosmetics. Bug 228

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