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