Fixed incorrect data type constants usage; confusing DT_xxx and DTYPE_xxx constants...
[public/netxms.git] / src / console / win32 / Graph.cpp
CommitLineData
23e62c09
VK
1// Graph.cpp : implementation file
2//
3
4#include "stdafx.h"
5#include "nxcon.h"
6#include "Graph.h"
7#include <math.h>
8
42d7ed00
VK
9#define ROW_DATA(row, dt) ((dt == DCI_DT_STRING) ? strtod(row->value.szString, NULL) : \
10 ((dt == DCI_DT_INTEGER) ? row->value.dwInt32 : \
11 ((dt == DCI_DT_INT64) ? row->value.qwInt64 : \
12 ((dt == DCI_DT_FLOAT) ? row->value.dFloat : 0) \
23e62c09
VK
13 ) \
14 ) \
15 )
16
17#ifdef _DEBUG
18#define new DEBUG_NEW
19#undef THIS_FILE
20static char THIS_FILE[] = __FILE__;
21#endif
22
23/////////////////////////////////////////////////////////////////////////////
24// CGraph
25
26CGraph::CGraph()
27{
28 m_iGridSize = 40;
29 m_dMaxValue = 100;
30 m_bAutoScale = TRUE;
31 m_bShowGrid = TRUE;
32 m_dwNumItems = 0;
33 m_rgbBkColor = RGB(0,0,0);
34 m_rgbGridColor = RGB(64, 64, 64);
35 m_rgbAxisColor = RGB(127, 127, 127);
36 m_rgbTextColor = RGB(255, 255, 255);
37 m_rgbLineColors[0] = RGB(0, 255, 0);
0890540a
VK
38 m_rgbLabelBkColor = RGB(255, 255, 170);
39 m_rgbLabelTextColor = RGB(85, 0, 0);
23e62c09 40 memset(m_pData, 0, sizeof(NXC_DCI_DATA *) * MAX_GRAPH_ITEMS);
0890540a
VK
41 m_bIsActive = FALSE;
42 memset(&m_rectInfo, 0, sizeof(RECT));
23e62c09
VK
43}
44
45CGraph::~CGraph()
46{
47 DWORD i;
48
49 for(i = 0; i < MAX_GRAPH_ITEMS; i++)
50 if (m_pData[i] != NULL)
51 NXCDestroyDCIData(m_pData[i]);
52}
53
54
55BEGIN_MESSAGE_MAP(CGraph, CWnd)
56 //{{AFX_MSG_MAP(CGraph)
57 ON_WM_PAINT()
0890540a
VK
58 ON_WM_MOUSEMOVE()
59 ON_WM_SETFOCUS()
60 ON_WM_KILLFOCUS()
23e62c09 61 //}}AFX_MSG_MAP
0890540a 62 ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
23e62c09
VK
63END_MESSAGE_MAP()
64
65
66/////////////////////////////////////////////////////////////////////////////
67// CGraph message handlers
68
69
70BOOL CGraph::Create(DWORD dwStyle, const RECT &rect, CWnd *pwndParent, int nId)
71{
72 return CWnd::Create(NULL, "", dwStyle, rect, pwndParent, nId);
73}
74
75
76//
77// Overloaded PreCreateWindow()
78//
79
80BOOL CGraph::PreCreateWindow(CREATESTRUCT& cs)
81{
82 if (!CWnd::PreCreateWindow(cs))
83 return FALSE;
84
85 cs.dwExStyle |= WS_EX_CLIENTEDGE;
86 cs.style &= ~WS_BORDER;
87 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
88 ::LoadCursor(NULL, IDC_ARROW), ::CreateSolidBrush(RGB(0, 0, 0)), NULL);
89
90 return TRUE;
91}
92
93
94//
95// WM_PAINT message handler
96//
97
98void CGraph::OnPaint()
99{
100 CPaintDC sdc(this); // original device context for painting
101 CDC dc; // In-memory dc
102 CBitmap bitmap; // Bitmap for in-memory drawing
103 CBitmap *pOldBitmap;
104 CPen pen, *pOldPen;
105 CFont font, *pOldFont;
106 RECT rect;
107 CSize textSize;
108 DWORD i, dwTimeStamp;
109 int iLeftMargin, iBottomMargin, iRightMargin = 5, iTopMargin = 5;
110 int x, y, iTimeLen, iStep, iGraphLen;
111 double dStep, dMark;
112 char szBuffer[256];
113
114 GetClientRect(&rect);
115
116 dc.CreateCompatibleDC(&sdc);
117 bitmap.CreateCompatibleBitmap(&sdc, rect.right, rect.bottom);
118 pOldBitmap = dc.SelectObject(&bitmap);
119 dc.SetBkColor(m_rgbBkColor);
120
121 // Setup text parameters
122 font.CreateFont(-MulDiv(7, GetDeviceCaps(GetDC()->m_hDC, LOGPIXELSY), 72),
123 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
124 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
125 VARIABLE_PITCH | FF_DONTCARE, "Verdana");
126 pOldFont = dc.SelectObject(&font);
127 dc.SetTextColor(m_rgbTextColor);
128
129 // Calculate text size and left margin
130 textSize = dc.GetTextExtent("00000.000");
131 iTimeLen = dc.GetTextExtent("00:00:00").cx;
132 iLeftMargin = textSize.cx + 10;
133 iBottomMargin = textSize.cy + 8;
134
135 // Draw grid
136 if (m_bShowGrid)
137 {
138 pen.CreatePen(PS_ALTERNATE | PS_COSMETIC, 0, m_rgbGridColor);
139 pOldPen = dc.SelectObject(&pen);
140 for(x = iLeftMargin + m_iGridSize; x < rect.right - iRightMargin; x += m_iGridSize)
141 {
142 dc.MoveTo(x, rect.bottom - iBottomMargin);
143 dc.LineTo(x, iTopMargin);
144 }
145 for(y = rect.bottom - iBottomMargin - m_iGridSize; y > iTopMargin; y -= m_iGridSize)
146 {
147 dc.MoveTo(iLeftMargin, y);
148 dc.LineTo(rect.right - iRightMargin, y);
149 }
150 dc.SelectObject(pOldPen);
151 pen.DeleteObject();
152 }
153
154 // Draw all parameters
155 ///////////////////////////////////
156
157 // Calculate data rectangle
0890540a
VK
158 memcpy(&m_rectGraph, &rect, sizeof(RECT));
159 m_rectGraph.left += iLeftMargin;
160 m_rectGraph.top += iTopMargin;
161 m_rectGraph.right -= iRightMargin;
162 m_rectGraph.bottom -= iBottomMargin;
163 iGraphLen = m_rectGraph.right - m_rectGraph.left + 1; // Actual data area length in pixels
23e62c09
VK
164 m_dSecondsPerPixel = (double)(m_dwTimeTo - m_dwTimeFrom) / (double)iGraphLen;
165
166 // Calculate max graph value
167 if (m_bAutoScale)
168 {
169 for(i = 0, m_dCurrMaxValue = 0; i < MAX_GRAPH_ITEMS; i++)
170 if (m_pData[i] != NULL)
171 {
172 NXC_DCI_ROW *pRow;
173 double dCurrValue;
174 DWORD j;
175
176 pRow = m_pData[i]->pRows;
177 for(j = 0; (j < m_pData[i]->dwNumRows) && (pRow->dwTimeStamp >= m_dwTimeFrom); j++)
178 {
179 dCurrValue = (double)ROW_DATA(pRow, m_pData[i]->wDataType);
180 if (dCurrValue > m_dCurrMaxValue)
181 m_dCurrMaxValue = dCurrValue;
182 inc_ptr(pRow, m_pData[i]->wRowSize, NXC_DCI_ROW);
183 }
184 }
185
186 if (m_dCurrMaxValue == 0)
187 m_dCurrMaxValue = 1;
188
189 // Round max value
190 for(double d = 0.0001; d < 1000000; d *= 10)
191 if ((m_dCurrMaxValue >= d) && (m_dCurrMaxValue <= d * 10))
192 {
193 m_dCurrMaxValue -= fmod(m_dCurrMaxValue, d);
194 m_dCurrMaxValue += d;
195 break;
196 }
197 }
198 else
199 {
200 m_dCurrMaxValue = m_dMaxValue;
201 }
202
203 // Draw each parameter
204 CRgn rgn;
0890540a 205 rgn.CreateRectRgn(m_rectGraph.left, m_rectGraph.top, m_rectGraph.right, m_rectGraph.bottom);
23e62c09
VK
206 dc.SelectClipRgn(&rgn);
207 for(i = 0; i < MAX_GRAPH_ITEMS; i++)
208 if (m_pData[i] != NULL)
0890540a 209 DrawLineGraph(dc, m_pData[i], m_rgbLineColors[i]);
23e62c09
VK
210 dc.SelectClipRgn(NULL);
211 rgn.DeleteObject();
212
23e62c09
VK
213 // Paint ordinates
214 pen.CreatePen(PS_SOLID, 3, m_rgbAxisColor);
215 pOldPen = dc.SelectObject(&pen);
216 dc.MoveTo(iLeftMargin, rect.bottom - iBottomMargin);
217 dc.LineTo(iLeftMargin, iTopMargin);
218 dc.MoveTo(iLeftMargin, rect.bottom - iBottomMargin);
219 dc.LineTo(rect.right - iRightMargin, rect.bottom - iBottomMargin);
220 dc.SelectObject(pOldPen);
221 pen.DeleteObject();
222
223 // Display ordinate marks
224 dStep = m_dCurrMaxValue / ((rect.bottom - iBottomMargin - iTopMargin) / m_iGridSize);
225 for(y = rect.bottom - iBottomMargin - textSize.cy / 2, dMark = 0; y > iTopMargin; y -= m_iGridSize * 2, dMark += dStep * 2)
226 {
227 sprintf(szBuffer, "%5.3f", dMark);
228 CSize cz = dc.GetTextExtent(szBuffer);
229 dc.TextOut(iLeftMargin - cz.cx - 5, y, szBuffer);
230 }
231
232 // Display absciss marks
233 y = rect.bottom - iBottomMargin + 3;
234 iStep = iTimeLen / m_iGridSize + 1; // How many grid lines we should skip
235 for(x = iLeftMargin; x < rect.right - iRightMargin; x += m_iGridSize * iStep)
236 {
237 dwTimeStamp = m_dwTimeFrom + (DWORD)((double)(x - iLeftMargin) * m_dSecondsPerPixel);
238 FormatTimeStamp(dwTimeStamp, szBuffer, TS_LONG_TIME);
239 dc.TextOut(x, y, szBuffer);
240 }
241
242 // Move drawing from in-memory DC to screen
243 sdc.BitBlt(0, 0, rect.right, rect.bottom, &dc, 0, 0, SRCCOPY);
244
245 // Cleanup
246 dc.SelectObject(pOldFont);
247 dc.SelectObject(pOldBitmap);
248 bitmap.DeleteObject();
249 font.DeleteObject();
250 dc.DeleteDC();
251}
252
253
254//
255// Set time frame this graph covers
256//
257
258void CGraph::SetTimeFrame(DWORD dwTimeFrom, DWORD dwTimeTo)
259{
260 m_dwTimeFrom = dwTimeFrom;
261 m_dwTimeTo = dwTimeTo;
262}
263
264
265//
266// Set data for specific item
267//
268
269void CGraph::SetData(DWORD dwIndex, NXC_DCI_DATA *pData)
270{
271 if (dwIndex < MAX_GRAPH_ITEMS)
272 {
273 if (m_pData[dwIndex] != NULL)
274 NXCDestroyDCIData(m_pData[dwIndex]);
275 m_pData[dwIndex] = pData;
276 }
277}
278
279
280//
281// Draw single line
282//
283
0890540a 284void CGraph::DrawLineGraph(CDC &dc, NXC_DCI_DATA *pData, COLORREF rgbColor)
23e62c09
VK
285{
286 DWORD i;
287 int x;
288 CPen pen, *pOldPen;
289 NXC_DCI_ROW *pRow;
290 double dScale;
291
292 if (pData->dwNumRows < 2)
293 return; // Nothing to draw
294
295 pen.CreatePen(PS_SOLID, 2, RGB(0, 255, 0));
296 pOldPen = dc.SelectObject(&pen);
297
298 // Calculate scale factor for values
0890540a
VK
299 dScale = (double)(m_rectGraph.bottom - m_rectGraph.top -
300 (m_rectGraph.bottom - m_rectGraph.top) % m_iGridSize) / m_dCurrMaxValue;
23e62c09
VK
301
302 // Move to first position
303 pRow = pData->pRows;
0890540a
VK
304 dc.MoveTo(m_rectGraph.right,
305 (int)(m_rectGraph.bottom - (double)ROW_DATA(pRow, pData->wDataType) * dScale - 1));
23e62c09
VK
306 inc_ptr(pRow, pData->wRowSize, NXC_DCI_ROW);
307
308 for(i = 1; (i < pData->dwNumRows) && (pRow->dwTimeStamp >= m_dwTimeFrom); i++)
309 {
310 // Calculate timestamp position on graph
0890540a
VK
311 x = m_rectGraph.right - (int)((double)(m_dwTimeTo - pRow->dwTimeStamp) / m_dSecondsPerPixel);
312 dc.LineTo(x, (int)(m_rectGraph.bottom - (double)ROW_DATA(pRow, pData->wDataType) * dScale - 1));
23e62c09
VK
313 inc_ptr(pRow, pData->wRowSize, NXC_DCI_ROW);
314 }
315
316 dc.SelectObject(pOldPen);
317}
0890540a
VK
318
319
320//
321// Set mouse tracking parameters
322//
323
324void CGraph::SetMouseTracking()
325{
326 TRACKMOUSEEVENT tme;
327
328 tme.cbSize = sizeof(TRACKMOUSEEVENT);
329 tme.dwFlags = TME_HOVER;
330 tme.hwndTrack = m_hWnd;
331 tme.dwHoverTime = 1000;
332 _TrackMouseEvent(&tme);
333}
334
335
336//
337// WM_MOUSEHOVER message handler
338//
339
340int CGraph::OnMouseHover(WPARAM wParam, LPARAM lParam)
341{
342 POINTS pt;
343
344 pt = MAKEPOINTS(lParam);
345 m_ptLastHoverPoint = CPoint(pt.x, pt.y);
346 if (PtInRect(&m_rectGraph, m_ptLastHoverPoint))
347 DrawInfoRect(pt);
348 return 0;
349}
350
351
352//
353// WM_MOUSEMOVE message handler
354//
355
356void CGraph::OnMouseMove(UINT nFlags, CPoint point)
357{
358 CWnd::OnMouseMove(nFlags, point);
359 if ((m_bIsActive) && (m_ptLastHoverPoint != point))
360 {
361 SetMouseTracking();
362 InvalidateRect(&m_rectInfo, FALSE);
363 memset(&m_rectInfo, 0, sizeof(RECT));
364 }
365}
366
367
368//
369// WM_SETFOCUS message handler
370//
371
372void CGraph::OnSetFocus(CWnd* pOldWnd)
373{
374 CWnd::OnSetFocus(pOldWnd);
375 m_bIsActive = TRUE;
376 SetMouseTracking();
377}
378
379
380//
381// WM_KILLFOCUS message handler
382//
383
384void CGraph::OnKillFocus(CWnd* pNewWnd)
385{
386 CWnd::OnKillFocus(pNewWnd);
387 m_bIsActive = FALSE;
388 InvalidateRect(&m_rectInfo, FALSE);
389 memset(&m_rectInfo, 0, sizeof(RECT));
390}
391
392
393//
394// Draw information rectangle at mouse coordinates
395//
396
397void CGraph::DrawInfoRect(POINTS pt)
398{
399 CDC *pDC;
400 CSize size;
401 char szBuffer[256], szTime[32];
402 DWORD dwTimeStamp;
403 double dValue;
404
405 // Prepare text for output
406 dwTimeStamp = m_dwTimeFrom + (DWORD)((pt.x - m_rectGraph.left) * m_dSecondsPerPixel);
407 dValue = m_dCurrMaxValue / (m_rectGraph.bottom - m_rectGraph.top -
408 (m_rectGraph.bottom - m_rectGraph.top) % m_iGridSize) *
409 (m_rectGraph.bottom - pt.y);
410 sprintf(szBuffer, "%s %5.3f", FormatTimeStamp(dwTimeStamp, szTime, TS_LONG_TIME), dValue);
411
412 // Prepare for drawing
413 pDC = GetDC();
414 pDC->SetTextColor(m_rgbLabelTextColor);
415 pDC->SetBkColor(m_rgbLabelBkColor);
416
417 // Calculate text size and draw it
418 size = pDC->GetTextExtent(szBuffer, strlen(szBuffer));
419 pDC->TextOut(pt.x, pt.y - size.cy - 1, szBuffer, strlen(szBuffer));
420
421 // Fill rectangle to be invalidated
422 m_rectInfo.left = pt.x;
423 m_rectInfo.top = pt.y - size.cy - 1;
424 m_rectInfo.right = pt.x + size.cx + 1;
425 m_rectInfo.bottom = pt.y;
426
427 // Cleanup
428 ReleaseDC(pDC);
429}