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::
104 force_redraw() {
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::
144 user_guide_bars_changed() {
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 }
PStatMonitor::get_collector_color
const LRGBColor & get_collector_color(int collector_index)
Returns the color associated with the indicated collector.
Definition: pStatMonitor.cxx:104
WinStatsGraph
This is just an abstract base class to provide a common pointer type for the various kinds of graphs ...
Definition: winStatsGraph.h:32
WinStatsGraph::set_pause
void set_pause(bool pause)
Changes the pause flag for the graph.
Definition: winStatsGraph.cxx:136
WinStatsGraph::set_scroll_speed
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...
Definition: winStatsGraph.cxx:128
WinStatsGraph::set_time_units
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 ...
Definition: winStatsGraph.cxx:120
WinStatsGraph::new_collector
virtual void new_collector(int collector_index)
Called whenever a new Collector definition is received from the client.
Definition: winStatsGraph.cxx:90
WinStatsGraph::clicked_label
virtual void clicked_label(int collector_index)
Called when the user single-clicks on a label.
Definition: winStatsGraph.cxx:153
WinStatsGraph::user_guide_bars_changed
void user_guide_bars_changed()
Called when the user guide bars have been changed.
Definition: winStatsGraph.cxx:144
WinStatsGraph::new_data
virtual void new_data(int thread_index, int frame_number)
Called whenever new data arrives.
Definition: winStatsGraph.cxx:97
WinStatsGraph::force_redraw
virtual void force_redraw()
Called when it is necessary to redraw the entire graph.
Definition: winStatsGraph.cxx:104
WinStatsGraph::changed_graph_size
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.
Definition: winStatsGraph.cxx:111
WinStatsLabelStack::is_setup
bool is_setup() const
Returns true if the label stack has been set up, false otherwise.
Definition: winStatsLabelStack.cxx:72
WinStatsLabelStack::setup
void setup(HWND parent_window)
Creates the actual window object.
Definition: winStatsLabelStack.cxx:51
WinStatsLabelStack::set_pos
void set_pos(int x, int y, int width, int height)
Sets the position and size of the label stack on its parent.
Definition: winStatsLabelStack.cxx:80
WinStatsMonitor
This class represents a connection to a PStatsClient and manages the data exchange with the client.
Definition: winStatsMonitor.h:38
winStatsGraph.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
winStatsLabelStack.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
winStatsMonitor.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.