1 /*
2  * PROJECT:     ReactOS Task Manager
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Graph plotting controls
5  * COPYRIGHT:   Copyright 2002 Robert Dickenson <robd@reactos.org>
6  *              Copyright 2021 Wu Haotian <rigoligo03@gmail.com>
7  */
8 
9 #include "precomp.h"
10 
11 #include <math.h>
12 
13 WNDPROC OldGraphCtrlWndProc;
14 
15 static void GraphCtrl_Init(TGraphCtrl* this)
16 {
17     int i;
18 
19     this->m_hWnd = 0;
20     this->m_hParentWnd = 0;
21     this->m_dcGrid = 0;
22     this->m_dcPlot = 0;
23     this->m_bitmapOldGrid = 0;
24     this->m_bitmapOldPlot = 0;
25     this->m_bitmapGrid = 0;
26     this->m_bitmapPlot = 0;
27     this->m_brushBack = 0;
28 
29     this->m_penPlot[0] = 0;
30     this->m_penPlot[1] = 0;
31     this->m_penPlot[2] = 0;
32     this->m_penPlot[3] = 0;
33 
34     /* since plotting is based on a LineTo for each new point
35      * we need a starting point (i.e. a "previous" point)
36      * use 0.0 as the default first point.
37      * these are public member variables, and can be changed outside
38      * (after construction).  Therefore m_perviousPosition could be set to
39      * a more appropriate value prior to the first call to SetPosition.
40      */
41     this->m_dPreviousPosition[0] = 0.0;
42     this->m_dPreviousPosition[1] = 0.0;
43     this->m_dPreviousPosition[2] = 0.0;
44     this->m_dPreviousPosition[3] = 0.0;
45 
46     /*  public variable for the number of decimal places on the y axis */
47     this->m_nYDecimals = 3;
48 
49     /*  set some initial values for the scaling until "SetRange" is called.
50      *  these are protected variables and must be set with SetRange
51      *  in order to ensure that m_dRange is updated accordingly
52      */
53     /*   m_dLowerLimit = -10.0; */
54     /*   m_dUpperLimit =  10.0; */
55     this->m_dLowerLimit = 0.0;
56     this->m_dUpperLimit = 100.0;
57     this->m_dRange      =  this->m_dUpperLimit - this->m_dLowerLimit;  /*  protected member variable */
58 
59     /*  m_nShiftPixels determines how much the plot shifts (in terms of pixels)  */
60     /*  with the addition of a new data point */
61     this->m_nShiftPixels     = 4;
62     this->m_nHalfShiftPixels = this->m_nShiftPixels / 2;  /*  protected */
63     this->m_nPlotShiftPixels = this->m_nShiftPixels + this->m_nHalfShiftPixels;  /*  protected */
64 
65     /*  Set how big the grid boxes are  */
66     this->m_nGridXPixels = 12;
67     this->m_nGridYPixels = 12;
68 
69     /*  Task manager should scroll grid with the graph. This variable contains the  */
70     /*  amount of pixels the grid paint origin should shift to left  */
71     this->m_nGridOffsetPixels = 0;
72 
73     /*  background, grid and data colors */
74     /*  these are public variables and can be set directly */
75     this->m_crBackColor = RGB(  0,   0,   0);  /*  see also SetBackgroundColor */
76     this->m_crGridColor = RGB(  0, 128,  64);  /*  see also SetGridColor */
77     this->m_crPlotColor[0] = RGB(255, 255, 255);  /*  see also SetPlotColor */
78     this->m_crPlotColor[1] = RGB(100, 255, 255);  /*  see also SetPlotColor */
79     this->m_crPlotColor[2] = RGB(255, 100, 255);  /*  see also SetPlotColor */
80     this->m_crPlotColor[3] = RGB(255, 255, 100);  /*  see also SetPlotColor */
81 
82     /*  protected variables */
83     for (i = 0; i < MAX_PLOTS; i++)
84     {
85         this->m_penPlot[i] = CreatePen(PS_SOLID, 0, this->m_crPlotColor[i]);
86     }
87     this->m_brushBack = CreateSolidBrush(this->m_crBackColor);
88 
89     /*  public member variables, can be set directly  */
90     strcpy(this->m_strXUnitsString, "Samples");  /*  can also be set with SetXUnits */
91     strcpy(this->m_strYUnitsString, "Y units");  /*  can also be set with SetYUnits */
92 
93     /*  protected bitmaps to restore the memory DC's */
94     this->m_bitmapOldGrid = NULL;
95     this->m_bitmapOldPlot = NULL;
96 }
97 
98 void GraphCtrl_Dispose(TGraphCtrl* this)
99 {
100     int plot;
101 
102     for (plot = 0; plot < MAX_PLOTS; plot++)
103         DeleteObject(this->m_penPlot[plot]);
104 
105     /*  just to be picky restore the bitmaps for the two memory dc's */
106     /*  (these dc's are being destroyed so there shouldn't be any leaks) */
107 
108     if (this->m_bitmapOldGrid != NULL) SelectObject(this->m_dcGrid, this->m_bitmapOldGrid);
109     if (this->m_bitmapOldPlot != NULL) SelectObject(this->m_dcPlot, this->m_bitmapOldPlot);
110     if (this->m_bitmapGrid    != NULL) DeleteObject(this->m_bitmapGrid);
111     if (this->m_bitmapPlot    != NULL) DeleteObject(this->m_bitmapPlot);
112     if (this->m_dcGrid        != NULL) DeleteDC(this->m_dcGrid);
113     if (this->m_dcPlot        != NULL) DeleteDC(this->m_dcPlot);
114     if (this->m_brushBack     != NULL) DeleteObject(this->m_brushBack);
115 }
116 
117 void GraphCtrl_Create(TGraphCtrl* this, HWND hWnd, HWND hParentWnd, UINT nID)
118 {
119     GraphCtrl_Init(this);
120     this->m_hParentWnd = hParentWnd;
121     this->m_hWnd = hWnd;
122 
123     GraphCtrl_Resize(this);
124 
125     return;
126 }
127 
128 void GraphCtrl_SetRange(TGraphCtrl* this, double dLower, double dUpper, int nDecimalPlaces)
129 {
130     /* ASSERT(dUpper > dLower); */
131     this->m_dLowerLimit     = dLower;
132     this->m_dUpperLimit     = dUpper;
133     this->m_nYDecimals      = nDecimalPlaces;
134     this->m_dRange          = this->m_dUpperLimit - this->m_dLowerLimit;
135     this->m_dVerticalFactor = (double)this->m_nPlotHeight / this->m_dRange;
136     /*  clear out the existing garbage, re-start with a clean plot */
137     GraphCtrl_InvalidateCtrl(this, FALSE);
138 }
139 
140 #if 0
141 void TGraphCtrl::SetXUnits(const char* string)
142 {
143     strncpy(m_strXUnitsString, string, sizeof(m_strXUnitsString) - 1);
144     /*  clear out the existing garbage, re-start with a clean plot */
145     InvalidateCtrl();
146 }
147 
148 void TGraphCtrl::SetYUnits(const char* string)
149 {
150     strncpy(m_strYUnitsString, string, sizeof(m_strYUnitsString) - 1);
151     /*  clear out the existing garbage, re-start with a clean plot */
152     InvalidateCtrl();
153 }
154 #endif
155 
156 void GraphCtrl_SetGridColor(TGraphCtrl* this, COLORREF color)
157 {
158     this->m_crGridColor = color;
159     /*  clear out the existing garbage, re-start with a clean plot */
160     GraphCtrl_InvalidateCtrl(this, FALSE);
161 }
162 
163 void GraphCtrl_SetPlotColor(TGraphCtrl* this, int plot, COLORREF color)
164 {
165     this->m_crPlotColor[plot] = color;
166     DeleteObject(this->m_penPlot[plot]);
167     this->m_penPlot[plot] = CreatePen(PS_SOLID, 0, this->m_crPlotColor[plot]);
168     /*  clear out the existing garbage, re-start with a clean plot */
169     GraphCtrl_InvalidateCtrl(this, FALSE);
170 }
171 
172 void GraphCtrl_SetBackgroundColor(TGraphCtrl* this, COLORREF color)
173 {
174     this->m_crBackColor = color;
175     DeleteObject(this->m_brushBack);
176     this->m_brushBack = CreateSolidBrush(this->m_crBackColor);
177     /*  clear out the existing garbage, re-start with a clean plot */
178     GraphCtrl_InvalidateCtrl(this, FALSE);
179 }
180 
181 void GraphCtrl_InvalidateCtrl(TGraphCtrl* this, BOOL bResize)
182 {
183     /*  There is a lot of drawing going on here - particularly in terms of  */
184     /*  drawing the grid.  Don't panic, this is all being drawn (only once) */
185     /*  to a bitmap.  The result is then BitBlt'd to the control whenever needed. */
186     int i;
187     int nCharacters;
188     //int nTopGridPix, nMidGridPix, nBottomGridPix;
189 
190     HPEN oldPen;
191     HPEN solidPen = CreatePen(PS_SOLID, 0, this->m_crGridColor);
192     /* HFONT axisFont, yUnitFont, oldFont; */
193     /* char strTemp[50]; */
194 
195     /*  in case we haven't established the memory dc's */
196     /* CClientDC dc(this); */
197     HDC dc = GetDC(this->m_hParentWnd);
198 
199     /*  if we don't have one yet, set up a memory dc for the grid */
200     if (this->m_dcGrid == NULL)
201     {
202         this->m_dcGrid = CreateCompatibleDC(dc);
203         this->m_bitmapGrid = CreateCompatibleBitmap(dc, this->m_nClientWidth, this->m_nClientHeight);
204         this->m_bitmapOldGrid = (HBITMAP)SelectObject(this->m_dcGrid, this->m_bitmapGrid);
205     }
206     else if(bResize)
207     {
208         // the size of the drawing area has changed
209         // so create a new bitmap of the appropriate size
210         if(this->m_bitmapGrid != NULL)
211         {
212             this->m_bitmapGrid = (HBITMAP)SelectObject(this->m_dcGrid, this->m_bitmapOldGrid);
213             DeleteObject(this->m_bitmapGrid);
214             this->m_bitmapGrid = CreateCompatibleBitmap(dc, this->m_nClientWidth, this->m_nClientHeight);
215             SelectObject(this->m_dcGrid, this->m_bitmapGrid);
216         }
217     }
218 
219     SetBkColor(this->m_dcGrid, this->m_crBackColor);
220 
221     /*  fill the grid background */
222     FillRect(this->m_dcGrid, &this->m_rectClient, this->m_brushBack);
223 
224     /*  draw the plot rectangle: */
225     /*  determine how wide the y axis scaling values are */
226     nCharacters = abs((int)log10(fabs(this->m_dUpperLimit)));
227     nCharacters = max(nCharacters, abs((int)log10(fabs(this->m_dLowerLimit))));
228 
229     /*  add the units digit, decimal point and a minus sign, and an extra space */
230     /*  as well as the number of decimal places to display */
231     nCharacters = nCharacters + 4 + this->m_nYDecimals;
232 
233     /*  adjust the plot rectangle dimensions */
234     /*  assume 6 pixels per character (this may need to be adjusted) */
235     /*   m_rectPlot.left = m_rectClient.left + 6*(nCharacters); */
236     this->m_rectPlot.left = this->m_rectClient.left;
237     this->m_nPlotWidth    = this->m_rectPlot.right - this->m_rectPlot.left;/* m_rectPlot.Width(); */
238 
239     /*  draw the plot rectangle */
240     oldPen = (HPEN)SelectObject(this->m_dcGrid, solidPen);
241     MoveToEx(this->m_dcGrid, this->m_rectPlot.left, this->m_rectPlot.top, NULL);
242     LineTo(this->m_dcGrid, this->m_rectPlot.right + 1, this->m_rectPlot.top);
243     LineTo(this->m_dcGrid, this->m_rectPlot.right + 1, this->m_rectPlot.bottom + 1);
244     LineTo(this->m_dcGrid, this->m_rectPlot.left, this->m_rectPlot.bottom + 1);
245     /*   LineTo(m_dcGrid, m_rectPlot.left, m_rectPlot.top); */
246 
247     /*  draw the horizontal axis */
248     for (i = this->m_rectPlot.top; i <= this->m_rectPlot.bottom; i += this->m_nGridYPixels)
249     {
250         MoveToEx(this->m_dcGrid, this->m_rectPlot.left, i, NULL);
251         LineTo(this->m_dcGrid, this->m_rectPlot.right, i);
252     }
253 
254     /*  draw the vertical axis */
255     /*  In order to keep grid position uniform when resizing, vertical axis should be  */
256     /*  drawn from right to left  */
257     for (i = this->m_rectPlot.right - this->m_nGridOffsetPixels; i >= this->m_rectPlot.left; i -= this->m_nGridXPixels)
258     {
259         MoveToEx(this->m_dcGrid, i, this->m_rectPlot.bottom, NULL);
260         LineTo(this->m_dcGrid, i, this->m_rectPlot.top);
261     }
262 
263     SelectObject(this->m_dcGrid, oldPen);
264     DeleteObject(solidPen);
265 
266 #if 0
267     /*  create some fonts (horizontal and vertical) */
268     /*  use a height of 14 pixels and 300 weight  */
269     /*  (these may need to be adjusted depending on the display) */
270     axisFont = CreateFont (14, 0, 0, 0, 300,
271                            FALSE, FALSE, 0, ANSI_CHARSET,
272                            OUT_DEFAULT_PRECIS,
273                            CLIP_DEFAULT_PRECIS,
274                            DEFAULT_QUALITY,
275                            DEFAULT_PITCH|FF_SWISS, "Arial");
276     yUnitFont = CreateFont (14, 0, 900, 0, 300,
277                             FALSE, FALSE, 0, ANSI_CHARSET,
278                             OUT_DEFAULT_PRECIS,
279                             CLIP_DEFAULT_PRECIS,
280                             DEFAULT_QUALITY,
281                             DEFAULT_PITCH|FF_SWISS, "Arial");
282 
283     /*  grab the horizontal font */
284     oldFont = (HFONT)SelectObject(m_dcGrid, axisFont);
285 
286     /*  y max */
287     SetTextColor(m_dcGrid, m_crGridColor);
288     SetTextAlign(m_dcGrid, TA_RIGHT|TA_TOP);
289     sprintf(strTemp, "%.*lf", m_nYDecimals, m_dUpperLimit);
290     TextOut(m_dcGrid, m_rectPlot.left-4, m_rectPlot.top, strTemp, wcslen(strTemp));
291 
292     /*  y min */
293     SetTextAlign(m_dcGrid, TA_RIGHT|TA_BASELINE);
294     sprintf(strTemp, "%.*lf", m_nYDecimals, m_dLowerLimit);
295     TextOut(m_dcGrid, m_rectPlot.left-4, m_rectPlot.bottom, strTemp, wcslen(strTemp));
296 
297     /*  x min */
298     SetTextAlign(m_dcGrid, TA_LEFT|TA_TOP);
299     TextOut(m_dcGrid, m_rectPlot.left, m_rectPlot.bottom+4, "0", 1);
300 
301     /*  x max */
302     SetTextAlign(m_dcGrid, TA_RIGHT|TA_TOP);
303     sprintf(strTemp, "%d", m_nPlotWidth/m_nShiftPixels);
304     TextOut(m_dcGrid, m_rectPlot.right, m_rectPlot.bottom+4, strTemp, wcslen(strTemp));
305 
306     /*  x units */
307     SetTextAlign(m_dcGrid, TA_CENTER|TA_TOP);
308     TextOut(m_dcGrid, (m_rectPlot.left+m_rectPlot.right)/2,
309             m_rectPlot.bottom+4, m_strXUnitsString, wcslen(m_strXUnitsString));
310 
311     /*  restore the font */
312     SelectObject(m_dcGrid, oldFont);
313 
314     /*  y units */
315     oldFont = (HFONT)SelectObject(m_dcGrid, yUnitFont);
316     SetTextAlign(m_dcGrid, TA_CENTER|TA_BASELINE);
317     TextOut(m_dcGrid, (m_rectClient.left+m_rectPlot.left)/2,
318             (m_rectPlot.bottom+m_rectPlot.top)/2, m_strYUnitsString, wcslen(m_strYUnitsString));
319     SelectObject(m_dcGrid, oldFont);
320 #endif
321     /*  at this point we are done filling the grid bitmap,  */
322     /*  no more drawing to this bitmap is needed until the settings are changed */
323 
324     /*  if we don't have one yet, set up a memory dc for the plot */
325     if (this->m_dcPlot == NULL)
326     {
327         this->m_dcPlot = CreateCompatibleDC(dc);
328         this->m_bitmapPlot = CreateCompatibleBitmap(dc, this->m_nClientWidth, this->m_nClientHeight);
329         this->m_bitmapOldPlot = (HBITMAP)SelectObject(this->m_dcPlot, this->m_bitmapPlot);
330     }
331     else if(bResize)
332     {
333         // the size of the drawing area has changed
334         // so create a new bitmap of the appropriate size
335         if(this->m_bitmapPlot != NULL)
336         {
337             this->m_bitmapPlot = (HBITMAP)SelectObject(this->m_dcPlot, this->m_bitmapOldPlot);
338             DeleteObject(this->m_bitmapPlot);
339             this->m_bitmapPlot = CreateCompatibleBitmap(dc, this->m_nClientWidth, this->m_nClientHeight);
340             SelectObject(this->m_dcPlot, this->m_bitmapPlot);
341             GraphCtrl_DrawPoint(this);
342         }
343     }
344 
345     /*  make sure the plot bitmap is cleared */
346     SetBkColor(this->m_dcPlot, this->m_crBackColor);
347     FillRect(this->m_dcPlot, &this->m_rectClient, this->m_brushBack);
348 
349     /*  finally, force the plot area to redraw */
350     InvalidateRect(this->m_hParentWnd, &this->m_rectClient, TRUE);
351     ReleaseDC(this->m_hParentWnd, dc);
352 }
353 
354 double GraphCtrl_AppendPoint(TGraphCtrl* this,
355                              double dNewPoint0, double dNewPoint1,
356                              double dNewPoint2, double dNewPoint3)
357 {
358     /*  append a data point to the plot & return the previous point */
359     double dPrevious;
360 
361     dPrevious = this->m_dCurrentPosition[0];
362     this->m_dCurrentPosition[0] = dNewPoint0;
363     this->m_dCurrentPosition[1] = dNewPoint1;
364     this->m_dCurrentPosition[2] = dNewPoint2;
365     this->m_dCurrentPosition[3] = dNewPoint3;
366     GraphCtrl_ShiftGrid(this);
367     GraphCtrl_DrawPoint(this);
368     /* Invalidate(); */
369     return dPrevious;
370 }
371 
372 void GraphCtrl_Paint(TGraphCtrl* this, HWND hWnd, HDC dc)
373 {
374     HDC memDC;
375     HBITMAP memBitmap;
376     HBITMAP oldBitmap; /*  bitmap originally found in CMemDC */
377 
378 /*   RECT rcClient; */
379 /*   GetClientRect(hWnd, &rcClient); */
380 /*   FillSolidRect(dc, &rcClient, RGB(255, 0, 255)); */
381 /*   m_nClientWidth = rcClient.right - rcClient.left; */
382 /*   m_nClientHeight = rcClient.bottom - rcClient.top; */
383 
384     /*  no real plotting work is performed here,  */
385     /*  just putting the existing bitmaps on the client */
386 
387     /*  to avoid flicker, establish a memory dc, draw to it */
388     /*  and then BitBlt it to the client */
389     memDC = CreateCompatibleDC(dc);
390     memBitmap = (HBITMAP)CreateCompatibleBitmap(dc, this->m_nClientWidth, this->m_nClientHeight);
391     oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);
392 
393     if (memDC != NULL)
394     {
395         /*  first drop the grid on the memory dc */
396         BitBlt(memDC, 0, 0, this->m_nClientWidth, this->m_nClientHeight, this->m_dcGrid, 0, 0, SRCCOPY);
397         /*  now add the plot on top as a "pattern" via SRCPAINT. */
398         /*  works well with dark background and a light plot */
399         BitBlt(memDC, 0, 0, this->m_nClientWidth, this->m_nClientHeight, this->m_dcPlot, 0, 0, SRCPAINT);  /* SRCPAINT */
400         /*  finally send the result to the display */
401         BitBlt(dc, 0, 0, this->m_nClientWidth, this->m_nClientHeight, memDC, 0, 0, SRCCOPY);
402     }
403     SelectObject(memDC, oldBitmap);
404     DeleteObject(memBitmap);
405     DeleteDC(memDC);
406 }
407 
408 void GraphCtrl_ShiftGrid(TGraphCtrl* this)
409 {
410     RECT rectCleanUp;
411     HPEN oldPen, solidPen;
412     int i;
413 
414     if (this->m_dcGrid == NULL)
415         return;
416 
417     solidPen = CreatePen(PS_SOLID, 0, this->m_crGridColor);
418 
419     /* Scroll the grid left: BitBlt it to itself */
420     BitBlt(this->m_dcGrid, this->m_rectPlot.left, this->m_rectPlot.top + 1,
421            this->m_nPlotWidth, this->m_nPlotHeight,
422            this->m_dcGrid, this->m_rectPlot.left + this->m_nShiftPixels, this->m_rectPlot.top + 1,
423            SRCCOPY);
424 
425     /* Set shift pixels */
426     this->m_nGridOffsetPixels = (this->m_nGridOffsetPixels + this->m_nShiftPixels) % this->m_nGridXPixels;
427 
428     /* Construct a rect in which needs update */
429     rectCleanUp = this->m_rectPlot;
430     rectCleanUp.left = rectCleanUp.right - this->m_nShiftPixels;
431 
432     /* Fill the cleanup area with the background */
433     FillRect(this->m_dcGrid, &rectCleanUp, this->m_brushBack);
434 
435     /* Draw the plot rectangle */
436     oldPen = (HPEN)SelectObject(this->m_dcGrid, solidPen);
437 
438     /* Redraw horizontal axis */
439     for (i = rectCleanUp.top; i < rectCleanUp.bottom; i += this->m_nGridYPixels)
440     {
441         MoveToEx(this->m_dcGrid, rectCleanUp.left, i, NULL);
442         LineTo(this->m_dcGrid, rectCleanUp.right, i);
443     }
444 
445     /* Redraw scrolled vertical axis */
446     MoveToEx(this->m_dcGrid, rectCleanUp.right - this->m_nGridOffsetPixels, rectCleanUp.top, NULL);
447     LineTo(this->m_dcGrid, rectCleanUp.right - this->m_nGridOffsetPixels, rectCleanUp.bottom);
448 
449     SelectObject(this->m_dcGrid, oldPen);
450     DeleteObject(solidPen);
451 }
452 
453 void GraphCtrl_DrawPoint(TGraphCtrl* this)
454 {
455     /*  this does the work of "scrolling" the plot to the left
456      *  and appending a new data point all of the plotting is
457      *  directed to the memory based bitmap associated with m_dcPlot
458      *  the will subsequently be BitBlt'd to the client in Paint
459      */
460     int currX, prevX, currY, prevY;
461     HPEN oldPen;
462     RECT rectCleanUp;
463     int i;
464 
465     if (this->m_dcPlot != NULL)
466     {
467         /*  shift the plot by BitBlt'ing it to itself
468          *  note: the m_dcPlot covers the entire client
469          *        but we only shift bitmap that is the size
470          *        of the plot rectangle
471          *  grab the right side of the plot (excluding m_nShiftPixels on the left)
472          *  move this grabbed bitmap to the left by m_nShiftPixels
473          */
474         BitBlt(this->m_dcPlot, this->m_rectPlot.left, this->m_rectPlot.top + 1,
475                this->m_nPlotWidth, this->m_nPlotHeight, this->m_dcPlot,
476                this->m_rectPlot.left+this->m_nShiftPixels, this->m_rectPlot.top + 1,
477                SRCCOPY);
478 
479         /*  establish a rectangle over the right side of plot */
480         /*  which now needs to be cleaned up prior to adding the new point */
481         rectCleanUp = this->m_rectPlot;
482         rectCleanUp.left  = rectCleanUp.right - this->m_nShiftPixels;
483 
484         /*  fill the cleanup area with the background */
485         FillRect(this->m_dcPlot, &rectCleanUp, this->m_brushBack);
486 
487         /*  draw the next line segment */
488         for (i = 0; i < MAX_PLOTS; i++)
489         {
490             /*  grab the plotting pen */
491             oldPen = (HPEN)SelectObject(this->m_dcPlot, this->m_penPlot[i]);
492 
493             /*  move to the previous point */
494             prevX = this->m_rectPlot.right-this->m_nPlotShiftPixels;
495             prevY = this->m_rectPlot.bottom -
496                 (long)((this->m_dPreviousPosition[i] - this->m_dLowerLimit) * this->m_dVerticalFactor);
497             MoveToEx(this->m_dcPlot, prevX, prevY, NULL);
498 
499             /*  draw to the current point */
500             currX = this->m_rectPlot.right-this->m_nHalfShiftPixels;
501             currY = this->m_rectPlot.bottom -
502                 (long)((this->m_dCurrentPosition[i] - this->m_dLowerLimit) * this->m_dVerticalFactor);
503             LineTo(this->m_dcPlot, currX, currY);
504 
505             /*  Restore the pen  */
506             SelectObject(this->m_dcPlot, oldPen);
507 
508             /*  if the data leaks over the upper or lower plot boundaries
509              *  fill the upper and lower leakage with the background
510              *  this will facilitate clipping on an as needed basis
511              *  as opposed to always calling IntersectClipRect
512              */
513             if ((prevY <= this->m_rectPlot.top) || (currY <= this->m_rectPlot.top))
514             {
515                 RECT rc;
516                 rc.bottom = this->m_rectPlot.top + 1;
517                 rc.left = prevX;
518                 rc.right = currX + 1;
519                 rc.top = this->m_rectClient.top;
520                 FillRect(this->m_dcPlot, &rc, this->m_brushBack);
521             }
522             if ((prevY >= this->m_rectPlot.bottom) || (currY >= this->m_rectPlot.bottom))
523             {
524                 RECT rc;
525                 rc.bottom = this->m_rectClient.bottom + 1;
526                 rc.left = prevX;
527                 rc.right = currX + 1;
528                 rc.top = this->m_rectPlot.bottom + 1;
529                 /* RECT rc(prevX, m_rectPlot.bottom+1, currX+1, m_rectClient.bottom+1); */
530                 FillRect(this->m_dcPlot, &rc, this->m_brushBack);
531             }
532 
533             /*  store the current point for connection to the next point */
534             this->m_dPreviousPosition[i] = this->m_dCurrentPosition[i];
535         }
536     }
537 }
538 
539 void GraphCtrl_Resize(TGraphCtrl* this)
540 {
541     /*  NOTE: Resize automatically gets called during the setup of the control */
542     GetClientRect(this->m_hWnd, &this->m_rectClient);
543 
544     /*  set some member variables to avoid multiple function calls */
545     this->m_nClientHeight = this->m_rectClient.bottom - this->m_rectClient.top;/* m_rectClient.Height(); */
546     this->m_nClientWidth  = this->m_rectClient.right - this->m_rectClient.left;/* m_rectClient.Width(); */
547 
548     /*  the "left" coordinate and "width" will be modified in  */
549     /*  InvalidateCtrl to be based on the width of the y axis scaling */
550 #if 0
551     this->m_rectPlot.left   = 20;
552     this->m_rectPlot.top    = 10;
553     this->m_rectPlot.right  = this->m_rectClient.right-10;
554     this->m_rectPlot.bottom = this->m_rectClient.bottom-25;
555 #else
556     this->m_rectPlot.left   = 0;
557     this->m_rectPlot.top    = -1;
558     this->m_rectPlot.right  = this->m_rectClient.right;
559     this->m_rectPlot.bottom = this->m_rectClient.bottom;
560 #endif
561 
562     /*  set some member variables to avoid multiple function calls */
563     this->m_nPlotHeight = this->m_rectPlot.bottom - this->m_rectPlot.top;/* m_rectPlot.Height(); */
564     this->m_nPlotWidth  = this->m_rectPlot.right - this->m_rectPlot.left;/* m_rectPlot.Width(); */
565 
566     /*  set the scaling factor for now, this can be adjusted  */
567     /*  in the SetRange functions */
568     this->m_dVerticalFactor = (double)this->m_nPlotHeight / this->m_dRange;
569 }
570 
571 #if 0
572 void TGraphCtrl::Reset()
573 {
574     /*  to clear the existing data (in the form of a bitmap) */
575     /*  simply invalidate the entire control */
576     InvalidateCtrl();
577 }
578 #endif
579 
580 extern TGraphCtrl PerformancePageCpuUsageHistoryGraph;
581 extern TGraphCtrl PerformancePageMemUsageHistoryGraph;
582 extern HWND hPerformancePageCpuUsageHistoryGraph;
583 extern HWND hPerformancePageMemUsageHistoryGraph;
584 
585 INT_PTR CALLBACK
586 GraphCtrl_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
587 {
588     RECT        rcClient;
589     HDC            hdc;
590     PAINTSTRUCT     ps;
591 
592     switch (message)
593     {
594     case WM_ERASEBKGND:
595         return TRUE;
596     /*
597      *  Filter out mouse  & keyboard messages
598      */
599     /* case WM_APPCOMMAND: */
600     case WM_CAPTURECHANGED:
601     case WM_LBUTTONDBLCLK:
602     case WM_LBUTTONDOWN:
603     case WM_LBUTTONUP:
604     case WM_MBUTTONDBLCLK:
605     case WM_MBUTTONDOWN:
606     case WM_MBUTTONUP:
607     case WM_MOUSEACTIVATE:
608     case WM_MOUSEHOVER:
609     case WM_MOUSELEAVE:
610     case WM_MOUSEMOVE:
611     /* case WM_MOUSEWHEEL: */
612     case WM_NCHITTEST:
613     case WM_NCLBUTTONDBLCLK:
614     case WM_NCLBUTTONDOWN:
615     case WM_NCLBUTTONUP:
616     case WM_NCMBUTTONDBLCLK:
617     case WM_NCMBUTTONDOWN:
618     case WM_NCMBUTTONUP:
619     /* case WM_NCMOUSEHOVER: */
620     /* case WM_NCMOUSELEAVE: */
621     case WM_NCMOUSEMOVE:
622     case WM_NCRBUTTONDBLCLK:
623     case WM_NCRBUTTONDOWN:
624     case WM_NCRBUTTONUP:
625     /* case WM_NCXBUTTONDBLCLK: */
626     /* case WM_NCXBUTTONDOWN: */
627     /* case WM_NCXBUTTONUP: */
628     case WM_RBUTTONDBLCLK:
629     case WM_RBUTTONDOWN:
630     case WM_RBUTTONUP:
631     /* case WM_XBUTTONDBLCLK: */
632     /* case WM_XBUTTONDOWN: */
633     /* case WM_XBUTTONUP: */
634     case WM_ACTIVATE:
635     case WM_CHAR:
636     case WM_DEADCHAR:
637     case WM_GETHOTKEY:
638     case WM_HOTKEY:
639     case WM_KEYDOWN:
640     case WM_KEYUP:
641     case WM_KILLFOCUS:
642     case WM_SETFOCUS:
643     case WM_SETHOTKEY:
644     case WM_SYSCHAR:
645     case WM_SYSDEADCHAR:
646     case WM_SYSKEYDOWN:
647     case WM_SYSKEYUP:
648         return 0;
649 
650     case WM_NCCALCSIZE:
651         return 0;
652 
653     case WM_SIZE:
654         if (hWnd == hPerformancePageMemUsageHistoryGraph)
655         {
656             GraphCtrl_Resize(&PerformancePageMemUsageHistoryGraph);
657             GraphCtrl_InvalidateCtrl(&PerformancePageMemUsageHistoryGraph, TRUE);
658         }
659         if (hWnd == hPerformancePageCpuUsageHistoryGraph)
660         {
661             GraphCtrl_Resize(&PerformancePageCpuUsageHistoryGraph);
662             GraphCtrl_InvalidateCtrl(&PerformancePageCpuUsageHistoryGraph, TRUE);
663         }
664         return 0;
665 
666     case WM_PAINT:
667         hdc = BeginPaint(hWnd, &ps);
668         GetClientRect(hWnd, &rcClient);
669         if (hWnd == hPerformancePageMemUsageHistoryGraph)
670             GraphCtrl_Paint(&PerformancePageMemUsageHistoryGraph, hWnd, hdc);
671         if (hWnd == hPerformancePageCpuUsageHistoryGraph)
672             GraphCtrl_Paint(&PerformancePageCpuUsageHistoryGraph, hWnd, hdc);
673         EndPaint(hWnd, &ps);
674         return 0;
675     }
676 
677     /*
678      *  We pass on all non-handled messages
679      */
680     return CallWindowProcW(OldGraphCtrlWndProc, hWnd, message, wParam, lParam);
681 }
682