Panda3D
gtkStatsGraph.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 gtkStatsGraph.cxx
10  * @author drose
11  * @date 2006-01-16
12  */
13 
14 #include "gtkStatsGraph.h"
15 #include "gtkStatsMonitor.h"
16 #include "gtkStatsLabelStack.h"
17 
18 const GdkColor GtkStatsGraph::rgb_white = {
19  0, 0xffff, 0xffff, 0xffff
20 };
21 const GdkColor GtkStatsGraph::rgb_light_gray = {
22  0, 0x9a9a, 0x9a9a, 0x9a9a,
23 };
24 const GdkColor GtkStatsGraph::rgb_dark_gray = {
25  0, 0x3333, 0x3333, 0x3333,
26 };
27 const GdkColor GtkStatsGraph::rgb_black = {
28  0, 0x0000, 0x0000, 0x0000
29 };
30 const GdkColor GtkStatsGraph::rgb_user_guide_bar = {
31  0, 0x8282, 0x9696, 0xffff
32 };
33 
34 /**
35  *
36  */
37 GtkStatsGraph::
38 GtkStatsGraph(GtkStatsMonitor *monitor) :
39  _monitor(monitor)
40 {
41  _parent_window = nullptr;
42  _window = nullptr;
43  _graph_window = nullptr;
44  _scale_area = nullptr;
45 
46  GtkWidget *parent_window = monitor->get_window();
47 
48  GdkDisplay *display = gdk_drawable_get_display(parent_window->window);
49  _hand_cursor = gdk_cursor_new_for_display(display, GDK_HAND2);
50 
51  _pixmap = nullptr;
52  _pixmap_gc = nullptr;
53 
54  _pixmap_xsize = 0;
55  _pixmap_ysize = 0;
56 
57  _window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
58 
59  // These calls were intended to kind of emulate the Windows MDI behavior,
60  // but it's just weird. gtk_window_set_transient_for(GTK_WINDOW(_window),
61  // GTK_WINDOW(parent_window));
62  // gtk_window_set_destroy_with_parent(GTK_WINDOW(_window), TRUE);
63 
64  gtk_widget_add_events(_window,
65  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
66  GDK_POINTER_MOTION_MASK);
67  g_signal_connect(G_OBJECT(_window), "delete_event",
68  G_CALLBACK(window_delete_event), this);
69  g_signal_connect(G_OBJECT(_window), "destroy",
70  G_CALLBACK(window_destroy), this);
71  g_signal_connect(G_OBJECT(_window), "button_press_event",
72  G_CALLBACK(button_press_event_callback), this);
73  g_signal_connect(G_OBJECT(_window), "button_release_event",
74  G_CALLBACK(button_release_event_callback), this);
75  g_signal_connect(G_OBJECT(_window), "motion_notify_event",
76  G_CALLBACK(motion_notify_event_callback), this);
77 
78  _graph_window = gtk_drawing_area_new();
79  gtk_widget_add_events(_graph_window,
80  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
81  GDK_POINTER_MOTION_MASK);
82  g_signal_connect(G_OBJECT(_graph_window), "expose_event",
83  G_CALLBACK(graph_expose_callback), this);
84  g_signal_connect(G_OBJECT(_graph_window), "configure_event",
85  G_CALLBACK(configure_graph_callback), this);
86  g_signal_connect(G_OBJECT(_graph_window), "button_press_event",
87  G_CALLBACK(button_press_event_callback), this);
88  g_signal_connect(G_OBJECT(_graph_window), "button_release_event",
89  G_CALLBACK(button_release_event_callback), this);
90  g_signal_connect(G_OBJECT(_graph_window), "motion_notify_event",
91  G_CALLBACK(motion_notify_event_callback), this);
92 
93  // A Frame to hold the graph.
94  GtkWidget *graph_frame = gtk_frame_new(nullptr);
95  gtk_frame_set_shadow_type(GTK_FRAME(graph_frame), GTK_SHADOW_IN);
96  gtk_container_add(GTK_CONTAINER(graph_frame), _graph_window);
97 
98  // A VBox to hold the graph's frame, and any numbers (scale legend? total?)
99  // above it.
100  _graph_vbox = gtk_vbox_new(FALSE, 0);
101  gtk_box_pack_end(GTK_BOX(_graph_vbox), graph_frame,
102  TRUE, TRUE, 0);
103 
104  // An HBox to hold the graph's frame, and the scale legend to the right of
105  // it.
106  _graph_hbox = gtk_hbox_new(FALSE, 0);
107  gtk_box_pack_start(GTK_BOX(_graph_hbox), _graph_vbox,
108  TRUE, TRUE, 0);
109 
110  // An HPaned to hold the label stack and the graph hbox.
111  _hpaned = gtk_hpaned_new();
112  gtk_container_add(GTK_CONTAINER(_window), _hpaned);
113  gtk_container_set_border_width(GTK_CONTAINER(_window), 8);
114 
115  gtk_paned_pack1(GTK_PANED(_hpaned), _label_stack.get_widget(), TRUE, TRUE);
116  gtk_paned_pack2(GTK_PANED(_hpaned), _graph_hbox, TRUE, TRUE);
117 
118  _drag_mode = DM_none;
119  _potential_drag_mode = DM_none;
120  _drag_scale_start = 0.0f;
121 
122  _pause = false;
123 }
124 
125 /**
126  *
127  */
128 GtkStatsGraph::
129 ~GtkStatsGraph() {
130  _monitor = nullptr;
131  release_pixmap();
132 
133  Brushes::iterator bi;
134  for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) {
135  GdkGC *gc = (*bi).second;
136  g_object_unref(gc);
137  }
138 
139  _label_stack.clear_labels();
140 
141  if (_window != nullptr) {
142  GtkWidget *window = _window;
143  _window = nullptr;
144  gtk_widget_destroy(window);
145  }
146 }
147 
148 /**
149  * Called whenever a new Collector definition is received from the client.
150  */
151 void GtkStatsGraph::
152 new_collector(int new_collector) {
153 }
154 
155 /**
156  * Called whenever new data arrives.
157  */
158 void GtkStatsGraph::
159 new_data(int thread_index, int frame_number) {
160 }
161 
162 /**
163  * Called when it is necessary to redraw the entire graph.
164  */
165 void GtkStatsGraph::
167 }
168 
169 /**
170  * Called when the user has resized the window, forcing a resize of the graph.
171  */
172 void GtkStatsGraph::
173 changed_graph_size(int graph_xsize, int graph_ysize) {
174 }
175 
176 /**
177  * Called when the user selects a new time units from the monitor pulldown
178  * menu, this should adjust the units for the graph to the indicated mask if
179  * it is a time-based graph.
180  */
181 void GtkStatsGraph::
182 set_time_units(int unit_mask) {
183 }
184 
185 /**
186  * Called when the user selects a new scroll speed from the monitor pulldown
187  * menu, this should adjust the speed for the graph to the indicated value.
188  */
189 void GtkStatsGraph::
190 set_scroll_speed(double scroll_speed) {
191 }
192 
193 /**
194  * Changes the pause flag for the graph. When this flag is true, the graph
195  * does not update in response to new data.
196  */
197 void GtkStatsGraph::
198 set_pause(bool pause) {
199  _pause = pause;
200 }
201 
202 /**
203  * Called when the user guide bars have been changed.
204  */
205 void GtkStatsGraph::
207  if (_scale_area != nullptr) {
208  gtk_widget_queue_draw(_scale_area);
209  }
210  gtk_widget_queue_draw(_graph_window);
211 }
212 
213 /**
214  * Called when the user single-clicks on a label.
215  */
216 void GtkStatsGraph::
217 clicked_label(int collector_index) {
218 }
219 
220 /**
221  * Should be called when the user closes the associated window. This tells
222  * the monitor to remove the graph.
223  */
224 void GtkStatsGraph::
225 close() {
226  _label_stack.clear_labels(false);
227  if (_window != nullptr) {
228  _window = nullptr;
229 
230  GtkStatsMonitor *monitor = _monitor;
231  _monitor = nullptr;
232  if (monitor != nullptr) {
233  monitor->remove_graph(this);
234  }
235  }
236 }
237 
238 /**
239  * Returns a GC suitable for drawing in the indicated collector's color.
240  */
241 GdkGC *GtkStatsGraph::
242 get_collector_gc(int collector_index) {
243  Brushes::iterator bi;
244  bi = _brushes.find(collector_index);
245  if (bi != _brushes.end()) {
246  return (*bi).second;
247  }
248 
249  // Ask the monitor what color this guy should be.
250  LRGBColor rgb = _monitor->get_collector_color(collector_index);
251 
252  GdkColor c;
253  c.red = (int)(rgb[0] * 65535.0f);
254  c.green = (int)(rgb[1] * 65535.0f);
255  c.blue = (int)(rgb[2] * 65535.0f);
256  GdkGC *gc = gdk_gc_new(_pixmap);
257  // g_object_ref(gc); Should this be ref_sink?
258  gdk_gc_set_rgb_fg_color(gc, &c);
259 
260  _brushes[collector_index] = gc;
261  return gc;
262 }
263 
264 /**
265  * This is called during the servicing of expose_event; it gives a derived
266  * class opportunity to do some further painting into the graph window.
267  */
268 void GtkStatsGraph::
269 additional_graph_window_paint() {
270 }
271 
272 /**
273  * Based on the mouse position within the graph window, look for draggable
274  * things the mouse might be hovering over and return the appropriate DragMode
275  * enum or DM_none if nothing is indicated.
276  */
277 GtkStatsGraph::DragMode GtkStatsGraph::
278 consider_drag_start(int graph_x, int graph_y) {
279  return DM_none;
280 }
281 
282 /**
283  * This should be called whenever the drag mode needs to change state. It
284  * provides hooks for a derived class to do something special.
285  */
286 void GtkStatsGraph::
287 set_drag_mode(GtkStatsGraph::DragMode drag_mode) {
288  _drag_mode = drag_mode;
289 }
290 
291 /**
292  * Called when the mouse button is depressed within the window, or any nested
293  * window.
294  */
295 gboolean GtkStatsGraph::
296 handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
297  bool double_click) {
298  if (_potential_drag_mode != DM_none) {
299  set_drag_mode(_potential_drag_mode);
300  _drag_start_x = graph_x;
301  _drag_start_y = graph_y;
302  // SetCapture(_window);
303  }
304  return TRUE;
305 }
306 
307 /**
308  * Called when the mouse button is released within the window, or any nested
309  * window.
310  */
311 gboolean GtkStatsGraph::
312 handle_button_release(GtkWidget *widget, int graph_x, int graph_y) {
313  set_drag_mode(DM_none);
314  // ReleaseCapture();
315 
316  return handle_motion(widget, graph_x, graph_y);
317 }
318 
319 /**
320  * Called when the mouse is moved within the window, or any nested window.
321  */
322 gboolean GtkStatsGraph::
323 handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
324  _potential_drag_mode = consider_drag_start(graph_x, graph_y);
325 
326  if (_potential_drag_mode == DM_guide_bar ||
327  _drag_mode == DM_guide_bar) {
328  gdk_window_set_cursor(_window->window, _hand_cursor);
329 
330  } else {
331  gdk_window_set_cursor(_window->window, nullptr);
332  }
333 
334  return TRUE;
335 }
336 
337 /**
338  * Sets up a backing-store bitmap of the indicated size.
339  */
340 void GtkStatsGraph::
341 setup_pixmap(int xsize, int ysize) {
342  release_pixmap();
343 
344  _pixmap_xsize = std::max(xsize, 0);
345  _pixmap_ysize = std::max(ysize, 0);
346 
347  _pixmap = gdk_pixmap_new(_graph_window->window, _pixmap_xsize, _pixmap_ysize, -1);
348  // g_object_ref(_pixmap); Should this be ref_sink?
349  _pixmap_gc = gdk_gc_new(_pixmap);
350  // g_object_ref(_pixmap_gc); Should this be ref_sink?
351 
352  gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white);
353  gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, 0, 0,
354  _pixmap_xsize, _pixmap_ysize);
355 }
356 
357 /**
358  * Frees the backing-store bitmap created by setup_pixmap().
359  */
360 void GtkStatsGraph::
361 release_pixmap() {
362  if (_pixmap != nullptr) {
363  g_object_unref(_pixmap);
364  g_object_unref(_pixmap_gc);
365  }
366 }
367 
368 /**
369  * Callback when the window is closed by the user.
370  */
371 gboolean GtkStatsGraph::
372 window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) {
373  // Returning FALSE to indicate we should destroy the window when the user
374  // selects "close".
375  return FALSE;
376 }
377 
378 /**
379  * Callback when the window is destroyed by the system (or by delete_event).
380  */
381 void GtkStatsGraph::
382 window_destroy(GtkWidget *widget, gpointer data) {
383  GtkStatsGraph *self = (GtkStatsGraph *)data;
384  self->close();
385 }
386 
387 /**
388  * Fills in the graph window.
389  */
390 gboolean GtkStatsGraph::
391 graph_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
392  GtkStatsGraph *self = (GtkStatsGraph *)data;
393 
394  if (self->_pixmap != nullptr) {
395  gdk_draw_drawable(self->_graph_window->window,
396  self->_graph_window->style->fg_gc[0],
397  self->_pixmap, 0, 0, 0, 0,
398  self->_pixmap_xsize, self->_pixmap_ysize);
399  }
400 
401  self->additional_graph_window_paint();
402 
403  return TRUE;
404 }
405 
406 /**
407  * Changes the size of the graph window
408  */
409 gboolean GtkStatsGraph::
410 configure_graph_callback(GtkWidget *widget, GdkEventConfigure *event,
411  gpointer data) {
412  GtkStatsGraph *self = (GtkStatsGraph *)data;
413 
414  self->changed_graph_size(event->width, event->height);
415  self->setup_pixmap(event->width, event->height);
416  self->force_redraw();
417 
418  return TRUE;
419 }
420 
421 /**
422  * Called when the mouse button is depressed within the graph window or main
423  * window.
424  */
425 gboolean GtkStatsGraph::
426 button_press_event_callback(GtkWidget *widget, GdkEventButton *event,
427  gpointer data) {
428  GtkStatsGraph *self = (GtkStatsGraph *)data;
429  int graph_x, graph_y;
430  gtk_widget_translate_coordinates(widget, self->_graph_window,
431  (int)event->x, (int)event->y,
432  &graph_x, &graph_y);
433 
434  bool double_click = (event->type == GDK_2BUTTON_PRESS);
435 
436  return self->handle_button_press(widget, graph_x, graph_y, double_click);
437 }
438 
439 /**
440  * Called when the mouse button is released within the graph window or main
441  * window.
442  */
443 gboolean GtkStatsGraph::
444 button_release_event_callback(GtkWidget *widget, GdkEventButton *event,
445  gpointer data) {
446  GtkStatsGraph *self = (GtkStatsGraph *)data;
447  int graph_x, graph_y;
448  gtk_widget_translate_coordinates(widget, self->_graph_window,
449  (int)event->x, (int)event->y,
450  &graph_x, &graph_y);
451 
452  return self->handle_button_release(widget, graph_x, graph_y);
453 }
454 
455 /**
456  * Called when the mouse is moved within the graph window or main window.
457  */
458 gboolean GtkStatsGraph::
459 motion_notify_event_callback(GtkWidget *widget, GdkEventMotion *event,
460  gpointer data) {
461  GtkStatsGraph *self = (GtkStatsGraph *)data;
462  int graph_x, graph_y;
463  gtk_widget_translate_coordinates(widget, self->_graph_window,
464  (int)event->x, (int)event->y,
465  &graph_x, &graph_y);
466 
467  return self->handle_motion(widget, graph_x, graph_y);
468 }
virtual void new_data(int thread_index, int frame_number)
Called whenever new data arrives.
void user_guide_bars_changed()
Called when the user guide bars have been changed.
void set_pause(bool pause)
Changes the pause flag for the graph.
const LRGBColor & get_collector_color(int collector_index)
Returns the color associated with the indicated collector.
void clear_labels(bool delete_widgets=true)
Removes the set of labels and starts a new set.
virtual void new_collector(int collector_index)
Called whenever a new Collector definition is received from the client.
virtual void force_redraw()
Called when it is necessary to redraw the entire graph.
virtual void clicked_label(int collector_index)
Called when the user single-clicks on a label.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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: gtkStatsGraph.h:29
This class represents a connection to a PStatsClient and manages the data exchange with the client.
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 ...
GtkWidget * get_window() const
Returns the window handle to the monitor's window.
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.