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 "tools/edit_gui_common.h"
30
31
32 #include "../../sys/win32/win_local.h"
33 #include "PropertyGrid.h"
34
35 class rvPropertyGridItem
36 {
37 public:
38
rvPropertyGridItem()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 */
rvPropertyGrid(void)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 */
Create(HWND parent,int id,int style)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 */
Move(int x,int y,int w,int h,BOOL redraw)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 */
StartEdit(int item,bool label)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 */
FinishEdit(void)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 */
CancelEdit(void)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 */
AddItem(const char * name,const char * value,EItemType type)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 */
RemoveItem(int index)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 */
RemoveAllItems(void)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 */
GetItemName(int index)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 */
GetItemValue(int index)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 */
WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)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 */
ReflectMessage(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)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 */
HandleDrawItem(WPARAM wParam,LPARAM lParam)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 }