Panda3D
winStatsGraph.cxx
1 // Filename: winStatsGraph.cxx
2 // Created by: drose (03Dec03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "winStatsGraph.h"
16 #include "winStatsMonitor.h"
17 #include "winStatsLabelStack.h"
18 
19 bool WinStatsGraph::_graph_window_class_registered = false;
20 const char * const WinStatsGraph::_graph_window_class_name = "graph";
21 
22 DWORD WinStatsGraph::graph_window_style =
23 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_VISIBLE;
24 
25 ////////////////////////////////////////////////////////////////////
26 // Function: WinStatsGraph::Constructor
27 // Access: Public
28 // Description:
29 ////////////////////////////////////////////////////////////////////
30 WinStatsGraph::
31 WinStatsGraph(WinStatsMonitor *monitor) :
32  _monitor(monitor)
33 {
34  _window = 0;
35  _graph_window = 0;
36  _sizewe_cursor = LoadCursor(NULL, IDC_SIZEWE);
37  _hand_cursor = LoadCursor(NULL, IDC_HAND);
38  _bitmap = 0;
39  _bitmap_dc = 0;
40 
41  _graph_left = 0;
42  _graph_top = 0;
43  _bitmap_xsize = 0;
44  _bitmap_ysize = 0;
45 
46  _dark_color = RGB(51, 51, 51);
47  _light_color = RGB(154, 154, 154);
48  _user_guide_bar_color = RGB(130, 150, 255);
49  _dark_pen = CreatePen(PS_SOLID, 1, _dark_color);
50  _light_pen = CreatePen(PS_SOLID, 1, _light_color);
51  _user_guide_bar_pen = CreatePen(PS_DASH, 1, _user_guide_bar_color);
52 
53  _drag_mode = DM_none;
54  _potential_drag_mode = DM_none;
55  _drag_scale_start = 0.0f;
56 
57  _pause = false;
58 }
59 
60 ////////////////////////////////////////////////////////////////////
61 // Function: WinStatsGraph::Destructor
62 // Access: Public, Virtual
63 // Description:
64 ////////////////////////////////////////////////////////////////////
65 WinStatsGraph::
66 ~WinStatsGraph() {
67  _monitor = (WinStatsMonitor *)NULL;
68  release_bitmap();
69 
70  DeleteObject(_dark_pen);
71  DeleteObject(_light_pen);
72  DeleteObject(_user_guide_bar_pen);
73 
74  Brushes::iterator bi;
75  for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) {
76  HBRUSH brush = (*bi).second;
77  DeleteObject(brush);
78  }
79 
80  if (_graph_window) {
81  DestroyWindow(_graph_window);
82  _graph_window = 0;
83  }
84 
85  if (_window) {
86  DestroyWindow(_window);
87  _window = 0;
88  }
89 }
90 
91 ////////////////////////////////////////////////////////////////////
92 // Function: WinStatsGraph::new_collector
93 // Access: Public, Virtual
94 // Description: Called whenever a new Collector definition is
95 // received from the client.
96 ////////////////////////////////////////////////////////////////////
97 void WinStatsGraph::
99 }
100 
101 ////////////////////////////////////////////////////////////////////
102 // Function: WinStatsGraph::new_data
103 // Access: Public, Virtual
104 // Description: Called whenever new data arrives.
105 ////////////////////////////////////////////////////////////////////
106 void WinStatsGraph::
107 new_data(int thread_index, int frame_number) {
108 }
109 
110 ////////////////////////////////////////////////////////////////////
111 // Function: WinStatsGraph::force_redraw
112 // Access: Public, Virtual
113 // Description: Called when it is necessary to redraw the entire graph.
114 ////////////////////////////////////////////////////////////////////
115 void WinStatsGraph::
117 }
118 
119 ////////////////////////////////////////////////////////////////////
120 // Function: WinStatsGraph::changed_graph_size
121 // Access: Public, Virtual
122 // Description: Called when the user has resized the window, forcing
123 // a resize of the graph.
124 ////////////////////////////////////////////////////////////////////
125 void WinStatsGraph::
126 changed_graph_size(int graph_xsize, int graph_ysize) {
127 }
128 
129 ////////////////////////////////////////////////////////////////////
130 // Function: WinStatsGraph::set_time_units
131 // Access: Public, Virtual
132 // Description: Called when the user selects a new time units from
133 // the monitor pulldown menu, this should adjust the
134 // units for the graph to the indicated mask if it is a
135 // time-based graph.
136 ////////////////////////////////////////////////////////////////////
137 void WinStatsGraph::
138 set_time_units(int unit_mask) {
139 }
140 
141 ////////////////////////////////////////////////////////////////////
142 // Function: WinStatsGraph::set_scroll_speed
143 // Access: Public
144 // Description: Called when the user selects a new scroll speed from
145 // the monitor pulldown menu, this should adjust the
146 // speed for the graph to the indicated value.
147 ////////////////////////////////////////////////////////////////////
148 void WinStatsGraph::
149 set_scroll_speed(double scroll_speed) {
150 }
151 
152 ////////////////////////////////////////////////////////////////////
153 // Function: WinStatsGraph::set_pause
154 // Access: Public
155 // Description: Changes the pause flag for the graph. When this flag
156 // is true, the graph does not update in response to new
157 // data.
158 ////////////////////////////////////////////////////////////////////
159 void WinStatsGraph::
160 set_pause(bool pause) {
161  _pause = pause;
162 }
163 
164 ////////////////////////////////////////////////////////////////////
165 // Function: WinStatsGraph::user_guide_bars_changed
166 // Access: Public
167 // Description: Called when the user guide bars have been changed.
168 ////////////////////////////////////////////////////////////////////
169 void WinStatsGraph::
171  InvalidateRect(_window, NULL, TRUE);
172  InvalidateRect(_graph_window, NULL, TRUE);
173 }
174 
175 ////////////////////////////////////////////////////////////////////
176 // Function: WinStatsGraph::clicked_label
177 // Access: Public, Virtual
178 // Description: Called when the user single-clicks on a label.
179 ////////////////////////////////////////////////////////////////////
180 void WinStatsGraph::
181 clicked_label(int collector_index) {
182 }
183 
184 ////////////////////////////////////////////////////////////////////
185 // Function: WinStatsGraph::close
186 // Access: Protected
187 // Description: Should be called when the user closes the associated
188 // window. This tells the monitor to remove the graph.
189 ////////////////////////////////////////////////////////////////////
190 void WinStatsGraph::
191 close() {
192  WinStatsMonitor *monitor = _monitor;
193  _monitor = (WinStatsMonitor *)NULL;
194  if (monitor != (WinStatsMonitor *)NULL) {
195  monitor->remove_graph(this);
196  }
197 }
198 
199 ////////////////////////////////////////////////////////////////////
200 // Function: WinStatsGraph::setup_label_stack
201 // Access: Protected
202 // Description: Sets up the label stack on the left edge of the
203 // frame.
204 ////////////////////////////////////////////////////////////////////
205 void WinStatsGraph::
206 setup_label_stack() {
207  _label_stack.setup(_window);
208  move_label_stack();
209 }
210 
211 ////////////////////////////////////////////////////////////////////
212 // Function: WinStatsGraph::move_label_stack
213 // Access: Protected
214 // Description: Repositions the label stack if its coordinates or
215 // size have changed.
216 ////////////////////////////////////////////////////////////////////
217 void WinStatsGraph::
218 move_label_stack() {
219  if (_label_stack.is_setup()) {
220  RECT rect;
221  GetClientRect(_window, &rect);
222 
223  rect.left += 8;
224  rect.right = _left_margin - 8;
225  rect.bottom -= _bottom_margin;
226 
227  _label_stack.set_pos(rect.left, rect.top,
228  rect.right - rect.left, rect.bottom - rect.top);
229  }
230 }
231 
232 ////////////////////////////////////////////////////////////////////
233 // Function: WinStatsGraph::get_collector_brush
234 // Access: Protected
235 // Description: Returns a brush suitable for drawing in the indicated
236 // collector's color.
237 ////////////////////////////////////////////////////////////////////
238 HBRUSH WinStatsGraph::
239 get_collector_brush(int collector_index) {
240  Brushes::iterator bi;
241  bi = _brushes.find(collector_index);
242  if (bi != _brushes.end()) {
243  return (*bi).second;
244  }
245 
246  // Ask the monitor what color this guy should be.
247  LRGBColor rgb = _monitor->get_collector_color(collector_index);
248  int r = (int)(rgb[0] * 255.0f);
249  int g = (int)(rgb[1] * 255.0f);
250  int b = (int)(rgb[2] * 255.0f);
251  HBRUSH brush = CreateSolidBrush(RGB(r, g, b));
252 
253  _brushes[collector_index] = brush;
254  return brush;
255 }
256 
257 ////////////////////////////////////////////////////////////////////
258 // Function: WinStatsGraph::window_proc
259 // Access: Protected
260 // Description: This window_proc should be called up to by the
261 // derived classes for any messages that are not
262 // specifically handled by the derived class.
263 ////////////////////////////////////////////////////////////////////
264 LONG WinStatsGraph::
265 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
266  switch (msg) {
267  case WM_DESTROY:
268  close();
269  break;
270 
271  case WM_SIZE:
272  move_label_stack();
273  InvalidateRect(hwnd, NULL, TRUE);
274  break;
275 
276  case WM_SIZING:
277  set_drag_mode(DM_sizing);
278  break;
279 
280  case WM_EXITSIZEMOVE:
281  set_drag_mode(DM_none);
282  break;
283 
284  case WM_SETCURSOR:
285  {
286  // Why is it so hard to ask for the cursor position within the
287  // window's client area?
288  POINT point;
289  GetCursorPos(&point);
290  WINDOWINFO winfo;
291  GetWindowInfo(hwnd, &winfo);
292  const RECT &rect = winfo.rcClient;
293  int x = point.x - rect.left;
294  int y = point.y - rect.top;
295  int width = rect.right - rect.left;
296  int height = rect.bottom - rect.top;
297 
298  _potential_drag_mode = consider_drag_start(x, y, width, height);
299 
300  switch (_potential_drag_mode) {
301  case DM_left_margin:
302  case DM_right_margin:
303  SetCursor(_sizewe_cursor);
304  return TRUE;
305 
306  case DM_guide_bar:
307  SetCursor(_hand_cursor);
308  return TRUE;
309 
310  default:
311  case DM_none:
312  break;
313  }
314  }
315  break;
316 
317  case WM_LBUTTONDOWN:
318  if (_potential_drag_mode != DM_none) {
319  set_drag_mode(_potential_drag_mode);
320  _drag_start_x = (PN_int16)LOWORD(lparam);
321  _drag_start_y = (PN_int16)HIWORD(lparam);
322  SetCapture(_window);
323  }
324  return 0;
325 
326  case WM_MOUSEMOVE:
327  if (_drag_mode == DM_left_margin) {
328  PN_int16 x = LOWORD(lparam);
329  _left_margin += (x - _drag_start_x);
330  _drag_start_x = x;
331  InvalidateRect(hwnd, NULL, TRUE);
332  move_label_stack();
333  return 0;
334 
335  } else if (_drag_mode == DM_right_margin) {
336  PN_int16 x = LOWORD(lparam);
337  _right_margin += (_drag_start_x - x);
338  _drag_start_x = x;
339  InvalidateRect(hwnd, NULL, TRUE);
340  return 0;
341  }
342  break;
343 
344  case WM_LBUTTONUP:
345  set_drag_mode(DM_none);
346  ReleaseCapture();
347  break;
348 
349  case WM_PAINT:
350  {
351  PAINTSTRUCT ps;
352  HDC hdc = BeginPaint(hwnd, &ps);
353 
354  // First, draw a frame around the graph.
355  RECT rect;
356  GetClientRect(hwnd, &rect);
357 
358  rect.left += _left_margin;
359  rect.top += _top_margin;
360  rect.right -= _right_margin;
361  rect.bottom -= _bottom_margin;
362 
363  if (rect.right > rect.left && rect.bottom > rect.top) {
364  DrawEdge(hdc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
365 
366  int graph_xsize = rect.right - rect.left;
367  int graph_ysize = rect.bottom - rect.top;
368  if (_bitmap_dc == 0 ||
369  graph_xsize != _bitmap_xsize ||
370  graph_ysize != _bitmap_ysize) {
371  // Oops, we need to change the bitmap (and graph) size.
372  changed_graph_size(graph_xsize, graph_ysize);
373  move_graph_window(rect.left, rect.top, graph_xsize, graph_ysize);
374  force_redraw();
375  }
376  }
377 
378  additional_window_paint(hdc);
379 
380  EndPaint(hwnd, &ps);
381  return 0;
382  }
383 
384  default:
385  break;
386  }
387 
388  return DefWindowProc(hwnd, msg, wparam, lparam);
389 }
390 
391 ////////////////////////////////////////////////////////////////////
392 // Function: WinStatsGraph::graph_window_proc
393 // Access: Protected, Virtual
394 // Description:
395 ////////////////////////////////////////////////////////////////////
396 LONG WinStatsGraph::
397 graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
398  switch (msg) {
399  case WM_DISPLAYCHANGE:
400  setup_bitmap(_bitmap_xsize, _bitmap_ysize);
401  force_redraw();
402  break;
403 
404  case WM_LBUTTONDOWN:
405  // Vector any uncaught WM_LBUTTONDOWN into the main window, so we
406  // can drag margins, etc.
407  if (_potential_drag_mode != DM_none) {
408  PN_int16 x = LOWORD(lparam) + _graph_left;
409  PN_int16 y = HIWORD(lparam) + _graph_top;
410  return window_proc(_window, msg, wparam, MAKELPARAM(x, y));
411  }
412  break;
413 
414  case WM_LBUTTONUP:
415  set_drag_mode(DM_none);
416  ReleaseCapture();
417  break;
418 
419  case WM_PAINT:
420  {
421  // Repaint the graph by copying the backing pixmap in.
422  PAINTSTRUCT ps;
423  HDC hdc = BeginPaint(hwnd, &ps);
424 
425  BitBlt(hdc, 0, 0,
426  _bitmap_xsize, _bitmap_ysize,
427  _bitmap_dc, 0, 0,
428  SRCCOPY);
429 
430  additional_graph_window_paint(hdc);
431 
432  EndPaint(hwnd, &ps);
433  return 0;
434  }
435 
436  default:
437  break;
438  }
439 
440  return DefWindowProc(hwnd, msg, wparam, lparam);
441 }
442 
443 ////////////////////////////////////////////////////////////////////
444 // Function: WinStatsGraph::additional_window_paint
445 // Access: Protected, Virtual
446 // Description: This is called during the servicing of WM_PAINT; it
447 // gives a derived class opportunity to do some further
448 // painting into the window (the outer window, not the
449 // graph window).
450 ////////////////////////////////////////////////////////////////////
451 void WinStatsGraph::
452 additional_window_paint(HDC hdc) {
453 }
454 
455 ////////////////////////////////////////////////////////////////////
456 // Function: WinStatsGraph::additional_graph_window_paint
457 // Access: Protected, Virtual
458 // Description: This is called during the servicing of WM_PAINT; it
459 // gives a derived class opportunity to do some further
460 // painting into the graph window.
461 ////////////////////////////////////////////////////////////////////
462 void WinStatsGraph::
463 additional_graph_window_paint(HDC hdc) {
464 }
465 
466 ////////////////////////////////////////////////////////////////////
467 // Function: WinStatsGraph::consider_drag_start
468 // Access: Protected, Virtual
469 // Description: Based on the mouse position within the window's
470 // client area, look for draggable things the mouse
471 // might be hovering over and return the appropriate
472 // DragMode enum or DM_none if nothing is indicated.
473 ////////////////////////////////////////////////////////////////////
474 WinStatsGraph::DragMode WinStatsGraph::
475 consider_drag_start(int mouse_x, int mouse_y, int width, int height) {
476  if (mouse_x >= _left_margin - 2 && mouse_x <= _left_margin + 2) {
477  return DM_left_margin;
478  } else if (mouse_x >= width - _right_margin - 2 && mouse_x <= width - _right_margin + 2) {
479  return DM_right_margin;
480  }
481 
482  return DM_none;
483 }
484 
485 ////////////////////////////////////////////////////////////////////
486 // Function: WinStatsGraph::set_drag_mode
487 // Access: Protected, Virtual
488 // Description: This should be called whenever the drag mode needs to
489 // change state. It provides hooks for a derived class
490 // to do something special.
491 ////////////////////////////////////////////////////////////////////
492 void WinStatsGraph::
493 set_drag_mode(WinStatsGraph::DragMode drag_mode) {
494  _drag_mode = drag_mode;
495 }
496 
497 ////////////////////////////////////////////////////////////////////
498 // Function: WinStatsGraph::move_graph_window
499 // Access: Protected, Virtual
500 // Description: Repositions the graph child window within the parent
501 // window according to the _margin variables.
502 ////////////////////////////////////////////////////////////////////
503 void WinStatsGraph::
504 move_graph_window(int graph_left, int graph_top, int graph_xsize, int graph_ysize) {
505  if (_graph_window == 0) {
506  create_graph_window();
507  }
508 
509  _graph_left = graph_left;
510  _graph_top = graph_top;
511 
512  SetWindowPos(_graph_window, 0,
513  _graph_left, _graph_top,
514  graph_xsize, graph_ysize,
515  SWP_NOZORDER | SWP_SHOWWINDOW);
516 
517  if (graph_xsize != _bitmap_xsize || graph_ysize != _bitmap_ysize) {
518  setup_bitmap(graph_xsize, graph_ysize);
519  }
520 }
521 
522 ////////////////////////////////////////////////////////////////////
523 // Function: WinStatsGraph::setup_bitmap
524 // Access: Private
525 // Description: Sets up a backing-store bitmap of the indicated size.
526 ////////////////////////////////////////////////////////////////////
527 void WinStatsGraph::
528 setup_bitmap(int xsize, int ysize) {
529  release_bitmap();
530  _bitmap_xsize = max(xsize, 0);
531  _bitmap_ysize = max(ysize, 0);
532 
533  HDC hdc = GetDC(_graph_window);
534  _bitmap_dc = CreateCompatibleDC(hdc);
535  _bitmap = CreateCompatibleBitmap(hdc, _bitmap_xsize, _bitmap_ysize);
536  SelectObject(_bitmap_dc, _bitmap);
537 
538  RECT rect = { 0, 0, _bitmap_xsize, _bitmap_ysize };
539  FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
540 
541  ReleaseDC(_window, hdc);
542 }
543 
544 ////////////////////////////////////////////////////////////////////
545 // Function: WinStatsGraph::release_bitmap
546 // Access: Private
547 // Description: Frees the backing-store bitmap created by
548 // setup_bitmap().
549 ////////////////////////////////////////////////////////////////////
550 void WinStatsGraph::
551 release_bitmap() {
552  if (_bitmap) {
553  DeleteObject(_bitmap);
554  _bitmap = 0;
555  }
556  if (_bitmap_dc) {
557  DeleteDC(_bitmap_dc);
558  _bitmap_dc = 0;
559  }
560 }
561 
562 ////////////////////////////////////////////////////////////////////
563 // Function: WinStatsGraph::create_graph_window
564 // Access: Private
565 // Description: Creates the child window that actually holds the graph.
566 ////////////////////////////////////////////////////////////////////
567 void WinStatsGraph::
568 create_graph_window() {
569  if (_graph_window) {
570  return;
571  }
572 
573  HINSTANCE application = GetModuleHandle(NULL);
574  register_graph_window_class(application);
575 
576  string window_title = "graph";
577  DWORD window_style = WS_CHILD | WS_CLIPSIBLINGS;
578 
579  _graph_window =
580  CreateWindow(_graph_window_class_name, window_title.c_str(), window_style,
581  0, 0, 0, 0,
582  _window, NULL, application, 0);
583  if (!_graph_window) {
584  nout << "Could not create graph window!\n";
585  exit(1);
586  }
587 
588  SetWindowLongPtr(_graph_window, 0, (LONG_PTR)this);
589 }
590 
591 ////////////////////////////////////////////////////////////////////
592 // Function: WinStatsGraph::register_graph_window_class
593 // Access: Private, Static
594 // Description: Registers the window class for the stripChart window, if
595 // it has not already been registered.
596 ////////////////////////////////////////////////////////////////////
597 void WinStatsGraph::
598 register_graph_window_class(HINSTANCE application) {
599  if (_graph_window_class_registered) {
600  return;
601  }
602 
603  WNDCLASS wc;
604 
605  ZeroMemory(&wc, sizeof(WNDCLASS));
606  wc.style = CS_DBLCLKS;
607  wc.lpfnWndProc = (WNDPROC)static_graph_window_proc;
608  wc.hInstance = application;
609  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
610  wc.hbrBackground = NULL;
611  wc.lpszMenuName = NULL;
612  wc.lpszClassName = _graph_window_class_name;
613 
614  // Reserve space to associate the this pointer with the window.
615  wc.cbWndExtra = sizeof(WinStatsGraph *);
616 
617  if (!RegisterClass(&wc)) {
618  nout << "Could not register graph window class!\n";
619  exit(1);
620  }
621 
622  _graph_window_class_registered = true;
623 }
624 
625 ////////////////////////////////////////////////////////////////////
626 // Function: WinStatsGraph::static_graph_window_proc
627 // Access: Private, Static
628 // Description:
629 ////////////////////////////////////////////////////////////////////
630 LONG WINAPI WinStatsGraph::
631 static_graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
632  WinStatsGraph *self = (WinStatsGraph *)GetWindowLongPtr(hwnd, 0);
633  if (self != (WinStatsGraph *)NULL && self->_graph_window == hwnd) {
634  return self->graph_window_proc(hwnd, msg, wparam, lparam);
635  } else {
636  return DefWindowProc(hwnd, msg, wparam, lparam);
637  }
638 }
virtual void new_collector(int collector_index)
Called whenever a new Collector definition is received from the client.
bool is_setup() const
Returns true if the label stack has been set up, false otherwise.
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
const LRGBColor & get_collector_color(int collector_index)
Returns the color associated with the indicated collector.
virtual void set_time_units(int unit_mask)
Called when the user selects a new time units from the monitor pulldown menu, this should adjust the ...
void user_guide_bars_changed()
Called when the user guide bars have been changed.
void setup(HWND parent_window)
Creates the actual window object.
virtual void changed_graph_size(int graph_xsize, int graph_ysize)
Called when the user has resized the window, forcing a resize of the graph.
virtual void set_scroll_speed(double scroll_speed)
Called when the user selects a new scroll speed from the monitor pulldown menu, this should adjust th...
This is just an abstract base class to provide a common pointer type for the various kinds of graphs ...
Definition: winStatsGraph.h:32
This class represents a connection to a PStatsClient and manages the data exchange with the client...
void set_pause(bool pause)
Changes the pause flag for the graph.
virtual void new_data(int thread_index, int frame_number)
Called whenever new data arrives.
virtual void force_redraw()
Called when it is necessary to redraw the entire graph.
void set_pos(int x, int y, int width, int height)
Sets the position and size of the label stack on its parent.
virtual void clicked_label(int collector_index)
Called when the user single-clicks on a label.