]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/common/RollupPanel.cpp
hello world
[icculus/iodoom3.git] / neo / tools / common / RollupPanel.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "../../sys/win32/win_local.h"
33 #include "RollupPanel.h"
34
35 // Based on original code by Johann Nadalutti
36
37 #define RP_PGBUTTONHEIGHT               18
38 #define RP_SCROLLBARWIDTH               6
39 #define RP_GRPBOXINDENT                 6
40 #define RP_SCROLLBARCOLOR               RGB(150,180,180)
41 #define RP_ROLLCURSOR                   MAKEINTRESOURCE(32649)  // see IDC_HAND (WINVER >= 0x0500)
42
43 //Popup Menu Ids
44 #define RP_IDM_EXPANDALL                0x100
45 #define RP_IDM_COLLAPSEALL              0x101
46 #define RP_IDM_STARTITEMS               0x102
47
48 idList<HWND>    rvRollupPanel::mDialogs;        
49 HHOOK                   rvRollupPanel::mDialogHook      = NULL;
50
51 #define DEFERPOS
52
53 /*
54 ================
55 rvRollupPanel::rvRollupPanel
56
57 constructor
58 ================
59 */
60 rvRollupPanel::rvRollupPanel ( void )
61 {
62         mStartYPos  = 0;
63         mItemHeight = 0;
64         mWindow         = NULL;
65 }
66
67 /*
68 ================
69 rvRollupPanel::~rvRollupPanel
70
71 destructor
72 ================
73 */
74 rvRollupPanel::~rvRollupPanel ( void )
75 {
76         // destroy the items
77         for ( ; mItems.Num(); )
78         {
79                 _RemoveItem ( 0 );
80         }
81 }
82
83 /*
84 ================
85 rvRollupPanel::Create
86
87 Create the rollup panel window
88 ================
89 */
90 bool rvRollupPanel::Create ( DWORD dwStyle, const RECT& rect, HWND parent, unsigned int id )
91 {
92         WNDCLASSEX wndClass;
93         memset ( &wndClass, 0, sizeof(wndClass) );
94         wndClass.cbSize            = sizeof(WNDCLASSEX);
95         wndClass.lpszClassName = "ROLLUP_PANEL";
96         wndClass.lpfnWndProc   = WindowProc;
97         wndClass.hbrBackground = (HBRUSH)GetSysColorBrush ( COLOR_3DFACE ); 
98         wndClass.hCursor       = LoadCursor((HINSTANCE) NULL, IDC_ARROW); 
99         wndClass.lpszMenuName  = NULL;
100         wndClass.hInstance     = win32.hInstance; 
101         wndClass.style             = CS_VREDRAW | CS_HREDRAW;
102         RegisterClassEx ( &wndClass );
103
104         mWindow = CreateWindowEx ( WS_EX_TOOLWINDOW, 
105                                                         "ROLLUP_PANEL", 
106                                                         "", 
107                                                         dwStyle|WS_CLIPSIBLINGS, 
108                                                         rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
109                                                         parent, 
110                                                         NULL, 
111                                                         win32.hInstance, 
112                                                         this );
113                                                         
114         if ( !mWindow )
115         {
116                 return false;
117         }
118
119         return true;    
120 }
121
122 /*
123 ================
124 rvRollupPanel::InsertItem
125
126 Insert and item into the rollup panel.  Return -1 if an error occured
127 ================
128 */
129 int rvRollupPanel::InsertItem ( const char* caption, HWND dialog, bool autoDestroy, int index )
130 {
131         assert ( caption );
132         assert ( dialog );
133
134         // -1 means add to the end      
135         if ( index > 0 && index >= mItems.Num() )
136         {
137                 index = -1;
138         }
139
140         // Get client rect
141         RECT r;
142         GetClientRect(mWindow,&r);
143
144         // Create the GroupBox control
145         HWND groupbox = CreateWindow ( "BUTTON", "", WS_CHILD|BS_GROUPBOX, 
146                                                                    r.left, r.top, r.right-r.left, r.bottom-r.top,
147                                                                    mWindow, 0, win32.hInstance, NULL );
148                                                                    
149         // Create the expand button
150         HWND button = CreateWindow ( "BUTTON", caption, WS_CHILD|BS_AUTOCHECKBOX|BS_PUSHLIKE|BS_FLAT, 
151                                                                    r.left, r.top, r.right-r.left, r.bottom-r.top,
152                                                                    mWindow, 0, win32.hInstance, NULL );
153
154         // Change the button's font
155         SendMessage ( button, WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0 );
156
157         // Add item to the item list
158         RPITEM* item = new RPITEM; 
159         item->mExpanded          = false;
160         item->mEnable            = true;
161         item->mDialog            = dialog;
162         item->mButton            = button;
163         item->mGroupBox          = groupbox;
164         item->mOldDlgProc        = (WNDPROC) GetWindowLong ( dialog, DWL_DLGPROC );
165         item->mOldButtonProc = (WNDPROC) GetWindowLong ( button, GWL_WNDPROC );
166         item->mAutoDestroy       = autoDestroy;
167         strcpy ( item->mCaption, caption );
168
169         if ( index < 0 )
170         {
171                 index = mItems.Append ( item );
172         }
173         else
174         { 
175                 mItems.Insert ( item, index );
176         }
177
178         // Store data with the dialog window in its user data 
179         SetWindowLong ( dialog, GWL_USERDATA,   (LONG)item );
180
181         // Attach item to button through user data
182         SetWindowLong ( button, GWL_USERDATA,   (LONG)item );
183         SetWindowLong ( button, GWL_ID,                 index );
184
185         // Subclass dialog
186         SetWindowLong ( dialog, DWL_DLGPROC, (LONG)DialogProc );
187
188         // SubClass button
189         SetWindowLong ( button, GWL_WNDPROC, (LONG)ButtonProc );
190
191         // Update
192         mItemHeight += RP_PGBUTTONHEIGHT+(RP_GRPBOXINDENT/2);
193         RecallLayout ( );
194
195         // One hook for all panel dialogs
196         if ( !mDialogHook )
197         {
198                 mDialogHook = SetWindowsHookEx( WH_GETMESSAGE, GetMsgProc, NULL, GetCurrentThreadId() );
199         }
200         
201         mDialogs.Append ( dialog );
202
203         return index;
204 }
205
206 /*
207 ================
208 rvRollupPanel::RemoveItem
209
210 Remove the item at the given index from the rollup panel
211 ================
212 */
213 void rvRollupPanel::RemoveItem ( int index )
214 {
215         // safety check
216         if ( index >= mItems.Num() || index < 0 )
217         {
218                 return;
219         }
220
221         // remove the item
222         _RemoveItem( index );
223
224         // update the layout
225         RecallLayout ( );       
226 }
227
228 /*
229 ================
230 rvRollupPanel::RemoveAllItems
231
232 Remove all items from the control
233 ================
234 */
235 void rvRollupPanel::RemoveAllItems()
236 {
237         for ( ; mItems.Num(); )
238         {
239                 _RemoveItem ( 0 );
240         }
241
242         // update layout
243         RecallLayout ( );
244 }
245
246 /*
247 ================
248 rvRollupPanel::_RemoveItem
249
250 called by RemoveItem and RemoveAllItems methods to acutally remove the item
251 ================
252 */
253 void rvRollupPanel::_RemoveItem ( int index )
254 {
255         RPITEM* item = mItems[index];
256
257         // get the item rect
258         RECT ir;
259         GetWindowRect ( item->mDialog, &ir );
260
261         // update item height
262         mItemHeight -= RP_PGBUTTONHEIGHT+(RP_GRPBOXINDENT/2);
263         if ( item->mExpanded )
264         {
265                 mItemHeight -= (ir.bottom-ir.top);
266         }
267
268         // destroy windows
269         if ( item->mButton ) 
270         {
271                 DestroyWindow ( item->mButton );
272         }
273         if ( item->mGroupBox )
274         {
275                 DestroyWindow ( item->mGroupBox );
276         }
277         if ( item->mDialog && item->mAutoDestroy )
278         {
279                 DestroyWindow ( item->mDialog );
280                 mDialogs.Remove ( item->mDialog );              
281         }
282
283         if ( mDialogs.Num () <= 0 )
284         {
285                 UnhookWindowsHookEx( mDialogHook );
286                 mDialogHook = NULL;
287         }
288
289         // finish up
290         mItems.RemoveIndex ( index );
291         delete item;            
292 }
293
294 /*
295 ================
296 rvRollupPanel::ExpandItem
297
298 expand or collapse the item at the given index
299 ================
300 */
301 void rvRollupPanel::ExpandItem( int index, bool expand )
302 {
303         // safety check
304         if ( index >= mItems.Num() || index < 0 )
305         {
306                 return;
307         }
308
309         _ExpandItem ( mItems[index], expand );
310
311         RecallLayout ( );
312
313         // scroll to this page (automatic page visibility)
314         if ( expand )
315         {
316                 ScrollToItem ( index, false );
317         }
318 }
319
320 /*
321 ================
322 rvRollupPanel::ExpandItem
323
324 expand or collapse the item at the given index
325 ================
326 */
327 void rvRollupPanel::ExpandAllItems( bool expand )
328 {
329         int i;
330         
331         // expand all items
332         for ( i=0; i < mItems.Num(); i ++ )
333         {
334                 _ExpandItem ( mItems[i], expand );
335         }
336
337         RecallLayout();
338 }
339
340 /*
341 ================
342 rvRollupPanel::ExpandItem
343
344 expand or collapse the item at the given index
345 ================
346 */
347 void rvRollupPanel::_ExpandItem ( RPITEM* item, bool expand )
348 {
349         // check if we need to change state
350         if ( item->mExpanded == expand || !item->mEnable )
351         {
352                 return;
353         }
354
355         RECT ir;
356         GetWindowRect ( item->mDialog, &ir );
357
358         // Expand-collapse
359         item->mExpanded = expand;
360
361         if ( expand )
362         {
363                 mItemHeight += (ir.bottom - ir.top);
364         }
365         else
366         {
367                 mItemHeight -= (ir.bottom - ir.top);
368         }
369 }
370
371 /*
372 ================
373 rvRollupPanel::EnableItem
374
375 enable/disable the item at the given index
376 ================
377 */
378 void rvRollupPanel::EnableItem ( int index, bool enable )
379 {
380         // safety check
381         if ( index >= mItems.Num() || index < 0 )
382         {
383                 return;
384         }
385
386         _EnableItem ( mItems[index], enable );
387         RecallLayout ( );
388 }
389
390 /*
391 ================
392 rvRollupPanel::EnableAllItems
393
394 enable/disable all items in the panel
395 ================
396 */
397 void rvRollupPanel::EnableAllItems ( bool enable )
398 {
399         int i;
400         
401         for ( i=0; i < mItems.Num(); i++ )
402         {
403                 _EnableItem ( mItems[i], enable );
404         }
405         
406         RecallLayout ( );
407 }
408
409 /*
410 ================
411 rvRollupPanel::_EnableItem
412
413 Called by EnableItem and EnableAllItems to do the work of enabling/disablgin
414 the window
415 ================
416 */
417 void rvRollupPanel::_EnableItem ( RPITEM* item, bool enable )
418 {
419         // check if we need to change state
420         if ( item->mEnable == enable )
421         {
422                 return;
423         }
424
425         RECT ir;
426         GetWindowRect ( item->mDialog, &ir );
427
428         item->mEnable = enable;
429
430         if ( item->mExpanded )
431         { 
432                 mItemHeight -= (ir.bottom-ir.top); 
433                 item->mExpanded = false;
434         }
435 }
436
437 /*
438 ================
439 rvRollupPanel::ScrollToItem
440
441 Scroll a page at the top of the Rollup Panel if top = true or just ensure 
442 item visibility into view if top = false 
443 ================
444 */
445 void rvRollupPanel::ScrollToItem ( int index, bool top )
446 {
447         // safety check
448         if ( index >= mItems.Num() || index < 0 ) 
449         {
450                 return;
451         }
452
453         RPITEM* item = mItems[index];
454
455         // get rects
456         RECT r;
457         RECT ir;
458         GetWindowRect ( mWindow, &r );
459         GetWindowRect ( item->mDialog, &ir );
460
461         // check page visibility
462         if ( top || ((ir.bottom > r.bottom) || (ir.top < r.top)))
463         {
464                 // compute new mStartYPos
465                 GetWindowRect( item->mButton, &ir );
466                 mStartYPos -= (ir.top-r.top);
467
468                 RecallLayout();
469         }
470 }
471
472 /*
473 ================
474 rvRollupPanel::MoveItemAt
475
476 newIndex can be equal to -1 (move at end)
477 return -1 if an error occurs
478 ================
479 */
480 int rvRollupPanel::MoveItemAt ( int index, int newIndex )
481 {
482         if ( index == newIndex || index >= mItems.Num() || index < 0 )
483         {
484                 return -1;
485         }
486
487         // remove page from its old position
488         RPITEM* item = mItems[index];
489         mItems.RemoveIndex ( index );
490
491         // insert at its new position
492         if ( newIndex < 0 )
493         {
494                 index = mItems.Append( item );
495         }
496         else
497         { 
498                 mItems.Insert ( item, newIndex );
499                 index = newIndex;
500         }
501
502         RecallLayout ( );
503         
504         return index;
505 }
506
507 /*
508 ================
509 rvRollupPanel::RecallLayout
510
511 Update the layout of the control based on current states
512 ================
513 */
514 void rvRollupPanel::RecallLayout ( void )
515 {
516         int      bottomPagePos;
517         RECT r;
518         int  posy;
519         int      i;
520         
521         // check StartPosY
522         GetClientRect ( mWindow, &r );
523         bottomPagePos = mStartYPos + mItemHeight;
524
525         if ( bottomPagePos < r.bottom-r.top )
526         {
527                 mStartYPos = (r.bottom-r.top) - mItemHeight;
528         }
529         if ( mStartYPos > 0 )
530         {
531                 mStartYPos = 0;
532         }
533
534         // update layout
535 #ifdef DEFERPOS                 
536         HDWP hdwp;
537         hdwp = BeginDeferWindowPos ( mItems.Num() * 3 );
538 #endif
539         posy = mStartYPos;
540
541         for ( i=0; i < mItems.Num(); i++ )
542         {
543                 RPITEM* item = mItems[i];
544
545                 // enable / disable button
546                 SendMessage ( item->mButton, BM_SETCHECK, (item->mEnable&item->mExpanded)?BST_CHECKED:BST_UNCHECKED, 0 );
547                 EnableWindow ( item->mButton, item->mEnable );
548
549                 // Expanded
550                 if ( item->mExpanded && item->mEnable ) 
551                 {
552                         RECT ir;
553                         GetWindowRect ( item->mDialog, &ir );
554
555                         // update GroupBox position and size
556 #ifdef DEFERPOS                 
557                         DeferWindowPos ( hdwp, 
558 #else
559                         SetWindowPos (   
560 #endif
561                                                          item->mGroupBox, 0, 2, posy, 
562                                                          (r.right-r.left)-3-RP_SCROLLBARWIDTH, 
563                                                          (ir.bottom-ir.top)+RP_PGBUTTONHEIGHT+RP_GRPBOXINDENT-4, 
564                                                          SWP_NOZORDER|SWP_SHOWWINDOW);
565
566                         //Update Dialog position and size
567 #ifdef DEFERPOS                 
568                         DeferWindowPos ( hdwp, 
569 #else
570                         SetWindowPos (   
571 #endif
572                                                          item->mDialog, 0, RP_GRPBOXINDENT, posy+RP_PGBUTTONHEIGHT, 
573                                                          (r.right-r.left)-RP_SCROLLBARWIDTH-(RP_GRPBOXINDENT*2), 
574                                                          ir.bottom-ir.top, SWP_NOZORDER|SWP_SHOWWINDOW);
575
576                         //Update Button's position and size
577 #ifdef DEFERPOS                 
578                         DeferWindowPos ( hdwp, 
579 #else
580                         SetWindowPos (   
581 #endif
582                                                          item->mButton, 0, RP_GRPBOXINDENT, posy, 
583                                                          (r.right-r.left)-RP_SCROLLBARWIDTH-(RP_GRPBOXINDENT*2), 
584                                                          RP_PGBUTTONHEIGHT, SWP_NOZORDER|SWP_SHOWWINDOW);
585
586                         posy += (ir.bottom-ir.top) + RP_PGBUTTONHEIGHT;
587                 } 
588                 // collapsed
589                 else 
590                 {
591                         // update GroupBox position and size
592 #ifdef DEFERPOS                 
593                         DeferWindowPos ( hdwp, 
594 #else
595                         SetWindowPos (   
596 #endif
597                                                          item->mGroupBox, 0, 2, posy, 
598                                                          (r.right-r.left)-3-RP_SCROLLBARWIDTH, 16, SWP_NOZORDER|SWP_SHOWWINDOW);
599
600                         // update Dialog position and size
601 #ifdef DEFERPOS                 
602                         DeferWindowPos ( hdwp, 
603 #else
604                         SetWindowPos (   
605 #endif
606                                                         item->mDialog, 0, RP_GRPBOXINDENT, 0, 0, 0,SWP_NOZORDER|SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE);
607
608                         // update Button's position and size
609 #ifdef DEFERPOS                 
610                         DeferWindowPos ( hdwp, 
611 #else
612                         SetWindowPos (   
613 #endif
614                                                         item->mButton, 0, RP_GRPBOXINDENT, posy, 
615                                                         (r.right-r.left)-RP_SCROLLBARWIDTH-(RP_GRPBOXINDENT*2), 
616                                                         RP_PGBUTTONHEIGHT, SWP_NOZORDER|SWP_SHOWWINDOW);
617
618                         posy += RP_PGBUTTONHEIGHT;
619                 }
620
621                 posy += (RP_GRPBOXINDENT/2);
622
623         }
624         
625 #ifdef DEFERPOS                 
626         EndDeferWindowPos ( hdwp );
627 #endif
628
629         // update Scroll Bar
630         RECT br;
631         SetRect ( &br, r.right-RP_SCROLLBARWIDTH,r.top,r.right,r.bottom);               
632         InvalidateRect( mWindow, &br, FALSE );
633         UpdateWindow ( mWindow );
634 }
635
636 /*
637 ================
638 rvRollupPanel::GetItemIndex
639
640 Return -1 if no matching item was found, otherwise the index of the item
641 ================
642 */
643 int rvRollupPanel::GetItemIndex ( HWND wnd )
644 {
645         int i;
646         
647         //Search matching button's hwnd
648         for ( i=0; i < mItems.Num(); i++ )
649         {
650                 if ( wnd == mItems[i]->mButton )
651                 {
652                         return i;
653                 }
654         }
655
656         return -1;
657 }
658
659 int rvRollupPanel::GetItemIndex ( const char* caption )
660 {
661         int i;
662         
663         //Search matching button's hwnd
664         for ( i=0; i < mItems.Num(); i++ )
665         {
666                 if ( !idStr::Icmp ( caption, mItems[i]->mCaption ) )
667                 {
668                         return i;
669                 }
670         }
671
672         return -1;
673 }
674
675 /*
676 ================
677 rvRollupPanel::GetItem
678
679 Return NULL if the index is invalid
680 ================
681 */
682 RPITEM* rvRollupPanel::GetItem ( int index )
683 {
684         // safety check
685         if ( index >= mItems.Num() || index < 0 ) 
686         {
687                 return NULL;
688         }
689
690         return mItems[index];
691 }
692
693 /*
694 ================
695 rvRollupPanel::DialogProc
696
697 Dialog procedure for items
698 ================
699 */
700 LRESULT CALLBACK rvRollupPanel::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
701 {
702         RPITEM*                 item  = (RPITEM*)GetWindowLong ( hWnd, GWL_USERDATA );
703         rvRollupPanel*  _this = (rvRollupPanel*)GetWindowLong ( GetParent ( hWnd ), GWL_USERDATA );
704
705         RECT r;
706         GetClientRect ( _this->mWindow, &r );
707
708         if ( _this->mItemHeight > r.bottom-r.top )
709         {
710                 switch (uMsg) 
711                 {
712                         case WM_LBUTTONDOWN:
713                         case WM_MBUTTONDOWN:
714                         {
715                                 POINT pos;
716                                 GetCursorPos ( &pos );
717                                 _this->mOldMouseYPos = pos.y;
718                                 ::SetCapture(hWnd);
719                                 return 0;
720                         }
721
722                         case WM_LBUTTONUP:
723                         case WM_MBUTTONUP:
724                         {
725                                 if ( ::GetCapture() == hWnd )
726                                 { 
727                                         ::ReleaseCapture(); 
728                                         return 0; 
729                                 }
730                                 break;
731                         }
732
733                         case WM_MOUSEMOVE:
734                                 if ( (::GetCapture() == hWnd) && (wParam==MK_LBUTTON || wParam==MK_MBUTTON)) 
735                                 {
736                                         POINT pos;
737                                         GetCursorPos(&pos);
738                                         _this->mStartYPos += (pos.y-_this->mOldMouseYPos);
739                                         _this->RecallLayout();
740                                         _this->mOldMouseYPos = pos.y;
741                                         InvalidateRect ( _this->mWindow, NULL, TRUE );
742                                         return 0;
743                                 }
744
745                                 break;
746
747                         case WM_SETCURSOR:
748                                 if ( (HWND)wParam == hWnd)
749                                 { 
750                                         SetCursor ( LoadCursor (NULL, RP_ROLLCURSOR) ); 
751                                         return TRUE; 
752                                 }
753                                 break;
754                 }
755         }
756
757         return ::CallWindowProc ( item->mOldDlgProc, hWnd, uMsg, wParam, lParam );
758 }
759
760 /*
761 ================
762 rvRollupPanel::DialogProc
763
764 Button procedure for items
765 ================
766 */
767 LRESULT CALLBACK rvRollupPanel::ButtonProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
768 {
769         if ( uMsg == WM_SETFOCUS )
770         {
771                 return FALSE;
772         }
773
774         RPITEM* item = (RPITEM*)GetWindowLong(hWnd, GWL_USERDATA);      
775         return ::CallWindowProc( item->mOldButtonProc, hWnd, uMsg, wParam, lParam );
776 }
777
778 /*
779 ================
780 rvRollupPanel::WindowProc
781
782 Window procedure for rollup panel
783 ================
784 */
785 LRESULT CALLBACK rvRollupPanel::WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
786 {
787         rvRollupPanel* panel;
788         panel = (rvRollupPanel*)GetWindowLong (hWnd, GWL_USERDATA);     
789         
790         switch ( uMsg )
791         {
792                 case WM_CREATE:
793                 {
794                         LPCREATESTRUCT  cs;
795
796                         // Attach the class to the window first
797                         cs = (LPCREATESTRUCT) lParam;
798                         panel = (rvRollupPanel*) cs->lpCreateParams;
799                         SetWindowLong ( hWnd, GWL_USERDATA, (LONG)panel );
800                         break;
801                 }
802                 
803                 case WM_COMMAND:
804                         panel->HandleCommand ( wParam, lParam );
805                         break;
806                         
807                 case WM_PAINT:
808                         return panel->HandlePaint ( wParam, lParam );                   
809                         
810                 case WM_SIZE:
811                         return panel->HandleSize ( wParam, lParam );
812                         
813                 case WM_LBUTTONDOWN:
814                         panel->HandleLButtonDown ( wParam, lParam );
815                         break;
816                         
817                 case WM_LBUTTONUP:
818                         panel->HandleLButtonUp ( wParam, lParam );
819                         break;
820                 
821                 case WM_MOUSEMOVE:
822                         panel->HandleMouseMove ( wParam, lParam );
823                         break;
824                         
825                 case WM_MOUSEWHEEL:
826                         panel->HandleMouseWheel ( wParam, lParam );
827                         break;
828                 
829                 case WM_MOUSEACTIVATE:
830                         panel->HandleMouseActivate ( wParam, lParam );
831                         break;
832                         
833                 case WM_CONTEXTMENU:
834                         return panel->HandleContextMenu ( wParam, lParam );                     
835         }
836                         
837         return DefWindowProc ( hWnd, uMsg, wParam, lParam );
838 }
839
840 /*
841 ================
842 rvRollupPanel::HandleCommand
843
844 Handle the WM_COMMAND message
845 ================
846 */
847 int rvRollupPanel::HandleCommand ( WPARAM wParam, LPARAM lParam ) 
848 {
849         // popup menu command to expand or collapse pages
850         if ( LOWORD(wParam) == RP_IDM_EXPANDALL )
851         {
852                 ExpandAllItems ( true );
853         }
854         else if ( LOWORD(wParam) == RP_IDM_COLLAPSEALL )
855         {
856                 ExpandAllItems ( false );
857         }
858
859         // popupMenu command to expand page
860         else if ( LOWORD(wParam) >= RP_IDM_STARTITEMS && 
861                           LOWORD(wParam) <  RP_IDM_STARTITEMS + GetItemCount ( ) )
862         {
863                 int index = LOWORD(wParam)-RP_IDM_STARTITEMS;
864                 ExpandItem ( index, !IsItemExpanded(index) );
865         }
866
867         // button command
868         else if ( HIWORD(wParam) == BN_CLICKED )
869         {
870                 int index = GetItemIndex ((HWND)lParam);
871                 if ( index != -1 ) 
872                 {
873                         ExpandItem ( index, !IsItemExpanded ( index ) );
874                         return 0;
875                 }
876         }
877
878         return 0;
879 }
880
881 /*
882 ================
883 rvRollupPanel::HandlePaint
884
885 Handle the WM_PAINT message
886 ================
887 */
888 int rvRollupPanel::HandlePaint( WPARAM wParam, LPARAM lParam ) 
889 {
890         HDC                     dc;
891         PAINTSTRUCT ps;
892         RECT            r;
893         RECT            br;
894         int                     sbPos;
895         int                     sbSize;
896         int                     clientHeight;
897         
898         dc = BeginPaint ( mWindow, &ps );
899
900         // scrollbar
901         GetClientRect ( mWindow, &r );
902         SetRect ( &br, r.right-RP_SCROLLBARWIDTH, r.top, r.right, r.bottom );
903         DrawEdge ( dc, &br, EDGE_RAISED, BF_RECT  );
904
905         sbPos = 0;
906         sbSize = 0;
907         clientHeight = (r.bottom-r.top) - 4;
908
909         if ( mItemHeight > (r.bottom-r.top) ) 
910         {
911                 sbSize = clientHeight - (((mItemHeight-(r.bottom-r.top)) * clientHeight ) / mItemHeight );
912                 sbPos  = -(mStartYPos * clientHeight) / mItemHeight;
913         } 
914         else 
915         {
916                 sbSize = clientHeight;
917         }
918
919         br.left         +=2;
920         br.right        -=1;
921         br.top          = sbPos+2;
922         br.bottom       = br.top+sbSize;
923
924         HBRUSH brush;
925         brush = CreateSolidBrush ( RP_SCROLLBARCOLOR );
926         FillRect ( dc, &br, brush );
927         DeleteObject ( brush );
928
929         SetRect ( &r, br.left,2,br.right,br.top );
930         FillRect ( dc, &r, (HBRUSH)GetStockObject ( BLACK_BRUSH ) );
931
932         SetRect ( &r, br.left,br.bottom,br.right,2+clientHeight );
933         FillRect ( dc, &r, (HBRUSH)GetStockObject ( BLACK_BRUSH ) );
934         
935         return 0;
936 }
937
938 /*
939 ================
940 rvRollupPanel::HandleSize
941
942 Handle the WM_SIZE message
943 ================
944 */
945 int rvRollupPanel::HandleSize ( WPARAM wParam, LPARAM lParam )
946 {
947         DefWindowProc ( mWindow, WM_SIZE, wParam, lParam );
948         RecallLayout();
949         return 0;
950 }
951
952 /*
953 ================
954 rvRollupPanel::HandleLButtonDown
955
956 Handle the WM_LBUTTONDOWN message
957 ================
958 */
959 int rvRollupPanel::HandleLButtonDown ( WPARAM wParam, LPARAM lParam ) 
960 {
961         RECT    r;
962         RECT    br;
963         POINT   point;
964         
965         GetClientRect ( mWindow, &r );
966         if ( mItemHeight <= r.bottom - r.top )
967         {
968                 return 0;
969         }
970
971         point.x = LOWORD(lParam);
972         point.y = HIWORD(lParam);
973
974         SetRect ( &br, r.right - RP_SCROLLBARWIDTH, r.top, r.right, r.bottom );
975
976         if ( (wParam & MK_LBUTTON) && PtInRect ( &br, point ) ) 
977         {
978                 SetCapture( mWindow );
979
980                 int clientHeight = (r.bottom-r.top) - 4;
981
982                 int sbSize = clientHeight - (((mItemHeight - (r.bottom-r.top)) * clientHeight) / mItemHeight );
983                 int     sbPos  = -(mStartYPos * clientHeight) / mItemHeight;
984
985                 // click inside scrollbar cursor
986                 if ( (point.y < (sbPos + sbSize)) && (point.y > sbPos )) 
987                 {
988                         mSBOffset = sbPos - point.y + 1;                
989                 } 
990                 // click outside scrollbar cursor (2 cases => above or below cursor)
991                 else 
992                 {
993                         int distup       = point.y - sbPos;     
994                         int distdown = (sbPos + sbSize) - point.y;
995                         
996                         if ( distup < distdown )
997                         {
998                                 //above
999                                 mSBOffset = 0;
1000                         }
1001                         else
1002                         {
1003                                 //below
1004                                 mSBOffset = -sbSize;
1005                         }
1006                 }
1007
1008                 // calc new m_nStartYPos from mouse pos
1009                 int targetPos = point.y + mSBOffset;
1010                 mStartYPos =- (targetPos * mItemHeight) / clientHeight;
1011
1012                 // update
1013                 RecallLayout();
1014         }
1015         
1016         return 0;
1017 }
1018
1019 /*
1020 ================
1021 rvRollupPanel::HandleLButtonUp
1022
1023 Handle the WM_LBUTTONUP message
1024 ================
1025 */
1026 int rvRollupPanel::HandleLButtonUp ( WPARAM wParam, LPARAM lParam ) 
1027 {
1028         if ( GetCapture() == mWindow )
1029         {
1030                 ReleaseCapture();
1031         }
1032         
1033         return 0;
1034 }
1035
1036 /*
1037 ================
1038 rvRollupPanel::HandleMouseMove
1039
1040 Handle the WM_MOUSEMOVE message
1041 ================
1042 */
1043 int rvRollupPanel::HandleMouseMove ( WPARAM wParam, LPARAM lParam ) 
1044 {
1045         RECT  r;
1046         RECT  br;
1047         POINT point;
1048         
1049         GetClientRect ( mWindow, &r );
1050         if ( mItemHeight <= r.bottom - r.top )
1051         {
1052                 return 0;
1053         }
1054
1055         point.x = LOWORD(lParam);
1056         point.y = HIWORD(lParam);
1057
1058         SetRect ( &br, r.right - RP_SCROLLBARWIDTH, r.top, r.right, r.bottom );
1059
1060         if ( (wParam & MK_LBUTTON) && (GetCapture() == mWindow )) 
1061         {
1062                 // calc new m_nStartYPos from mouse pos
1063                 int clientHeight        = (r.bottom-r.top) - 4;
1064                 int targetPos           = point.y + mSBOffset;
1065                 
1066                 mStartYPos =- (targetPos * mItemHeight) / clientHeight;
1067
1068                 RecallLayout ( );
1069                 
1070                 InvalidateRect ( mWindow, NULL, FALSE );                
1071 //              UpdateWindow ( mWindow );
1072         }
1073
1074         return 0;
1075 }
1076
1077 /*
1078 ================
1079 rvRollupPanel::HandleMouseWheel
1080
1081 Handle the WM_MOUSEWHEEL message
1082 ================
1083 */
1084 int rvRollupPanel::HandleMouseWheel ( WPARAM wParam, LPARAM lParam ) 
1085 {
1086         // calc new m_nStartYPos
1087         mStartYPos += (HIWORD(wParam) / 4);
1088
1089         RecallLayout();
1090
1091         return 0;
1092 }
1093
1094 /*
1095 ================
1096 rvRollupPanel::HandleMouseActivate
1097
1098 Handle the WM_MOUSEACTIVATE message
1099 ================
1100 */
1101 int rvRollupPanel::HandleMouseActivate  ( WPARAM wParam, LPARAM lParam ) 
1102 {
1103         SetFocus ( mWindow );
1104         return 0;
1105 }
1106
1107 /*
1108 ================
1109 rvRollupPanel::HandleContextMenu
1110
1111 Handle the WM_CONTEXTMENU message
1112 ================
1113 */
1114 int rvRollupPanel::HandleContextMenu ( WPARAM wParam, LPARAM lParam )
1115 {
1116         HMENU menu;
1117         int       i;
1118         POINT point;
1119         
1120         menu = CreatePopupMenu ( );
1121         if ( !menu )
1122         {
1123                 return 0;
1124         }
1125
1126         point.x = LOWORD(lParam);
1127         point.y = HIWORD(lParam);
1128
1129         AppendMenu ( menu, MF_STRING,           RP_IDM_EXPANDALL,       "Expand all"    );
1130         AppendMenu ( menu, MF_STRING,           RP_IDM_COLLAPSEALL,     "Collapse all"  );
1131         AppendMenu ( menu, MF_SEPARATOR,        0,                                      ""                              );
1132
1133         //Add all pages with checked style for expanded ones
1134         for ( i=0; i < mItems.Num(); i++ )
1135         {
1136                 char itemName[1024];
1137                 GetWindowText ( mItems[i]->mButton, itemName, 1023 );
1138                 AppendMenu ( menu, MF_STRING, RP_IDM_STARTITEMS + i, itemName );        
1139
1140                 if ( mItems[i]->mExpanded )
1141                 {
1142                         CheckMenuItem ( menu, RP_IDM_STARTITEMS + i, MF_CHECKED);
1143                 }
1144
1145                 TrackPopupMenu ( menu, TPM_LEFTALIGN|TPM_LEFTBUTTON, point.x, point.y, 0, mWindow, NULL );
1146         }
1147         
1148         return 0;
1149 }
1150
1151 /*
1152 ================
1153 rvRollupPanel::GetMsgProc
1154
1155 Ensures normal dialog functions work in the alpha select dialog
1156 ================
1157 */
1158 LRESULT FAR PASCAL rvRollupPanel::GetMsgProc ( int nCode, WPARAM wParam, LPARAM lParam )
1159 {
1160         LPMSG lpMsg = (LPMSG) lParam;
1161
1162         if ( nCode >= 0 && PM_REMOVE == wParam )
1163         {
1164                 // Don't translate non-input events.
1165                 if ( (lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST) )
1166                 {
1167                         int i;
1168                         for ( i = 0; i < mDialogs.Num(); i ++ )
1169                         {
1170                                 if ( IsDialogMessage( mDialogs[i], lpMsg) )
1171                                 {
1172                                         // The value returned from this hookproc is ignored, 
1173                                         // and it cannot be used to tell Windows the message has been handled.
1174                                         // To avoid further processing, convert the message to WM_NULL 
1175                                         // before returning.
1176                                         lpMsg->message = WM_NULL;
1177                                         lpMsg->lParam  = 0;
1178                                         lpMsg->wParam  = 0;
1179                                         break;
1180                                 }
1181                         }
1182                 }
1183         }
1184
1185         return CallNextHookEx ( mDialogHook, nCode, wParam, lParam);
1186
1187
1188 /*
1189 ================
1190 rvRollupPanel::AutoSize
1191
1192 Automatically set the width of the control based on the dialogs it contains
1193 ================
1194 */
1195 void rvRollupPanel::AutoSize ( void )
1196 {
1197         int i;
1198         int width = 0;
1199         for ( i = 0; i < mItems.Num(); i ++ )
1200         {
1201                 RECT r;
1202                 int  w;
1203                 GetWindowRect ( mItems[i]->mDialog, &r );
1204                 w = (r.right-r.left)+RP_SCROLLBARWIDTH+(RP_GRPBOXINDENT*2);
1205                 if ( w > width )
1206                 {
1207                         width = w;
1208                 }
1209         }
1210         
1211         RECT cr;
1212         GetWindowRect ( mWindow, &cr );
1213         SetWindowPos ( mWindow, NULL, 0, 0, width, cr.bottom-cr.top, SWP_NOMOVE|SWP_NOZORDER );
1214 }
1215