]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/common/PropTree/PropTreeList.cpp
hello world
[icculus/iodoom3.git] / neo / tools / common / PropTree / PropTreeList.cpp
1 // PropTreeList.cpp : implementation file
2 //
3 //  Copyright (C) 1998-2001 Scott Ramsay
4 //      sramsay@gonavi.com
5 //      http://www.gonavi.com
6 //
7 //  This material is provided "as is", with absolutely no warranty expressed
8 //  or implied. Any use is at your own risk.
9 // 
10 //  Permission to use or copy this software for any purpose is hereby granted 
11 //  without fee, provided the above notices are retained on all copies.
12 //  Permission to modify the code and to distribute modified code is granted,
13 //  provided the above notices are retained, and a notice that the code was
14 //  modified is included with the above copyright notice.
15 // 
16 //      If you use this code, drop me an email.  I'd like to know if you find the code
17 //      useful.
18
19 //#include "stdafx.h"
20 #include "../../../idlib/precompiled.h"
21 #pragma hdrstop
22
23 #include "PropTree.h"
24 #include "../../../sys/win32/rc/proptree_Resource.h"
25 #include "PropTreeList.h"
26
27 #ifdef _DEBUG
28 #define new DEBUG_NEW
29 #undef THIS_FILE
30 static char THIS_FILE[] = __FILE__;
31 #endif
32
33 #define PROPTREEITEM_EXPANDCOLUMN               16                      // width of the expand column
34 #define PROPTREEITEM_COLRNG                             5                       // width of splitter
35 #define PROPTREEITEM_DEFHEIGHT                  21                      // default heigt of an item
36
37 extern HINSTANCE ghInst;
38
39 /////////////////////////////////////////////////////////////////////////////
40 // CPropTreeList
41
42 CPropTreeList::CPropTreeList() :
43         m_pProp(NULL),
44         m_BackBufferSize(0,0),
45         m_bColDrag(FALSE),
46         m_nPrevCol(0)
47 {
48 }
49
50 CPropTreeList::~CPropTreeList()
51 {
52 }
53
54
55 BEGIN_MESSAGE_MAP(CPropTreeList, CWnd)
56         //{{AFX_MSG_MAP(CPropTreeList)
57         ON_WM_SIZE()
58         ON_WM_PAINT()
59         ON_WM_SETCURSOR()
60         ON_WM_LBUTTONDOWN()
61         ON_WM_LBUTTONUP()
62         ON_WM_LBUTTONDBLCLK()
63         ON_WM_MOUSEMOVE()
64         ON_WM_MOUSEWHEEL()
65         ON_WM_KEYDOWN()
66         ON_WM_GETDLGCODE()
67         ON_WM_VSCROLL()
68         //}}AFX_MSG_MAP
69 END_MESSAGE_MAP()
70
71
72 /////////////////////////////////////////////////////////////////////////////
73 // CPropTreeList message handlers
74
75 void CPropTreeList::SetPropOwner(CPropTree* pProp)
76 {
77         m_pProp = pProp;
78 }
79
80
81 BOOL CPropTreeList::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
82 {
83         CWnd* pWnd = this;
84
85         LPCTSTR pszCreateClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW));
86
87         return pWnd->Create(pszCreateClass, _T(""), dwStyle, rect, pParentWnd, nID);
88 }
89
90
91 void CPropTreeList::OnSize(UINT nType, int cx, int cy) 
92 {
93         CWnd::OnSize(nType, cx, cy);
94
95         RecreateBackBuffer(cx, cy);
96
97         if (m_pProp)
98         {
99                 UpdateResize();
100                 Invalidate();
101                 UpdateWindow();
102
103                 // inform all items that a resize has been made
104                 m_pProp->UpdateMoveAllItems();
105         }
106 }
107
108
109 void CPropTreeList::RecreateBackBuffer(int cx, int cy)
110 {
111         if (m_BackBufferSize.cx<cx || m_BackBufferSize.cy<cy)
112         {
113                 m_BackBufferSize = CSize(cx, cy);
114
115                 CWindowDC dc(NULL);
116
117                 int nPlanes = dc.GetDeviceCaps(PLANES);
118                 int nBitCount = dc.GetDeviceCaps(BITSPIXEL);
119
120                 m_BackBuffer.DeleteObject();
121                 m_BackBuffer.CreateBitmap(cx, cy, nPlanes, nBitCount, NULL);
122         }
123 }
124
125
126 void CPropTreeList::UpdateResize()
127 {
128         SCROLLINFO si;
129         LONG nHeight;
130         CRect rc;
131
132         ASSERT(m_pProp!=NULL);
133
134         GetClientRect(rc);
135         nHeight = rc.Height() + 1;
136
137         ZeroMemory(&si, sizeof(SCROLLINFO));
138         si.cbSize = sizeof(SCROLLINFO);
139         si.fMask = SIF_RANGE|SIF_PAGE;
140         si.nMin = 0;
141         si.nMax = m_pProp->GetRootItem()->GetTotalHeight();
142         si.nPage = nHeight;
143
144         if ((int)si.nPage>si.nMax)
145                 m_pProp->SetOriginOffset(0);
146
147         SetScrollInfo(SB_VERT, &si, TRUE);
148
149         // force set column for clipping
150         m_pProp->SetColumn(m_pProp->GetColumn());
151 }
152
153
154 void CPropTreeList::OnPaint() 
155 {
156         CPaintDC dc(this);
157         CDC memdc;
158         CBitmap* pOldBitmap;
159
160         ASSERT(m_pProp!=NULL);
161
162         m_pProp->ClearVisibleList();
163
164         memdc.CreateCompatibleDC(&dc);
165         pOldBitmap = memdc.SelectObject(&m_BackBuffer);
166
167         CRect rc;
168         GetClientRect(rc);
169
170         // draw control background
171         memdc.SelectObject(GetSysColorBrush(COLOR_BTNFACE));
172         memdc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
173
174         // draw control inside fill color
175         rc.DeflateRect(2,2);
176         memdc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), m_pProp->IsWindowEnabled() ? WHITENESS : PATCOPY);
177         rc.InflateRect(2,2);
178
179         // draw expand column
180         memdc.SelectObject(GetSysColorBrush(COLOR_BTNFACE));
181         memdc.PatBlt(0, 0, PROPTREEITEM_EXPANDCOLUMN, rc.Height(), PATCOPY);
182
183         // draw edge
184         memdc.DrawEdge(&rc, BDR_SUNKENOUTER, BF_RECT);
185
186         CPropTreeItem* pItem;
187         LONG nTotal = 0;
188
189         ASSERT(m_pProp->GetRootItem()!=NULL);
190
191         rc.DeflateRect(2,2);
192
193         // create clip region
194         HRGN hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
195         SelectClipRgn(memdc.m_hDC, hRgn);
196
197         // draw all items
198         for (pItem = m_pProp->GetRootItem()->GetChild(); pItem; pItem = pItem->GetSibling())
199         {
200                 LONG nHeight = pItem->DrawItem(&memdc, rc, 0, nTotal);
201                 nTotal += nHeight;
202         }
203
204         // remove clip region
205         SelectClipRgn(memdc.m_hDC, NULL);
206         DeleteObject(hRgn);
207
208         // copy back buffer to the display
209         dc.GetClipBox(&rc);
210         dc.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &memdc, rc.left, rc.top, SRCCOPY);
211         memdc.DeleteDC();
212 }
213
214
215 BOOL CPropTreeList::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
216 {
217         if (nHitTest==HTCLIENT)
218         {
219                 CPoint pt;
220
221                 ASSERT(m_pProp!=NULL);
222
223                 GetCursorPos(&pt);
224                 ScreenToClient(&pt);
225
226                 switch (m_pProp->HitTest(pt))
227                 {
228                         case HTCOLUMN:
229                                 SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(IDC_SPLITTER)));
230                                 return TRUE;
231
232                         case HTCHECKBOX:
233                         case HTBUTTON:
234                         case HTEXPAND:
235                                 SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(IDC_FPOINT)));
236                                 return TRUE;
237                 }
238         }
239
240         return CWnd::OnSetCursor(pWnd, nHitTest, message);
241 }
242
243
244 void CPropTreeList::OnLButtonDown(UINT, CPoint point) 
245 {
246         ASSERT(m_pProp!=NULL);
247
248         if (m_pProp->IsDisableInput())
249                 return;
250
251         m_pProp->SendNotify(NM_CLICK);
252
253         if (!m_pProp->IsWindowEnabled())
254                 return;
255
256         SetFocus();
257
258         LONG nHit = m_pProp->HitTest(point);
259
260         CPropTreeItem* pItem;
261         CRect rc;
262         CDC* pDC;
263
264         switch (nHit)
265         {
266                 case HTCOLUMN:
267                         if (m_pProp->SendNotify(PTN_COLUMNCLICK))
268                                 break;
269
270                         m_bColDrag = TRUE;
271                         SetCapture();
272
273                         m_nPrevCol = m_pProp->GetOrigin().x;
274
275                         // paint drag line
276                         pDC = GetDC();
277                         GetClientRect(rc);
278                         pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT);
279                         ReleaseDC(pDC);
280                         break;
281
282                 case HTCHECKBOX:
283                         if ((pItem = m_pProp->FindItem(point))!=NULL)
284                         {
285                                 pItem->Check(!pItem->IsChecked());
286                                 m_pProp->SendNotify(PTN_CHECKCLICK, pItem);
287                                 Invalidate();
288                         }
289                         break;
290                 case HTBUTTON:
291                         if ((pItem = m_pProp->FindItem(point))!=NULL)
292                         {
293                                 pItem->Check();
294                                 m_pProp->SendNotify(PTN_ITEMBUTTONCLICK, pItem);
295                                 Invalidate();
296                         }
297                         break;
298                 case HTEXPAND:
299                         if ((pItem = m_pProp->FindItem(point))!=NULL)
300                         {
301                                 if (pItem->GetChild() && !m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem))
302                                 {
303                                         pItem->Expand(!pItem->IsExpanded());
304
305                                         UpdateResize();
306                                         Invalidate();
307                                         UpdateWindow();
308                                         CheckVisibleFocus();
309                                 }
310                         }
311                         break;
312
313                 default:
314                         if ((pItem = m_pProp->FindItem(point))!=NULL)
315                         {
316                                 CPropTreeItem* pOldFocus = m_pProp->GetFocusedItem();
317
318                                 m_pProp->SelectItems(NULL, FALSE);
319                                 m_pProp->SetFocusedItem(pItem);
320
321                                 pItem->Select();
322
323                                 Invalidate();
324
325                                 if (pItem!=pOldFocus)
326                                         m_pProp->SendNotify(PTN_SELCHANGE, pItem);
327
328                                 if (nHit==HTATTRIBUTE && !pItem->IsRootLevel())
329                                 {
330                                         if (!m_pProp->SendNotify(PTN_PROPCLICK, pItem) && !pItem->IsReadOnly())
331                                                 pItem->Activate(CPropTreeItem::ACTIVATE_TYPE_MOUSE, point);
332                                 }
333                         }
334                         else
335                         {
336                                 m_pProp->SelectItems(NULL, FALSE);
337                                 m_pProp->SetFocusedItem(NULL);
338                                 m_pProp->SendNotify(PTN_SELCHANGE);
339                                 Invalidate();
340                         }
341                         break;
342         }
343 }
344
345
346 void CPropTreeList::OnLButtonUp(UINT, CPoint point) 
347 {
348         if (m_bColDrag)
349         {
350                 CDC* pDC = GetDC();
351                 CRect rc;
352
353                 GetClientRect(rc);
354                 pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT);
355                 ReleaseDC(pDC);
356
357                 m_bColDrag = FALSE;
358                 ReleaseCapture();
359
360                 m_pProp->SetColumn(point.x);
361                 m_pProp->UpdateMoveAllItems();
362                 Invalidate();
363         } else {
364                 LONG nHit = m_pProp->HitTest(point);
365                 CPropTreeItem* pItem;
366
367                 switch (nHit)
368                 {
369                         case HTBUTTON:
370                                 if ((pItem = m_pProp->FindItem(point))!=NULL)
371                                 {
372                                         pItem->Check( FALSE );
373                                         Invalidate();
374                                 }
375                                 break;
376                         default:
377                                 break;
378                 }
379         }
380 }
381
382
383 void CPropTreeList::OnLButtonDblClk(UINT, CPoint point)
384 {
385         ASSERT(m_pProp!=NULL);
386
387         m_pProp->SendNotify(NM_DBLCLK);
388
389         CPropTreeItem* pItem;
390         CPropTreeItem* pOldFocus;
391
392         if ((pItem = m_pProp->FindItem(point))!=NULL && pItem->GetChild())
393         {
394                 switch (m_pProp->HitTest(point))
395                 {
396                         case HTCOLUMN:
397                                 break;
398
399                         case HTCHECKBOX:
400                                 pItem->Check(!pItem->IsChecked());
401                                 m_pProp->SendNotify(PTN_CHECKCLICK, pItem);
402                                 Invalidate();
403                                 break;
404
405                         case HTATTRIBUTE:
406                                 if (!pItem->IsRootLevel())
407                                         break;
408
409                                 // pass thru to default
410
411                         default:
412                                 pOldFocus = m_pProp->GetFocusedItem();
413                                 m_pProp->SelectItems(NULL, FALSE);
414                                 m_pProp->SetFocusedItem(pItem);
415                                 pItem->Select();
416
417                                 if (pItem!=pOldFocus)
418                                         m_pProp->SendNotify(PTN_SELCHANGE, pItem);
419
420                                 // pass thru to HTEXPAND
421
422                         case HTEXPAND:
423                                 if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem))
424                                 {
425                                         pItem->Expand(!pItem->IsExpanded());
426
427                                         UpdateResize();
428                                         Invalidate();
429                                         UpdateWindow();
430                                         CheckVisibleFocus();
431                                 }
432                                 break;
433                 }
434         }
435 }
436
437
438 void CPropTreeList::OnMouseMove(UINT, CPoint point)
439 {
440         if (m_bColDrag)
441         {
442                 CDC* pDC = GetDC();
443                 CRect rc;
444
445                 GetClientRect(rc);
446                 pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT);
447                 pDC->PatBlt(point.x - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT);
448                 m_nPrevCol = point.x;
449                 ReleaseDC(pDC);
450         }
451 }
452
453
454 BOOL CPropTreeList::OnMouseWheel(UINT, short zDelta, CPoint) 
455 {
456         SCROLLINFO si;
457
458         ZeroMemory(&si, sizeof(SCROLLINFO));
459         si.cbSize = sizeof(SCROLLINFO);
460         si.fMask = SIF_RANGE;
461
462         GetScrollInfo(SB_VERT, &si);
463
464         CRect rc;
465         GetClientRect(rc);
466
467         if (si.nMax - si.nMin < rc.Height())
468                 return TRUE;
469
470         SetFocus();
471         OnVScroll(zDelta < 0 ? SB_LINEDOWN : SB_LINEUP, 0, NULL);
472
473         return TRUE;
474 }
475
476
477 void CPropTreeList::OnKeyDown(UINT nChar, UINT, UINT) 
478 {
479
480         CPropTreeItem* pItem;
481
482         ASSERT(m_pProp!=NULL);
483
484         if (m_pProp->IsDisableInput() || !m_pProp->IsWindowEnabled())
485                 return;
486
487         switch (nChar)
488         {
489                 case VK_RETURN:
490                         if ((pItem = m_pProp->GetFocusedItem())!=NULL && !pItem->IsRootLevel() && !pItem->IsReadOnly())
491                         {
492                                 pItem->Activate(CPropTreeItem::ACTIVATE_TYPE_KEYBOARD, CPoint(0,0));
493                         }
494                         break;
495
496                 case VK_HOME:
497                         if (m_pProp->FocusFirst())
498                                 Invalidate();
499                         break;
500
501                 case VK_END:
502                         if (m_pProp->FocusLast())
503                                 Invalidate();
504                         break;
505
506                 case VK_LEFT:
507                         if ((pItem = m_pProp->GetFocusedItem())!=NULL)
508                         {
509                                 if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem))
510                                 {
511                                         if (pItem->GetChild() && pItem->IsExpanded())
512                                         {
513                                                 pItem->Expand(FALSE);
514                                                 UpdateResize();
515                                                 Invalidate();
516                                                 UpdateWindow();
517                                                 CheckVisibleFocus();
518                                                 break;
519                                         }
520                                 }
521                         }
522                         else
523                                 break;
524                         // pass thru to next case VK_UP
525                 case VK_UP:
526                         if (m_pProp->FocusPrev())
527                                 Invalidate();
528                         break;
529
530                 case VK_RIGHT:
531                         if ((pItem = m_pProp->GetFocusedItem())!=NULL)
532                         {
533                                 if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem))
534                                 {
535                                         if (pItem->GetChild() && !pItem->IsExpanded())
536                                         {
537                                                 pItem->Expand();
538                                                 UpdateResize();
539                                                 Invalidate();
540                                                 UpdateWindow();
541                                                 CheckVisibleFocus();
542                                                 break;
543                                         }
544                                 }
545                         }
546                         else
547                                 break;
548                         // pass thru to next case VK_DOWN
549                 case VK_DOWN:
550                         if (m_pProp->FocusNext())
551                                 Invalidate();
552                         break;
553         }
554 }
555
556
557 UINT CPropTreeList::OnGetDlgCode() 
558 {
559         return DLGC_WANTARROWS|DLGC_WANTCHARS|DLGC_WANTALLKEYS;
560 }
561
562
563 void CPropTreeList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar*) 
564 {
565         SCROLLINFO si;
566         CRect rc;
567         LONG nHeight;
568
569         SetFocus();
570
571         GetClientRect(rc);
572         nHeight = rc.Height() + 1;
573
574         ZeroMemory(&si, sizeof(SCROLLINFO));
575         si.cbSize = sizeof(SCROLLINFO);
576         si.fMask = SIF_RANGE;
577
578         GetScrollInfo(SB_VERT, &si);
579
580         LONG ny = m_pProp->GetOrigin().y;
581
582         switch (nSBCode)
583         {
584                 case SB_LINEDOWN:
585                         ny += PROPTREEITEM_DEFHEIGHT;
586                         break;
587
588                 case SB_LINEUP:
589                         ny -= PROPTREEITEM_DEFHEIGHT;
590                         break;
591
592                 case SB_PAGEDOWN:
593                         ny += nHeight;
594                         break;
595
596                 case SB_PAGEUP:
597                         ny -= nHeight;
598                         break;
599
600                 case SB_THUMBTRACK:
601                         ny = nPos;
602                         break;
603         }
604
605         ny = __min(__max(ny, si.nMin), si.nMax - nHeight);
606
607         m_pProp->SetOriginOffset(ny);
608         si.fMask = SIF_POS;
609         si.nPos = ny;
610
611         SetScrollInfo(SB_VERT, &si, TRUE);
612         Invalidate();
613 }
614
615
616 void CPropTreeList::CheckVisibleFocus()
617 {
618         ASSERT(m_pProp!=NULL);
619
620         CPropTreeItem* pItem;
621         
622         if ((pItem = m_pProp->GetFocusedItem())==NULL)
623                 return;
624
625         if (!m_pProp->IsItemVisible(pItem))
626         {
627                 if (m_pProp->IsSingleSelection())
628                         pItem->Select(FALSE);
629
630                 m_pProp->SetFocusedItem(NULL);
631                 m_pProp->SendNotify(PTN_SELCHANGE, NULL);
632
633                 Invalidate();
634         }
635 }