Panda3D
|
00001 // Filename: gtkStatsPianoRoll.cxx 00002 // Created by: drose (16Jan06) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "gtkStatsPianoRoll.h" 00016 #include "gtkStatsMonitor.h" 00017 #include "numeric_types.h" 00018 #include "gtkStatsLabelStack.h" 00019 00020 static const int default_piano_roll_width = 400; 00021 static const int default_piano_roll_height = 200; 00022 00023 //////////////////////////////////////////////////////////////////// 00024 // Function: GtkStatsPianoRoll::Constructor 00025 // Access: Public 00026 // Description: 00027 //////////////////////////////////////////////////////////////////// 00028 GtkStatsPianoRoll:: 00029 GtkStatsPianoRoll(GtkStatsMonitor *monitor, int thread_index) : 00030 PStatPianoRoll(monitor, thread_index, 00031 default_piano_roll_width, 00032 default_piano_roll_height), 00033 GtkStatsGraph(monitor) 00034 { 00035 // Let's show the units on the guide bar labels. There's room. 00036 set_guide_bar_units(get_guide_bar_units() | GBU_show_units); 00037 00038 // Add a DrawingArea widget on top of the graph, to display all of 00039 // the scale units. 00040 _scale_area = gtk_drawing_area_new(); 00041 g_signal_connect(G_OBJECT(_scale_area), "expose_event", 00042 G_CALLBACK(expose_event_callback), this); 00043 gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area, 00044 FALSE, FALSE, 0); 00045 gtk_widget_set_size_request(_scale_area, 0, 20); 00046 00047 00048 gtk_widget_set_size_request(_graph_window, default_piano_roll_width, 00049 default_piano_roll_height); 00050 00051 const PStatClientData *client_data = 00052 GtkStatsGraph::_monitor->get_client_data(); 00053 string thread_name = client_data->get_thread_name(_thread_index); 00054 string window_title = thread_name + " thread piano roll"; 00055 gtk_window_set_title(GTK_WINDOW(_window), window_title.c_str()); 00056 00057 gtk_widget_show_all(_window); 00058 gtk_widget_show(_window); 00059 00060 // Allow the window to be resized as small as the user likes. We 00061 // have to do this after the window has been shown; otherwise, it 00062 // will affect the window's initial size. 00063 gtk_widget_set_size_request(_window, 0, 0); 00064 00065 clear_region(); 00066 } 00067 00068 //////////////////////////////////////////////////////////////////// 00069 // Function: GtkStatsPianoRoll::Destructor 00070 // Access: Public, Virtual 00071 // Description: 00072 //////////////////////////////////////////////////////////////////// 00073 GtkStatsPianoRoll:: 00074 ~GtkStatsPianoRoll() { 00075 } 00076 00077 //////////////////////////////////////////////////////////////////// 00078 // Function: GtkStatsPianoRoll::idle 00079 // Access: Public, Virtual 00080 // Description: Called as each frame's data is made available. There 00081 // is no gurantee the frames will arrive in order, or 00082 // that all of them will arrive at all. The monitor 00083 // should be prepared to accept frames received 00084 // out-of-order or missing. 00085 //////////////////////////////////////////////////////////////////// 00086 void GtkStatsPianoRoll:: 00087 new_data(int thread_index, int frame_number) { 00088 if (!_pause) { 00089 update(); 00090 } 00091 } 00092 00093 //////////////////////////////////////////////////////////////////// 00094 // Function: GtkStatsPianoRoll::force_redraw 00095 // Access: Public, Virtual 00096 // Description: Called when it is necessary to redraw the entire graph. 00097 //////////////////////////////////////////////////////////////////// 00098 void GtkStatsPianoRoll:: 00099 force_redraw() { 00100 PStatPianoRoll::force_redraw(); 00101 } 00102 00103 //////////////////////////////////////////////////////////////////// 00104 // Function: GtkStatsPianoRoll::changed_graph_size 00105 // Access: Public, Virtual 00106 // Description: Called when the user has resized the window, forcing 00107 // a resize of the graph. 00108 //////////////////////////////////////////////////////////////////// 00109 void GtkStatsPianoRoll:: 00110 changed_graph_size(int graph_xsize, int graph_ysize) { 00111 PStatPianoRoll::changed_size(graph_xsize, graph_ysize); 00112 } 00113 00114 //////////////////////////////////////////////////////////////////// 00115 // Function: GtkStatsPianoRoll::set_time_units 00116 // Access: Public, Virtual 00117 // Description: Called when the user selects a new time units from 00118 // the monitor pulldown menu, this should adjust the 00119 // units for the graph to the indicated mask if it is a 00120 // time-based graph. 00121 //////////////////////////////////////////////////////////////////// 00122 void GtkStatsPianoRoll:: 00123 set_time_units(int unit_mask) { 00124 int old_unit_mask = get_guide_bar_units(); 00125 if ((old_unit_mask & (GBU_hz | GBU_ms)) != 0) { 00126 unit_mask = unit_mask & (GBU_hz | GBU_ms); 00127 unit_mask |= (old_unit_mask & GBU_show_units); 00128 set_guide_bar_units(unit_mask); 00129 00130 gtk_widget_queue_draw(_scale_area); 00131 } 00132 } 00133 00134 //////////////////////////////////////////////////////////////////// 00135 // Function: GtkStatsPianoRoll::clicked_label 00136 // Access: Public, Virtual 00137 // Description: Called when the user single-clicks on a label. 00138 //////////////////////////////////////////////////////////////////// 00139 void GtkStatsPianoRoll:: 00140 clicked_label(int collector_index) { 00141 if (collector_index >= 0) { 00142 GtkStatsGraph::_monitor->open_strip_chart(_thread_index, collector_index, false); 00143 } 00144 } 00145 00146 //////////////////////////////////////////////////////////////////// 00147 // Function: GtkStatsPianoRoll::set_horizontal_scale 00148 // Access: Public 00149 // Description: Changes the amount of time the width of the 00150 // horizontal axis represents. This may force a redraw. 00151 //////////////////////////////////////////////////////////////////// 00152 void GtkStatsPianoRoll:: 00153 set_horizontal_scale(double time_width) { 00154 PStatPianoRoll::set_horizontal_scale(time_width); 00155 00156 gtk_widget_queue_draw(_graph_window); 00157 gtk_widget_queue_draw(_scale_area); 00158 } 00159 00160 //////////////////////////////////////////////////////////////////// 00161 // Function: GtkStatsPianoRoll::clear_region 00162 // Access: Protected 00163 // Description: Erases the chart area. 00164 //////////////////////////////////////////////////////////////////// 00165 void GtkStatsPianoRoll:: 00166 clear_region() { 00167 gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white); 00168 gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, 0, 0, 00169 get_xsize(), get_ysize()); 00170 } 00171 00172 //////////////////////////////////////////////////////////////////// 00173 // Function: GtkStatsPianoRoll::begin_draw 00174 // Access: Protected, Virtual 00175 // Description: Erases the chart area in preparation for drawing a 00176 // bunch of bars. 00177 //////////////////////////////////////////////////////////////////// 00178 void GtkStatsPianoRoll:: 00179 begin_draw() { 00180 clear_region(); 00181 00182 // Draw in the guide bars. 00183 int num_guide_bars = get_num_guide_bars(); 00184 for (int i = 0; i < num_guide_bars; i++) { 00185 draw_guide_bar(_pixmap, get_guide_bar(i)); 00186 } 00187 } 00188 00189 //////////////////////////////////////////////////////////////////// 00190 // Function: GtkStatsPianoRoll::draw_bar 00191 // Access: Protected, Virtual 00192 // Description: Draws a single bar on the chart. 00193 //////////////////////////////////////////////////////////////////// 00194 void GtkStatsPianoRoll:: 00195 draw_bar(int row, int from_x, int to_x) { 00196 if (row >= 0 && row < _label_stack.get_num_labels()) { 00197 int y = _label_stack.get_label_y(row, _graph_window); 00198 int height = _label_stack.get_label_height(row); 00199 00200 int collector_index = get_label_collector(row); 00201 GdkGC *gc = get_collector_gc(collector_index); 00202 00203 gdk_draw_rectangle(_pixmap, gc, TRUE, 00204 from_x, y - height + 2, 00205 to_x - from_x, height - 4); 00206 } 00207 } 00208 00209 //////////////////////////////////////////////////////////////////// 00210 // Function: GtkStatsPianoRoll::end_draw 00211 // Access: Protected, Virtual 00212 // Description: Called after all the bars have been drawn, this 00213 // triggers a refresh event to draw it to the window. 00214 //////////////////////////////////////////////////////////////////// 00215 void GtkStatsPianoRoll:: 00216 end_draw() { 00217 gtk_widget_queue_draw(_graph_window); 00218 } 00219 00220 //////////////////////////////////////////////////////////////////// 00221 // Function: GtkStatsPianoRoll::idle 00222 // Access: Protected, Virtual 00223 // Description: Called at the end of the draw cycle. 00224 //////////////////////////////////////////////////////////////////// 00225 void GtkStatsPianoRoll:: 00226 idle() { 00227 if (_labels_changed) { 00228 update_labels(); 00229 } 00230 } 00231 00232 //////////////////////////////////////////////////////////////////// 00233 // Function: GtkStatsPianoRoll::additional_graph_window_paint 00234 // Access: Protected, Virtual 00235 // Description: This is called during the servicing of expose_event; 00236 // it gives a derived class opportunity to do some 00237 // further painting into the graph window. 00238 //////////////////////////////////////////////////////////////////// 00239 void GtkStatsPianoRoll:: 00240 additional_graph_window_paint() { 00241 int num_user_guide_bars = get_num_user_guide_bars(); 00242 for (int i = 0; i < num_user_guide_bars; i++) { 00243 draw_guide_bar(_graph_window->window, get_user_guide_bar(i)); 00244 } 00245 } 00246 00247 //////////////////////////////////////////////////////////////////// 00248 // Function: GtkStatsPianoRoll::consider_drag_start 00249 // Access: Protected, Virtual 00250 // Description: Based on the mouse position within the graph window, 00251 // look for draggable things the mouse might be hovering 00252 // over and return the appropriate DragMode enum or 00253 // DM_none if nothing is indicated. 00254 //////////////////////////////////////////////////////////////////// 00255 GtkStatsGraph::DragMode GtkStatsPianoRoll:: 00256 consider_drag_start(int graph_x, int graph_y) { 00257 if (graph_y >= 0 && graph_y < get_ysize()) { 00258 if (graph_x >= 0 && graph_x < get_xsize()) { 00259 // See if the mouse is over a user-defined guide bar. 00260 int x = graph_x; 00261 double from_height = pixel_to_height(x - 2); 00262 double to_height = pixel_to_height(x + 2); 00263 _drag_guide_bar = find_user_guide_bar(from_height, to_height); 00264 if (_drag_guide_bar >= 0) { 00265 return DM_guide_bar; 00266 } 00267 00268 } else { 00269 // The mouse is left or right of the graph; maybe create a new 00270 // guide bar. 00271 return DM_new_guide_bar; 00272 } 00273 } 00274 00275 return GtkStatsGraph::consider_drag_start(graph_x, graph_y); 00276 } 00277 00278 //////////////////////////////////////////////////////////////////// 00279 // Function: GtkStatsPianoRoll::handle_button_press 00280 // Access: Protected, Virtual 00281 // Description: Called when the mouse button is depressed within the 00282 // graph window. 00283 //////////////////////////////////////////////////////////////////// 00284 gboolean GtkStatsPianoRoll:: 00285 handle_button_press(GtkWidget *widget, int graph_x, int graph_y, 00286 bool double_click) { 00287 if (double_click) { 00288 // Double-clicking on a color bar in the graph is the same as 00289 // double-clicking on the corresponding label. 00290 clicked_label(get_collector_under_pixel(graph_x, graph_y)); 00291 return TRUE; 00292 } 00293 00294 if (_potential_drag_mode == DM_none) { 00295 set_drag_mode(DM_scale); 00296 _drag_scale_start = pixel_to_height(graph_x); 00297 //SetCapture(_graph_window); 00298 return TRUE; 00299 00300 } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) { 00301 set_drag_mode(DM_guide_bar); 00302 _drag_start_x = graph_x; 00303 //SetCapture(_graph_window); 00304 return TRUE; 00305 } 00306 00307 return GtkStatsGraph::handle_button_press(widget, graph_x, graph_y, 00308 double_click); 00309 } 00310 00311 //////////////////////////////////////////////////////////////////// 00312 // Function: GtkStatsPianoRoll::handle_button_release 00313 // Access: Protected, Virtual 00314 // Description: Called when the mouse button is released within the 00315 // graph window. 00316 //////////////////////////////////////////////////////////////////// 00317 gboolean GtkStatsPianoRoll:: 00318 handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { 00319 if (_drag_mode == DM_scale) { 00320 set_drag_mode(DM_none); 00321 //ReleaseCapture(); 00322 return handle_motion(widget, graph_x, graph_y); 00323 00324 } else if (_drag_mode == DM_guide_bar) { 00325 if (graph_x < 0 || graph_x >= get_xsize()) { 00326 remove_user_guide_bar(_drag_guide_bar); 00327 } else { 00328 move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_x)); 00329 } 00330 set_drag_mode(DM_none); 00331 //ReleaseCapture(); 00332 return handle_motion(widget, graph_x, graph_y); 00333 } 00334 00335 return GtkStatsGraph::handle_button_release(widget, graph_x, graph_y); 00336 } 00337 00338 //////////////////////////////////////////////////////////////////// 00339 // Function: GtkStatsPianoRoll::ns_motion_notify_event_callback 00340 // Access: Protected, Virtual 00341 // Description: Called when the mouse is moved within the 00342 // graph window. 00343 //////////////////////////////////////////////////////////////////// 00344 gboolean GtkStatsPianoRoll:: 00345 handle_motion(GtkWidget *widget, int graph_x, int graph_y) { 00346 if (_drag_mode == DM_none && _potential_drag_mode == DM_none) { 00347 // When the mouse is over a color bar, highlight it. 00348 _label_stack.highlight_label(get_collector_under_pixel(graph_x, graph_y)); 00349 00350 /* 00351 // Now we want to get a WM_MOUSELEAVE when the mouse leaves the 00352 // graph window. 00353 TRACKMOUSEEVENT tme = { 00354 sizeof(TRACKMOUSEEVENT), 00355 TME_LEAVE, 00356 _graph_window, 00357 0 00358 }; 00359 TrackMouseEvent(&tme); 00360 */ 00361 00362 } else { 00363 // If the mouse is in some drag mode, stop highlighting. 00364 _label_stack.highlight_label(-1); 00365 } 00366 00367 if (_drag_mode == DM_scale) { 00368 double ratio = (double)graph_x / (double)get_xsize(); 00369 if (ratio > 0.0f) { 00370 set_horizontal_scale(_drag_scale_start / ratio); 00371 } 00372 return TRUE; 00373 00374 } else if (_drag_mode == DM_new_guide_bar) { 00375 // We haven't created the new guide bar yet; we won't until the 00376 // mouse comes within the graph's region. 00377 if (graph_x >= 0 && graph_x < get_xsize()) { 00378 set_drag_mode(DM_guide_bar); 00379 _drag_guide_bar = add_user_guide_bar(pixel_to_height(graph_x)); 00380 return TRUE; 00381 } 00382 00383 } else if (_drag_mode == DM_guide_bar) { 00384 move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_x)); 00385 return TRUE; 00386 } 00387 00388 return GtkStatsGraph::handle_motion(widget, graph_x, graph_y); 00389 } 00390 00391 //////////////////////////////////////////////////////////////////// 00392 // Function: GtkStatsPianoRoll::get_collector_under_pixel 00393 // Access: Private 00394 // Description: Returns the collector index associated with the 00395 // indicated vertical row, or -1. 00396 //////////////////////////////////////////////////////////////////// 00397 int GtkStatsPianoRoll:: 00398 get_collector_under_pixel(int xpoint, int ypoint) { 00399 if (_label_stack.get_num_labels() == 0) { 00400 return -1; 00401 } 00402 00403 // Assume all of the labels are the same height. 00404 int height = _label_stack.get_label_height(0); 00405 int row = (get_ysize() - ypoint) / height; 00406 if (row >= 0 && row < _label_stack.get_num_labels()) { 00407 return _label_stack.get_label_collector_index(row); 00408 } else { 00409 return -1; 00410 } 00411 } 00412 00413 //////////////////////////////////////////////////////////////////// 00414 // Function: GtkStatsPianoRoll::update_labels 00415 // Access: Private 00416 // Description: Resets the list of labels. 00417 //////////////////////////////////////////////////////////////////// 00418 void GtkStatsPianoRoll:: 00419 update_labels() { 00420 _label_stack.clear_labels(); 00421 for (int i = 0; i < get_num_labels(); i++) { 00422 _label_stack.add_label(GtkStatsGraph::_monitor, this, 00423 _thread_index, 00424 get_label_collector(i), true); 00425 } 00426 _labels_changed = false; 00427 } 00428 00429 //////////////////////////////////////////////////////////////////// 00430 // Function: GtkStatsPianoRoll::draw_guide_bar 00431 // Access: Private 00432 // Description: Draws the line for the indicated guide bar on the 00433 // graph. 00434 //////////////////////////////////////////////////////////////////// 00435 void GtkStatsPianoRoll:: 00436 draw_guide_bar(GdkDrawable *surface, const PStatGraph::GuideBar &bar) { 00437 int x = height_to_pixel(bar._height); 00438 00439 if (x > 0 && x < get_xsize() - 1) { 00440 // Only draw it if it's not too close to the top. 00441 switch (bar._style) { 00442 case GBS_target: 00443 gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_light_gray); 00444 break; 00445 00446 case GBS_user: 00447 gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_user_guide_bar); 00448 break; 00449 00450 case GBS_normal: 00451 gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_dark_gray); 00452 break; 00453 } 00454 gdk_draw_line(surface, _pixmap_gc, x, 0, x, get_ysize()); 00455 } 00456 } 00457 00458 //////////////////////////////////////////////////////////////////// 00459 // Function: GtkStatsPianoRoll::draw_guide_labels 00460 // Access: Private 00461 // Description: This is called during the servicing of expose_event. 00462 //////////////////////////////////////////////////////////////////// 00463 void GtkStatsPianoRoll:: 00464 draw_guide_labels() { 00465 int i; 00466 int num_guide_bars = get_num_guide_bars(); 00467 for (i = 0; i < num_guide_bars; i++) { 00468 draw_guide_label(get_guide_bar(i)); 00469 } 00470 00471 int num_user_guide_bars = get_num_user_guide_bars(); 00472 for (i = 0; i < num_user_guide_bars; i++) { 00473 draw_guide_label(get_user_guide_bar(i)); 00474 } 00475 } 00476 00477 //////////////////////////////////////////////////////////////////// 00478 // Function: GtkStatsPianoRoll::draw_guide_label 00479 // Access: Private 00480 // Description: Draws the text for the indicated guide bar label at 00481 // the top of the graph. 00482 //////////////////////////////////////////////////////////////////// 00483 void GtkStatsPianoRoll:: 00484 draw_guide_label(const PStatGraph::GuideBar &bar) { 00485 GdkGC *gc = gdk_gc_new(_scale_area->window); 00486 00487 switch (bar._style) { 00488 case GBS_target: 00489 gdk_gc_set_rgb_fg_color(gc, &rgb_light_gray); 00490 break; 00491 00492 case GBS_user: 00493 gdk_gc_set_rgb_fg_color(gc, &rgb_user_guide_bar); 00494 break; 00495 00496 case GBS_normal: 00497 gdk_gc_set_rgb_fg_color(gc, &rgb_dark_gray); 00498 break; 00499 } 00500 00501 int x = height_to_pixel(bar._height); 00502 const string &label = bar._label; 00503 00504 PangoLayout *layout = gtk_widget_create_pango_layout(_window, label.c_str()); 00505 int width, height; 00506 pango_layout_get_pixel_size(layout, &width, &height); 00507 00508 if (bar._style != GBS_user) { 00509 double from_height = pixel_to_height(x - width); 00510 double to_height = pixel_to_height(x + width); 00511 if (find_user_guide_bar(from_height, to_height) >= 0) { 00512 // Omit the label: there's a user-defined guide bar in the same space. 00513 g_object_unref(layout); 00514 g_object_unref(gc); 00515 return; 00516 } 00517 } 00518 00519 if (x >= 0 && x < get_xsize()) { 00520 // Now convert our x to a coordinate within our drawing area. 00521 int junk_y; 00522 00523 // The x coordinate comes from the graph_window. 00524 gtk_widget_translate_coordinates(_graph_window, _scale_area, 00525 x, 0, 00526 &x, &junk_y); 00527 00528 int this_x = x - width / 2; 00529 gdk_draw_layout(_scale_area->window, gc, this_x, 00530 _scale_area->allocation.height - height, layout); 00531 } 00532 00533 g_object_unref(layout); 00534 g_object_unref(gc); 00535 } 00536 00537 //////////////////////////////////////////////////////////////////// 00538 // Function: GtkStatsPianoRoll::expose_event_callback 00539 // Access: Private, Static 00540 // Description: Draws in the scale labels. 00541 //////////////////////////////////////////////////////////////////// 00542 gboolean GtkStatsPianoRoll:: 00543 expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { 00544 GtkStatsPianoRoll *self = (GtkStatsPianoRoll *)data; 00545 self->draw_guide_labels(); 00546 00547 return TRUE; 00548 }