]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/common/PropertyGrid.cpp
hello world
[icculus/iodoom3.git] / neo / tools / common / PropertyGrid.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 "PropertyGrid.h"
34
35 class rvPropertyGridItem
36 {
37 public:
38
39         rvPropertyGridItem ( )
40         {
41         }
42         
43         idStr                                           mName;
44         idStr                                           mValue;
45         rvPropertyGrid::EItemType       mType;
46 };
47
48 /*
49 ================
50 rvPropertyGrid::rvPropertyGrid
51
52 constructor
53 ================
54 */
55 rvPropertyGrid::rvPropertyGrid ( void )
56 {
57         mWindow                 = NULL;
58         mEdit                   = NULL;
59         mListWndProc    = NULL;
60         mSplitter               = 100;
61         mSelectedItem   = -1;
62         mEditItem               = -1;
63         mState                  = STATE_NORMAL;
64 }
65
66 /*
67 ================
68 rvPropertyGrid::Create
69
70 Create a new property grid control with the given id and parent
71 ================
72 */
73 bool rvPropertyGrid::Create ( HWND parent, int id, int style )
74 {
75         mStyle = style;
76
77         // Create the List view
78         mWindow = CreateWindowEx ( 0, "LISTBOX", "", WS_VSCROLL|WS_CHILD|WS_VISIBLE|LBS_OWNERDRAWFIXED|LBS_NOINTEGRALHEIGHT|LBS_NOTIFY, 0, 0, 0, 0, parent, (HMENU)id, win32.hInstance, 0 );    
79         mListWndProc = (WNDPROC)GetWindowLong ( mWindow, GWL_WNDPROC );
80         SetWindowLong ( mWindow, GWL_USERDATA, (LONG)this );
81         SetWindowLong ( mWindow, GWL_WNDPROC, (LONG)WndProc );
82
83         LoadLibrary ( "Riched20.dll" );
84         mEdit = CreateWindowEx ( 0, "RichEdit20A", "", WS_CHILD, 0, 0, 0, 0, mWindow, (HMENU) 999, win32.hInstance, NULL );
85         SendMessage ( mEdit, EM_SETEVENTMASK, 0, ENM_KEYEVENTS );
86
87         // Set the font of the list box
88         HDC                     dc;
89         LOGFONT         lf;
90         
91         dc = GetDC ( mWindow );
92         ZeroMemory ( &lf, sizeof(lf) );
93         lf.lfHeight = -MulDiv(8, GetDeviceCaps(dc, LOGPIXELSY), 72);
94         strcpy ( lf.lfFaceName, "MS Shell Dlg" );       
95         SendMessage ( mWindow, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );             
96         SendMessage ( mEdit, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );               
97         ReleaseDC ( mWindow, dc );
98
99         RemoveAllItems ( );
100                 
101         return true;
102 }
103
104 /*
105 ================
106 rvPropertyGrid::Move
107
108 Move the window
109 ================
110 */
111 void rvPropertyGrid::Move ( int x, int y, int w, int h, BOOL redraw )
112 {
113         MoveWindow ( mWindow, x, y, w, h, redraw );
114 }
115
116 /*
117 ================
118 rvPropertyGrid::StartEdit
119
120 Start editing
121 ================
122 */
123 void rvPropertyGrid::StartEdit ( int item, bool label )
124 {
125         rvPropertyGridItem* gitem;
126         RECT                            rItem;
127                         
128         gitem = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, item, 0 );
129         if ( NULL == gitem )
130         {
131                 return;
132         }
133         
134         SendMessage ( mWindow, LB_GETITEMRECT, item, (LPARAM)&rItem );
135         if ( label )
136         {
137                 rItem.right = rItem.left + mSplitter - 1;
138         }
139         else
140         {
141                 rItem.left = rItem.left + mSplitter + 1;
142         }
143
144         mState = STATE_EDIT;
145         mEditItem = item;
146         mEditLabel = label;
147                 
148         SetWindowText ( mEdit, label?gitem->mName:gitem->mValue );                                      
149         MoveWindow ( mEdit, rItem.left, rItem.top + 2,
150                                 rItem.right - rItem.left,
151                                 rItem.bottom - rItem.top - 2, TRUE );
152         ShowWindow ( mEdit, SW_SHOW );          
153         
154         SetFocus ( mEdit );
155 }
156
157 /*
158 ================
159 rvPropertyGrid::FinishEdit
160
161 Finish editing by copying the data in the edit control to the internal value
162 ================
163 */
164 void rvPropertyGrid::FinishEdit ( void )
165 {
166         char                            value[1024];
167         rvPropertyGridItem* item;
168         bool                            update;
169         
170         if ( mState != STATE_EDIT )
171         {
172                 return;
173         }
174
175         assert ( mEditItem >= 0 );
176         
177         mState = STATE_FINISHEDIT;
178         
179         update = false;
180         item = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, mEditItem, 0 );
181         assert ( item );                                                                                                        
182                                                 
183         GetWindowText ( mEdit, value, 1023 );
184         
185         if ( !value[0] )
186         {
187                 mState = STATE_EDIT;
188                 MessageBeep ( MB_ICONASTERISK );
189                 return;
190         }
191         
192         if ( !mEditLabel && item->mValue.Cmp ( value ) )
193         {
194                 NMPROPGRID nmpg;
195                 nmpg.hdr.code = PGN_ITEMCHANGED;
196                 nmpg.hdr.hwndFrom = mWindow;
197                 nmpg.hdr.idFrom = GetWindowLong ( mWindow, GWL_ID );
198                 nmpg.mName  = item->mName;
199                 nmpg.mValue = value;                                                                            
200
201                 if ( !SendMessage ( GetParent ( mWindow ), WM_NOTIFY, 0, (LONG)&nmpg ) )
202                 {
203                         mState = STATE_EDIT;
204                         SetFocus ( mEdit );
205                         return;
206                 }
207
208                 // The item may have been destroyed and recreated in the notify call so get it again
209                 item = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, mEditItem, 0 );
210                 if ( item )
211                 {
212                         item->mValue = value;                                           
213                         update = true;
214                 }
215         }
216         else if ( mEditLabel && item->mName.Cmp ( value ) )
217         {
218                 int sel;
219                 sel = AddItem ( value, "", PGIT_STRING );
220                 SetCurSel ( sel );
221                 StartEdit ( sel, false );
222                 return;
223         }       
224
225         SetCurSel ( mEditItem );
226
227         mState = STATE_NORMAL;
228         mEditItem = -1;
229
230         ShowWindow ( mEdit, SW_HIDE );  
231         SetFocus ( mWindow );
232 }
233
234 /*
235 ================
236 rvPropertyGrid::CancelEdit
237
238 Stop editing without saving the data
239 ================
240 */
241 void rvPropertyGrid::CancelEdit ( void )
242 {
243         if ( mState == STATE_EDIT && !mEditLabel )
244         {
245                 if ( !*GetItemValue ( mEditItem ) )
246                 {
247                         RemoveItem ( mEditItem );                       
248                 }
249         }
250
251         mSelectedItem = mEditItem;
252         mEditItem = -1;
253         mState = STATE_NORMAL;
254         ShowWindow ( mEdit, SW_HIDE );
255         SetFocus ( mWindow );   
256         SetCurSel ( mSelectedItem );
257 }
258
259 /*
260 ================
261 rvPropertyGrid::AddItem
262
263 Add a new item to the property grid
264 ================
265 */
266 int rvPropertyGrid::AddItem ( const char* name, const char* value, EItemType type )
267 {
268         rvPropertyGridItem* item;
269         int                                     insert;
270
271         // Cant add headers if headers arent enabled
272         if ( type == PGIT_HEADER && !(mStyle&PGS_HEADERS) )
273         {
274                 return -1;
275         }
276
277         item = new rvPropertyGridItem;
278         item->mName = name;
279         item->mValue = value;
280         item->mType = type;
281         
282         insert = SendMessage(mWindow,LB_GETCOUNT,0,0) - ((mStyle&PGS_ALLOWINSERT)?1:0);
283         
284         return SendMessage ( mWindow, LB_INSERTSTRING, insert, (LONG)item );
285 }
286
287 /*
288 ================
289 rvPropertyGrid::RemoveItem
290
291 Remove the item at the given index
292 ================
293 */
294 void rvPropertyGrid::RemoveItem ( int index )
295 {
296         if ( index < 0 || index >= SendMessage ( mWindow, LB_GETCOUNT, 0, 0 ) )
297         {
298                 return;
299         }
300         
301         delete (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, index, 0 );
302         
303         SendMessage ( mWindow, LB_DELETESTRING, index, 0 );
304 }
305
306 /*
307 ================
308 rvPropertyGrid::RemoveAllItems
309
310 Remove all items from the property grid
311 ================
312 */
313 void rvPropertyGrid::RemoveAllItems ( void )
314 {
315         int i;
316         
317         // free the memory for all the items
318         for ( i = SendMessage ( mWindow, LB_GETCOUNT, 0, 0 ); i > 0; i -- )
319         {               
320                 delete (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, i - 1, 0 );
321         }
322
323         // remove all items from the listbox itself
324         SendMessage ( mWindow, LB_RESETCONTENT, 0, 0 ); 
325
326         if ( mStyle & PGS_ALLOWINSERT )
327         {
328                 // Add the item used to add items
329                 rvPropertyGridItem* item;
330                 item = new rvPropertyGridItem;
331                 item->mName = "";
332                 item->mValue = "";
333                 SendMessage ( mWindow, LB_ADDSTRING, 0, (LONG)item );
334         }
335 }
336
337 /*
338 ================
339 rvPropertyGrid::GetItemName
340
341 Return name of item at given index
342 ================
343 */
344 const char* rvPropertyGrid::GetItemName ( int index )
345 {
346         rvPropertyGridItem* item;
347         
348         item = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, index, 0 );
349         if ( !item )
350         {
351                 return "";
352         }
353         
354         return item->mName;
355 }
356         
357 /*
358 ================
359 rvPropertyGrid::GetItemValue
360
361 Return value of item at given index
362 ================
363 */
364 const char* rvPropertyGrid::GetItemValue ( int index )
365 {
366         rvPropertyGridItem* item;
367         
368         item = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, index, 0 );
369         if ( !item )
370         {
371                 return "";
372         }
373         
374         return item->mValue;
375 }
376
377 /*
378 ================
379 rvPropertyGrid::WndProc
380
381 Window procedure for property grid
382 ================
383 */
384 LRESULT CALLBACK rvPropertyGrid::WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
385 {
386         rvPropertyGrid* grid = (rvPropertyGrid*) GetWindowLong ( hWnd, GWL_USERDATA );
387         
388         switch ( msg )
389         {                       
390                 case WM_SETFOCUS:
391 //                      grid->mEditItem = -1;
392                         break;
393                         
394                 case WM_KEYDOWN:
395                 {
396                         NMKEY nmkey;
397                         nmkey.hdr.code = NM_KEYDOWN;
398                         nmkey.hdr.hwndFrom = grid->mWindow;
399                         nmkey.nVKey = wParam;
400                         nmkey.uFlags = HIWORD(lParam);
401                         nmkey.hdr.idFrom = GetWindowLong ( hWnd, GWL_ID );
402                         SendMessage ( GetParent ( hWnd ), WM_NOTIFY, nmkey.hdr.idFrom, (LPARAM)&nmkey );                
403                         break;
404                 }
405                 
406                 case WM_CHAR:
407                 {
408                         switch ( wParam )
409                         {
410                                 case VK_RETURN:
411                                         if ( grid->mSelectedItem >= 0 )
412                                         {
413                                                 grid->StartEdit ( grid->mSelectedItem, (*grid->GetItemName ( grid->mSelectedItem ))?false:true);
414                                         }
415                                         break;
416                         }
417                         break;
418                 }
419         
420                 case WM_KILLFOCUS:
421                         grid->mSelectedItem = -1;
422                         break;
423                                         
424                 case WM_NOTIFY:
425                 {
426                         NMHDR* hdr;
427                         hdr = (NMHDR*)lParam;
428                         if ( hdr->idFrom == 999 )
429                         {
430                                 if ( hdr->code == EN_MSGFILTER )
431                                 {
432                                         MSGFILTER* filter;
433                                         filter = (MSGFILTER*)lParam;
434                                         if ( filter->msg == WM_KEYDOWN )
435                                         {
436                                                 switch ( filter->wParam )
437                                                 {
438                                                         case VK_RETURN:
439                                                         case VK_TAB:
440                                                                 grid->FinishEdit ( );
441                                                                 return 1;
442                                                                 
443                                                         case VK_ESCAPE:
444                                                                 grid->CancelEdit ( );
445                                                                 return 1;
446                                                 }
447                                         }                                                       
448
449                                         if ( filter->msg == WM_CHAR || filter->msg == WM_KEYUP )
450                                         {
451                                                 switch ( filter->wParam )
452                                                 {
453                                                         case VK_RETURN:
454                                                         case VK_TAB:
455                                                         case VK_ESCAPE:
456                                                                 return 1;
457                                                 }
458                                         }
459                                 }
460                         }
461                         break;
462                 }
463                 
464                 case WM_COMMAND:
465                         if ( lParam == (long)grid->mEdit )
466                         {
467                                 if ( HIWORD(wParam) == EN_KILLFOCUS )
468                                 {
469                                         grid->FinishEdit ( );
470                                         return true;
471                                 }
472                         }
473                         break;
474
475                 case WM_LBUTTONDBLCLK:
476                         grid->mSelectedItem = SendMessage ( hWnd, LB_ITEMFROMPOINT, 0, lParam );
477                         
478                         // fall through
479
480                 case WM_LBUTTONDOWN:
481                 {
482                         int                                     item;
483                         rvPropertyGridItem* gitem;
484                         RECT                            rItem;
485                         POINT                           pt;
486                         
487                         if ( grid->mState == rvPropertyGrid::STATE_EDIT )
488                         {
489                                 break;
490                         }
491                         
492                         item  = (short)LOWORD(SendMessage ( hWnd, LB_ITEMFROMPOINT, 0, lParam ));
493                         if ( item == -1 )
494                         {
495                                 break;
496                         }
497                         
498                         gitem = (rvPropertyGridItem*)SendMessage ( hWnd, LB_GETITEMDATA, item, 0 );
499                         pt.x  = LOWORD(lParam);
500                         pt.y  = HIWORD(lParam);
501
502                         SendMessage ( hWnd, LB_GETITEMRECT, item, (LPARAM)&rItem );
503
504                         if ( !gitem->mName.Icmp ( "" ) )
505                         {
506                                 rItem.right = rItem.left + grid->mSplitter - 1;
507                                 if ( PtInRect ( &rItem, pt) )
508                                 {
509                                         grid->SetCurSel ( item );
510                                         grid->StartEdit ( item, true );
511                                 }
512                         }
513                         else if ( grid->mSelectedItem == item )
514                         {                                       
515                                 rItem.left = rItem.left + grid->mSplitter + 1;
516                                 if ( PtInRect ( &rItem, pt) )
517                                 {
518                                         grid->StartEdit ( item, false );
519                                 }
520                         }
521                         
522                         if ( grid->mState == rvPropertyGrid::STATE_EDIT )
523                         {
524                                 ClientToScreen ( hWnd, &pt );
525                                 ScreenToClient ( grid->mEdit, &pt );
526                                 SendMessage ( grid->mEdit, WM_LBUTTONDOWN, wParam, MAKELONG(pt.x,pt.y) );
527                                 return 0;
528                         }
529                                                 
530                         break;
531                 }
532                 
533                 case WM_ERASEBKGND:
534                 {
535                         RECT rClient;
536                         GetClientRect ( hWnd, &rClient );
537                         FillRect ( (HDC)wParam, &rClient, GetSysColorBrush ( COLOR_3DFACE ) );
538                         return TRUE;
539                 }
540                         
541                 case WM_SETCURSOR:
542                 {
543                         POINT point;
544                         GetCursorPos ( &point );        
545                         ScreenToClient ( hWnd, &point );
546                         if ( point.x >= grid->mSplitter - 2 && point.x <= grid->mSplitter + 2 )
547                         {
548                                 SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_SIZEWE)));
549                                 return TRUE;
550                         }
551                         break;
552                 }
553         }
554         
555         return CallWindowProc ( grid->mListWndProc, hWnd, msg, wParam, lParam );
556 }
557
558 /*
559 ================
560 rvPropertyGrid::ReflectMessage
561
562 Handle messages sent to the parent window
563 ================
564 */
565 bool rvPropertyGrid::ReflectMessage ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
566 {
567         switch ( msg )
568         {                                       
569                 case WM_COMMAND:
570                 {
571                         if ( (HWND)lParam == mWindow )
572                         {
573                                 switch ( HIWORD(wParam) )
574                                 {
575                                         case LBN_SELCHANGE:
576                                                 mSelectedItem = SendMessage ( mWindow, LB_GETCURSEL, 0, 0 );
577                                                 break;
578                                 }
579                         }
580                         break;
581                 }
582         
583                 case WM_DRAWITEM:
584                         HandleDrawItem ( wParam, lParam );
585                         return true;
586         
587                 case WM_MEASUREITEM:
588                 {
589                         MEASUREITEMSTRUCT* mis = (MEASUREITEMSTRUCT*) lParam;
590                         mis->itemHeight = 18;
591                         return true;
592                 }
593         }
594         
595         return false;
596 }
597
598 /*
599 ================
600 rvPropertyGrid::HandleDrawItem
601
602 Handle the draw item message
603 ================
604 */
605 int rvPropertyGrid::HandleDrawItem ( WPARAM wParam, LPARAM lParam )
606 {
607         DRAWITEMSTRUCT*         dis  = (DRAWITEMSTRUCT*) lParam;
608         rvPropertyGridItem* item = (rvPropertyGridItem*) dis->itemData;
609         RECT                            rTemp;
610         HBRUSH                          brush;
611         
612         if ( !item )
613         {
614                 return 0;
615         }
616
617         rTemp = dis->rcItem;
618         if ( mStyle & PGS_HEADERS )
619         {
620                 brush = GetSysColorBrush ( COLOR_SCROLLBAR );
621                 rTemp.right = rTemp.left + 10;
622                 FillRect ( dis->hDC, &rTemp, brush );           
623                 rTemp.left = rTemp.right;
624                 rTemp.right = dis->rcItem.right;
625         }
626         
627         if ( item->mType == PGIT_HEADER )
628         {
629                 brush = GetSysColorBrush ( COLOR_SCROLLBAR );
630         }
631         else if ( dis->itemState & ODS_SELECTED )
632         {
633                 brush = GetSysColorBrush ( COLOR_HIGHLIGHT );
634         }
635         else
636         {
637                 brush = GetSysColorBrush ( COLOR_WINDOW );              
638         }
639
640         FillRect ( dis->hDC, &rTemp, brush );
641
642         HPEN pen = CreatePen ( PS_SOLID, 1, GetSysColor ( COLOR_SCROLLBAR ) );
643         HPEN oldpen = (HPEN)SelectObject ( dis->hDC, pen );
644         MoveToEx ( dis->hDC, dis->rcItem.left, dis->rcItem.top, NULL );
645         LineTo ( dis->hDC, dis->rcItem.right, dis->rcItem.top );
646         MoveToEx ( dis->hDC, dis->rcItem.left, dis->rcItem.bottom, NULL );
647         LineTo ( dis->hDC, dis->rcItem.right, dis->rcItem.bottom);
648
649         if ( item->mType != PGIT_HEADER )
650         {
651                 MoveToEx ( dis->hDC, dis->rcItem.left + mSplitter, dis->rcItem.top, NULL );
652                 LineTo ( dis->hDC, dis->rcItem.left + mSplitter, dis->rcItem.bottom );
653         }
654         SelectObject ( dis->hDC, oldpen );
655         DeleteObject ( pen );                   
656
657         int colorIndex = ( (dis->itemState & ODS_SELECTED ) ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT );
658         SetTextColor ( dis->hDC, GetSysColor ( colorIndex ) );
659         SetBkMode ( dis->hDC, TRANSPARENT );
660         SetBkColor ( dis->hDC, GetSysColor ( COLOR_3DFACE ) );
661
662         RECT rText;
663         rText = rTemp;
664         rText.right = rText.left + mSplitter;
665         rText.left += 2;
666
667         DrawText ( dis->hDC, item->mName, item->mName.Length(), &rText, DT_LEFT|DT_VCENTER|DT_SINGLELINE );
668         
669         rText.left = dis->rcItem.left + mSplitter + 2;
670         rText.right = dis->rcItem.right;
671         DrawText ( dis->hDC, item->mValue, item->mValue.Length(), &rText, DT_LEFT|DT_VCENTER|DT_SINGLELINE );
672         
673         return 0;
674 }