Browse Source

menu stuff

David Rose 22 years ago
parent
commit
5d8bb3dfab

+ 67 - 3
pandatool/src/pstatserver/pStatClientData.cxx

@@ -156,12 +156,31 @@ get_collector_fullname(int index) const {
 //  Description: Indicates whether the given collector has level data
 //  Description: Indicates whether the given collector has level data
 //               (and consequently, whether it should appear on the
 //               (and consequently, whether it should appear on the
 //               Levels menu).
 //               Levels menu).
+//
+//               The return value is true if anything changed, false
+//               otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void PStatClientData::
+bool PStatClientData::
 set_collector_has_level(int index, bool flag) {
 set_collector_has_level(int index, bool flag) {
+  bool any_changed = false;
   slot_collector(index);
   slot_collector(index);
-  nassertv(index >= 0 && index < (int)_collectors.size());
-  _collectors[index]._is_level = flag;
+  nassertr(index >= 0 && index < (int)_collectors.size(), false);
+
+  if (_collectors[index]._is_level != flag) {
+    any_changed = true;
+    _collectors[index]._is_level = flag;
+
+    // Turning this on for a given collector also implicitly turns all
+    // of its ancestors.
+    if (flag) {
+      PStatCollectorDef *def = _collectors[index]._def;
+      if (def->_parent_index != 0) {
+        set_collector_has_level(def->_parent_index, flag);
+      }
+    }
+  }
+
+  return any_changed;
 }
 }
 
 
 
 
@@ -178,6 +197,32 @@ get_collector_has_level(int index) const {
           _collectors[index]._is_level);
           _collectors[index]._is_level);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientData::get_num_toplevel_collectors
+//       Access: Public
+//  Description: Returns the total number of collectors that are
+//               toplevel collectors.  These are the collectors that
+//               are the children of "Frame", which is collector 0.
+////////////////////////////////////////////////////////////////////
+int PStatClientData::
+get_num_toplevel_collectors() const {
+  return _toplevel_collectors.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientData::get_toplevel_collector
+//       Access: Public
+//  Description: Returns the collector index of the nth toplevel
+//               collector.  Use this function to iterate through the
+//               n toplevel collectors indicated by
+//               get_num_toplevel_collectors().
+////////////////////////////////////////////////////////////////////
+int PStatClientData::
+get_toplevel_collector(int n) const {
+  nassertr(n >= 0 && n < (int)_toplevel_collectors.size(), 0);
+  return _toplevel_collectors[n];
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClientData::get_num_threads
 //     Function: PStatClientData::get_num_threads
 //       Access: Public
 //       Access: Public
@@ -278,6 +323,7 @@ add_collector(PStatCollectorDef *def) {
   }
   }
 
 
   _collectors[def->_index]._def = def;
   _collectors[def->_index]._def = def;
+  update_toplevel_collectors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -343,3 +389,21 @@ slot_collector(int collector_index) {
     _collectors.push_back(collector);
     _collectors.push_back(collector);
   }
   }
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientData::update_toplevel_collectors
+//       Access: Private
+//  Description: Rebuilds the list of toplevel collectors.
+////////////////////////////////////////////////////////////////////
+void PStatClientData::
+update_toplevel_collectors() {
+  _toplevel_collectors.clear();
+
+  Collectors::const_iterator ci;
+  for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
+    PStatCollectorDef *def = (*ci)._def;
+    if (def->_parent_index == 0) {
+      _toplevel_collectors.push_back(def->_index);
+    }
+  }
+}

+ 8 - 2
pandatool/src/pstatserver/pStatClientData.h

@@ -50,9 +50,12 @@ public:
   const PStatCollectorDef &get_collector_def(int index) const;
   const PStatCollectorDef &get_collector_def(int index) const;
   string get_collector_name(int index) const;
   string get_collector_name(int index) const;
   string get_collector_fullname(int index) const;
   string get_collector_fullname(int index) const;
-  void set_collector_has_level(int index, bool flag);
+  bool set_collector_has_level(int index, bool flag);
   bool get_collector_has_level(int index) const;
   bool get_collector_has_level(int index) const;
 
 
+  int get_num_toplevel_collectors() const;
+  int get_toplevel_collector(int index) const;
+
   int get_num_threads() const;
   int get_num_threads() const;
   bool has_thread(int index) const;
   bool has_thread(int index) const;
   string get_thread_name(int index) const;
   string get_thread_name(int index) const;
@@ -68,7 +71,7 @@ public:
                         PStatFrameData *frame_data);
                         PStatFrameData *frame_data);
 private:
 private:
   void slot_collector(int collector_index);
   void slot_collector(int collector_index);
-
+  void update_toplevel_collectors();
 
 
 private:
 private:
   bool _is_alive;
   bool _is_alive;
@@ -83,6 +86,9 @@ private:
   typedef pvector<Collector> Collectors;
   typedef pvector<Collector> Collectors;
   Collectors _collectors;
   Collectors _collectors;
 
 
+  typedef pvector<int> ToplevelCollectors;
+  ToplevelCollectors _toplevel_collectors;
+  
   class Thread {
   class Thread {
   public:
   public:
     string _name;
     string _name;

+ 1 - 0
pandatool/src/win-stats/Sources.pp

@@ -12,6 +12,7 @@
 
 
   #define SOURCES \
   #define SOURCES \
     winStats.cxx \
     winStats.cxx \
+    winStatsChartMenu.cxx winStatsChartMenu.h \
     winStatsGraph.cxx winStatsGraph.h \
     winStatsGraph.cxx winStatsGraph.h \
     winStatsLabel.cxx winStatsLabel.h \
     winStatsLabel.cxx winStatsLabel.h \
     winStatsLabelStack.cxx winStatsLabelStack.h \
     winStatsLabelStack.cxx winStatsLabelStack.h \

+ 185 - 0
pandatool/src/win-stats/winStatsChartMenu.cxx

@@ -0,0 +1,185 @@
+// Filename: winStatsChartMenu.cxx
+// Created by:  drose (08Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "winStatsChartMenu.h"
+#include "pStatMonitor.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsChartMenu::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WinStatsChartMenu::
+WinStatsChartMenu(PStatMonitor *monitor, int thread_index) :
+  _monitor(monitor),
+  _thread_index(thread_index)
+{
+  _menu = CreatePopupMenu();
+  do_update();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsChartMenu::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WinStatsChartMenu::
+~WinStatsChartMenu() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsChartMenu::get_menu_handle
+//       Access: Public
+//  Description: Returns the Windows menu handle for this particular
+//               menu.
+////////////////////////////////////////////////////////////////////
+HMENU WinStatsChartMenu::
+get_menu_handle() {
+  return _menu;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsChartMenu::add_to_menu_bar
+//       Access: Public
+//  Description: Adds the menu to the end of the indicated menu bar.
+////////////////////////////////////////////////////////////////////
+void WinStatsChartMenu::
+add_to_menu_bar(HMENU menu_bar) {
+  const PStatClientData *client_data = _monitor->get_client_data();
+  string thread_name = client_data->get_thread_name(_thread_index);
+
+  MENUITEMINFO mii;
+  memset(&mii, 0, sizeof(mii));
+  mii.cbSize = sizeof(mii);
+
+  mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU; 
+  mii.fType = MFT_STRING; 
+  mii.hSubMenu = _menu; 
+  mii.dwTypeData = (char *)thread_name.c_str(); 
+  InsertMenuItem(menu_bar, GetMenuItemCount(menu_bar), TRUE, &mii);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsChartMenu::check_update
+//       Access: Public
+//  Description: Checks to see if the menu needs to be updated
+//               (e.g. because of new data from the client), and
+//               updates it if necessary.
+////////////////////////////////////////////////////////////////////
+void WinStatsChartMenu::
+check_update() {
+  PStatView &view = _monitor->get_view(_thread_index);
+  if (view.get_level_index() != _last_level_index) {
+    do_update();
+  }
+}
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsChartMenu::do_update
+//       Access: Public
+//  Description: Unconditionally updates the menu with the latest data
+//               from the client.
+////////////////////////////////////////////////////////////////////
+void WinStatsChartMenu::
+do_update() {
+  PStatView &view = _monitor->get_view(_thread_index);
+  _last_level_index = view.get_level_index();
+
+  // First, remove all of the old entries from the menu.
+  int num_items = GetMenuItemCount(_menu);
+  for (int i = num_items - 1; i >= 0; i--) {
+    DeleteMenu(_menu, i, MF_BYPOSITION);
+  }
+
+  // Now rebuild the menu with the new set of entries.
+
+  // The menu item(s) for the thread's frame time goes first.
+  add_view(_menu, view.get_top_level());
+
+  bool needs_separator = true;
+
+  // And then the menu item(s) for each of the level values.
+  const PStatClientData *client_data = _monitor->get_client_data();
+  int num_toplevel_collectors = client_data->get_num_toplevel_collectors();
+  for (int tc = 0; tc < num_toplevel_collectors; tc++) {
+    int collector = client_data->get_toplevel_collector(tc);
+    if (client_data->has_collector(collector) && 
+        client_data->get_collector_has_level(collector)) {
+
+      // We put a separator between the above frame collector and the
+      // first level collector.
+      if (needs_separator) {
+        MENUITEMINFO mii;
+        memset(&mii, 0, sizeof(mii));
+        mii.cbSize = sizeof(mii);
+        mii.fMask = MIIM_FTYPE; 
+        mii.fType = MFT_SEPARATOR; 
+        InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
+
+        needs_separator = false;
+      }
+
+      PStatView &level_view = _monitor->get_level_view(collector, _thread_index);
+      add_view(_menu, level_view.get_top_level());
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsChartMenu::add_view
+//       Access: Private
+//  Description: Adds a new entry or entries to the menu for the
+//               indicated view and its children.
+////////////////////////////////////////////////////////////////////
+void WinStatsChartMenu::
+add_view(HMENU parent_menu, const PStatViewLevel *view_level) {
+  int collector = view_level->get_collector();
+
+  const PStatClientData *client_data = _monitor->get_client_data();
+  string collector_name = client_data->get_collector_name(collector);
+
+  MENUITEMINFO mii;
+  memset(&mii, 0, sizeof(mii));
+  mii.cbSize = sizeof(mii);
+
+  mii.fMask = MIIM_STRING | MIIM_FTYPE; 
+  mii.fType = MFT_STRING; 
+  mii.dwTypeData = (char *)collector_name.c_str(); 
+  InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii);
+
+  int num_children = view_level->get_num_children();
+  if (num_children != 0) {
+    // If the collector has any children, add a menu entry to go
+    // directly to each of its children.
+    HMENU submenu = CreatePopupMenu();
+    string submenu_name = collector_name + " components";
+
+    mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU; 
+    mii.fType = MFT_STRING; 
+    mii.hSubMenu = submenu; 
+    mii.dwTypeData = (char *)submenu_name.c_str(); 
+    InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii);
+
+    // Reverse the order since the menus are listed from the top down;
+    // we want to be visually consistent with the graphs, which list
+    // these labels from the bottom up.
+    for (int c = num_children - 1; c >= 0; c--) {
+      add_view(submenu, view_level->get_child(c));
+    }
+  }
+}

+ 57 - 0
pandatool/src/win-stats/winStatsChartMenu.h

@@ -0,0 +1,57 @@
+// Filename: winStatsChartMenu.h
+// Created by:  drose (08Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WINSTATSCHARTMENU_H
+#define WINSTATSCHARTMENU_H
+
+#include "pandatoolbase.h"
+
+#include <windows.h>
+
+class PStatMonitor;
+class PStatView;
+class PStatViewLevel;
+
+////////////////////////////////////////////////////////////////////
+//       Class : WinStatsChartMenu
+// Description : A pulldown menu of charts available for a particular
+//               thread.
+////////////////////////////////////////////////////////////////////
+class WinStatsChartMenu {
+public:
+  WinStatsChartMenu(PStatMonitor *monitor, int thread_index);
+  ~WinStatsChartMenu();
+
+  HMENU get_menu_handle();
+  void add_to_menu_bar(HMENU menu_bar);
+
+  void check_update();
+  void do_update();
+
+private:
+  void add_view(HMENU parent_menu, const PStatViewLevel *view_level);
+
+  PStatMonitor *_monitor;
+  int _thread_index;
+
+  int _last_level_index;
+  HMENU _menu;
+};
+
+#endif
+

+ 284 - 0
pandatool/src/win-stats/winStatsLabel.cxx

@@ -0,0 +1,284 @@
+// Filename: winStatsLabel.cxx
+// Created by:  drose (07Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "winStatsLabel.h"
+#include "winStatsMonitor.h"
+
+int WinStatsLabel::_left_margin = 2;
+int WinStatsLabel::_right_margin = 2;
+int WinStatsLabel::_top_margin = 2;
+int WinStatsLabel::_bottom_margin = 2;
+
+bool WinStatsLabel::_window_class_registered = false;
+const char * const WinStatsLabel::_window_class_name = "label";
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WinStatsLabel::
+WinStatsLabel(WinStatsMonitor *monitor, int collector_index) :
+  _collector_index(collector_index)
+{
+  _window = 0;
+  _text = monitor->get_client_data()->get_collector_name(_collector_index);
+
+  RGBColorf rgb = monitor->get_collector_color(_collector_index);
+  int r = (int)(rgb[0] * 255.0f);
+  int g = (int)(rgb[1] * 255.0f);
+  int b = (int)(rgb[2] * 255.0f);
+  _bg_color = RGB(r, g, b);
+  _bg_brush = CreateSolidBrush(RGB(r, g, b));
+
+  // Should our foreground be black or white?
+  float bright =
+    rgb[0] * 0.299 +
+    rgb[1] * 0.587 +
+    rgb[2] * 0.114;
+
+  if (bright >= 0.5) {
+    _fg_color = RGB(0, 0, 0);
+  } else {
+    _fg_color = RGB(255, 255, 255);
+  }
+
+  _x = 0;
+  _y = 0;
+  _width = 0;
+  _height = 0;
+  _ideal_width = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WinStatsLabel::
+~WinStatsLabel() {
+  if (_window) {
+    DestroyWindow(_window);
+    _window = 0;
+  }
+  DeleteObject(_bg_brush);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::setup
+//       Access: Public
+//  Description: Creates the actual window.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabel::
+setup(HWND parent_window) {
+  if (_window) {
+    DestroyWindow(_window);
+    _window = 0;
+  }
+
+  create_window(parent_window);
+
+  HDC hdc = GetDC(_window);
+  HGDIOBJ hfnt = GetStockObject(ANSI_VAR_FONT); 
+  SelectObject(hdc, hfnt);
+
+  SIZE size;
+  GetTextExtentPoint32(hdc, _text.data(), _text.length(), &size);
+  _height = size.cy + _top_margin + _bottom_margin;
+  _ideal_width = size.cx + _left_margin + _right_margin;
+
+  ReleaseDC(_window, hdc);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::set_pos
+//       Access: Public
+//  Description: Sets the position of the label on its parent.  The
+//               position describes the lower-left corner of the
+//               rectangle, not the upper-left.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabel::
+set_pos(int x, int y, int width) {
+  _x = x;
+  _y = y;
+  _width = width;
+  SetWindowPos(_window, 0, x, y - _height, _width, _height, 
+               SWP_NOZORDER | SWP_SHOWWINDOW);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::get_x
+//       Access: Public
+//  Description: Returns the x position of the label on its parent.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabel::
+get_x() const {
+  return _x;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::get_y
+//       Access: Public
+//  Description: Returns the y position of the label on its parent.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabel::
+get_y() const {
+  return _y;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::get_width
+//       Access: Public
+//  Description: Returns the width of the label as we requested it.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabel::
+get_width() const {
+  return _width;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::get_height
+//       Access: Public
+//  Description: Returns the height of the label as we requested it.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabel::
+get_height() const {
+  return _height;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::get_ideal_width
+//       Access: Public
+//  Description: Returns the width the label would really prefer to be.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabel::
+get_ideal_width() const {
+  return _ideal_width;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::create_window
+//       Access: Private
+//  Description: Creates the window for this label.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabel::
+create_window(HWND parent_window) {
+  if (_window) {
+    return;
+  }
+
+  HINSTANCE application = GetModuleHandle(NULL);
+  register_window_class(application);
+
+  _window = 
+    CreateWindow(_window_class_name, _text.c_str(), WS_CHILD,
+                 0, 0, 0, 0,
+                 parent_window, NULL, application, 0);
+  if (!_window) {
+    nout << "Could not create Label window!\n";
+    exit(1);
+  }
+
+  SetWindowLongPtr(_window, 0, (LONG_PTR)this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::register_window_class
+//       Access: Private, Static
+//  Description: Registers the window class for the label window, if
+//               it has not already been registered.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabel::
+register_window_class(HINSTANCE application) {
+  if (_window_class_registered) {
+    return;
+  }
+
+  WNDCLASS wc;
+
+  ZeroMemory(&wc, sizeof(WNDCLASS));
+  wc.style = 0;
+  wc.lpfnWndProc = (WNDPROC)static_window_proc;
+  wc.hInstance = application;
+  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+  wc.hbrBackground = NULL;
+  wc.lpszMenuName = NULL;
+  wc.lpszClassName = _window_class_name;
+
+  // Reserve space to associate the this pointer with the window.
+  wc.cbWndExtra = sizeof(WinStatsLabel *);
+  
+  if (!RegisterClass(&wc)) {
+    nout << "Could not register Label window class!\n";
+    exit(1);
+  }
+
+  _window_class_registered = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::static_window_proc
+//       Access: Private, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LONG WINAPI WinStatsLabel::
+static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+  WinStatsLabel *self = (WinStatsLabel *)GetWindowLongPtr(hwnd, 0);
+  if (self != (WinStatsLabel *)NULL && self->_window == hwnd) {
+    return self->window_proc(hwnd, msg, wparam, lparam);
+  } else {
+    return DefWindowProc(hwnd, msg, wparam, lparam);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabel::window_proc
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LONG WinStatsLabel::
+window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+  switch (msg) {
+  case WM_PAINT:
+    {
+      PAINTSTRUCT ps;
+      HDC hdc = BeginPaint(hwnd, &ps);
+
+      RECT rect = { 0, 0, _width, _height };
+      FillRect(hdc, &rect, _bg_brush);
+
+      HGDIOBJ hfnt = GetStockObject(ANSI_VAR_FONT); 
+      SelectObject(hdc, hfnt);
+      SetTextAlign(hdc, TA_RIGHT | TA_TOP);
+
+      SetBkColor(hdc, _bg_color);
+      SetBkMode(hdc, OPAQUE);
+      SetTextColor(hdc, _fg_color);
+
+      TextOut(hdc, _width - _right_margin, _top_margin,
+              _text.data(), _text.length()); 
+      EndPaint(hwnd, &ps);
+      return 0;
+    }
+
+  default:
+    break;
+  }
+
+  return DefWindowProc(hwnd, msg, wparam, lparam);
+}

+ 77 - 0
pandatool/src/win-stats/winStatsLabel.h

@@ -0,0 +1,77 @@
+// Filename: winStatsLabel.h
+// Created by:  drose (07Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WINSTATSLABEL_H
+#define WINSTATSLABEL_H
+
+#include "pandatoolbase.h"
+
+#include <windows.h>
+
+class WinStatsMonitor;
+
+////////////////////////////////////////////////////////////////////
+//       Class : WinStatsLabel
+// Description : A text label that will draw in color appropriate for
+//               a particular collector.  It also responds when the
+//               user double-clicks on it.  This is handy for putting
+//               colored labels on strip charts.
+////////////////////////////////////////////////////////////////////
+class WinStatsLabel {
+public:
+  WinStatsLabel(WinStatsMonitor *monitor, int collector_index);
+  ~WinStatsLabel();
+
+  void setup(HWND parent_window);
+  void set_pos(int x, int y, int width);
+
+  int get_x() const;
+  int get_y() const;
+  int get_width() const;
+  int get_height() const;
+  int get_ideal_width() const;
+
+private:
+  void create_window(HWND parent_window);
+  static void register_window_class(HINSTANCE application);
+
+  static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+  LONG WINAPI window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+
+  int _collector_index;
+  string _text;
+  HWND _window;
+  COLORREF _bg_color;
+  COLORREF _fg_color;
+  HBRUSH _bg_brush;
+
+  int _x;
+  int _y;
+  int _width;
+  int _height;
+  int _ideal_width;
+
+  static int _left_margin, _right_margin;
+  static int _top_margin, _bottom_margin;
+
+  static bool _window_class_registered;
+  static const char * const _window_class_name;
+};
+
+#endif
+

+ 282 - 0
pandatool/src/win-stats/winStatsLabelStack.cxx

@@ -0,0 +1,282 @@
+// Filename: winStatsLabelStack.cxx
+// Created by:  drose (07Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "winStatsLabelStack.h"
+#include "winStatsLabel.h"
+
+bool WinStatsLabelStack::_window_class_registered = false;
+const char * const WinStatsLabelStack::_window_class_name = "stack";
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WinStatsLabelStack::
+WinStatsLabelStack() {
+  _window = 0;
+
+  _x = 0;
+  _y = 0;
+  _width = 0;
+  _height = 0;
+  _ideal_width = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WinStatsLabelStack::
+~WinStatsLabelStack() {
+  clear_labels();
+  if (_window) {
+    DestroyWindow(_window);
+    _window = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::setup
+//       Access: Public
+//  Description: Creates the actual window object.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabelStack::
+setup(HWND parent_window) {
+  if (_window) {
+    DestroyWindow(_window);
+    _window = 0;
+  }
+
+  create_window(parent_window);
+
+  _ideal_width = 0;
+  Labels::iterator li;
+  for (li = _labels.begin(); li != _labels.end(); ++li) {
+    WinStatsLabel *label = (*li);
+    label->setup(_window);
+    _ideal_width = max(_ideal_width, label->get_ideal_width());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::is_setup
+//       Access: Public
+//  Description: Returns true if the label stack has been set up,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool WinStatsLabelStack::
+is_setup() const {
+  return (_window != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::set_pos
+//       Access: Public
+//  Description: Sets the position and size of the label stack on its parent.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabelStack::
+set_pos(int x, int y, int width, int height) {
+  _x = x;
+  _y = y;
+  _width = width;
+  _height = height;
+  SetWindowPos(_window, 0, x, y, _width, _height, 
+               SWP_NOZORDER | SWP_SHOWWINDOW);
+  
+  Labels::iterator li;
+  int yp = height;
+  for (li = _labels.begin(); li != _labels.end(); ++li) {
+    WinStatsLabel *label = (*li);
+    label->set_pos(0, yp, _width);
+    yp -= label->get_height();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::get_x
+//       Access: Public
+//  Description: Returns the x position of the stack on its parent.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabelStack::
+get_x() const {
+  return _x;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::get_y
+//       Access: Public
+//  Description: Returns the y position of the stack on its parent.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabelStack::
+get_y() const {
+  return _y;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::get_width
+//       Access: Public
+//  Description: Returns the width of the stack as we requested it.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabelStack::
+get_width() const {
+  return _width;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::get_height
+//       Access: Public
+//  Description: Returns the height of the stack as we requested it.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabelStack::
+get_height() const {
+  return _height;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::get_ideal_width
+//       Access: Public
+//  Description: Returns the width the stack would really prefer to be.
+////////////////////////////////////////////////////////////////////
+int WinStatsLabelStack::
+get_ideal_width() const {
+  return _ideal_width;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::clear_labels
+//       Access: Public
+//  Description: Removes the set of labels and starts a new set.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabelStack::
+clear_labels() {
+  Labels::iterator li;
+  for (li = _labels.begin(); li != _labels.end(); ++li) {
+    delete (*li);
+  }
+  _labels.clear();
+  _ideal_width = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::add_label
+//       Access: Public
+//  Description: Adds a new label to the top of the stack.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabelStack::
+add_label(WinStatsMonitor *monitor, int collector_index) {
+  int yp = _height;
+  if (!_labels.empty()) {
+    WinStatsLabel *top_label = _labels.back();
+    yp = top_label->get_y() - top_label->get_height();
+  }
+  WinStatsLabel *label = new WinStatsLabel(monitor, collector_index);
+  if (_window) {
+    label->setup(_window);
+    label->set_pos(0, yp, _width);
+  }
+  _ideal_width = max(_ideal_width, label->get_ideal_width());
+  _labels.push_back(label);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::create_window
+//       Access: Private
+//  Description: Creates the window for this stack.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabelStack::
+create_window(HWND parent_window) {
+  if (_window) {
+    return;
+  }
+
+  HINSTANCE application = GetModuleHandle(NULL);
+  register_window_class(application);
+
+  _window = 
+    CreateWindow(_window_class_name, "label stack", WS_CHILD | WS_CLIPCHILDREN,
+                 0, 0, 0, 0,
+                 parent_window, NULL, application, 0);
+  if (!_window) {
+    nout << "Could not create Label Stack window!\n";
+    exit(1);
+  }
+
+  SetWindowLongPtr(_window, 0, (LONG_PTR)this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::register_window_class
+//       Access: Private, Static
+//  Description: Registers the window class for the label window, if
+//               it has not already been registered.
+////////////////////////////////////////////////////////////////////
+void WinStatsLabelStack::
+register_window_class(HINSTANCE application) {
+  if (_window_class_registered) {
+    return;
+  }
+
+  WNDCLASS wc;
+
+  ZeroMemory(&wc, sizeof(WNDCLASS));
+  wc.style = 0;
+  wc.lpfnWndProc = (WNDPROC)static_window_proc;
+  wc.hInstance = application;
+  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+  wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
+  wc.lpszMenuName = NULL;
+  wc.lpszClassName = _window_class_name;
+
+  // Reserve space to associate the this pointer with the window.
+  wc.cbWndExtra = sizeof(WinStatsLabelStack *);
+  
+  if (!RegisterClass(&wc)) {
+    nout << "Could not register Label Stack window class!\n";
+    exit(1);
+  }
+
+  _window_class_registered = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::static_window_proc
+//       Access: Private, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LONG WINAPI WinStatsLabelStack::
+static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+  WinStatsLabelStack *self = (WinStatsLabelStack *)GetWindowLongPtr(hwnd, 0);
+  if (self != (WinStatsLabelStack *)NULL && self->_window == hwnd) {
+    return self->window_proc(hwnd, msg, wparam, lparam);
+  } else {
+    return DefWindowProc(hwnd, msg, wparam, lparam);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsLabelStack::window_proc
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LONG WinStatsLabelStack::
+window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+  return DefWindowProc(hwnd, msg, wparam, lparam);
+}

+ 75 - 0
pandatool/src/win-stats/winStatsLabelStack.h

@@ -0,0 +1,75 @@
+// Filename: winStatsLabelStack.h
+// Created by:  drose (07Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WINSTATSLABELSTACK_H
+#define WINSTATSLABELSTACK_H
+
+#include "pandatoolbase.h"
+#include "pvector.h"
+
+#include <windows.h>
+
+class WinStatsLabel;
+class WinStatsMonitor;
+
+////////////////////////////////////////////////////////////////////
+//       Class : WinStatsLabelStack
+// Description : A window that contains a stack of labels from bottom
+//               to top.
+////////////////////////////////////////////////////////////////////
+class WinStatsLabelStack {
+public:
+  WinStatsLabelStack();
+  ~WinStatsLabelStack();
+
+  void setup(HWND parent_window);
+  bool is_setup() const;
+  void set_pos(int x, int y, int width, int height);
+
+  int get_x() const;
+  int get_y() const;
+  int get_width() const;
+  int get_height() const;
+  int get_ideal_width() const;
+
+  void clear_labels();
+  void add_label(WinStatsMonitor *monitor, int collector_index);
+
+private:
+  void create_window(HWND parent_window);
+  static void register_window_class(HINSTANCE application);
+
+  static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+  LONG WINAPI window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+
+  HWND _window;
+  int _x;
+  int _y;
+  int _width;
+  int _height;
+  int _ideal_width;
+
+  typedef pvector<WinStatsLabel *> Labels;
+  Labels _labels;
+
+  static bool _window_class_registered;
+  static const char * const _window_class_name;
+};
+
+#endif
+

+ 46 - 1
pandatool/src/win-stats/winStatsMonitor.cxx

@@ -18,8 +18,10 @@
 
 
 #include "winStatsMonitor.h"
 #include "winStatsMonitor.h"
 #include "winStatsStripChart.h"
 #include "winStatsStripChart.h"
+#include "winStatsChartMenu.h"
 
 
 #include "pStatCollectorDef.h"
 #include "pStatCollectorDef.h"
+#include "indent.h"
 
 
 bool WinStatsMonitor::_window_class_registered = false;
 bool WinStatsMonitor::_window_class_registered = false;
 const char * const WinStatsMonitor::_window_class_name = "monitor";
 const char * const WinStatsMonitor::_window_class_name = "monitor";
@@ -32,6 +34,7 @@ const char * const WinStatsMonitor::_window_class_name = "monitor";
 WinStatsMonitor::
 WinStatsMonitor::
 WinStatsMonitor() {
 WinStatsMonitor() {
   _window = 0;
   _window = 0;
+  _menu_bar = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -48,6 +51,12 @@ WinStatsMonitor::
   }
   }
   _graphs.clear();
   _graphs.clear();
 
 
+  ChartMenus::iterator mi;
+  for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
+    delete (*mi);
+  }
+  _chart_menus.clear();
+
   if (_window) {
   if (_window) {
     DestroyWindow(_window);
     DestroyWindow(_window);
     _window = 0;
     _window = 0;
@@ -135,6 +144,30 @@ new_collector(int collector_index) {
     WinStatsGraph *graph = (*gi);
     WinStatsGraph *graph = (*gi);
     graph->new_collector(collector_index);
     graph->new_collector(collector_index);
   }
   }
+
+  // We might need to update our menus.
+  ChartMenus::iterator mi;
+  for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
+    (*mi)->do_update();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsMonitor::new_thread
+//       Access: Public, Virtual
+//  Description: Called whenever a new Thread definition is
+//               received from the client.  Generally, the client will
+//               send all of its threads over shortly after
+//               connecting, but there's no guarantee that they will
+//               all be received before the first frames are received.
+//               The monitor should be prepared to accept new Thread
+//               definitions midstream.
+////////////////////////////////////////////////////////////////////
+void WinStatsMonitor::
+new_thread(int thread_index) {
+  WinStatsChartMenu *chart_menu = new WinStatsChartMenu(this, thread_index);
+  chart_menu->add_to_menu_bar(_menu_bar);
+  _chart_menus.push_back(chart_menu);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -184,6 +217,11 @@ lost_connection() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void WinStatsMonitor::
 void WinStatsMonitor::
 idle() {
 idle() {
+  // Check if any of our chart menus need updating.
+  ChartMenus::iterator mi;
+  for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
+    (*mi)->check_update();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -246,6 +284,13 @@ create_window() {
   HINSTANCE application = GetModuleHandle(NULL);
   HINSTANCE application = GetModuleHandle(NULL);
   register_window_class(application);
   register_window_class(application);
 
 
+  _menu_bar = CreateMenu();
+
+  ChartMenus::iterator mi;
+  for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
+    (*mi)->add_to_menu_bar(_menu_bar);
+  }
+
   _window_title = get_client_progname() + " on " + get_client_hostname();
   _window_title = get_client_progname() + " on " + get_client_hostname();
   DWORD window_style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | 
   DWORD window_style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | 
     WS_CLIPSIBLINGS | WS_VISIBLE;
     WS_CLIPSIBLINGS | WS_VISIBLE;
@@ -253,7 +298,7 @@ create_window() {
   _window = 
   _window = 
     CreateWindow(_window_class_name, _window_title.c_str(), window_style,
     CreateWindow(_window_class_name, _window_title.c_str(), window_style,
                  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
-                 NULL, NULL, application, 0);
+                 NULL, _menu_bar, application, 0);
   if (!_window) {
   if (!_window) {
     nout << "Could not create monitor window!\n";
     nout << "Could not create monitor window!\n";
     exit(1);
     exit(1);

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

@@ -28,6 +28,8 @@
 
 
 #include <windows.h>
 #include <windows.h>
 
 
+class WinStatsChartMenu;
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : WinStatsMonitor
 //       Class : WinStatsMonitor
 // Description : This class represents a connection to a PStatsClient
 // Description : This class represents a connection to a PStatsClient
@@ -45,6 +47,7 @@ public:
   virtual void got_bad_version(int client_major, int client_minor,
   virtual void got_bad_version(int client_major, int client_minor,
                                int server_major, int server_minor);
                                int server_major, int server_minor);
   virtual void new_collector(int collector_index);
   virtual void new_collector(int collector_index);
+  virtual void new_thread(int thread_index);
   virtual void new_data(int thread_index, int frame_number);
   virtual void new_data(int thread_index, int frame_number);
   virtual void lost_connection();
   virtual void lost_connection();
   virtual void idle();
   virtual void idle();
@@ -65,7 +68,11 @@ private:
   typedef pset<WinStatsGraph *> Graphs;
   typedef pset<WinStatsGraph *> Graphs;
   Graphs _graphs;
   Graphs _graphs;
 
 
+  typedef pvector<WinStatsChartMenu *> ChartMenus;
+  ChartMenus _chart_menus;
+
   HWND _window;
   HWND _window;
+  HMENU _menu_bar;
   string _window_title;
   string _window_title;
 
 
   static bool _window_class_registered;
   static bool _window_class_registered;

+ 2 - 0
pandatool/src/win-stats/winStatsStripChart.cxx

@@ -337,10 +337,12 @@ static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 LONG WinStatsStripChart::
 LONG WinStatsStripChart::
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+  /*
   switch (msg) {
   switch (msg) {
   default:
   default:
     break;
     break;
   }
   }
+  */
 
 
   return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam);
   return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam);
 }
 }