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