Browse Source

user guide bars

David Rose 22 years ago
parent
commit
3089ac52d7

+ 7 - 2
pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx

@@ -130,10 +130,15 @@ begin_draw() {
 
     if (x >= 5 && x <= get_xsize() - 5) {
       // Only draw it if it's not too close to either edge.
-      if (bar._is_target) {
+      switch (bar._style) {
+      case GBS_target:
         _pixmap.draw_line(_light_gc, x, text_height + 4, x, get_ysize());
-      } else {
+        break;
+
+      case GBS_normal:
+      default:
         _pixmap.draw_line(_dark_gc, x, text_height + 4, x, get_ysize());
+        break;
       }
     }
   }

+ 7 - 2
pandatool/src/gtk-stats/gtkStatsStripChart.cxx

@@ -255,10 +255,15 @@ end_draw(int from_x, int to_x) {
 
     if (y >= 5) {
       // Only draw it if it's not too close to the top.
-      if (bar._is_target) {
+      switch (bar._style) {
+      case GBS_target:
         _pixmap.draw_line(_light_gc, from_x, y, to_x, y);
-      } else {
+        break;
+
+      case GBS_normal:
+      default:
         _pixmap.draw_line(_dark_gc, from_x, y, to_x, y);
+        break;
       }
     }
   }

+ 1 - 0
pandatool/src/pstatserver/pStatGraph.I

@@ -130,6 +130,7 @@ set_guide_bar_units(int guide_bar_units) {
   if (_guide_bar_units != guide_bar_units) {
     _guide_bar_units = guide_bar_units;
     normal_guide_bars();
+    user_guide_bar_labels();
   }
 }
 

+ 114 - 7
pandatool/src/pstatserver/pStatGraph.cxx

@@ -31,10 +31,10 @@
 //  Description:
 ////////////////////////////////////////////////////////////////////
 PStatGraph::GuideBar::
-GuideBar(float height, const string &label, bool is_target) :
+GuideBar(float height, const string &label, PStatGraph::GuideBarStyle style) :
   _height(height),
   _label(label),
-  _is_target(is_target)
+  _style(style)
 {
 }
 
@@ -47,7 +47,7 @@ PStatGraph::GuideBar::
 GuideBar(const PStatGraph::GuideBar &copy) :
   _height(copy._height),
   _label(copy._label),
-  _is_target(copy._is_target)
+  _style(copy._style)
 {
 }
 
@@ -104,12 +104,101 @@ get_num_guide_bars() const {
 const PStatGraph::GuideBar &PStatGraph::
 get_guide_bar(int n) const {
 #ifndef NDEBUG
-  static GuideBar bogus_bar(0.0, "bogus", false);
+  static GuideBar bogus_bar(0.0, "bogus", GBS_normal);
   nassertr(n >= 0 && n < (int)_guide_bars.size(), bogus_bar);
 #endif
   return _guide_bars[n];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGraph::get_num_user_guide_bars
+//       Access: Public
+//  Description: Returns the current number of user-defined guide
+//               bars.  Not all of these may be visible.
+////////////////////////////////////////////////////////////////////
+int PStatGraph::
+get_num_user_guide_bars() const {
+  return _user_guide_bars.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGraph::get_user_guide_bar
+//       Access: Public
+//  Description: Returns the nth user-defined guide bar.
+////////////////////////////////////////////////////////////////////
+const PStatGraph::GuideBar &PStatGraph::
+get_user_guide_bar(int n) const {
+#ifndef NDEBUG
+  static GuideBar bogus_bar(0.0, "bogus", GBS_user);
+  nassertr(n >= 0 && n < (int)_user_guide_bars.size(), bogus_bar);
+#endif
+  return _user_guide_bars[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGraph::move_user_guide_bar
+//       Access: Public
+//  Description: Adjusts the height of the nth user-defined guide bar.
+////////////////////////////////////////////////////////////////////
+void PStatGraph::
+move_user_guide_bar(int n, float height) {
+  nassertv(n >= 0 && n < (int)_user_guide_bars.size());
+  string label = format_number(height, _guide_bar_units, _unit_name);
+  _user_guide_bars[n]._height = height;
+  _user_guide_bars[n]._label = label;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGraph::add_user_guide_bar
+//       Access: Public
+//  Description: Creates a new user guide bar and returns its index
+//               number.
+////////////////////////////////////////////////////////////////////
+int PStatGraph::
+add_user_guide_bar(float height) {
+  int n = (int)_user_guide_bars.size();
+  GuideBar bar = make_guide_bar(height);
+  bar._style = GBS_user;
+  _user_guide_bars.push_back(bar);
+
+  return n;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGraph::remove_user_guide_bar
+//       Access: Public
+//  Description: Removes the user guide bar with the indicated index
+//               number.  All subsequent index numbers are adjusted
+//               down one.
+////////////////////////////////////////////////////////////////////
+void PStatGraph::
+remove_user_guide_bar(int n) {
+  nassertv(n >= 0 && n < (int)_user_guide_bars.size());
+  _user_guide_bars.erase(_user_guide_bars.begin() + n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGraph::find_user_guide_bar
+//       Access: Public
+//  Description: Returns the index number of the first user guide bar
+//               found whose height is within the indicated range, or
+//               -1 if no user guide bars fall within the range.
+////////////////////////////////////////////////////////////////////
+int PStatGraph::
+find_user_guide_bar(float from_height, float to_height) const {
+  GuideBars::const_iterator gbi;
+  for (gbi = _user_guide_bars.begin();
+       gbi != _user_guide_bars.end();
+       ++gbi) {
+    const GuideBar &bar = (*gbi);
+    if (bar._height >= from_height && bar._height <= to_height) {
+      return (int)(gbi - _user_guide_bars.begin());
+    }
+  }
+
+  return -1;
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatGraph::format_number
@@ -222,6 +311,22 @@ update_guide_bars(int num_bars, float scale) {
   _guide_bars_changed = true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGraph::user_guide_bar_labels
+//       Access: Protected
+//  Description: Rederives the labels for the user-defined guide bars.
+////////////////////////////////////////////////////////////////////
+void PStatGraph::
+user_guide_bar_labels() {
+  GuideBars::iterator gbi;
+  for (gbi = _user_guide_bars.begin();
+       gbi != _user_guide_bars.end();
+       ++gbi) {
+    GuideBar &bar = (*gbi);
+    bar._label = format_number(bar._height, _guide_bar_units, _unit_name);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatGraph::make_guide_bar
 //       Access: Protected
@@ -232,14 +337,16 @@ PStatGraph::GuideBar PStatGraph::
 make_guide_bar(float value) const {
   string label = format_number(value, _guide_bar_units, _unit_name);
 
-  bool is_target = false;
+  GuideBarStyle style = GBS_normal;
 
   if ((_guide_bar_units & GBU_named) == 0) {
     // If it's a time unit, check to see if it matches our target
     // frame rate.
     float hz = 1.0 / value;
-    is_target = IS_NEARLY_EQUAL(hz, _target_frame_rate);
+    if (IS_THRESHOLD_EQUAL(hz, _target_frame_rate, 0.001)) {
+      style = GBS_target;
+    }
   }
 
-  return GuideBar(value, label, is_target);
+  return GuideBar(value, label, style);
 }

+ 17 - 2
pandatool/src/pstatserver/pStatGraph.h

@@ -55,14 +55,20 @@ public:
   INLINE int get_xsize() const;
   INLINE int get_ysize() const;
 
+  enum GuideBarStyle {
+    GBS_normal,
+    GBS_target,
+    GBS_user,
+  };
+  
   class GuideBar {
   public:
-    GuideBar(float height, const string &label, bool is_target);
+    GuideBar(float height, const string &label, GuideBarStyle style);
     GuideBar(const GuideBar &copy);
 
     float _height;
     string _label;
-    bool _is_target;
+    GuideBarStyle _style;
   };
 
   enum GuideBarUnits {
@@ -75,6 +81,13 @@ public:
   int get_num_guide_bars() const;
   const GuideBar &get_guide_bar(int n) const;
 
+  int get_num_user_guide_bars() const;
+  const GuideBar &get_user_guide_bar(int n) const;
+  void move_user_guide_bar(int n, float height);
+  int add_user_guide_bar(float height);
+  void remove_user_guide_bar(int n);
+  int find_user_guide_bar(float from_height, float to_height) const;
+
   INLINE void set_guide_bar_units(int unit_mask);
   INLINE int get_guide_bar_units() const;
   INLINE void set_guide_bar_unit_name(const string &unit_name);
@@ -87,6 +100,7 @@ public:
 protected:
   virtual void normal_guide_bars()=0;
   void update_guide_bars(int num_bars, float scale);
+  void user_guide_bar_labels();
   GuideBar make_guide_bar(float value) const;
 
   bool _labels_changed;
@@ -106,6 +120,7 @@ protected:
 
   typedef pvector<GuideBar> GuideBars;
   GuideBars _guide_bars;
+  GuideBars _user_guide_bars;
   int _guide_bar_units;
   string _unit_name;
 };

+ 115 - 13
pandatool/src/win-stats/winStatsGraph.cxx

@@ -39,6 +39,7 @@ WinStatsGraph(WinStatsMonitor *monitor, int thread_index) :
   _window = 0;
   _graph_window = 0;
   _sizewe_cursor = LoadCursor(NULL, IDC_SIZEWE);
+  _hand_cursor = LoadCursor(NULL, IDC_HAND);
   _bitmap = 0;
   _bitmap_dc = 0;
 
@@ -47,8 +48,12 @@ WinStatsGraph(WinStatsMonitor *monitor, int thread_index) :
   _bitmap_xsize = 0;
   _bitmap_ysize = 0;
 
-  _dark_pen = CreatePen(PS_SOLID, 1, RGB(51, 51, 51));
-  _light_pen = CreatePen(PS_SOLID, 1, RGB(154, 154, 154));
+  _dark_color = RGB(51, 51, 51);
+  _light_color = RGB(154, 154, 154);
+  _user_guide_bar_color = RGB(130, 150, 255);
+  _dark_pen = CreatePen(PS_SOLID, 1, _dark_color);
+  _light_pen = CreatePen(PS_SOLID, 1, _light_color);
+  _user_guide_bar_pen = CreatePen(PS_DASH, 1, _user_guide_bar_color);
 
   _drag_mode = DM_none;
   _potential_drag_mode = DM_none;
@@ -67,6 +72,7 @@ WinStatsGraph::
 
   DeleteObject(_dark_pen);
   DeleteObject(_light_pen);
+  DeleteObject(_user_guide_bar_pen);
   
   Brushes::iterator bi;
   for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) {
@@ -85,6 +91,17 @@ WinStatsGraph::
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsGraph::get_thread_index
+//       Access: Public
+//  Description: Returns the thread index associated with this
+//               particular graph.
+////////////////////////////////////////////////////////////////////
+int WinStatsGraph::
+get_thread_index() const {
+  return _thread_index;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WinStatsGraph::new_collector
 //       Access: Public, Virtual
@@ -135,6 +152,37 @@ void WinStatsGraph::
 set_time_units(int unit_mask) {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsGraph::move_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Adjusts the height of the nth user-defined guide bar.
+////////////////////////////////////////////////////////////////////
+void WinStatsGraph::
+move_user_guide_bar(int n, float height) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsGraph::add_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Creates a new user guide bar and returns its index
+//               number.
+////////////////////////////////////////////////////////////////////
+int WinStatsGraph::
+add_user_guide_bar(float height) {
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsGraph::remove_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Removes the user guide bar with the indicated index
+//               number.  All subsequent index numbers are adjusted
+//               down one.
+////////////////////////////////////////////////////////////////////
+void WinStatsGraph::
+remove_user_guide_bar(int n) {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WinStatsGraph::close
 //       Access: Protected
@@ -229,27 +277,34 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 
   case WM_SETCURSOR:
     {
-      // Why is it so hard to ask what the cursor position within the
-      // window's client area is?
+      // Why is it so hard to ask for the cursor position within the
+      // window's client area?
       POINT point;
       GetCursorPos(&point);
       WINDOWINFO winfo;
       GetWindowInfo(hwnd, &winfo);
       const RECT &rect = winfo.rcClient;
+      int x = point.x - rect.left;
+      int y = point.y - rect.top;
+      int width = rect.right - rect.left;
+      int height = rect.bottom - rect.top;
+
+      _potential_drag_mode = consider_drag_start(x, y, width, height);
 
-      // Display a double-headed arrow to drag the left or right margins.
-      if (point.x >= rect.left + _left_margin - 1 && point.x <= rect.left + _left_margin + 1) {
+      switch (_potential_drag_mode) {
+      case DM_left_margin:
+      case DM_right_margin:
         SetCursor(_sizewe_cursor);
-        _potential_drag_mode = DM_left_margin;
         return TRUE;
-      }
-      if (point.x >= rect.right - _right_margin - 2 && point.x <= rect.right - _right_margin) {
-        SetCursor(_sizewe_cursor);
-        _potential_drag_mode = DM_right_margin;
+
+      case DM_guide_bar:
+        SetCursor(_hand_cursor);
         return TRUE;
-      }
 
-      _potential_drag_mode = DM_none;
+      default:
+      case DM_none:
+        break;
+      }
     }
     break;
 
@@ -340,6 +395,21 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
     force_redraw();
     break;
 
+  case WM_LBUTTONDOWN:
+    // Vector any uncaught WM_LBUTTONDOWN into the main window, so we
+    // can drag margins, etc.
+    if (_potential_drag_mode != DM_none) {
+      PN_int16 x = LOWORD(lparam) + _graph_left;
+      PN_int16 y = HIWORD(lparam) + _graph_top;
+      return window_proc(_window, msg, wparam, MAKELPARAM(x, y));
+    }
+    break;
+
+  case WM_LBUTTONUP:
+    _drag_mode = DM_none;
+    ReleaseCapture();
+    break;
+
   case WM_PAINT:
     {
       // Repaint the graph by copying the backing pixmap in.
@@ -351,6 +421,8 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
              _bitmap_dc, 0, 0,
              SRCCOPY);
 
+      additional_graph_window_paint(hdc);
+
       EndPaint(hwnd, &ps);
       return 0;
     }
@@ -374,6 +446,36 @@ void WinStatsGraph::
 additional_window_paint(HDC hdc) {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsGraph::additional_graph_window_paint
+//       Access: Protected, Virtual
+//  Description: This is called during the servicing of WM_PAINT; it
+//               gives a derived class opportunity to do some further
+//               painting into the graph window.
+////////////////////////////////////////////////////////////////////
+void WinStatsGraph::
+additional_graph_window_paint(HDC hdc) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsGraph::consider_drag_start
+//       Access: Protected, Virtual
+//  Description: Based on the mouse position within the window's
+//               client area, look for draggable things the mouse
+//               might be hovering over and return the apprioprate
+//               DragMode enum or DM_none if nothing is indicated.
+////////////////////////////////////////////////////////////////////
+WinStatsGraph::DragMode WinStatsGraph::
+consider_drag_start(int mouse_x, int mouse_y, int width, int height) {
+  if (mouse_x >= _left_margin - 2 && mouse_x <= _left_margin + 2) {
+    return DM_left_margin;
+  } else if (mouse_x >= width - _right_margin - 2 && mouse_x <= width - _right_margin + 2) {
+    return DM_right_margin;
+  }
+
+  return DM_none;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WinStatsGraph::setup_bitmap
 //       Access: Private

+ 26 - 8
pandatool/src/win-stats/winStatsGraph.h

@@ -34,10 +34,23 @@ class WinStatsMonitor;
 //               that may be created for a WinStatsMonitor.
 ////////////////////////////////////////////////////////////////////
 class WinStatsGraph {
+public:
+  // What is the user adjusting by dragging the mouse in a window?
+  enum DragMode {
+    DM_none,
+    DM_scale,
+    DM_left_margin,
+    DM_right_margin,
+    DM_guide_bar,
+    DM_new_guide_bar,
+  };
+
 public:
   WinStatsGraph(WinStatsMonitor *monitor, int thread_index);
   virtual ~WinStatsGraph();
 
+  int get_thread_index() const;
+
   virtual void new_collector(int collector_index);
   virtual void new_data(int thread_index, int frame_number);
   virtual void force_redraw();
@@ -45,6 +58,10 @@ public:
 
   virtual void set_time_units(int unit_mask);
 
+  virtual void move_user_guide_bar(int n, float height);
+  virtual int add_user_guide_bar(float height);
+  virtual void remove_user_guide_bar(int n);
+
 protected:
   void close();
 
@@ -57,7 +74,9 @@ protected:
   virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
 
   virtual void additional_window_paint(HDC hdc);
-
+  virtual void additional_graph_window_paint(HDC hdc);
+  virtual DragMode consider_drag_start(int mouse_x, int mouse_y, 
+                                       int width, int height);
 
 protected:
   // Table of brushes for our various collectors.
@@ -71,6 +90,7 @@ protected:
   WinStatsLabelStack _label_stack;
 
   HCURSOR _sizewe_cursor;
+  HCURSOR _hand_cursor;
 
   HBITMAP _bitmap;
   HDC _bitmap_dc;
@@ -80,20 +100,18 @@ protected:
   int _left_margin, _right_margin;
   int _top_margin, _bottom_margin;
 
+  COLORREF _dark_color;
+  COLORREF _light_color;
+  COLORREF _user_guide_bar_color;
   HPEN _dark_pen;
   HPEN _light_pen;
+  HPEN _user_guide_bar_pen;
 
-  // What is the user adjusting by dragging the mouse in a window?
-  enum DragMode {
-    DM_none,
-    DM_scale,
-    DM_left_margin,
-    DM_right_margin,
-  };
   DragMode _drag_mode;
   DragMode _potential_drag_mode;
   int _drag_start_x, _drag_start_y;
   float _drag_scale_start;
+  int _drag_guide_bar;
 
 private:
   void setup_bitmap(int xsize, int ysize);

+ 57 - 0
pandatool/src/win-stats/winStatsMonitor.cxx

@@ -365,6 +365,63 @@ set_time_units(int unit_mask) {
   SetMenuItemInfo(_options_menu, MI_time_hz, FALSE, &mii);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsMonitor::move_user_guide_bar
+//       Access: Public
+//  Description: Adjusts the height of the nth user-defined guide bar
+//               for all graphs that share the indicated thread.
+////////////////////////////////////////////////////////////////////
+void WinStatsMonitor::
+move_user_guide_bar(int thread_index, int n, float height) {
+  Graphs::iterator gi;
+  for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
+    WinStatsGraph *graph = (*gi);
+    if (graph->get_thread_index() == thread_index) {
+      graph->move_user_guide_bar(n, height);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsMonitor::add_user_guide_bar
+//       Access: Public
+//  Description: Creates a new user guide bar and returns its index
+//               number for all graphs that share the indicated
+//               thread.
+////////////////////////////////////////////////////////////////////
+int WinStatsMonitor::
+add_user_guide_bar(int thread_index, float height) {
+  int result = -1;
+
+  Graphs::iterator gi;
+  for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
+    WinStatsGraph *graph = (*gi);
+    if (graph->get_thread_index() == thread_index) {
+      result = graph->add_user_guide_bar(height);
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsMonitor::remove_user_guide_bar
+//       Access: Public
+//  Description: Removes the user guide bar with the indicated index
+//               number, for all graphs that share the indicated
+//               thread.
+////////////////////////////////////////////////////////////////////
+void WinStatsMonitor::
+remove_user_guide_bar(int thread_index, int n) {
+  Graphs::iterator gi;
+  for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
+    WinStatsGraph *graph = (*gi);
+    if (graph->get_thread_index() == thread_index) {
+      graph->remove_user_guide_bar(n);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WinStatsMonitor::add_graph
 //       Access: Private

+ 4 - 0
pandatool/src/win-stats/winStatsMonitor.h

@@ -72,6 +72,10 @@ public:
   int get_menu_id(const MenuDef &menu_def);
 
   void set_time_units(int unit_mask);
+
+  void move_user_guide_bar(int thread_index, int n, float height);
+  int add_user_guide_bar(int thread_index, float height);
+  void remove_user_guide_bar(int thread_index, int n);
   
 private:
   void add_graph(WinStatsGraph *graph);

+ 224 - 27
pandatool/src/win-stats/winStatsPianoRoll.cxx

@@ -133,6 +133,56 @@ set_horizontal_scale(float time_width) {
   InvalidateRect(_window, &rect, TRUE);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsPianoRoll::move_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Adjusts the height of the nth user-defined guide bar.
+////////////////////////////////////////////////////////////////////
+void WinStatsPianoRoll::
+move_user_guide_bar(int n, float height) {
+  RECT rect;
+  GetClientRect(_window, &rect);
+  rect.bottom = _top_margin;
+  InvalidateRect(_window, &rect, TRUE);
+
+  InvalidateRect(_graph_window, NULL, TRUE);
+
+  PStatPianoRoll::move_user_guide_bar(n, height);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsPianoRoll::add_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Creates a new user guide bar and returns its index
+//               number.
+////////////////////////////////////////////////////////////////////
+int WinStatsPianoRoll::
+add_user_guide_bar(float height) {
+  RECT rect;
+  GetClientRect(_window, &rect);
+  rect.bottom = _top_margin;
+  InvalidateRect(_window, &rect, TRUE);
+
+  return PStatPianoRoll::add_user_guide_bar(height);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsPianoRoll::remove_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Removes the user guide bar with the indicated index
+//               number.  All subsequent index numbers are adjusted
+//               down one.
+////////////////////////////////////////////////////////////////////
+void WinStatsPianoRoll::
+remove_user_guide_bar(int n) {
+  RECT rect;
+  GetClientRect(_window, &rect);
+  rect.bottom = _top_margin;
+  InvalidateRect(_window, &rect, TRUE);
+
+  return PStatPianoRoll::remove_user_guide_bar(n);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WinStatsPianoRoll::clear_region
 //       Access: Protected
@@ -157,19 +207,7 @@ begin_draw() {
   // Draw in the guide bars.
   int num_guide_bars = get_num_guide_bars();
   for (int i = 0; i < num_guide_bars; i++) {
-    const GuideBar &bar = get_guide_bar(i);
-    int x = height_to_pixel(bar._height);
-
-    if (x > 0 && x < get_xsize() - 1) {
-      // Only draw it if it's not too close to either edge.
-      if (bar._is_target) {
-        SelectObject(_bitmap_dc, _light_pen);
-      } else {
-        SelectObject(_bitmap_dc, _dark_pen);
-      }
-      MoveToEx(_bitmap_dc, x, 0, NULL);
-      LineTo(_bitmap_dc, x, get_ysize());
-    }
+    draw_guide_bar(_bitmap_dc, get_guide_bar(i));
   }
 }
 
@@ -224,12 +262,18 @@ idle() {
 ////////////////////////////////////////////////////////////////////
 LONG WinStatsPianoRoll::
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
-  /*
   switch (msg) {
+  case WM_LBUTTONDOWN:
+    if (_potential_drag_mode == DM_new_guide_bar) {
+      _drag_mode = DM_new_guide_bar;
+      SetCapture(_graph_window);
+      return 0;
+    }
+    break;
+
   default:
     break;
   }
-  */
 
   return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam);
 }
@@ -243,13 +287,21 @@ LONG WinStatsPianoRoll::
 graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
   switch (msg) {
   case WM_LBUTTONDOWN:
-    {
+    if (_potential_drag_mode == DM_none) {
       _drag_mode = DM_scale;
       PN_int16 x = LOWORD(lparam);
       _drag_scale_start = pixel_to_height(x);
       SetCapture(_graph_window);
+      return 0;
+
+    } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
+      _drag_mode = DM_guide_bar;
+      PN_int16 x = LOWORD(lparam);
+      _drag_start_x = x;
+      SetCapture(_graph_window);
+      return 0;
     }
-    return 0;
+    break;
 
   case WM_MOUSEMOVE: 
     if (_drag_mode == DM_scale) {
@@ -259,6 +311,22 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
         set_horizontal_scale(_drag_scale_start / ratio);
       }
       return 0;
+
+    } else if (_drag_mode == DM_new_guide_bar) {
+      // We haven't created the new guide bar yet; we won't until the
+      // mouse comes within the graph's region.
+      PN_int16 x = LOWORD(lparam);
+      if (x >= 0 && x < get_xsize()) {
+        _drag_mode = DM_guide_bar;
+        _drag_guide_bar = 
+          WinStatsGraph::_monitor->add_user_guide_bar(WinStatsGraph::_thread_index, pixel_to_height(x));
+        return 0;
+      }
+
+    } else if (_drag_mode == DM_guide_bar) {
+      PN_int16 x = LOWORD(lparam);
+      WinStatsGraph::_monitor->move_user_guide_bar(WinStatsGraph::_thread_index, _drag_guide_bar, pixel_to_height(x));
+      return 0;
     }
     break;
 
@@ -267,6 +335,17 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
       _drag_mode = DM_none;
       ReleaseCapture();
       return 0;
+
+    } else if (_drag_mode == DM_guide_bar) {
+      PN_int16 x = LOWORD(lparam);
+      if (x < 0 || x >= get_xsize()) {
+        WinStatsGraph::_monitor->remove_user_guide_bar(WinStatsGraph::_thread_index, _drag_guide_bar);
+      } else {
+        WinStatsGraph::_monitor->move_user_guide_bar(WinStatsGraph::_thread_index, _drag_guide_bar, pixel_to_height(x));
+      }
+      _drag_mode = DM_none;
+      ReleaseCapture();
+      return 0;
     }
     break;
 
@@ -292,21 +371,66 @@ additional_window_paint(HDC hdc) {
   SelectObject(hdc, hfnt);
   SetTextAlign(hdc, TA_LEFT | TA_BOTTOM);
   SetBkMode(hdc, TRANSPARENT);
-  SetTextColor(hdc, RGB(0, 0, 0));
 
+  int y = _top_margin;
+
+  int i;
   int num_guide_bars = get_num_guide_bars();
-  for (int i = 0; i < num_guide_bars; i++) {
-    const GuideBar &bar = get_guide_bar(i);
-    int x = height_to_pixel(bar._height);
+  for (i = 0; i < num_guide_bars; i++) {
+    draw_guide_label(hdc, y, get_guide_bar(i));
+  }
 
-    const string &label = bar._label;
-    SIZE size;
-    GetTextExtentPoint32(hdc, label.data(), label.length(), &size);
-    x -= size.cx / 2;
+  int num_user_guide_bars = get_num_user_guide_bars();
+  for (i = 0; i < num_user_guide_bars; i++) {
+    draw_guide_label(hdc, y, get_user_guide_bar(i));
+  }
+}
 
-    TextOut(hdc, x + _graph_left, _top_margin,
-            label.data(), label.length()); 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsPianoRoll::additional_graph_window_paint
+//       Access: Protected, Virtual
+//  Description: This is called during the servicing of WM_PAINT; it
+//               gives a derived class opportunity to do some further
+//               painting into the window (the outer window, not the
+//               graph window).
+////////////////////////////////////////////////////////////////////
+void WinStatsPianoRoll::
+additional_graph_window_paint(HDC hdc) {
+  int num_user_guide_bars = get_num_user_guide_bars();
+  for (int i = 0; i < num_user_guide_bars; i++) {
+    draw_guide_bar(hdc, get_user_guide_bar(i));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsPianoRoll::consider_drag_start
+//       Access: Protected, Virtual
+//  Description: Based on the mouse position within the window's
+//               client area, look for draggable things the mouse
+//               might be hovering over and return the apprioprate
+//               DragMode enum or DM_none if nothing is indicated.
+////////////////////////////////////////////////////////////////////
+WinStatsGraph::DragMode WinStatsPianoRoll::
+consider_drag_start(int mouse_x, int mouse_y, int width, int height) {
+  if (mouse_y >= _graph_top && mouse_y < _graph_top + get_ysize()) {
+    if (mouse_x >= _graph_left && mouse_x < _graph_left + get_xsize()) {
+      // See if the mouse is over a user-defined guide bar.
+      int x = mouse_x - _graph_left;
+      float from_height = pixel_to_height(x - 2);
+      float to_height = pixel_to_height(x + 2);
+      _drag_guide_bar = find_user_guide_bar(from_height, to_height);
+      if (_drag_guide_bar >= 0) {
+        return DM_guide_bar;
+      }
+
+    } else {
+      // The mouse is above or below the graph; maybe create a new
+      // guide bar.
+      return DM_new_guide_bar;
+    }
   }
+
+  return WinStatsGraph::consider_drag_start(mouse_x, mouse_y, width, height);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -326,6 +450,79 @@ update_labels() {
   _labels_changed = false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsPianoRoll::draw_guide_bar
+//       Access: Private
+//  Description: Draws the line for the indicated guide bar on the
+//               graph.
+////////////////////////////////////////////////////////////////////
+void WinStatsPianoRoll::
+draw_guide_bar(HDC hdc, const PStatGraph::GuideBar &bar) {
+  int x = height_to_pixel(bar._height);
+
+  if (x > 0 && x < get_xsize() - 1) {
+    // Only draw it if it's not too close to either edge.
+    switch (bar._style) {
+    case GBS_target:
+      SelectObject(hdc, _light_pen);
+      break;
+
+    case GBS_user:
+      SelectObject(hdc, _user_guide_bar_pen);
+      break;
+      
+    case GBS_normal:
+      SelectObject(hdc, _dark_pen);
+      break;
+    }
+    MoveToEx(hdc, x, 0, NULL);
+    LineTo(hdc, x, get_ysize());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsPianoRoll::draw_guide_label
+//       Access: Private
+//  Description: Draws the text for the indicated guide bar label at
+//               the top of the graph.
+////////////////////////////////////////////////////////////////////
+void WinStatsPianoRoll::
+draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar) {
+  switch (bar._style) {
+  case GBS_target:
+    SetTextColor(hdc, _light_color);
+    break;
+    
+  case GBS_user:
+    SetTextColor(hdc, _user_guide_bar_color);
+    break;
+    
+  case GBS_normal:
+    SetTextColor(hdc, _dark_color);
+    break;
+  }
+
+  int x = height_to_pixel(bar._height);
+  const string &label = bar._label;
+  SIZE size;
+  GetTextExtentPoint32(hdc, label.data(), label.length(), &size);
+
+  if (bar._style != GBS_user) {
+    float from_height = pixel_to_height(x - size.cx / 2 - 1);
+    float to_height = pixel_to_height(x + size.cx / 2 + 1);
+    if (find_user_guide_bar(from_height, to_height) >= 0) {
+      // Omit the label: there's a user-defined guide bar in the same space.
+      return;
+    }
+  }
+
+  int this_x = _graph_left + x - size.cx / 2;
+  if (x >= 0 && x < get_xsize()) {
+    TextOut(hdc, this_x, y,
+            label.data(), label.length()); 
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WinStatsPianoRoll::create_window
 //       Access: Private

+ 10 - 0
pandatool/src/win-stats/winStatsPianoRoll.h

@@ -47,6 +47,10 @@ public:
   virtual void set_time_units(int unit_mask);
   void set_horizontal_scale(float time_width);
 
+  virtual void move_user_guide_bar(int n, float height);
+  virtual int add_user_guide_bar(float height);
+  virtual void remove_user_guide_bar(int n);
+
 protected:
   void clear_region();
   virtual void begin_draw();
@@ -57,9 +61,15 @@ protected:
   LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
   virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
   virtual void additional_window_paint(HDC hdc);
+  virtual void additional_graph_window_paint(HDC hdc);
+  virtual DragMode consider_drag_start(int mouse_x, int mouse_y, 
+                                       int width, int height);
 
 private:
   void update_labels();
+  void draw_guide_bar(HDC hdc, const GuideBar &bar);
+  void draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar);
+
   void create_window();
   static void register_window_class(HINSTANCE application);
 

+ 214 - 27
pandatool/src/win-stats/winStatsStripChart.cxx

@@ -154,6 +154,56 @@ set_vertical_scale(float value_height) {
   InvalidateRect(_window, &rect, TRUE);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsStripChart::move_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Adjusts the height of the nth user-defined guide bar.
+////////////////////////////////////////////////////////////////////
+void WinStatsStripChart::
+move_user_guide_bar(int n, float height) {
+  RECT rect;
+  GetClientRect(_window, &rect);
+  rect.left = _right_margin;
+  InvalidateRect(_window, &rect, TRUE);
+
+  InvalidateRect(_graph_window, NULL, TRUE);
+
+  PStatStripChart::move_user_guide_bar(n, height);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsStripChart::add_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Creates a new user guide bar and returns its index
+//               number.
+////////////////////////////////////////////////////////////////////
+int WinStatsStripChart::
+add_user_guide_bar(float height) {
+  RECT rect;
+  GetClientRect(_window, &rect);
+  rect.left = _right_margin;
+  InvalidateRect(_window, &rect, TRUE);
+
+  return PStatStripChart::add_user_guide_bar(height);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsStripChart::remove_user_guide_bar
+//       Access: Public, Virtual
+//  Description: Removes the user guide bar with the indicated index
+//               number.  All subsequent index numbers are adjusted
+//               down one.
+////////////////////////////////////////////////////////////////////
+void WinStatsStripChart::
+remove_user_guide_bar(int n) {
+  RECT rect;
+  GetClientRect(_window, &rect);
+  rect.left = _right_margin;
+  InvalidateRect(_window, &rect, TRUE);
+
+  return PStatStripChart::remove_user_guide_bar(n);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WinStatsStripChart::update_labels
 //       Access: Protected, Virtual
@@ -284,19 +334,7 @@ end_draw(int from_x, int to_x) {
   // Draw in the guide bars.
   int num_guide_bars = get_num_guide_bars();
   for (int i = 0; i < num_guide_bars; i++) {
-    const GuideBar &bar = get_guide_bar(i);
-    int y = height_to_pixel(bar._height);
-
-    if (y > 0) {
-      // Only draw it if it's not too close to the top.
-      if (bar._is_target) {
-        SelectObject(_bitmap_dc, _light_pen);
-      } else {
-        SelectObject(_bitmap_dc, _dark_pen);
-      }
-      MoveToEx(_bitmap_dc, from_x, y, NULL);
-      LineTo(_bitmap_dc, to_x + 1, y);
-    }
+    draw_guide_bar(_bitmap_dc, from_x, to_x, get_guide_bar(i));
   }
 
   RECT rect = { 
@@ -312,12 +350,18 @@ end_draw(int from_x, int to_x) {
 ////////////////////////////////////////////////////////////////////
 LONG WinStatsStripChart::
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
-  /*
   switch (msg) {
+  case WM_LBUTTONDOWN:
+    if (_potential_drag_mode == DM_new_guide_bar) {
+      _drag_mode = DM_new_guide_bar;
+      SetCapture(_graph_window);
+      return 0;
+    }
+    break;
+
   default:
     break;
   }
-  */
 
   return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam);
 }
@@ -331,13 +375,21 @@ LONG WinStatsStripChart::
 graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
   switch (msg) {
   case WM_LBUTTONDOWN:
-    {
+    if (_potential_drag_mode == DM_none) {
       _drag_mode = DM_scale;
       PN_int16 y = HIWORD(lparam);
       _drag_scale_start = pixel_to_height(y);
       SetCapture(_graph_window);
+      return 0;
+
+    } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
+      _drag_mode = DM_guide_bar;
+      PN_int16 y = HIWORD(lparam);
+      _drag_start_y = y;
+      SetCapture(_graph_window);
+      return 0;
     }
-    return 0;
+    break;
 
   case WM_MOUSEMOVE: 
     if (_drag_mode == DM_scale) {
@@ -347,6 +399,22 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
         set_vertical_scale(_drag_scale_start / ratio);
       }
       return 0;
+
+    } else if (_drag_mode == DM_new_guide_bar) {
+      // We haven't created the new guide bar yet; we won't until the
+      // mouse comes within the graph's region.
+      PN_int16 y = HIWORD(lparam);
+      if (y >= 0 && y < get_ysize()) {
+        _drag_mode = DM_guide_bar;
+        _drag_guide_bar = 
+          WinStatsGraph::_monitor->add_user_guide_bar(_thread_index, pixel_to_height(y));
+        return 0;
+      }
+
+    } else if (_drag_mode == DM_guide_bar) {
+      PN_int16 y = HIWORD(lparam);
+      WinStatsGraph::_monitor->move_user_guide_bar(_thread_index, _drag_guide_bar, pixel_to_height(y));
+      return 0;
     }
     break;
 
@@ -355,6 +423,17 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
       _drag_mode = DM_none;
       ReleaseCapture();
       return 0;
+
+    } else if (_drag_mode == DM_guide_bar) {
+      PN_int16 y = HIWORD(lparam);
+      if (y < 0 || y >= get_ysize()) {
+        WinStatsGraph::_monitor->remove_user_guide_bar(_thread_index, _drag_guide_bar);
+      } else {
+        WinStatsGraph::_monitor->move_user_guide_bar(_thread_index, _drag_guide_bar, pixel_to_height(y));
+      }
+      _drag_mode = DM_none;
+      ReleaseCapture();
+      return 0;
     }
     break;
 
@@ -375,24 +454,109 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 ////////////////////////////////////////////////////////////////////
 void WinStatsStripChart::
 additional_window_paint(HDC hdc) {
+  // Draw in the labels for the guide bars.
   HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); 
   SelectObject(hdc, hfnt);
   SetTextAlign(hdc, TA_LEFT | TA_TOP);
   SetBkMode(hdc, TRANSPARENT);
-  SetTextColor(hdc, RGB(0, 0, 0));
 
   RECT rect;
   GetClientRect(_window, &rect);
   int x = rect.right - _right_margin + 2;
   int last_y = -100;
 
+  int i;
   int num_guide_bars = get_num_guide_bars();
-  for (int i = 0; i < num_guide_bars; i++) {
-    const GuideBar &bar = get_guide_bar(i);
-    last_y = draw_guide_label(hdc, x, bar._height, last_y);
+  for (i = 0; i < num_guide_bars; i++) {
+    last_y = draw_guide_label(hdc, x, get_guide_bar(i), last_y);
   }
 
-  draw_guide_label(hdc, x, get_vertical_scale(), last_y);
+  last_y = -100;
+  int num_user_guide_bars = get_num_user_guide_bars();
+  for (i = 0; i < num_user_guide_bars; i++) {
+    last_y = draw_guide_label(hdc, x, get_user_guide_bar(i), last_y);
+  }
+
+  GuideBar top_value = make_guide_bar(get_vertical_scale());
+  draw_guide_label(hdc, x, top_value, last_y);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsStripChart::additional_graph_window_paint
+//       Access: Protected, Virtual
+//  Description: This is called during the servicing of WM_PAINT; it
+//               gives a derived class opportunity to do some further
+//               painting into the window (the outer window, not the
+//               graph window).
+////////////////////////////////////////////////////////////////////
+void WinStatsStripChart::
+additional_graph_window_paint(HDC hdc) {
+  int num_user_guide_bars = get_num_user_guide_bars();
+  for (int i = 0; i < num_user_guide_bars; i++) {
+    draw_guide_bar(hdc, 0, get_xsize(), get_user_guide_bar(i));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsStripChart::consider_drag_start
+//       Access: Protected, Virtual
+//  Description: Based on the mouse position within the window's
+//               client area, look for draggable things the mouse
+//               might be hovering over and return the apprioprate
+//               DragMode enum or DM_none if nothing is indicated.
+////////////////////////////////////////////////////////////////////
+WinStatsGraph::DragMode WinStatsStripChart::
+consider_drag_start(int mouse_x, int mouse_y, int width, int height) {
+  if (mouse_x >= _graph_left && mouse_x < _graph_left + get_xsize()) {
+    if (mouse_y >= _graph_top && mouse_y < _graph_top + get_ysize()) {
+      // See if the mouse is over a user-defined guide bar.
+      int y = mouse_y - _graph_top;
+      float from_height = pixel_to_height(y + 2);
+      float to_height = pixel_to_height(y - 2);
+      _drag_guide_bar = find_user_guide_bar(from_height, to_height);
+      if (_drag_guide_bar >= 0) {
+        return DM_guide_bar;
+      }
+
+    } else {
+      // The mouse is above or below the graph; maybe create a new
+      // guide bar.
+      return DM_new_guide_bar;
+    }
+  }
+
+  return WinStatsGraph::consider_drag_start(mouse_x, mouse_y, width, height);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsStripChart::draw_guide_bar
+//       Access: Private
+//  Description: Draws the line for the indicated guide bar on the
+//               graph.
+////////////////////////////////////////////////////////////////////
+void WinStatsStripChart::
+draw_guide_bar(HDC hdc, int from_x, int to_x, 
+               const PStatGraph::GuideBar &bar) {
+  int y = height_to_pixel(bar._height);
+
+  if (y > 0) {
+    // Only draw it if it's not too close to the top.
+    switch (bar._style) {
+    case GBS_target:
+      SelectObject(hdc, _light_pen);
+      break;
+
+    case GBS_user:
+      SelectObject(hdc, _user_guide_bar_pen);
+      break;
+      
+    case GBS_normal:
+      SelectObject(hdc, _dark_pen);
+      break;
+    }
+    MoveToEx(hdc, from_x, y, NULL);
+    LineTo(hdc, to_x + 1, y);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -404,15 +568,38 @@ additional_window_paint(HDC hdc) {
 //               given.  Returns the top pixel value of the new label.
 ////////////////////////////////////////////////////////////////////
 int WinStatsStripChart::
-draw_guide_label(HDC hdc, int x, float value, int last_y) {
-  int y = height_to_pixel(value);
-  string label = format_number(value, get_guide_bar_units(),
-                               get_guide_bar_unit_name());
+draw_guide_label(HDC hdc, int x, const PStatGraph::GuideBar &bar, int last_y) {
+  switch (bar._style) {
+  case GBS_target:
+    SetTextColor(hdc, _light_color);
+    break;
+    
+  case GBS_user:
+    SetTextColor(hdc, _user_guide_bar_color);
+    break;
+    
+  case GBS_normal:
+    SetTextColor(hdc, _dark_color);
+    break;
+  }
+
+  int y = height_to_pixel(bar._height);
+  const string &label = bar._label;
   SIZE size;
   GetTextExtentPoint32(hdc, label.data(), label.length(), &size);
 
+  if (bar._style != GBS_user) {
+    float from_height = pixel_to_height(y + size.cy / 2 + 1);
+    float to_height = pixel_to_height(y - size.cy / 2 - 1);
+    if (find_user_guide_bar(from_height, to_height) >= 0) {
+      // Omit the label: there's a user-defined guide bar in the same space.
+      return last_y;
+    }
+  }
+
   int this_y = _graph_top + y - size.cy / 2;
-  if (last_y < this_y || last_y > this_y + size.cy) {
+  if (y >= 0 && y < get_ysize() &&
+      (last_y < this_y || last_y > this_y + size.cy)) {
     TextOut(hdc, x, this_y,
             label.data(), label.length()); 
     last_y = this_y;

+ 9 - 1
pandatool/src/win-stats/winStatsStripChart.h

@@ -47,6 +47,10 @@ public:
   virtual void set_time_units(int unit_mask);
   void set_vertical_scale(float value_height);
 
+  virtual void move_user_guide_bar(int n, float height);
+  virtual int add_user_guide_bar(float height);
+  virtual void remove_user_guide_bar(int n);
+
 protected:
   virtual void update_labels();
 
@@ -60,9 +64,13 @@ protected:
   LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
   virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
   virtual void additional_window_paint(HDC hdc);
+  virtual void additional_graph_window_paint(HDC hdc);
+  virtual DragMode consider_drag_start(int mouse_x, int mouse_y, 
+                                       int width, int height);
 
 private:
-  int draw_guide_label(HDC hdc, int x, float value, int last_y);
+  void draw_guide_bar(HDC hdc, int from_x, int to_x, const GuideBar &bar);
+  int draw_guide_label(HDC hdc, int x, const GuideBar &bar, int last_y);
   void create_window();
   static void register_window_class(HINSTANCE application);