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  */
89 void WinStatsGraph::
90 new_collector(int new_collector) {
91 }
92 
93 /**
94  * Called whenever new data arrives.
95  */
96 void WinStatsGraph::
97 new_data(int thread_index, int frame_number) {
98 }
99 
100 /**
101  * Called when it is necessary to redraw the entire graph.
102  */
103 void WinStatsGraph::
105 }
106 
107 /**
108  * Called when the user has resized the window, forcing a resize of the graph.
109  */
110 void WinStatsGraph::
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  */
119 void WinStatsGraph::
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  */
127 void WinStatsGraph::
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  */
135 void WinStatsGraph::
136 set_pause(bool pause) {
137  _pause = pause;
138 }
139 
140 /**
141  * Called when the user guide bars have been changed.
142  */
143 void WinStatsGraph::
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  */
152 void WinStatsGraph::
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 }
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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:29
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.