Panda3D
Loading...
Searching...
No Matches
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
18bool WinStatsGraph::_graph_window_class_registered = false;
19const char * const WinStatsGraph::_graph_window_class_name = "graph";
20
21DWORD WinStatsGraph::graph_window_style =
22WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_VISIBLE;
23
24/**
25 *
26 */
27WinStatsGraph::
28WinStatsGraph(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 */
60WinStatsGraph::
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 */
90new_collector(int new_collector) {
91}
92
93/**
94 * Called whenever new data arrives.
95 */
97new_data(int thread_index, int frame_number) {
98}
99
100/**
101 * Called when it is necessary to redraw the entire graph.
102 */
106
107/**
108 * Called when the user has resized the window, forcing a resize of the graph.
109 */
111changed_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 */
120set_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 */
128set_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 */
136set_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 */
153clicked_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 */
160void WinStatsGraph::
161close() {
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 */
172void WinStatsGraph::
173setup_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 */
181void WinStatsGraph::
182move_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 */
199HBRUSH WinStatsGraph::
200get_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 */
222LONG WinStatsGraph::
223window_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 */
352LONG WinStatsGraph::
353graph_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 */
404void WinStatsGraph::
405additional_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 */
412void WinStatsGraph::
413additional_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 */
421WinStatsGraph::DragMode WinStatsGraph::
422consider_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 */
436void WinStatsGraph::
437set_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 */
445void WinStatsGraph::
446move_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 */
467void WinStatsGraph::
468setup_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 */
487void WinStatsGraph::
488release_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 */
502void WinStatsGraph::
503create_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 */
530void WinStatsGraph::
531register_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 */
561LONG WINAPI WinStatsGraph::
562static_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 ...
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.