Panda3D

winStatsStripChart.cxx

00001 // Filename: winStatsStripChart.cxx
00002 // Created by:  drose (03Dec03)
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 "winStatsStripChart.h"
00016 #include "winStatsMonitor.h"
00017 #include "pStatCollectorDef.h"
00018 #include "numeric_types.h"
00019 
00020 static const int default_strip_chart_width = 400;
00021 static const int default_strip_chart_height = 100;
00022 
00023 // Surely we aren't expected to hardcode the size of a normal
00024 // checkbox.  But Windows seems to require this data to be passed to
00025 // CreateWindow(), so what else can I do?
00026 size_t WinStatsStripChart::_check_box_height = 13;
00027 size_t WinStatsStripChart::_check_box_width = 13;
00028 
00029 bool WinStatsStripChart::_window_class_registered = false;
00030 const char * const WinStatsStripChart::_window_class_name = "strip";
00031 
00032 ////////////////////////////////////////////////////////////////////
00033 //     Function: WinStatsStripChart::Constructor
00034 //       Access: Public
00035 //  Description:
00036 ////////////////////////////////////////////////////////////////////
00037 WinStatsStripChart::
00038 WinStatsStripChart(WinStatsMonitor *monitor, int thread_index,
00039                    int collector_index, bool show_level) :
00040   PStatStripChart(monitor, 
00041                   show_level ? monitor->get_level_view(collector_index, thread_index) : monitor->get_view(thread_index), 
00042                   thread_index,
00043                   collector_index, 
00044                   default_strip_chart_width,
00045                   default_strip_chart_height),
00046   WinStatsGraph(monitor)
00047 {
00048   _brush_origin = 0;
00049 
00050   _left_margin = 96;
00051   _right_margin = 32;
00052   _top_margin = 16;
00053   _bottom_margin = 8;
00054 
00055   if (show_level) {
00056     // If it's a level-type graph, show the appropriate units.
00057     if (_unit_name.empty()) {
00058       set_guide_bar_units(GBU_named);
00059     } else {
00060       set_guide_bar_units(GBU_named | GBU_show_units);
00061     }
00062 
00063   } else {
00064     // If it's a time-type graph, show the ms/Hz units.
00065     set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
00066   }
00067 
00068   _smooth_check_box = 0;
00069 
00070   create_window();
00071   clear_region();
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: WinStatsStripChart::Destructor
00076 //       Access: Public, Virtual
00077 //  Description:
00078 ////////////////////////////////////////////////////////////////////
00079 WinStatsStripChart::
00080 ~WinStatsStripChart() {
00081 }
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: WinStatsStripChart::new_collector
00085 //       Access: Public, Virtual
00086 //  Description: Called whenever a new Collector definition is
00087 //               received from the client.
00088 ////////////////////////////////////////////////////////////////////
00089 void WinStatsStripChart::
00090 new_collector(int collector_index) {
00091   WinStatsGraph::new_collector(collector_index);
00092 }
00093 
00094 ////////////////////////////////////////////////////////////////////
00095 //     Function: WinStatsStripChart::new_data
00096 //       Access: Public, Virtual
00097 //  Description: Called as each frame's data is made available.  There
00098 //               is no gurantee the frames will arrive in order, or
00099 //               that all of them will arrive at all.  The monitor
00100 //               should be prepared to accept frames received
00101 //               out-of-order or missing.
00102 ////////////////////////////////////////////////////////////////////
00103 void WinStatsStripChart::
00104 new_data(int thread_index, int frame_number) {
00105   if (is_title_unknown()) {
00106     string window_title = get_title_text();
00107     if (!is_title_unknown()) {
00108       SetWindowText(_window, window_title.c_str());
00109     }
00110   }
00111 
00112   if (!_pause) {
00113     update();
00114 
00115     string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name());
00116     if (_net_value_text != text) {
00117       _net_value_text = text;
00118       RECT rect;
00119       GetClientRect(_window, &rect);
00120       rect.bottom = _top_margin;
00121       InvalidateRect(_window, &rect, TRUE);
00122     }
00123   }
00124 }
00125 
00126 ////////////////////////////////////////////////////////////////////
00127 //     Function: WinStatsStripChart::force_redraw
00128 //       Access: Public, Virtual
00129 //  Description: Called when it is necessary to redraw the entire graph.
00130 ////////////////////////////////////////////////////////////////////
00131 void WinStatsStripChart::
00132 force_redraw() {
00133   PStatStripChart::force_redraw();
00134 }
00135 
00136 ////////////////////////////////////////////////////////////////////
00137 //     Function: WinStatsStripChart::changed_graph_size
00138 //       Access: Public, Virtual
00139 //  Description: Called when the user has resized the window, forcing
00140 //               a resize of the graph.
00141 ////////////////////////////////////////////////////////////////////
00142 void WinStatsStripChart::
00143 changed_graph_size(int graph_xsize, int graph_ysize) {
00144   PStatStripChart::changed_size(graph_xsize, graph_ysize);
00145 }
00146 
00147 ////////////////////////////////////////////////////////////////////
00148 //     Function: WinStatsStripChart::set_time_units
00149 //       Access: Public, Virtual
00150 //  Description: Called when the user selects a new time units from
00151 //               the monitor pulldown menu, this should adjust the
00152 //               units for the graph to the indicated mask if it is a
00153 //               time-based graph.
00154 ////////////////////////////////////////////////////////////////////
00155 void WinStatsStripChart::
00156 set_time_units(int unit_mask) {
00157   int old_unit_mask = get_guide_bar_units();
00158   if ((old_unit_mask & (GBU_hz | GBU_ms)) != 0) {
00159     unit_mask = unit_mask & (GBU_hz | GBU_ms);
00160     unit_mask |= (old_unit_mask & GBU_show_units);
00161     set_guide_bar_units(unit_mask);
00162 
00163     RECT rect;
00164     GetClientRect(_window, &rect);
00165     rect.left = _right_margin;
00166     InvalidateRect(_window, &rect, TRUE);
00167 
00168     GetClientRect(_window, &rect);
00169     rect.bottom = _top_margin;
00170     InvalidateRect(_window, &rect, TRUE);
00171   }
00172 }
00173 
00174 ////////////////////////////////////////////////////////////////////
00175 //     Function: WinStatsStripChart::set_scroll_speed
00176 //       Access: Public
00177 //  Description: Called when the user selects a new scroll speed from
00178 //               the monitor pulldown menu, this should adjust the
00179 //               speed for the graph to the indicated value.
00180 ////////////////////////////////////////////////////////////////////
00181 void WinStatsStripChart::
00182 set_scroll_speed(double scroll_speed) {
00183   // The speed factor indicates chart widths per minute.
00184   if (scroll_speed != 0.0f) {
00185     set_horizontal_scale(60.0f / scroll_speed);
00186   }
00187 }
00188 
00189 ////////////////////////////////////////////////////////////////////
00190 //     Function: WinStatsStripChart::clicked_label
00191 //       Access: Public, Virtual
00192 //  Description: Called when the user single-clicks on a label.
00193 ////////////////////////////////////////////////////////////////////
00194 void WinStatsStripChart::
00195 clicked_label(int collector_index) {
00196   if (collector_index < 0) {
00197     // Clicking on whitespace in the graph is the same as clicking on
00198     // the top label.
00199     collector_index = get_collector_index();
00200   }
00201 
00202   if (collector_index == get_collector_index() && collector_index != 0) {
00203     // Clicking on the top label means to go up to the parent level.
00204     const PStatClientData *client_data = 
00205       WinStatsGraph::_monitor->get_client_data();
00206     if (client_data->has_collector(collector_index)) {
00207       const PStatCollectorDef &def =
00208         client_data->get_collector_def(collector_index);
00209       if (def._parent_index == 0 && get_view().get_show_level()) {
00210         // Unless the parent is "Frame", and we're not a time collector.
00211       } else {
00212         set_collector_index(def._parent_index);
00213       }
00214     }
00215 
00216   } else {
00217     // Clicking on any other label means to focus on that.
00218     set_collector_index(collector_index);
00219   }
00220 }
00221 
00222 ////////////////////////////////////////////////////////////////////
00223 //     Function: WinStatsStripChart::set_vertical_scale
00224 //       Access: Public
00225 //  Description: Changes the value the height of the vertical axis
00226 //               represents.  This may force a redraw.
00227 ////////////////////////////////////////////////////////////////////
00228 void WinStatsStripChart::
00229 set_vertical_scale(double value_height) {
00230   PStatStripChart::set_vertical_scale(value_height);
00231 
00232   RECT rect;
00233   GetClientRect(_window, &rect);
00234   rect.left = _right_margin;
00235   InvalidateRect(_window, &rect, TRUE);
00236 }
00237 
00238 ////////////////////////////////////////////////////////////////////
00239 //     Function: WinStatsStripChart::update_labels
00240 //       Access: Protected, Virtual
00241 //  Description: Resets the list of labels.
00242 ////////////////////////////////////////////////////////////////////
00243 void WinStatsStripChart::
00244 update_labels() {
00245   PStatStripChart::update_labels();
00246 
00247   _label_stack.clear_labels();
00248   for (int i = 0; i < get_num_labels(); i++) {
00249     _label_stack.add_label(WinStatsGraph::_monitor, this, _thread_index,
00250                            get_label_collector(i), false);
00251   }
00252   _labels_changed = false;
00253 }
00254 
00255 ////////////////////////////////////////////////////////////////////
00256 //     Function: WinStatsStripChart::clear_region
00257 //       Access: Protected, Virtual
00258 //  Description: Erases the chart area.
00259 ////////////////////////////////////////////////////////////////////
00260 void WinStatsStripChart::
00261 clear_region() {
00262   RECT rect = { 0, 0, get_xsize(), get_ysize() };
00263   FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
00264 }
00265 
00266 ////////////////////////////////////////////////////////////////////
00267 //     Function: WinStatsStripChart::copy_region
00268 //       Access: Protected, Virtual
00269 //  Description: Should be overridden by the user class to copy a
00270 //               region of the chart from one part of the chart to
00271 //               another.  This is used to implement scrolling.
00272 ////////////////////////////////////////////////////////////////////
00273 void WinStatsStripChart::
00274 copy_region(int start_x, int end_x, int dest_x) {
00275   BitBlt(_bitmap_dc, dest_x, 0, 
00276          end_x - start_x, get_ysize(),
00277          _bitmap_dc, start_x, 0,
00278          SRCCOPY);
00279 
00280   // Also shift the brush origin over, so we still get proper
00281   // dithering.
00282   _brush_origin += (dest_x - start_x);
00283   SetBrushOrgEx(_bitmap_dc, _brush_origin, 0, NULL);
00284 
00285   RECT rect = { 
00286     dest_x, 0, dest_x + end_x - start_x, get_ysize() 
00287   };
00288   InvalidateRect(_graph_window, &rect, FALSE);
00289 }
00290 
00291 ////////////////////////////////////////////////////////////////////
00292 //     Function: WinStatsStripChart::draw_slice
00293 //       Access: Protected, Virtual
00294 //  Description: Draws a single vertical slice of the strip chart, at
00295 //               the given pixel position, and corresponding to the
00296 //               indicated level data.
00297 ////////////////////////////////////////////////////////////////////
00298 void WinStatsStripChart::
00299 draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) {
00300   // Start by clearing the band first.
00301   RECT rect = { x, 0, x + w, get_ysize() };
00302   FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
00303 
00304   double overall_time = 0.0;
00305   int y = get_ysize();
00306 
00307   FrameData::const_iterator fi;
00308   for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
00309     const ColorData &cd = (*fi);
00310     overall_time += cd._net_value;
00311     HBRUSH brush = get_collector_brush(cd._collector_index);
00312 
00313     if (overall_time > get_vertical_scale()) {
00314       // Off the top.  Go ahead and clamp it by hand, in case it's so
00315       // far off the top we'd overflow the 16-bit pixel value.
00316       rect.top = 0;
00317       rect.bottom = y;
00318       FillRect(_bitmap_dc, &rect, brush);
00319       // And we can consider ourselves done now.
00320       return;
00321     }
00322 
00323     int top_y = height_to_pixel(overall_time);
00324     rect.top = top_y;
00325     rect.bottom = y;
00326     FillRect(_bitmap_dc, &rect, brush);
00327     y = top_y;
00328   }
00329 }
00330 
00331 ////////////////////////////////////////////////////////////////////
00332 //     Function: WinStatsStripChart::draw_empty
00333 //       Access: Protected, Virtual
00334 //  Description: Draws a single vertical slice of background color.
00335 ////////////////////////////////////////////////////////////////////
00336 void WinStatsStripChart::
00337 draw_empty(int x, int w) {
00338   RECT rect = { x, 0, x + w, get_ysize() };
00339   FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
00340 }
00341 
00342 ////////////////////////////////////////////////////////////////////
00343 //     Function: WinStatsStripChart::draw_cursor
00344 //       Access: Protected, Virtual
00345 //  Description: Draws a single vertical slice of foreground color.
00346 ////////////////////////////////////////////////////////////////////
00347 void WinStatsStripChart::
00348 draw_cursor(int x) {
00349   RECT rect = { x, 0, x + 1, get_ysize() };
00350   FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
00351 }
00352 
00353 ////////////////////////////////////////////////////////////////////
00354 //     Function: WinStatsStripChart::end_draw
00355 //       Access: Protected, Virtual
00356 //  Description: Should be overridden by the user class.  This hook
00357 //               will be called after drawing a series of color bars
00358 //               in the strip chart; it gives the pixel range that
00359 //               was just redrawn.
00360 ////////////////////////////////////////////////////////////////////
00361 void WinStatsStripChart::
00362 end_draw(int from_x, int to_x) {
00363   // Draw in the guide bars.
00364   int num_guide_bars = get_num_guide_bars();
00365   for (int i = 0; i < num_guide_bars; i++) {
00366     draw_guide_bar(_bitmap_dc, from_x, to_x, get_guide_bar(i));
00367   }
00368 
00369   RECT rect = { 
00370     from_x, 0, to_x + 1, get_ysize() 
00371   };
00372   InvalidateRect(_graph_window, &rect, FALSE);
00373 }
00374 
00375 ////////////////////////////////////////////////////////////////////
00376 //     Function: WinStatsStripChart::window_proc
00377 //       Access: Protected
00378 //  Description: 
00379 ////////////////////////////////////////////////////////////////////
00380 LONG WinStatsStripChart::
00381 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
00382   switch (msg) {
00383   case WM_LBUTTONDOWN:
00384     if (_potential_drag_mode == DM_new_guide_bar) {
00385       set_drag_mode(DM_new_guide_bar);
00386       SetCapture(_graph_window);
00387       return 0;
00388     }
00389     break;
00390 
00391   case WM_COMMAND:
00392     switch (LOWORD(wparam)) {
00393     case BN_CLICKED:
00394       if ((HWND)lparam == _smooth_check_box) {
00395         int result = SendMessage(_smooth_check_box, BM_GETCHECK, 0, 0);
00396         set_average_mode(result == BST_CHECKED);
00397         return 0;
00398       }
00399       break;
00400     }
00401     break;
00402 
00403   default:
00404     break;
00405   }
00406 
00407   return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam);
00408 }
00409 
00410 ////////////////////////////////////////////////////////////////////
00411 //     Function: WinStatsStripChart::graph_window_proc
00412 //       Access: Protected
00413 //  Description: 
00414 ////////////////////////////////////////////////////////////////////
00415 LONG WinStatsStripChart::
00416 graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
00417   switch (msg) {
00418   case WM_LBUTTONDOWN:
00419     if (_potential_drag_mode == DM_none) {
00420       set_drag_mode(DM_scale);
00421       PN_int16 y = HIWORD(lparam);
00422       _drag_scale_start = pixel_to_height(y);
00423       SetCapture(_graph_window);
00424       return 0;
00425 
00426     } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
00427       set_drag_mode(DM_guide_bar);
00428       PN_int16 y = HIWORD(lparam);
00429       _drag_start_y = y;
00430       SetCapture(_graph_window);
00431       return 0;
00432     }
00433     break;
00434 
00435   case WM_MOUSEMOVE: 
00436     if (_drag_mode == DM_none && _potential_drag_mode == DM_none) {
00437       // When the mouse is over a color bar, highlight it.
00438       PN_int16 x = LOWORD(lparam);
00439       PN_int16 y = HIWORD(lparam);
00440       _label_stack.highlight_label(get_collector_under_pixel(x, y));
00441 
00442       // Now we want to get a WM_MOUSELEAVE when the mouse leaves the
00443       // graph window.
00444       TRACKMOUSEEVENT tme = {
00445         sizeof(TRACKMOUSEEVENT),
00446         TME_LEAVE,
00447         _graph_window,
00448         0
00449       };
00450       TrackMouseEvent(&tme);
00451 
00452     } else {
00453       // If the mouse is in some drag mode, stop highlighting.
00454       _label_stack.highlight_label(-1);
00455     }
00456 
00457     if (_drag_mode == DM_scale) {
00458       PN_int16 y = HIWORD(lparam);
00459       double ratio = 1.0f - ((double)y / (double)get_ysize());
00460       if (ratio > 0.0f) {
00461         set_vertical_scale(_drag_scale_start / ratio);
00462       }
00463       return 0;
00464 
00465     } else if (_drag_mode == DM_new_guide_bar) {
00466       // We haven't created the new guide bar yet; we won't until the
00467       // mouse comes within the graph's region.
00468       PN_int16 y = HIWORD(lparam);
00469       if (y >= 0 && y < get_ysize()) {
00470         set_drag_mode(DM_guide_bar);
00471         _drag_guide_bar = add_user_guide_bar(pixel_to_height(y));
00472         return 0;
00473       }
00474 
00475     } else if (_drag_mode == DM_guide_bar) {
00476       PN_int16 y = HIWORD(lparam);
00477       move_user_guide_bar(_drag_guide_bar, pixel_to_height(y));
00478       return 0;
00479     }
00480     break;
00481 
00482   case WM_MOUSELEAVE:
00483     // When the mouse leaves the graph, stop highlighting.
00484     _label_stack.highlight_label(-1);
00485     break;
00486 
00487   case WM_LBUTTONUP:
00488     if (_drag_mode == DM_scale) {
00489       set_drag_mode(DM_none);
00490       ReleaseCapture();
00491       return 0;
00492 
00493     } else if (_drag_mode == DM_guide_bar) {
00494       PN_int16 y = HIWORD(lparam);
00495       if (y < 0 || y >= get_ysize()) {
00496         remove_user_guide_bar(_drag_guide_bar);
00497       } else {
00498         move_user_guide_bar(_drag_guide_bar, pixel_to_height(y));
00499       }
00500       set_drag_mode(DM_none);
00501       ReleaseCapture();
00502       return 0;
00503     }
00504     break;
00505 
00506   case WM_LBUTTONDBLCLK:
00507     {
00508       // Double-clicking on a color bar in the graph is the same as
00509       // double-clicking on the corresponding label.
00510       PN_int16 x = LOWORD(lparam);
00511       PN_int16 y = HIWORD(lparam);
00512       clicked_label(get_collector_under_pixel(x, y));
00513       return 0;
00514     }
00515     break;
00516 
00517   default:
00518     break;
00519   }
00520 
00521   return WinStatsGraph::graph_window_proc(hwnd, msg, wparam, lparam);
00522 }
00523 
00524 ////////////////////////////////////////////////////////////////////
00525 //     Function: WinStatsStripChart::additional_window_paint
00526 //       Access: Protected, Virtual
00527 //  Description: This is called during the servicing of WM_PAINT; it
00528 //               gives a derived class opportunity to do some further
00529 //               painting into the window (the outer window, not the
00530 //               graph window).
00531 ////////////////////////////////////////////////////////////////////
00532 void WinStatsStripChart::
00533 additional_window_paint(HDC hdc) {
00534   // Draw in the labels for the guide bars.
00535   HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); 
00536   SelectObject(hdc, hfnt);
00537   SetTextAlign(hdc, TA_LEFT | TA_TOP);
00538   SetBkMode(hdc, TRANSPARENT);
00539 
00540   RECT rect;
00541   GetClientRect(_window, &rect);
00542   int x = rect.right - _right_margin + 2;
00543   int last_y = -100;
00544 
00545   int i;
00546   int num_guide_bars = get_num_guide_bars();
00547   for (i = 0; i < num_guide_bars; i++) {
00548     last_y = draw_guide_label(hdc, x, get_guide_bar(i), last_y);
00549   }
00550 
00551   GuideBar top_value = make_guide_bar(get_vertical_scale());
00552   draw_guide_label(hdc, x, top_value, last_y);
00553 
00554   last_y = -100;
00555   int num_user_guide_bars = get_num_user_guide_bars();
00556   for (i = 0; i < num_user_guide_bars; i++) {
00557     last_y = draw_guide_label(hdc, x, get_user_guide_bar(i), last_y);
00558   }
00559 
00560   // Now draw the "net value" label at the top.
00561   SetTextAlign(hdc, TA_RIGHT | TA_BOTTOM);
00562   SetTextColor(hdc, RGB(0, 0, 0));
00563   TextOut(hdc, rect.right - _right_margin, _top_margin,
00564           _net_value_text.data(), _net_value_text.length()); 
00565 
00566   // Also draw the "Smooth" label on the check box.  This isn't part
00567   // of the check box itself, because doing that doesn't use the right
00568   // font!  Surely this isn't the correct Windows(tm) way to do this
00569   // sort of thing, but I don't know any better for now.
00570   SetTextAlign(hdc, TA_LEFT | TA_BOTTOM);
00571   TextOut(hdc, _left_margin + _check_box_width + 2, _top_margin, "Smooth", 6);
00572 }
00573 
00574 ////////////////////////////////////////////////////////////////////
00575 //     Function: WinStatsStripChart::additional_graph_window_paint
00576 //       Access: Protected, Virtual
00577 //  Description: This is called during the servicing of WM_PAINT; it
00578 //               gives a derived class opportunity to do some further
00579 //               painting into the window (the outer window, not the
00580 //               graph window).
00581 ////////////////////////////////////////////////////////////////////
00582 void WinStatsStripChart::
00583 additional_graph_window_paint(HDC hdc) {
00584   int num_user_guide_bars = get_num_user_guide_bars();
00585   for (int i = 0; i < num_user_guide_bars; i++) {
00586     draw_guide_bar(hdc, 0, get_xsize(), get_user_guide_bar(i));
00587   }
00588 }
00589 
00590 ////////////////////////////////////////////////////////////////////
00591 //     Function: WinStatsStripChart::consider_drag_start
00592 //       Access: Protected, Virtual
00593 //  Description: Based on the mouse position within the window's
00594 //               client area, look for draggable things the mouse
00595 //               might be hovering over and return the apprioprate
00596 //               DragMode enum or DM_none if nothing is indicated.
00597 ////////////////////////////////////////////////////////////////////
00598 WinStatsGraph::DragMode WinStatsStripChart::
00599 consider_drag_start(int mouse_x, int mouse_y, int width, int height) {
00600   if (mouse_x >= _graph_left && mouse_x < _graph_left + get_xsize()) {
00601     if (mouse_y >= _graph_top && mouse_y < _graph_top + get_ysize()) {
00602       // See if the mouse is over a user-defined guide bar.
00603       int y = mouse_y - _graph_top;
00604       double from_height = pixel_to_height(y + 2);
00605       double to_height = pixel_to_height(y - 2);
00606       _drag_guide_bar = find_user_guide_bar(from_height, to_height);
00607       if (_drag_guide_bar >= 0) {
00608         return DM_guide_bar;
00609       }
00610 
00611     } else {
00612       // The mouse is above or below the graph; maybe create a new
00613       // guide bar.
00614       return DM_new_guide_bar;
00615     }
00616   }
00617 
00618   return WinStatsGraph::consider_drag_start(mouse_x, mouse_y, width, height);
00619 }
00620 
00621 ////////////////////////////////////////////////////////////////////
00622 //     Function: WinStatsStripChart::set_drag_mode
00623 //       Access: Protected, Virtual
00624 //  Description: This should be called whenever the drag mode needs to
00625 //               change state.  It provides hooks for a derived class
00626 //               to do something special.
00627 ////////////////////////////////////////////////////////////////////
00628 void WinStatsStripChart::
00629 set_drag_mode(WinStatsGraph::DragMode drag_mode) {
00630   WinStatsGraph::set_drag_mode(drag_mode);
00631 
00632   switch (_drag_mode) {
00633   case DM_scale:
00634   case DM_left_margin:
00635   case DM_right_margin:
00636   case DM_sizing:
00637     // Disable smoothing for these expensive operations.
00638     set_average_mode(false);
00639     break;
00640 
00641   default:
00642     // Restore smoothing according to the current setting of the check
00643     // box.
00644     int result = SendMessage(_smooth_check_box, BM_GETCHECK, 0, 0);
00645     set_average_mode(result == BST_CHECKED);
00646   }
00647 }
00648 
00649 ////////////////////////////////////////////////////////////////////
00650 //     Function: WinStatsStripChart::move_graph_window
00651 //       Access: Protected, Virtual
00652 //  Description: Repositions the graph child window within the parent
00653 //               window according to the _margin variables.
00654 ////////////////////////////////////////////////////////////////////
00655 void WinStatsStripChart::
00656 move_graph_window(int graph_left, int graph_top, int graph_xsize, int graph_ysize) {
00657   WinStatsGraph::move_graph_window(graph_left, graph_top, graph_xsize, graph_ysize);
00658   if (_smooth_check_box != 0) {
00659     SetWindowPos(_smooth_check_box, 0, 
00660                  _left_margin, _top_margin - _check_box_height - 1,
00661                  0, 0,
00662                  SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
00663     InvalidateRect(_smooth_check_box, NULL, TRUE);
00664   }
00665 }
00666 
00667 ////////////////////////////////////////////////////////////////////
00668 //     Function: WinStatsStripChart::draw_guide_bar
00669 //       Access: Private
00670 //  Description: Draws the line for the indicated guide bar on the
00671 //               graph.
00672 ////////////////////////////////////////////////////////////////////
00673 void WinStatsStripChart::
00674 draw_guide_bar(HDC hdc, int from_x, int to_x, 
00675                const PStatGraph::GuideBar &bar) {
00676   int y = height_to_pixel(bar._height);
00677 
00678   if (y > 0) {
00679     // Only draw it if it's not too close to the top.
00680     switch (bar._style) {
00681     case GBS_target:
00682       SelectObject(hdc, _light_pen);
00683       break;
00684 
00685     case GBS_user:
00686       SelectObject(hdc, _user_guide_bar_pen);
00687       break;
00688       
00689     case GBS_normal:
00690       SelectObject(hdc, _dark_pen);
00691       break;
00692     }
00693     MoveToEx(hdc, from_x, y, NULL);
00694     LineTo(hdc, to_x + 1, y);
00695   }
00696 }
00697 
00698 ////////////////////////////////////////////////////////////////////
00699 //     Function: WinStatsStripChart::draw_guide_label
00700 //       Access: Private
00701 //  Description: Draws the text for the indicated guide bar label to
00702 //               the right of the graph, unless it would overlap with
00703 //               the indicated last label, whose top pixel value is
00704 //               given.  Returns the top pixel value of the new label.
00705 ////////////////////////////////////////////////////////////////////
00706 int WinStatsStripChart::
00707 draw_guide_label(HDC hdc, int x, const PStatGraph::GuideBar &bar, int last_y) {
00708   switch (bar._style) {
00709   case GBS_target:
00710     SetTextColor(hdc, _light_color);
00711     break;
00712     
00713   case GBS_user:
00714     SetTextColor(hdc, _user_guide_bar_color);
00715     break;
00716     
00717   case GBS_normal:
00718     SetTextColor(hdc, _dark_color);
00719     break;
00720   }
00721 
00722   int y = height_to_pixel(bar._height);
00723   const string &label = bar._label;
00724   SIZE size;
00725   GetTextExtentPoint32(hdc, label.data(), label.length(), &size);
00726 
00727   if (bar._style != GBS_user) {
00728     double from_height = pixel_to_height(y + size.cy);
00729     double to_height = pixel_to_height(y - size.cy);
00730     if (find_user_guide_bar(from_height, to_height) >= 0) {
00731       // Omit the label: there's a user-defined guide bar in the same space.
00732       return last_y;
00733     }
00734   }
00735 
00736   int this_y = _graph_top + y - size.cy / 2;
00737   if (y >= 0 && y < get_ysize() &&
00738       (last_y < this_y || last_y > this_y + size.cy)) {
00739     TextOut(hdc, x, this_y,
00740             label.data(), label.length()); 
00741     last_y = this_y;
00742   }
00743 
00744   return last_y;
00745 }
00746 
00747 
00748 ////////////////////////////////////////////////////////////////////
00749 //     Function: WinStatsStripChart::create_window
00750 //       Access: Private
00751 //  Description: Creates the window for this strip chart.
00752 ////////////////////////////////////////////////////////////////////
00753 void WinStatsStripChart::
00754 create_window() {
00755   if (_window) {
00756     return;
00757   }
00758 
00759   HINSTANCE application = GetModuleHandle(NULL);
00760   register_window_class(application);
00761 
00762   string window_title = get_title_text();
00763 
00764   RECT win_rect = { 
00765     0, 0,
00766     _left_margin + get_xsize() + _right_margin, 
00767     _top_margin + get_ysize() + _bottom_margin
00768   };  
00769   
00770   // compute window size based on desired client area size
00771   AdjustWindowRect(&win_rect, graph_window_style, FALSE);
00772 
00773   _window = 
00774     CreateWindow(_window_class_name, window_title.c_str(), graph_window_style,
00775                  CW_USEDEFAULT, CW_USEDEFAULT,
00776                  win_rect.right - win_rect.left,
00777                  win_rect.bottom - win_rect.top,
00778                  WinStatsGraph::_monitor->get_window(), NULL, application, 0);
00779   if (!_window) {
00780     nout << "Could not create StripChart window!\n";
00781     exit(1);
00782   }
00783 
00784   SetWindowLongPtr(_window, 0, (LONG_PTR)this);
00785   setup_label_stack();
00786 
00787   _smooth_check_box = 
00788     CreateWindow("BUTTON", "",
00789                  WS_CHILD | BS_AUTOCHECKBOX,
00790                  0, 0, _check_box_width, _check_box_height,
00791                  _window, NULL, application, 0);
00792 
00793   // Ensure that the window is on top of the stack.
00794   SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0, 
00795                SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
00796 }
00797 
00798 ////////////////////////////////////////////////////////////////////
00799 //     Function: WinStatsStripChart::register_window_class
00800 //       Access: Private, Static
00801 //  Description: Registers the window class for the stripChart window, if
00802 //               it has not already been registered.
00803 ////////////////////////////////////////////////////////////////////
00804 void WinStatsStripChart::
00805 register_window_class(HINSTANCE application) {
00806   if (_window_class_registered) {
00807     return;
00808   }
00809 
00810   WNDCLASS wc;
00811 
00812   ZeroMemory(&wc, sizeof(WNDCLASS));
00813   wc.style = 0;
00814   wc.lpfnWndProc = (WNDPROC)static_window_proc;
00815   wc.hInstance = application;
00816   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
00817   wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
00818   wc.lpszMenuName = NULL;
00819   wc.lpszClassName = _window_class_name;
00820 
00821   // Reserve space to associate the this pointer with the window.
00822   wc.cbWndExtra = sizeof(WinStatsStripChart *);
00823   
00824   if (!RegisterClass(&wc)) {
00825     nout << "Could not register StripChart window class!\n";
00826     exit(1);
00827   }
00828 
00829   _window_class_registered = true;
00830 }
00831 
00832 ////////////////////////////////////////////////////////////////////
00833 //     Function: WinStatsStripChart::static_window_proc
00834 //       Access: Private, Static
00835 //  Description: 
00836 ////////////////////////////////////////////////////////////////////
00837 LONG WINAPI WinStatsStripChart::
00838 static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
00839   WinStatsStripChart *self = (WinStatsStripChart *)GetWindowLongPtr(hwnd, 0);
00840   if (self != (WinStatsStripChart *)NULL && self->_window == hwnd) {
00841     return self->window_proc(hwnd, msg, wparam, lparam);
00842   } else {
00843     return DefWindowProc(hwnd, msg, wparam, lparam);
00844   }
00845 }
 All Classes Functions Variables Enumerations