!XT (Code) Update copyright headers in Code/Sandbox.
[CRYENGINE.git] / Code / Sandbox / Plugins / MFCToolsPlugin / Controls / ColorGradientCtrl.cpp
blobacf8ec8c49ca88756f72308e13a99ed29326cff6
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "ColorGradientCtrl.h"
5 #include "MemDC.h"
6 #include "Dialogs/CustomColorDialog.h"
7 #include "WndGridHelper.h"
8 #include "Util/MFCUtil.h"
10 IMPLEMENT_DYNAMIC(CColorGradientCtrl, CWnd)
12 #define MIN_TIME_EPSILON 0.01f
14 //////////////////////////////////////////////////////////////////////////
15 CColorGradientCtrl::CColorGradientCtrl()
17 m_bAutoDelete = false;
18 m_nActiveKey = -1;
19 m_nHitKeyIndex = -1;
20 m_nKeyDrawRadius = 3;
21 m_bTracking = false;
22 m_pSpline = 0;
23 m_fMinTime = -1;
24 m_fMaxTime = 1;
25 m_fMinValue = -1;
26 m_fMaxValue = 1;
27 m_fTooltipScaleX = 1;
28 m_fTooltipScaleY = 1;
29 m_bLockFirstLastKey = false;
30 m_bNoZoom = true;
32 ClearSelection();
34 m_bSelectedKeys.reserve(0);
36 m_fTimeMarker = -10;
38 m_grid.zoom.x = 100;
41 CColorGradientCtrl::~CColorGradientCtrl()
45 //////////////////////////////////////////////////////////////////////////
46 BEGIN_MESSAGE_MAP(CColorGradientCtrl, CWnd)
47 ON_WM_SIZE()
48 ON_WM_ERASEBKGND()
49 ON_WM_PAINT()
50 ON_WM_LBUTTONDOWN()
51 ON_WM_RBUTTONDOWN()
52 ON_WM_MOUSEMOVE()
53 ON_WM_LBUTTONUP()
54 ON_WM_RBUTTONUP()
55 ON_WM_SETCURSOR()
56 ON_WM_LBUTTONDBLCLK()
57 ON_WM_RBUTTONDOWN()
58 ON_WM_KEYDOWN()
59 END_MESSAGE_MAP()
61 /////////////////////////////////////////////////////////////////////////////
62 // CColorGradientCtrl message handlers
64 /////////////////////////////////////////////////////////////////////////////
65 BOOL CColorGradientCtrl::Create(DWORD dwStyle, const CRect& rc, CWnd* pParentWnd, UINT nID)
67 if (dwStyle & CLRGRD_STYLE_AUTO_DELETE)
68 m_bAutoDelete = true;
69 LPCTSTR lpClassName = AfxRegisterWndClass(CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, AfxGetApp()->LoadStandardCursor(IDC_ARROW), NULL, NULL);
70 return CreateEx(0, lpClassName, "SplineCtrl", dwStyle, rc, pParentWnd, nID);
73 //////////////////////////////////////////////////////////////////////////
74 void CColorGradientCtrl::PostNcDestroy()
76 if (m_bAutoDelete)
77 delete this;
80 //////////////////////////////////////////////////////////////////////////
81 BOOL CColorGradientCtrl::OnEraseBkgnd(CDC* pDC)
83 return TRUE;
86 //////////////////////////////////////////////////////////////////////////
87 void CColorGradientCtrl::OnSize(UINT nType, int cx, int cy)
89 __super::OnSize(nType, cx, cy);
91 m_offscreenBitmap.DeleteObject();
92 if (!m_offscreenBitmap.GetSafeHandle())
94 CDC* pDC = GetDC();
95 m_offscreenBitmap.CreateCompatibleBitmap(pDC, cx, cy);
96 ReleaseDC(pDC);
99 CRect rc;
100 GetClientRect(rc);
101 m_rcGradient = rc;
102 m_rcGradient.bottom -= 11;
103 //m_rcGradient.DeflateRect(4,4);
105 m_grid.rect = m_rcGradient;
106 if (m_bNoZoom)
107 m_grid.zoom.x = m_grid.rect.Width();
109 m_rcKeys = rc;
110 m_rcKeys.top = m_rcKeys.bottom - 10;
112 if (m_tooltip.m_hWnd)
114 m_tooltip.DelTool(this, 1);
115 m_tooltip.AddTool(this, "", m_rcGradient, 1);
119 //////////////////////////////////////////////////////////////////////////
120 BOOL CColorGradientCtrl::PreTranslateMessage(MSG* pMsg)
122 if (!m_tooltip.m_hWnd)
124 CRect rc;
125 GetClientRect(rc);
126 m_tooltip.Create(this);
127 m_tooltip.SetDelayTime(TTDT_AUTOPOP, 500);
128 m_tooltip.SetDelayTime(TTDT_INITIAL, 0);
129 m_tooltip.SetDelayTime(TTDT_RESHOW, 0);
130 m_tooltip.SetMaxTipWidth(600);
131 m_tooltip.AddTool(this, "", rc, 1);
132 m_tooltip.Activate(FALSE);
134 m_tooltip.RelayEvent(pMsg);
136 return __super::PreTranslateMessage(pMsg);
139 //////////////////////////////////////////////////////////////////////////
140 void CColorGradientCtrl::SetZoom(float fZoom)
142 m_grid.zoom.x = fZoom;
145 //////////////////////////////////////////////////////////////////////////
146 void CColorGradientCtrl::SetOrigin(float fOffset)
148 m_grid.origin.x = fOffset;
151 //////////////////////////////////////////////////////////////////////////
152 CPoint CColorGradientCtrl::KeyToPoint(int nKey)
154 if (nKey >= 0)
156 return TimeToPoint(m_pSpline->GetKeyTime(nKey));
158 return CPoint(0, 0);
161 //////////////////////////////////////////////////////////////////////////
162 CPoint CColorGradientCtrl::TimeToPoint(float time)
164 return CPoint(m_grid.WorldToClient(Vec2(time, 0)).x, m_rcGradient.Height() / 2);
166 CPoint point;
167 point.x = (time - m_fMinTime) * (m_rcGradient.Width() / (m_fMaxTime - m_fMinTime)) + m_rcGradient.left;
168 point.y = m_rcGradient.Height() / 2;
169 return point;
172 //////////////////////////////////////////////////////////////////////////
173 COLORREF CColorGradientCtrl::TimeToColor(float time)
175 ISplineInterpolator::ValueType val;
176 m_pSpline->Interpolate(time, val);
177 COLORREF col = ValueToColor(val);
178 return col;
181 //////////////////////////////////////////////////////////////////////////
182 void CColorGradientCtrl::PointToTimeValue(CPoint point, float& time, ISplineInterpolator::ValueType& val)
184 time = XOfsToTime(point.x);
185 ColorToValue(TimeToColor(time), val);
188 //////////////////////////////////////////////////////////////////////////
189 float CColorGradientCtrl::XOfsToTime(int x)
191 return m_grid.ClientToWorld(CPoint(x, 0)).x;
193 // m_fMinTime to m_fMaxTime time range.
194 float time = m_fMinTime + (float)((m_fMaxTime - m_fMinTime) * (x - m_rcGradient.left)) / m_rcGradient.Width();
195 return time;
198 //////////////////////////////////////////////////////////////////////////
199 CPoint CColorGradientCtrl::XOfsToPoint(int x)
201 return TimeToPoint(XOfsToTime(x));
204 //////////////////////////////////////////////////////////////////////////
205 COLORREF CColorGradientCtrl::XOfsToColor(int x)
207 return TimeToColor(XOfsToTime(x));
210 //////////////////////////////////////////////////////////////////////////
211 void CColorGradientCtrl::OnPaint()
213 CPaintDC PaintDC(this);
215 CRect rcClient;
216 GetClientRect(&rcClient);
218 if (m_pSpline)
219 m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
221 if (!m_offscreenBitmap.GetSafeHandle())
222 m_offscreenBitmap.CreateCompatibleBitmap(&PaintDC, rcClient.Width(), rcClient.Height());
224 CMemoryDC dc(PaintDC, &m_offscreenBitmap);
226 if (!IsWindowEnabled())
228 CRect rc;
229 GetClientRect(rc);
230 rc.IntersectRect(rc, CRect(PaintDC.m_ps.rcPaint));
231 CBrush keysBrush;
232 keysBrush.CreateSolidBrush(GetXtremeColor(COLOR_BTNFACE));
233 dc.FillRect(rc, &keysBrush);
234 return;
237 //////////////////////////////////////////////////////////////////////////
238 // Fill keys backgound.
239 //////////////////////////////////////////////////////////////////////////
240 CRect rcKeys;
241 rcKeys.IntersectRect(m_rcKeys, CRect(PaintDC.m_ps.rcPaint));
242 CBrush keysBrush;
243 keysBrush.CreateSolidBrush(GetXtremeColor(COLOR_BTNFACE));
244 dc.FillRect(rcKeys, &keysBrush);
245 //////////////////////////////////////////////////////////////////////////
247 //Draw Keys and Curve
248 if (m_pSpline)
250 DrawGradient(&dc);
251 DrawKeys(&dc);
256 //////////////////////////////////////////////////////////////////////////
257 void CColorGradientCtrl::DrawGradient(CDC* pDC)
259 int cx = m_rcGradient.Width();
260 int cy = m_rcGradient.Height();
262 //Draw Curve
263 // create and select a thick, white pen
264 CPen pen;
265 pen.CreatePen(PS_SOLID, 1, RGB(128, 255, 128));
266 CPen* pOldPen = pDC->SelectObject(&pen);
268 CRect rcClip;
269 pDC->GetClipBox(rcClip);
270 rcClip.IntersectRect(rcClip, m_rcGradient);
272 for (int x = rcClip.left; x < rcClip.right; x++)
274 COLORREF col = XOfsToColor(x);
275 CPen pen;
276 pen.CreatePen(PS_SOLID, 1, col);
277 pDC->SelectObject(&pen);
278 pDC->MoveTo(CPoint(x, m_rcGradient.top));
279 pDC->LineTo(CPoint(x, m_rcGradient.bottom));
282 // Put back the old objects
283 pDC->SelectObject(pOldPen);
286 //////////////////////////////////////////////////////////////////////////
287 void CColorGradientCtrl::DrawKeys(CDC* pDC)
289 if (!m_pSpline)
290 return;
292 // create and select a white pen
293 CPen pen;
294 pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
295 CPen* pOldPen = pDC->SelectObject(&pen);
297 CRect rcClip;
298 pDC->GetClipBox(rcClip);
300 m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
302 for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
304 float time = m_pSpline->GetKeyTime(i);
305 CPoint pt = TimeToPoint(time);
307 if (pt.x < rcClip.left - 8 || pt.x > rcClip.right + 8)
308 continue;
310 COLORREF clr = TimeToColor(time);
311 CBrush brush(clr);
312 CBrush* pOldBrush = pDC->SelectObject(&brush);
314 // Find the midpoints of the top, right, left, and bottom
315 // of the client area. They will be the vertices of our polygon.
316 CPoint pts[3];
317 pts[0].x = pt.x;
318 pts[0].y = m_rcKeys.top + 1;
319 pts[1].x = pt.x - 5;
320 pts[1].y = m_rcKeys.top + 8;
321 pts[2].x = pt.x + 5;
322 pts[2].y = m_rcKeys.top + 8;
323 pDC->Polygon(pts, 3);
325 if (m_bSelectedKeys[i])
327 CPen pen;
328 pen.CreatePen(PS_SOLID, 1, RGB(200, 0, 0));
329 CPen* pOldPen = pDC->SelectObject(&pen);
330 pDC->Polygon(pts, 3);
331 pDC->SelectObject(pOldPen);
334 pDC->SelectObject(pOldBrush);
337 CPen timePen;
338 timePen.CreatePen(PS_SOLID, 1, RGB(255, 0, 255));
339 if (!(GetStyle() & CLRGRD_STYLE_NO_TIME_MARKER))
341 pDC->SelectObject(&timePen);
342 CPoint pt = TimeToPoint(m_fTimeMarker);
343 pDC->MoveTo(pt.x, m_rcGradient.top + 1);
344 pDC->LineTo(pt.x, m_rcGradient.bottom - 1);
347 pDC->SelectObject(pOldPen);
350 void CColorGradientCtrl::UpdateTooltip()
352 if (m_nHitKeyIndex >= 0)
354 float time = m_pSpline->GetKeyTime(m_nHitKeyIndex);
355 ISplineInterpolator::ValueType val;
356 m_pSpline->GetKeyValue(m_nHitKeyIndex, val);
358 COLORREF col = TimeToColor(time);
359 int cont_s = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_IN_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
360 int cont_d = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_OUT_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
362 CString tipText;
363 tipText.Format("%.2f : %d,%d,%d [%d,%d]",
364 time * m_fTooltipScaleX, GetRValue(col), GetGValue(col), GetBValue(col), cont_s, cont_d);
365 m_tooltip.UpdateTipText(tipText, this, 1);
366 m_tooltip.Activate(TRUE);
370 /////////////////////////////////////////////////////////////////////////////
371 //Mouse Message Handlers
372 //////////////////////////////////////////////////////////////////////////
373 void CColorGradientCtrl::OnLButtonDown(UINT nFlags, CPoint point)
375 CWnd::OnLButtonDown(nFlags, point);
377 if (m_bTracking)
378 return;
379 if (!m_pSpline)
380 return;
382 SetFocus();
384 switch (m_hitCode)
386 case HIT_KEY:
387 StartTracking();
388 SetActiveKey(m_nHitKeyIndex);
389 break;
392 case HIT_SPLINE:
394 // Cycle the spline slope of the nearest key.
395 int flags = m_pSpline->GetKeyFlags(m_nHitKeyIndex);
396 if (m_nHitKeyDist < 0)
397 // Toggle left side.
398 flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_IN_SHIFT;
399 if (m_nHitKeyDist > 0)
400 // Toggle right side.
401 flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_OUT_SHIFT;
402 m_pSpline->SetKeyFlags(m_nHitKeyIndex, flags);
403 m_pSpline->Update();
405 SetActiveKey(-1);
406 SendNotifyEvent( CLRGRDN_CHANGE );
407 if (m_updateCallback)
408 m_updateCallback(this);
409 break;
413 case HIT_NOTHING:
414 SetActiveKey(-1);
415 break;
417 Invalidate();
420 //////////////////////////////////////////////////////////////////////////
421 void CColorGradientCtrl::OnRButtonDown(UINT nFlags, CPoint point)
423 CWnd::OnRButtonDown(nFlags, point);
425 NMHDR nmh;
426 nmh.hwndFrom = m_hWnd;
427 nmh.idFrom = ::GetDlgCtrlID(m_hWnd);
428 nmh.code = NM_RCLICK;
430 GetOwner()->SendMessage(WM_NOTIFY, (WPARAM)GetDlgCtrlID(), (LPARAM)&nmh);
433 //////////////////////////////////////////////////////////////////////////
434 void CColorGradientCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
436 if (!m_pSpline)
437 return;
439 RECT rc;
440 GetClientRect(&rc);
442 switch (m_hitCode)
444 case HIT_SPLINE:
446 int iIndex = InsertKey(point);
447 SetActiveKey(iIndex);
448 EditKey(iIndex);
450 RedrawWindow();
452 break;
453 case HIT_KEY:
455 EditKey(m_nHitKeyIndex);
457 break;
460 CWnd::OnLButtonDblClk(nFlags, point);
463 //////////////////////////////////////////////////////////////////////////
464 void CColorGradientCtrl::OnMouseMove(UINT nFlags, CPoint point)
466 if (!m_pSpline)
467 return;
469 if (GetCapture() != this)
470 StopTracking();
472 if (m_bTracking)
473 TrackKey(point);
475 if (m_bTracking)
476 UpdateTooltip();
478 CWnd::OnMouseMove(nFlags, point);
481 //////////////////////////////////////////////////////////////////////////
482 void CColorGradientCtrl::OnLButtonUp(UINT nFlags, CPoint point)
484 if (!m_pSpline)
485 return;
487 if (m_bTracking)
488 StopTracking();
490 CWnd::OnLButtonUp(nFlags, point);
493 //////////////////////////////////////////////////////////////////////////
494 void CColorGradientCtrl::OnRButtonUp(UINT nFlags, CPoint point)
496 if (!m_pSpline)
497 return;
498 CWnd::OnRButtonUp(nFlags, point);
501 /////////////////////////////////////////////////////////////////////////////
502 void CColorGradientCtrl::SetActiveKey(int nIndex)
504 ClearSelection();
506 //Activate New Key
507 if (nIndex >= 0)
509 m_bSelectedKeys[nIndex] = true;
511 m_nActiveKey = nIndex;
512 Invalidate();
515 /////////////////////////////////////////////////////////////////////////////
516 void CColorGradientCtrl::SetSpline(ISplineInterpolator* pSpline, BOOL bRedraw)
518 if (pSpline != m_pSpline)
520 //if (pSpline && pSpline->GetNumDimensions() != 3)
521 //return;
522 m_pSpline = pSpline;
523 m_nActiveKey = -1;
526 ClearSelection();
528 if (bRedraw)
529 RedrawWindow();
532 //////////////////////////////////////////////////////////////////////////
533 ISplineInterpolator* CColorGradientCtrl::GetSpline()
535 return m_pSpline;
538 /////////////////////////////////////////////////////////////////////////////
539 BOOL CColorGradientCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
541 BOOL b = FALSE;
543 CPoint point;
544 GetCursorPos(&point);
545 ScreenToClient(&point);
547 switch (HitTest(point))
549 case HIT_SPLINE:
551 HCURSOR hCursor;
552 hCursor = AfxGetApp()->LoadCursor(IDC_ARRWHITE);
553 SetCursor(hCursor);
554 b = TRUE;
555 } break;
556 case HIT_KEY:
558 HCURSOR hCursor;
559 hCursor = AfxGetApp()->LoadCursor(IDC_ARRBLCK);
560 SetCursor(hCursor);
561 b = TRUE;
562 } break;
563 default:
564 break;
567 if (m_tooltip.m_hWnd && m_pSpline)
569 if (m_nHitKeyIndex >= 0)
571 UpdateTooltip();
573 else if (!m_bTracking)
575 m_tooltip.Activate(FALSE);
579 if (!b)
580 return CWnd::OnSetCursor(pWnd, nHitTest, message);
581 else return TRUE;
584 /////////////////////////////////////////////////////////////////////////////
585 void CColorGradientCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
587 BOOL bProcessed = false;
589 if (m_nActiveKey != -1 && m_pSpline)
591 switch (nChar)
593 case VK_DELETE:
595 RemoveKey(m_nActiveKey);
596 bProcessed = true;
597 } break;
598 case VK_UP:
600 CUndo undo("Move Spline Key");
601 CPoint point = KeyToPoint(m_nActiveKey);
602 point.x -= 1;
603 SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
604 TrackKey(point);
605 bProcessed = true;
606 } break;
607 case VK_DOWN:
609 CUndo undo("Move Spline Key");
610 CPoint point = KeyToPoint(m_nActiveKey);
611 point.x += 1;
612 SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
613 TrackKey(point);
614 bProcessed = true;
615 } break;
616 case VK_LEFT:
618 CUndo undo("Move Spline Key");
619 CPoint point = KeyToPoint(m_nActiveKey);
620 point.x -= 1;
621 SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
622 TrackKey(point);
623 bProcessed = true;
624 } break;
625 case VK_RIGHT:
627 CUndo undo("Move Spline Key");
628 CPoint point = KeyToPoint(m_nActiveKey);
629 point.y += 1;
630 SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
631 TrackKey(point);
632 bProcessed = true;
633 } break;
635 default:
636 break; //do nothing
639 RedrawWindow();
642 if (!bProcessed)
643 CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
646 //////////////////////////////////////////////////////////////////////////////
647 CColorGradientCtrl::EHitCode CColorGradientCtrl::HitTest(CPoint point)
649 if (!m_pSpline)
650 return HIT_NOTHING;
652 ISplineInterpolator::ValueType val;
653 float time;
654 PointToTimeValue(point, time, val);
656 CPoint splinePt = TimeToPoint(time);
658 //bool bSplineHit = abs(splinePt.x-point.x) < 4 && abs(splinePt.y-point.y) < 4;
660 CRect rc;
661 GetClientRect(rc);
663 m_nHitKeyIndex = -1;
665 if (rc.PtInRect(point))
667 m_nHitKeyDist = 0xFFFF;
668 m_hitCode = HIT_SPLINE;
670 for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
672 CPoint splinePt = TimeToPoint(m_pSpline->GetKeyTime(i));
673 if (abs(point.x - splinePt.x) < abs(m_nHitKeyDist))
675 m_nHitKeyIndex = i;
676 m_nHitKeyDist = point.x - splinePt.x;
679 if (abs(m_nHitKeyDist) < 4)
680 m_hitCode = HIT_KEY;
682 else
683 m_hitCode = HIT_NOTHING;
685 return m_hitCode;
688 ///////////////////////////////////////////////////////////////////////////////
689 void CColorGradientCtrl::StartTracking()
691 m_bTracking = TRUE;
692 SetCapture();
694 GetIEditor()->GetIUndoManager()->Begin();
695 SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
697 HCURSOR hCursor;
698 hCursor = AfxGetApp()->LoadCursor(IDC_ARRBLCKCROSS);
699 SetCursor(hCursor);
703 //////////////////////////////////////////////////////////////////////////
704 void CColorGradientCtrl::TrackKey(CPoint point)
706 if (point.x < m_rcGradient.left || point.y > m_rcGradient.right)
707 return;
709 int nKey = m_nHitKeyIndex;
711 if (nKey >= 0)
713 ISplineInterpolator::ValueType val;
714 float time;
715 PointToTimeValue(point, time, val);
717 // Clamp to min/max time.
718 if (time < m_fMinTime || time > m_fMaxTime)
719 return;
721 int i;
722 for (i = 0; i < m_pSpline->GetKeyCount(); i++)
724 // Switch to next key.
725 if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON)
727 if (i != nKey)
728 return;
732 if (!m_bLockFirstLastKey || (nKey != 0 && nKey != m_pSpline->GetKeyCount() - 1))
734 m_pSpline->SetKeyTime(nKey, time);
735 m_pSpline->Update();
738 SendNotifyEvent(CLRGRDN_CHANGE);
739 if (m_updateCallback)
740 m_updateCallback(this);
742 RedrawWindow();
746 //////////////////////////////////////////////////////////////////////////
747 void CColorGradientCtrl::StopTracking()
749 if (!m_bTracking)
750 return;
752 GetIEditor()->GetIUndoManager()->Accept("Spline Move");
754 if (m_nHitKeyIndex >= 0)
756 CPoint point;
757 GetCursorPos(&point);
758 ScreenToClient(&point);
760 CRect rc;
761 GetClientRect(rc);
762 rc.InflateRect(100, 100);
763 if (!rc.PtInRect(point))
765 RemoveKey(m_nHitKeyIndex);
769 m_bTracking = FALSE;
770 ReleaseCapture();
773 //////////////////////////////////////////////////////////////////////////
774 void CColorGradientCtrl::EditKey(int nKey)
776 if (!m_pSpline)
777 return;
779 if (nKey < 0 || nKey >= m_pSpline->GetKeyCount())
780 return;
782 SetActiveKey(nKey);
784 ISplineInterpolator::ValueType val;
785 m_pSpline->GetKeyValue(nKey, val);
787 SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
789 CCustomColorDialog dlg(ValueToColor(val));
790 dlg.SetColorChangeCallback(functor(*this, &CColorGradientCtrl::OnKeyColorChanged));
791 if (dlg.DoModal() == IDOK)
793 CUndo undo("Modify Gradient Color");
794 OnKeyColorChanged(dlg.GetColor());
796 else
798 OnKeyColorChanged(ValueToColor(val));
802 //////////////////////////////////////////////////////////////////////////
803 void CColorGradientCtrl::OnKeyColorChanged(COLORREF color)
805 int nKey = m_nActiveKey;
806 if (!m_pSpline)
807 return;
808 if (nKey < 0 || nKey >= m_pSpline->GetKeyCount())
809 return;
811 ISplineInterpolator::ValueType val;
812 ColorToValue(color, val);
813 m_pSpline->SetKeyValue(nKey, val);
814 Invalidate();
816 if (m_bLockFirstLastKey)
818 if (nKey == 0)
819 m_pSpline->SetKeyValue(m_pSpline->GetKeyCount() - 1, val);
820 else if (nKey == m_pSpline->GetKeyCount() - 1)
821 m_pSpline->SetKeyValue(0, val);
823 m_pSpline->Update();
824 SendNotifyEvent(CLRGRDN_CHANGE);
825 if (m_updateCallback)
826 m_updateCallback(this);
828 GetIEditor()->UpdateViews(eRedrawViewports);
831 //////////////////////////////////////////////////////////////////////////
832 void CColorGradientCtrl::RemoveKey(int nKey)
834 if (!m_pSpline)
835 return;
836 if (m_bLockFirstLastKey)
838 if (nKey == 0 || nKey == m_pSpline->GetKeyCount() - 1)
839 return;
842 CUndo undo("Remove Spline Key");
844 SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
845 m_nActiveKey = -1;
846 m_nHitKeyIndex = -1;
847 if (m_pSpline)
849 m_pSpline->RemoveKey(nKey);
850 m_pSpline->Update();
852 SendNotifyEvent(CLRGRDN_CHANGE);
853 if (m_updateCallback)
854 m_updateCallback(this);
856 Invalidate();
859 //////////////////////////////////////////////////////////////////////////
860 int CColorGradientCtrl::InsertKey(CPoint point)
862 CUndo undo("Spline Insert Key");
864 ISplineInterpolator::ValueType val;
866 float time;
867 PointToTimeValue(point, time, val);
869 if (time < m_fMinTime || time > m_fMaxTime)
870 return -1;
872 int i;
873 for (i = 0; i < m_pSpline->GetKeyCount(); i++)
875 // Skip if any key already have time that is very close.
876 if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON)
877 return i;
880 SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
882 m_pSpline->InsertKey(time, val);
883 m_pSpline->Interpolate(time, val);
884 ClearSelection();
885 Invalidate();
887 SendNotifyEvent(CLRGRDN_CHANGE);
888 if (m_updateCallback)
889 m_updateCallback(this);
891 for (i = 0; i < m_pSpline->GetKeyCount(); i++)
893 // Find key with added time.
894 if (m_pSpline->GetKeyTime(i) == time)
895 return i;
898 return -1;
901 //////////////////////////////////////////////////////////////////////////
902 void CColorGradientCtrl::ClearSelection()
904 m_nActiveKey = -1;
905 if (m_pSpline)
906 m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
907 for (int i = 0; i < (int)m_bSelectedKeys.size(); i++)
908 m_bSelectedKeys[i] = false;
911 //////////////////////////////////////////////////////////////////////////
912 void CColorGradientCtrl::SetTimeMarker(float fTime)
914 if (!m_pSpline)
915 return;
918 CPoint pt = TimeToPoint(m_fTimeMarker);
919 CRect rc = CRect(pt.x, m_rcGradient.top, pt.x, m_rcGradient.bottom);
920 rc.NormalizeRect();
921 rc.InflateRect(1, 0);
922 InvalidateRect(rc);
925 CPoint pt = TimeToPoint(fTime);
926 CRect rc = CRect(pt.x, m_rcGradient.top, pt.x, m_rcGradient.bottom);
927 rc.NormalizeRect();
928 rc.InflateRect(1, 0);
929 InvalidateRect(rc);
931 m_fTimeMarker = fTime;
934 //////////////////////////////////////////////////////////////////////////
935 void CColorGradientCtrl::SendNotifyEvent(int nEvent)
937 NMHDR nmh;
938 nmh.hwndFrom = m_hWnd;
939 nmh.idFrom = ::GetDlgCtrlID(m_hWnd);
940 nmh.code = nEvent;
942 GetOwner()->SendMessage(WM_NOTIFY, (WPARAM)GetDlgCtrlID(), (LPARAM)&nmh);
945 //////////////////////////////////////////////////////////////////////////
946 COLORREF CColorGradientCtrl::ValueToColor(ISplineInterpolator::ValueType val)
948 return CMFCUtils::ColorLinearToGamma(ColorF(val[0], val[1], val[2]));
951 //////////////////////////////////////////////////////////////////////////
952 void CColorGradientCtrl::ColorToValue(COLORREF col, ISplineInterpolator::ValueType& val)
954 ColorF colLin = CMFCUtils::ColorGammaToLinear(col);
955 val[0] = colLin.r;
956 val[1] = colLin.g;
957 val[2] = colLin.b;
958 val[3] = 0;