/**********************************************************************

  Audacity: A Digital Audio Editor

  KeyView.cpp

*******************************************************************//*!

\class KeyView
\brief Provides multiple views of keyboard shortcuts

*//*********************************************************************/

#include "../Audacity.h"
#include "KeyView.h"

#include <wx/setup.h> // for wxUSE_* macros
#include <wx/defs.h>
#include <wx/settings.h>
#include <wx/vlbox.h>

#include "../AColor.h"
#include "../ShuttleGui.h"
#include "../commands/CommandManager.h"

#include <wx/dc.h>
#include <wx/menu.h>

#if wxUSE_ACCESSIBILITY
#include "WindowAccessible.h"

// ----------------------------------------------------------------------------
// KeyViewAx
//
// wxAccessible object providing information for KeyView.
// ----------------------------------------------------------------------------

class KeyViewAx final : public WindowAccessible
{
public:

   KeyViewAx(KeyView *view);

   void SetCurrentLine(int row);
   void ListUpdated();
   bool LineToId(int line, int & childId);
   bool IdToLine(int childId, int & line);

   // Can return either a child object, or an integer
   // representing the child element, starting from 1.
   wxAccStatus HitTest(const wxPoint & pt, int *childId, wxAccessible **childObject) override;

   // Retrieves the address of an IDispatch interface for the specified child.
   // All objects must support this property.
   wxAccStatus GetChild(int childId, wxAccessible **child) override;

   // Gets the number of children.
   wxAccStatus GetChildCount(int *childCount) override;

   // Gets the default action for this object (0) or > 0 (the action for a child).
   // Return wxACC_OK even if there is no action. actionName is the action, or the empty
   // string if there is no action.
   // The retrieved string describes the action that is performed on an object,
   // not what the object does as a result. For example, a toolbar button that prints
   // a document has a default action of "Press" rather than "Prints the current document."
   wxAccStatus GetDefaultAction(int childId, wxString *actionName) override;

   // Returns the description for this object or a child.
   wxAccStatus GetDescription(int childId, wxString *description) override;

   // Gets the window with the keyboard focus.
   // If childId is 0 and child is NULL, no object in
   // this subhierarchy has the focus.
   // If this object has the focus, child should be 'this'.
   wxAccStatus GetFocus(int *childId, wxAccessible **child) override;

   // Returns help text for this object or a child, similar to tooltip text.
   wxAccStatus GetHelpText(int childId, wxString *helpText) override;

   // Returns the keyboard shortcut for this object or child.
   // Return e.g. ALT+K
   wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override;

   // Returns the rectangle for this object (id = 0) or a child element (id > 0).
   // rect is in screen coordinates.
   wxAccStatus GetLocation(wxRect & rect, int elementId) override;

   // Navigates from fromId to toId/toObject.
   wxAccStatus Navigate(wxNavDir navDir, int fromId,
                                int *toId, wxAccessible **toObject) override;

   // Gets the name of the specified object.
   wxAccStatus GetName(int childId, wxString *name) override;

   // Gets the parent, or NULL.
   wxAccStatus GetParent(wxAccessible **parent) override;

   // Returns a role constant.
   wxAccStatus GetRole(int childId, wxAccRole *role) override;

   // Gets a variant representing the selected children
   // of this object.
   // Acceptable values:
   // - a null variant (IsNull() returns TRUE)
   // - a list variant (GetType() == wxT("list"))
   // - an integer representing the selected child element,
   //   or 0 if this object is selected (GetType() == wxT("long"))
   // - a "void*" pointer to a wxAccessible child object
   wxAccStatus GetSelections(wxVariant *selections) override;

   // Returns a state constant.
   wxAccStatus GetState(int childId, long *state) override;

   // Returns a localized string representing the value for the object
   // or child.
   wxAccStatus GetValue(int childId, wxString *strValue) override;

#if defined(__WXMAC__)
   // Selects the object or child.
   wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags) override;
#endif

private:
   KeyView *mView;
   int mLastId;
};

#endif

// Various drawing constants
#define KV_BITMAP_SIZE 16
#define KV_LEFT_MARGIN 2
#define KV_COLUMN_SPACER 5
#define KV_VSCROLL_WIDTH 16   /* figure this out automatically? */

// Define the KeyNode arrays

// Define the event table
BEGIN_EVENT_TABLE(KeyView, wxVListBox)
   EVT_LEFT_DOWN(KeyView::OnLeftDown)
   EVT_KEY_DOWN(KeyView::OnKeyDown)
   EVT_LISTBOX(wxID_ANY, KeyView::OnSelected)
   EVT_SET_FOCUS(KeyView::OnSetFocus)
   EVT_KILL_FOCUS(KeyView::OnKillFocus)
   EVT_SIZE(KeyView::OnSize)
   EVT_SCROLLWIN(KeyView::OnScroll)
END_EVENT_TABLE();

wxString    KeyView::CommandTranslated="Command";


// ============================================================================
// KeyView class
// ============================================================================
KeyView::KeyView(wxWindow *parent,
                 wxWindowID id,
                 const wxPoint & pos,
                 const wxSize & size)
: wxVListBox(parent, id, pos, size, wxBORDER_THEME | wxHSCROLL | wxVSCROLL),
  mScrollX(0),
  mWidth(0)
{
#if wxUSE_ACCESSIBILITY
   // Create and set accessibility object
   SetAccessible(mAx = safenew KeyViewAx(this));
#endif

   SetMinSize({-1, 150});

   // The default view
   mViewType = ViewByTree;

   // Calculate measurements used for columns and scrolling
   RecalcExtents();
}

KeyView::~KeyView()
{
}

//
// Returns the index of the selected node
//
int
KeyView::GetSelected() const
{
   return LineToIndex(GetSelection());
}

//
// Returns the name of the control
//
wxString
KeyView::GetName() const
{
   // Just forward request
   return wxVListBox::GetName();
}

//
// Returns the label for the given index
//
wxString
KeyView::GetLabel(int index) const
{
   // Make sure index is valid
   if (index < 0 || index >= (int) mNodes.size())
   {
      wxASSERT(false);
      return wxEmptyString;
   }

   return mNodes[index].label;
}

//
// Returns the prefix (if available) prepended to the label for the given index
//
wxString
KeyView::GetFullLabel(int index) const
{
   // Make sure index is valid
   if (index < 0 || index >= (int) mNodes.size())
   {
      wxASSERT(false);
      return wxEmptyString;
   }

   // Cache the node and label
   const KeyNode & node = mNodes[index];
   wxString label = node.label;

   // Prepend the prefix if available
   if (!node.prefix.empty())
   {
      label = node.prefix + wxT(" - ") + label;
   }

   return label;
}

//
// Returns the index for the given name
//
int
KeyView::GetIndexByName(const CommandID & name) const
{
   int cnt = (int) mNodes.size();

   // Search the nodes for the key
   for (int i = 0; i < cnt; i++)
   {
      if (name == mNodes[i].name)
      {
         return mNodes[i].index;
      }
   }

   return wxNOT_FOUND;
}

//
// Returns the command manager name for the given index
//
CommandID
KeyView::GetName(int index) const
{
   // Make sure index is valid
   if (index < 0 || index >= (int) mNodes.size())
   {
      wxASSERT(false);
      return {};
   }

   return mNodes[index].name;
}

//
// Returns the command manager index for the given key combination
//
CommandID
KeyView::GetNameByKey(const NormalizedKeyString & key) const
{
   int cnt = (int) mNodes.size();

   // Search the nodes for the key
   for (int i = 0; i < cnt; i++)
   {
      if ( key == mNodes[i].key )
      {
         return mNodes[i].name;
      }
   }

   return {};
}

//
// Returns the index for the given key
//
int
KeyView::GetIndexByKey(const NormalizedKeyString & key) const
{
   int cnt = (int) mNodes.size();

   // Search the nodes for the key
   for (int i = 0; i < cnt; i++)
   {
      if ( key == mNodes[i].key )
      {
         return mNodes[i].index;
      }
   }

   return wxNOT_FOUND;
}

//
// Returns the key for the given index
//
NormalizedKeyString
KeyView::GetKey(int index) const
{
   // Make sure index is valid
   if (index < 0 || index >= (int) mNodes.size())
   {
      wxASSERT(false);
      return {};
   }

   return mNodes[index].key;
}

//
// Use to determine if a key can be assigned to the given index
//
bool
KeyView::CanSetKey(int index) const
{
   // Make sure index is valid
   if (index < 0 || index >= (int) mNodes.size())
   {
      wxASSERT(false);
      return false;
   }

   // Parents can't be assigned keys
   return !mNodes[index].isparent;
}

//
// Sets the key for the given index
//
bool
KeyView::SetKey(int index, const NormalizedKeyString & key)
{
   // Make sure index is valid
   if (index < 0 || index >= (int) mNodes.size())
   {
      wxASSERT(false);
      return false;
   }

   // Cache the node
   KeyNode & node = mNodes[index];

   // Do not allow setting keys on branches
   if (node.isparent)
   {
      return false;
   }

   // Set the NEW key
   node.key = key;

   // Check to see if the key column needs to be expanded
   int x, y;
   GetTextExtent(node.key.Display(), &x, &y);
   if (x > mKeyWidth || y > mLineHeight)
   {
      // New key is wider than column so recalc extents (will refresh view)
      RecalcExtents();
      return true;
   }

   // Refresh the view lines
   RefreshAll();

   return true;
}

//
// Sets the key for the given name
//
bool
KeyView::SetKeyByName(const CommandID & name, const NormalizedKeyString & key)
{
   int index = GetIndexByName(name);

   // Bail is the name wasn't found
   if (index == wxNOT_FOUND)
   {
      return false;
   }

   // Go set the key
   return SetKey(index, key);
}

//
// Sets the view type
//
void
KeyView::SetView(ViewByType type)
{
   int index = LineToIndex(GetSelection());

   // Handle an existing selection
   if (index != wxNOT_FOUND)
   {
      // Cache the currently selected node
      KeyNode & node = mNodes[index];

      // Expand branches if switching to Tree view and a line
      // is currently selected
      if (type == ViewByTree)
      {
         // Cache the node's depth
         int depth = node.depth;

         // Search for its parents, setting each one as open
         for (int i = node.index - 1; i >= 0 && depth > 1; i--)
         {
            if (mNodes[i].depth < depth)
            {
               mNodes[i].isopen = true;
               depth = mNodes[i].depth;
            }
         }
      }
   }

   // Unselect any currently selected line...do even if none selected
   SelectNode(-1);

   // Save NEW type
   mViewType = type;

   // Refresh the view lines
   RefreshLines();

   // Reselect old node (if possible)
   if (index != wxNOT_FOUND)
   {
      SelectNode(index);
   }

   // ensure that a node is selected so that when the keyview is the focus,
   // this is indicated visually, and the Narrator screen reader reads it.
   if ((GetSelection() == wxNOT_FOUND))
   {
      SelectNode(LineToIndex(0));
   }

   return;
}

//
// Sets the filter
//
void
KeyView::SetFilter(const wxString & filter)
{
   int index = LineToIndex(GetSelection());

   // Unselect any currently selected line...do even if none selected
   SelectNode(-1);

   // Save the filter
   mFilter = filter.Lower();

   // Refresh the view lines
   RefreshLines();

   // Reselect old node (if possible)
   if (index != wxNOT_FOUND)
   {
      SelectNode(index);
   }

   // ensure that a node is selected so that when the keyview is the focus,
   // this is indicated visually, and the Narrator screen reader reads it.
   if ((GetSelection() == wxNOT_FOUND))
   {
      SelectNode(LineToIndex(0));
   }
}

//
// Expand all branches
//
void
KeyView::ExpandAll()
{
   int cnt = (int) mNodes.size();

   // Set all parent nodes to open
   for (int i = 0; i < cnt; i++)
   {
      KeyNode & node = mNodes[i];

      if (node.isparent)
      {
         node.isopen = true;
      }
   }

   RefreshLines();
}

//
// Collapse all branches
//
void
KeyView::CollapseAll()
{
   int cnt = (int) mNodes.size();

   // Set all parent nodes to closed
   for (int i = 0; i < cnt; i++)
   {
      KeyNode & node = mNodes[i];

      if (node.isparent)
      {
         node.isopen = false;
      }
   }

   RefreshLines();
}

//
// Recalculate the measurements used for columns and scrolling
//
void
KeyView::RecalcExtents()
{
   // Reset
   mLineHeight = 0;
   mCommandWidth = 0;
   mKeyWidth = 0;

   // Examine all nodes
   int cnt = (int) mNodes.size();
   for (int i = 0; i < cnt; i++)
   {
      KeyNode & node = mNodes[i];
      int x, y;

      if (node.iscat)
      {
         // Measure the category
         GetTextExtent(node.category, &x, &y);
      }
      else if (node.ispfx)
      {
         // Measure the prefix
         GetTextExtent(node.prefix, &x, &y);
      }
      else
      {
         // Measure the key
         GetTextExtent(node.key.Display(), &x, &y);
         mLineHeight = wxMax(mLineHeight, y);
         mKeyWidth = wxMax(mKeyWidth, x);

         // Prepend prefix for view types other than tree
         wxString label = node.label;
         if (mViewType != ViewByTree && !node.prefix.empty())
         {
            label = node.prefix + wxT(" - ") + label;
         }

         // Measure the label
         GetTextExtent(label, &x, &y);
      }

      // Finish calc for command column
      mLineHeight = wxMax(mLineHeight, y);
      mCommandWidth = wxMax(mCommandWidth, x);
   }

   // Update horizontal scrollbar
   UpdateHScroll();
}

//
// Update the horizontal scrollbar or remove it if not needed
//
void
KeyView::UpdateHScroll()
{
   // Get the internal dimensions of the view
   wxRect r = GetClientRect();

   // Calculate the full line width
   mWidth = KV_LEFT_MARGIN +
            mKeyWidth +
            KV_COLUMN_SPACER +
            mCommandWidth +
            KV_VSCROLL_WIDTH;

   // Retrieve the current horizontal scroll amount
   mScrollX = GetScrollPos(wxHORIZONTAL);

   if (mWidth <= r.GetWidth())
   {
      // Remove the scrollbar if it will fit within client width
      SetScrollbar(wxHORIZONTAL, 0, 0, 0);
   }
   else
   {
      // Set scrollbar metrics
      SetScrollbar(wxHORIZONTAL, mScrollX, r.GetWidth(), mWidth);
   }

   // Refresh the entire view
   RefreshAll();
}

//
// Process a NEW set of bindings
//
void
KeyView::RefreshBindings(const CommandIDs & names,
                         const wxArrayString & categories,
                         const TranslatableStrings & prefixes,
                         const TranslatableStrings & labels,
                         const std::vector<NormalizedKeyString> & keys,
                         bool bSort
                         )
{
   // Start clean
   mNodes.clear();

   // Same as in RecalcExtents() but do it inline
   mLineHeight = 0;
   mKeyWidth = 0;
   mCommandWidth = 0;

   wxString lastcat;
   wxString lastpfx;
   int nodecnt = 0;
   int depth = 1;
   bool incat = false;
   bool inpfx = false;

   // Examine all names...all arrays passed have the same indexes
   int cnt = (int) names.size();
   for (int i = 0; i < cnt; i++)
   {
      auto name = names[i];
      int x, y;

      // Remove any menu code from the category and prefix
      wxString cat = wxMenuItem::GetLabelText(categories[i]);
      wxString pfx = wxMenuItem::GetLabelText(prefixes[i].Translation());

      // Append "Menu" this node is for a menu title
      if (cat != wxT("Command"))
      {
         cat.Append(wxT(" "));
         cat += _("Menu");
      }

      // Process a NEW category
      if (cat != lastcat)
      {
         // A NEW category always finishes any current subtree
         if (inpfx)
         {
            // Back to category level
            depth--;
            inpfx = false;
         }

         // Only time this is not true is during the first iteration
         if (incat)
         {
            // Back to root level
            depth--;
            incat = false;
         }

         // Remember for next iteration
         lastcat = cat;

         // Add a NEW category node
         if (!cat.empty())
         {
            KeyNode node;

            // Fill in the node info
            node.name = CommandID{};    // don't associate branches with a command
            node.category = cat;
            node.prefix = pfx;
            node.label = cat;
            node.index = nodecnt++;
            node.iscat = true;
            node.isparent = true;
            node.depth = depth++;
            node.isopen = true;

            // Add it to the tree
            mNodes.push_back(node);
            incat = true;

            // Measure category
            GetTextExtent(cat, &x, &y);
            mLineHeight = wxMax(mLineHeight, y);
            mCommandWidth = wxMax(mCommandWidth, x);
         }
      }

      // Process a NEW prefix
      if (pfx != lastpfx)
      {
         // Done with prefix branch
         if (inpfx)
         {
            depth--;
            inpfx = false;
         }

         // Remember for next iteration
         lastpfx = pfx;

         // Add a NEW prefix node
         if (!pfx.empty())
         {
            KeyNode node;

            // Fill in the node info
            node.name = CommandID{};    // don't associate branches with a command
            node.category = cat;
            node.prefix = pfx;
            node.label = pfx;
            node.index = nodecnt++;
            node.ispfx = true;
            node.isparent = true;
            node.depth = depth++;
            node.isopen = true;

            // Add it to the tree
            mNodes.push_back(node);
            inpfx = true;
         }
      }

      // Add the key entry
      KeyNode node;
      node.category = cat;
      node.prefix = pfx;

      // Labels for undo and redo change according to the last command
      // which can be undone/redone, so give them a special check in order
      // not to confuse users
      if (name == wxT("Undo"))
      {
         node.label = _("Undo");
      }
      else if (name == wxT("Redo"))
      {
         node.label = _("Redo");
      }
      else
      {
         auto label = labels[i];
         node.label = label.Strip().Translation();
      }

      // Fill in remaining info
      node.name = name;
      node.key = keys[i];
      node.index = nodecnt++;
      node.depth = depth;

      // Add it to the tree
      mNodes.push_back(node);

      // Measure key
      GetTextExtent(node.key.Display(), &x, &y);
      mLineHeight = wxMax(mLineHeight, y);
      mKeyWidth = wxMax(mKeyWidth, x);

      // Prepend prefix for all view types to determine maximum
      // column widths
      wxString label = node.label;
      if (!node.prefix.empty())
      {
         label = node.prefix + wxT(" - ") + label;
      }

      // Measure label
      GetTextExtent(label, &x, &y);
      mLineHeight = wxMax(mLineHeight, y);
      mCommandWidth = wxMax(mCommandWidth, x);
   }

#if 0
   // For debugging
   for (int j = 0; j < mNodes.size(); j++)
   {
      KeyNode & node = mNodes[j];
      wxLogDebug(wxT("NODE line %4d index %4d depth %1d open %1d parent %1d cat %1d pfx %1d name %s STR %s | %s | %s"),
         node.line,
         node.index,
         node.depth,
         node.isopen,
         node.isparent,
         node.iscat,
         node.ispfx,
         node.name,
         node.category,
         node.prefix,
         node.label);
   }
#endif

   // Update horizontal scrollbar
   UpdateHScroll();

   // Refresh the view lines
   RefreshLines(bSort);

   // Set the selected node if we've just reprepared the list and nothing was selected.
   if ((GetSelection()==wxNOT_FOUND) && bSort )
   {
      SelectNode(LineToIndex(0));
   }
}

//
// Refresh the list of lines within the current view
//
void
KeyView::RefreshLines(bool bSort)
{
   int cnt = (int) mNodes.size();
   int linecnt = 0;
   mLines.clear();

   // Process a filter if one is set
   if (!mFilter.empty())
   {
      // Examine all nodes
      for (int i = 0; i < cnt; i++)
      {
         KeyNode & node = mNodes[i];

         // Reset line number
         node.line = wxNOT_FOUND;

         // Search columns based on view type
         wxString searchit;
         switch (mViewType)
         {
            // The x"01" separator is used to prevent finding a
            // match comprising the end of the label and beginning
            // of the key.  It was chosen since it's not very likely
            // to appear in the filter itself.
            case ViewByTree:
               searchit = node.label.Lower() +
                          wxT("\01x") +
                          node.key.Display().Lower();
            break;

            case ViewByName:
               searchit = node.label.Lower();
            break;

            case ViewByKey:
               searchit = node.key.Display().Lower();
            break;
         }
         if (searchit.Find(mFilter) == wxNOT_FOUND)
         {
            // Not found so continue to next node
            continue;
         }

         // For the Key View, if the filter is a single character,
         // then it has to be the last character in the searchit string,
         // and be preceded by nothing or +.
         if ((mViewType == ViewByKey) &&
               (mFilter.length() == 1) &&
               (mFilter != searchit.Last() ||
                  ((searchit.length() > 1) &&
                     ((wxString)(searchit.GetChar(searchit.length() - 2)) != wxT("+")))))
         {
            // Not suitable so continue to next node
            continue;
         }

         // For tree view, we must make sure all parent nodes are included
         // whether they match the filter or not.
         if (mViewType == ViewByTree)
         {
            std::vector<KeyNode*> queue;
            int depth = node.depth;

            // This node is a category or prefix node, so always mark them
            // as open.
            //
            // What this is really doing is resolving a situation where the
            // the filter matches a parent node and nothing underneath.  In
            // this case, the node would never be marked as open.
            if (node.isparent)
            {
               node.isopen = true;
            }

            // Examine siblings until a parent is found.
            for (int j = node.index - 1; j >= 0 && depth > 0; j--)
            {
               // Found a parent
               if (mNodes[j].depth < depth)
               {
                  // Examine all previously added nodes to see if this nodes
                  // ancestors need to be added prior to adding this node.
                  bool found = false;
                  for (int k = (int) mLines.size() - 1; k >= 0; k--)
                  {
                     // The node indexes match, so we've found the parent of the
                     // child node.
                     if (mLines[k]->index == mNodes[j].index)
                     {
                        found = true;
                        break;
                     }
                  }

                  // The parent wasn't found so remember it for later
                  // addition.  Can't add directory to mLines here since
                  // they will wind up in reverse order.
                  if (!found)
                  {
                     queue.push_back(&mNodes[j]);
                  }

                  // Traverse up the tree
                  depth = mNodes[j].depth;
               }
            }

            // Add any queues nodes to list.  This will all be
            // parent nodes, so mark them as open.
            for (int j = (int) queue.size() - 1; j >= 0; j--)
            {
               queue[j]->isopen = true;
               queue[j]->line = linecnt++;
               mLines.push_back(queue[j]);
            }
         }

         // Finally add the child node
         node.line = linecnt++;
         mLines.push_back(&node);
      }
   }
   else
   {
      // Examine all nodes - non-filtered
      for (int i = 0; i < cnt; i++)
      {
         KeyNode & node = mNodes[i];

         // Reset line number
         node.line = wxNOT_FOUND;

         // Node is either a category or prefix
         if (node.isparent)
         {
            // Only need to do this for tree views
            if (mViewType != ViewByTree)
            {
               continue;
            }

            // Add the node
            node.line = linecnt++;
            mLines.push_back(&node);

            // If this node is not open, then skip all of it's decendants
            if (!node.isopen)
            {
               bool iscat = node.iscat;
               bool ispfx = node.ispfx;

               // Skip nodes until we find a node that has a different
               // category or prefix
               while (i < cnt)
               {
                  KeyNode & skip = mNodes[i];

                  if ((iscat && skip.category != node.category) ||
                      (ispfx && skip.prefix != node.prefix))
                  {
                     break;
                  }

                  // Bump to next node
                  i++;
               }

               // Index is pointing to the node that was different or
               // past the end, so back off to last node of this branch.
               i--;
            }
            continue;
         }

         // Add child node to list
         node.line = linecnt++;
         mLines.push_back(&node);
      }
   }

   // Sorting is costly.  If bSort is false, we do not have to sort.
   // bSort false means we know that the list will be updated again before
   // the user needs to see it.
   if( bSort )
   {
      //To see how many lines are being sorted (and how often).
      //wxLogDebug("Sorting %i lines for type %i", mLines.size(), mViewType);

      // Speed up the comparison function used in sorting
      // by only translating this string once.
      CommandTranslated = _("Command");

      // Sort list based on type
      switch (mViewType)
      {
         case ViewByTree:
            std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByTree);
         break;

         case ViewByName:
            std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByName);
         break;

         case ViewByKey:
            std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByKey);
         break;
      }
   }

   // Now, reassign the line numbers
   for (int i = 0; i < (int) mLines.size(); i++)
   {
      mLines[i]->line = i;
   }

#if 0
   // For debugging
   for (int j = 0; j < mLines.size(); j++)
   {
      KeyNode & node = *mLines[j];
      wxLogDebug(wxT("LINE line %4d index %4d depth %1d open %1d parent %1d cat %1d pfx %1d name %s STR %s | %s | %s"),
         node.line,
         node.index,
         node.depth,
         node.isopen,
         node.isparent,
         node.iscat,
         node.ispfx,
         node.name,
         node.category,
         node.prefix,
         node.label);
   }
#endif

   // Tell listbox the NEW count and refresh the entire view
   SetItemCount(mLines.size());
   RefreshAll();

#if wxUSE_ACCESSIBILITY
   // Let accessibility know that the list has changed
   if( bSort )
      mAx->ListUpdated();
#endif
}

//
// Select a node
//
// Parameter can be wxNOT_FOUND to clear selection
//
void
KeyView::SelectNode(int index)
{
   int line = IndexToLine(index);

   // Tell the listbox to select the line
   SetSelection(line);

#if wxUSE_ACCESSIBILITY
   // And accessibility
   mAx->SetCurrentLine(line);
#endif

   // Always send an event to let parent know of selection change
   //
   // Must do this ourselves becuase we want to send notifications
   // even if there isn't an item selected and SendSelectedEvent()
   // doesn't allow sending an event for indexes not in the listbox.
   wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
   event.SetEventObject(this);
   event.SetInt(line);
   (void)GetEventHandler()->ProcessEvent(event);
}

//
// Converts a line index to a node index
//
int
KeyView::LineToIndex(int line) const
{
   if (line < 0 || line >= (int) mLines.size())
   {
      return wxNOT_FOUND;
   }

   return mLines[line]->index;
}

//
// Converts a node index to a line index
//
int
KeyView::IndexToLine(int index) const
{
   if (index < 0 || index >= (int) mNodes.size())
   {
      return wxNOT_FOUND;
   }

   return mNodes[index].line;
}

//
// Draw the background for a given line
//
// This is called by the listbox when it needs to redraw the view.
//
void
KeyView::OnDrawBackground(wxDC & dc, const wxRect & rect, size_t line) const
{
   const KeyNode *node = mLines[line];
   wxRect r = rect;
   wxRect r2 = rect; // for just the key shortcut.
   wxCoord indent = 0;

   // When in tree view mode, each younger branch gets indented by the
   // width of the open/close bitmaps
   if (mViewType == ViewByTree)
   {
      indent += mKeyWidth + KV_COLUMN_SPACER + node->depth * KV_BITMAP_SIZE;
   }

   // Offset left side by the indentation (if any) and scroll amounts
   r.x = indent - mScrollX;
   r2.x = -mScrollX;

   // If the line width is less than the client width, then we want to
   // extend the background to the right edge of the client view.  Otherwise,
   // go all the way to the end of the line width...this will draw past the
   // right edge, but that's what we want.
   r.width = wxMax(mWidth, r.width);
   r2.width = mKeyWidth;

   // Selected lines get a solid background
   if (IsSelected(line))
   {
      if (FindFocus() == this)
      {
         // Focused lines get highlighted background
         dc.SetPen(*wxTRANSPARENT_PEN);
         dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
         dc.DrawRectangle(r);
         
         // and they also get a dotted focus rect.  This could just be left out.
         // The focus rect does very little for us, as it is the same size as the 
         // rectangle itself.  Consequently for themes that have black text it
         // disappears.  But on HiContrast you do get a dotted green border which
         // may have some utility.
         AColor::DrawFocus(dc, r);

         if (mViewType == ViewByTree){
            dc.DrawRectangle(r2);
            AColor::DrawFocus(dc, r2);
         }
      }
      else
      {
         // Non focused lines get a light background
         dc.SetPen(*wxTRANSPARENT_PEN);
         dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)));
         dc.DrawRectangle(r);
         if (mViewType == ViewByTree)
            dc.DrawRectangle(r2);
      }
   }
   else
   {
      // Non-selected lines get a thin bottom border
      dc.SetPen(wxColour(240, 240, 240));
      AColor::Line(dc, r.GetLeft(), r.GetBottom(), r.GetRight(), r.GetBottom());
      if (mViewType == ViewByTree )
         AColor::Line(dc, r2.GetLeft(), r2.GetBottom(), r2.GetRight(), r2.GetBottom());
   }
}

//
// Draw a line
//
// This is called by the listbox when it needs to redraw the view.
//
void
KeyView::OnDrawItem(wxDC & dc, const wxRect & rect, size_t line) const
{
   const KeyNode *node = mLines[line];
   wxString label = node->label;

   // Make sure the DC has a valid font
   dc.SetFont(GetFont());

   // Set the text color based on selection and focus
   if (IsSelected(line) && FindFocus() == this)
   {
      dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT));
   }
   else
   {
      dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT));
   }

   // Tree views get bitmaps
   if (mViewType == ViewByTree)
   {
      // Adjust left edge to account for scrolling
      wxCoord x = rect.x - mScrollX;

      if (node->iscat || node->ispfx)
      {
         wxCoord bx = x + mKeyWidth  + KV_COLUMN_SPACER;
         wxCoord by = rect.y;

         if (node->ispfx)
         {
            bx += KV_BITMAP_SIZE;
         }

         dc.SetBrush(*wxTRANSPARENT_BRUSH);
         dc.SetPen(*wxBLACK_PEN);
         dc.DrawRectangle(bx + 3, by + 4, 9, 9);
         if (node->isopen)
         {
            AColor::Line(dc, bx + 5, by + 8, bx + 9, by + 8);
         }
         else
         {
            AColor::Line(dc, bx + 7, by + 6, bx + 7, by + 10);
            AColor::Line(dc, bx + 5, by + 8, bx + 9, by + 8);
         }
      }

      // Indent text
      x += KV_LEFT_MARGIN;

      // Draw the key and command columns
      dc.DrawText(node->key.Display(), x , rect.y);
      dc.DrawText(label, x + mKeyWidth  + KV_COLUMN_SPACER + node->depth * KV_BITMAP_SIZE, rect.y);
   }
   else
   {
      // Adjust left edge by margin and account for scrolling
      wxCoord x = rect.x + KV_LEFT_MARGIN - mScrollX;

      // Prepend prefix if available
      if (!node->prefix.empty())
      {
         label = node->prefix + wxT(" - ") + label;
      }

      // don't swap the columns based on view type
      if((mViewType == ViewByName) || (mViewType == ViewByKey))
      {
         // Draw key columnd and then command column
         dc.DrawText(node->key.Display(), x, rect.y);
         dc.DrawText(label, x + mKeyWidth + KV_COLUMN_SPACER, rect.y);
      }
   }

   return;
}

//
// Provide the height of the given line
//
// This is called by the listbox when it needs to redraw the view.
//
wxCoord
KeyView::OnMeasureItem(size_t WXUNUSED(line)) const
{
   // All lines are of equal height
   //
   // (add a magic 1 for decenders...looks better...not required)
   return mLineHeight + 1;
}

//
// Handle the wxEVT_LISTBOX event
//
void
KeyView::OnSelected(wxCommandEvent & event)
{
   // Allow further processing
   event.Skip();

#if wxUSE_ACCESSIBILITY
   // Tell accessibility of the change
   mAx->SetCurrentLine(event.GetInt());
#endif
}

//
// Handle the wxEVT_SET_FOCUS event
//
void
KeyView::OnSetFocus(wxFocusEvent & event)
{
   // Allow further processing
   event.Skip();

   // Refresh the selected line to pull in any changes while
   // focus was away...like when setting a NEW key value.  This
   // will also refresh the visual (highlighted) state.
   if (GetSelection() != wxNOT_FOUND)
   {
	   RefreshRow(GetSelection());
   }

#if wxUSE_ACCESSIBILITY
   // Tell accessibility of the change
   mAx->SetCurrentLine(GetSelection());
#endif
}

//
// Handle the wxEVT_KILL_FOCUS event
//
void
KeyView::OnKillFocus(wxFocusEvent & event)
{
   // Allow further processing
   event.Skip();

   // Refresh the selected line to adjust visual highlighting.
   if (GetSelection() != wxNOT_FOUND)
   {
	   RefreshRow(GetSelection());
   }
}

//
// Handle the wxEVT_SIZE event
//
void
KeyView::OnSize(wxSizeEvent & WXUNUSED(event))
{
   // Update horizontal scrollbar
   UpdateHScroll();
}

//
// Handle the wxEVT_SCROLL event
//
void
KeyView::OnScroll(wxScrollWinEvent & event)
{
   // We only care bout the horizontal scrollbar.
   if (event.GetOrientation() != wxHORIZONTAL)
   {
      // Allow further processing
      event.Skip();
      return;
   }

   // Get NEW scroll position and scroll the view
   mScrollX = event.GetPosition();
   SetScrollPos(wxHORIZONTAL, mScrollX);

   // Refresh the entire view
   RefreshAll();
}

//
// Handle the wxEVT_KEY_DOWN event
//
void
KeyView::OnKeyDown(wxKeyEvent & event)
{
   int line = GetSelection();

   int keycode = event.GetKeyCode();
   switch (keycode)
   {
      // The LEFT key moves selection to parent or collapses selected
      // node if it is expanded.
      case WXK_LEFT:
      {
         // Nothing selected...nothing to do
         if (line == wxNOT_FOUND)
         {
            // Allow further processing
            event.Skip();
            break;
         }

         KeyNode *node = mLines[line];

         // Collapse the node if it is open
         if (node->isopen)
         {
            // No longer open
            node->isopen = false;

            // Don't want the view to scroll vertically, so remember the current
            // top line.
            size_t topline = GetVisibleBegin();

            // Refresh the view now that the number of lines have changed
            RefreshLines();

            // Reset the original top line
            ScrollToRow(topline);

            // And make sure current line is still selected
            SelectNode(LineToIndex(line));
         }
         else
         {
            // Move selection to the parent of this node
            for (int i = line - 1; i >= 0; i--)
            {
               // Found the parent
               if (mLines[i]->depth < node->depth)
               {
                  // So select it
                  SelectNode(LineToIndex(i));
                  break;
               }
            }
         }

         // Further processing of the event is not wanted
         // (we didn't call event.Skip()
      }
      break;

      // The RIGHT key moves the selection to the first child or expands
      // the node if it is a parent.
      case WXK_RIGHT:
      {
         // Nothing selected...nothing to do
         if (line == wxNOT_FOUND)
         {
            // Allow further processing
            event.Skip();
            break;
         }

         KeyNode *node = mLines[line];

         // Only want parent nodes
         if (node->isparent)
         {
            // It is open so move select to first child
            if (node->isopen)
            {
               // But only if there is one
               if (line < (int) mLines.size() - 1)
               {
                  SelectNode(LineToIndex(line + 1));
               }
            }
            else
            {
               // Node is now open
               node->isopen = true;

               // Don't want the view to scroll vertically, so remember the current
               // top line.
               size_t topline = GetVisibleBegin();

               // Refresh the view now that the number of lines have changed
               RefreshLines();

               // Reset the original top line
               ScrollToRow(topline);

               // And make sure current line is still selected
               SelectNode(LineToIndex(line));
            }
         }

         // Further processing of the event is not wanted
         // (we didn't call event.Skip()
      }
      break;

      // Move selection to next node whose 1st character matches
      // the keycode
      default:
      {
         int cnt = (int) mLines.size();
         bool found = false;

         // Search the entire list if none is currently selected
         if (line == wxNOT_FOUND)
         {
            line = cnt;
         }
         else
         {
            // Search from the node following the current one
            for (int i = line + 1; i < cnt; i++)
            {
               wxString label;

               // Get the string to search based on view type
               if (mViewType == ViewByTree)
               {
                  label = GetLabel(LineToIndex(i));
               }
               else if (mViewType == ViewByName)
               {
                  label = GetFullLabel(LineToIndex(i));
               }
               else if (mViewType == ViewByKey)
               {
                  label = GetKey(LineToIndex(i)).Display();
               }

               // Move selection if they match
               if (label.Left(1).IsSameAs(keycode, false))
               {
                  SelectNode(LineToIndex(i));

                  found = true;

                  break;
               }
            }
         }

         // A match wasn't found
         if (!found)
         {
            // So scan from the start of the list to the current node
            for (int i = 0; i < line; i++)
            {
               wxString label;

               // Get the string to search based on view type
               if (mViewType == ViewByTree)
               {
                  label = GetLabel(LineToIndex(i));
               }
               else if (mViewType == ViewByName)
               {
                  label = GetFullLabel(LineToIndex(i));
               }
               else if (mViewType == ViewByKey)
               {
                  label = GetKey(LineToIndex(i)).Display();
               }

               // Move selection if they match
               if (label.Left(1).IsSameAs(keycode, false))
               {
                  SelectNode(LineToIndex(i));

                  found = true;

                  break;
               }
            }
         }

         // A node wasn't found so allow further processing
         if (!found) {
            event.Skip();
         }

         // Otherwise, further processing of the event is not wanted
         // (we didn't call event.Skip()
      }
   }
}

//
// Handle the wxEVT_LEFT_DOWN event
//
void
KeyView::OnLeftDown(wxMouseEvent & event)
{
   // Only check if for tree view
   if (mViewType != ViewByTree)
   {
      // Allow further processing (important for focus handling)
      event.Skip();

      return;
   }

   // Get the mouse position when the button was pressed
   wxPoint pos = event.GetPosition();

   // And see if it was on a line within the view
   int line = VirtualHitTest(pos.y);

   // It was on a line
   if (line != wxNOT_FOUND)
   {
      KeyNode *node = mLines[line];

      // Toggle the open state if this is a parent node
      if (node->isparent)
      {
         // Toggle state
         node->isopen = !node->isopen;

         // Don't want the view to scroll vertically, so remember the current
         // top line.
         size_t topline = GetVisibleBegin();

         // Refresh the view now that the number of lines have changed
         RefreshLines();

         // Reset the original top line
         ScrollToRow(topline);

         // And make sure current line is still selected
         SelectNode(LineToIndex(line));

         // If a node is closed near the bottom of the tree,
         // the node may move down, and no longer be at the
         // mouse pointer position. So don't allow further processing as this
         // selects the line at the mouse position. Bug 1723.
         // So we need to set the focus.
         SetFocus();
         return;
      }
   }

   // Allow further processing (important for focus handling)
   event.Skip();
}

//
// Sort compare function for tree view
//
// We want to leave the "menu" nodes alone as they are in the
// order as they appear in the menus.  But, we want to sort the
// "command" nodes.
//
// To accomplish this, we prepend each label with it's line number
// (in hex) for "menu" nodes.  This ensures they will remain in
// their original order.
//
// We prefix all "command" nodes with "ffffffff" (highest hex value)
// to allow the sort to reorder them as needed.
//
bool
KeyView::CmpKeyNodeByTree(KeyNode *t1, KeyNode *t2)
{
   unsigned int k1UInt= 0xffffffff;
   unsigned int k2UInt= 0xffffffff;

   // This is a "command" node if its category is "Command"
   // and it is a child of the "Command" category.  This latter
   // test ensures that the "Command" parent will be handled
   // as a "menu" node and remain at the bottom of the list.
   if (t1->category != CommandTranslated || t1->isparent)
      k1UInt = (unsigned int) t1->line;

   // See above for explanation
   if (t2->category != CommandTranslated || t2->isparent)
      k2UInt = (unsigned int) t2->line;

   if( k1UInt < k2UInt ) 
      return true;
   if( k1UInt > k2UInt )
      return false;

   return ( t1->label < t2->label );
}

//
// Sort compare function for command view
//
// Nothing special here, just a standard ascending sort.
//
bool
KeyView::CmpKeyNodeByName(KeyNode *t1, KeyNode *t2)
{
   wxString k1 = t1->label;
   wxString k2 = t2->label;

   // Prepend prefix if available
   if (!t1->prefix.empty())
   {
      k1 = t1->prefix + wxT(" - ") + k1;
   }

   // Prepend prefix if available
   if (!t2->prefix.empty())
   {
      k2 = t2->prefix + wxT(" - ") + k2;
   }

   return (k1 < k2);
}

//
// Sort compare function for key view
//
// We want all nodes with key assignments to appear in ascending order
// at the top of the list and all nodes without assignment to appear in
// ascending order at the bottom of the list.
//
// We accomplish this by by prefixing all non-assigned entries with 0xff.
// This will force them to the end, but still allow them to be sorted in
// ascending order.
//
// The assigned entries simply get sorted as normal.
//
bool
KeyView::CmpKeyNodeByKey(KeyNode *t1, KeyNode *t2)
{
   wxString k1 = t1->key.Display();
   wxString k2 = t2->key.Display();

   // Left node is unassigned, so prefix it
   if(k1.empty())
   {
      k1 = wxT("\xff");
   }

   // Right node is unassigned, so prefix it
   if(k2.empty())
   {
      k2 = wxT("\xff");
   }

   // Add prefix if available
   if (!t1->prefix.empty())
   {
      k1 += t1->prefix + wxT(" - ");
   }

   // Add prefix if available
   if (!t2->prefix.empty())
   {
      k2 += t2->prefix + wxT(" - ");
   }

   // Add labels
   k1 += t1->label;
   k2 += t2->label;

   return (k1 < k2);
}

#if wxUSE_ACCESSIBILITY

//
// Return parenthood state of line
//
bool
KeyView::HasChildren(int line)
{
   // Make sure line is valid
   if (line < 0 || line >= (int) mLines.size())
   {
      wxASSERT(false);
      return false;
   }

   return mLines[line]->isparent;
}

//
// Returns espanded/collapsed state of line
//
bool
KeyView::IsExpanded(int line)
{
   // Make sure line is valid
   if (line < 0 || line >= (int) mLines.size())
   {
      wxASSERT(false);
      return false;
   }

   return mLines[line]->isopen;
}

//
// Returns the height of the line
//
wxCoord
KeyView::GetLineHeight(int line)
{
   // Make sure line is valid
   if (line < 0 || line >= (int) mLines.size())
   {
      wxASSERT(false);
      return 0;
   }

   return OnGetRowHeight(line);
}

//
// Returns the value to be presented to accessibility
//
// Currently, the command and key are both provided.
//
wxString
KeyView::GetValue(int line)
{
   // Make sure line is valid
   if (line < 0 || line >= (int) mLines.size())
   {
      wxASSERT(false);
      return wxEmptyString;
   }
   int index = LineToIndex(line);

   // Get the label and key values
   wxString value;
   if (mViewType == ViewByTree)
   {
      value = GetLabel(index);
   }
   else
   {
      value = GetFullLabel(index);
   }
   wxString key = GetKey(index).Display();

   // Add the key if it isn't empty
   if (!key.empty())
   {
      if (mViewType == ViewByKey)
      {
         value = key + wxT(" ") + value;
      }
      else
      {
         value = value + wxT(" ") + key;
      }
   }

   return value;
}

//
// Returns the current view type
//
ViewByType
KeyView::GetViewType()
{
   return mViewType;
}

// ============================================================================
// Accessibility provider for the KeyView class
// ============================================================================
KeyViewAx::KeyViewAx(KeyView *view)
: WindowAccessible(view)
{
   mView = view;
   mLastId = -1;
}

//
// Send an event notification to accessibility that the view
// has changed.
//
void
KeyViewAx::ListUpdated()
{
   NotifyEvent(wxACC_EVENT_OBJECT_REORDER,
               mView,
               wxOBJID_CLIENT,
               0);
}

//
// Inform accessibility a NEW line has been selected and/or a previously
// selected line is being unselected
//
void
KeyViewAx::SetCurrentLine(int line)
{
   // Only send selection remove notification if a line was
   // previously selected
   if (mLastId != -1)
   {
      NotifyEvent(wxACC_EVENT_OBJECT_SELECTIONREMOVE,
               mView,
               wxOBJID_CLIENT,
               mLastId);
   }

   // Nothing is selected now
   mLastId = -1;

   // Just clearing selection
   if (line != wxNOT_FOUND)
   {
      // Convert line number to childId
      LineToId(line, mLastId);

      // Send notifications that the line has focus
      if (mView == wxWindow::FindFocus()) {
         NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
                     mView,
                     wxOBJID_CLIENT,
                     mLastId);
      }

      // And is selected
      NotifyEvent(wxACC_EVENT_OBJECT_SELECTION,
                  mView,
                  wxOBJID_CLIENT,
                  mLastId);
   }
}

//
// Convert the childId to a line number and return FALSE if it
// represents a child or TRUE if it a line
//
bool
KeyViewAx::IdToLine(int childId, int & line)
{
   if (childId == wxACC_SELF)
   {
      return false;
   }

   // Convert to line
   line = childId - 1;

   // Make sure id is valid
   if (line < 0 || line >= (int) mView->GetItemCount())
   {
      // Indicate the control itself in this case
      return false;
   }

   return true;
}

//
// Convert the line number to a childId.
//
bool
KeyViewAx::LineToId(int line, int & childId)
{
   // Make sure line is valid
   if (line < 0 || line >= (int) mView->GetItemCount())
   {
      // Indicate the control itself in this case
      childId = wxACC_SELF;
      return false;
   }

   // Convert to line
   childId = line + 1;

   return true;
}

// Can return either a child object, or an integer
// representing the child element, starting from 1.
wxAccStatus
KeyViewAx::HitTest(const wxPoint & pt, int *childId, wxAccessible **childObject)
{
   // Just to be safe
   *childObject = NULL;

   wxPoint pos = mView->ScreenToClient(pt);

   // See if it's on a line within the view
   int line = mView->HitTest(pos);

   // It was on a line
   if (line != wxNOT_FOUND)
   {
      LineToId(line, *childId);
      return wxACC_OK;
   }

   // Let the base class handle it
   return wxACC_NOT_IMPLEMENTED;
}

// Retrieves the address of an IDispatch interface for the specified child.
// All objects must support this property.
wxAccStatus
KeyViewAx::GetChild(int childId, wxAccessible** child)
{
   if (childId == wxACC_SELF)
   {
      *child = this;
   }
   else
   {
      *child = NULL;
   }

   return wxACC_OK;
}

// Gets the number of children.
wxAccStatus
KeyViewAx::GetChildCount(int *childCount)
{
   *childCount = (int) mView->GetItemCount();

   return wxACC_OK;
}

// Gets the default action for this object (0) or > 0 (the action for a child).
// Return wxACC_OK even if there is no action. actionName is the action, or the empty
// string if there is no action.
// The retrieved string describes the action that is performed on an object,
// not what the object does as a result. For example, a toolbar button that prints
// a document has a default action of "Press" rather than "Prints the current document."
wxAccStatus
KeyViewAx::GetDefaultAction(int WXUNUSED(childId), wxString *actionName)
{
   actionName->clear();

   return wxACC_OK;
}

// Returns the description for this object or a child.
wxAccStatus
KeyViewAx::GetDescription(int WXUNUSED(childId), wxString *description)
{
   description->clear();

   return wxACC_OK;
}

// Returns help text for this object or a child, similar to tooltip text.
wxAccStatus
KeyViewAx::GetHelpText(int WXUNUSED(childId), wxString *helpText)
{
   helpText->clear();

   return wxACC_OK;
}

// Returns the keyboard shortcut for this object or child.
// Return e.g. ALT+K
wxAccStatus
KeyViewAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut)
{
   shortcut->clear();

   return wxACC_OK;
}

// Returns the rectangle for this object (id = 0) or a child element (id > 0).
// rect is in screen coordinates.
wxAccStatus
KeyViewAx::GetLocation(wxRect & rect, int elementId)
{
   int line;

   if (IdToLine(elementId, line))
   {
      if (!mView->IsVisible(line))
      {
         return wxACC_FAIL;
      }

      wxRect rectLine;

      rectLine.width = mView->GetClientSize().GetWidth();

      // iterate over all visible lines
      for (int i = (int) mView->GetVisibleBegin(); i <= line; i++)
      {
         wxCoord hLine = mView->GetLineHeight(i);

         rectLine.height = hLine;

         rect = rectLine;
         wxPoint margins = mView->GetMargins();
         rect.Deflate(margins.x, margins.y);
         rectLine.y += hLine;
      }

      rect.SetPosition(mView->ClientToScreen(rect.GetPosition()));
   }
   else
   {
      rect = mView->GetRect();
      rect.SetPosition(mView->GetParent()->ClientToScreen(rect.GetPosition()));
   }

   return wxACC_OK;
}

wxAccStatus
KeyViewAx::Navigate(wxNavDir WXUNUSED(navDir),
                    int WXUNUSED(fromId),
                    int *WXUNUSED(toId),
                    wxAccessible **WXUNUSED(toObject))
{
   return wxACC_NOT_IMPLEMENTED;
}

// Gets the name of the specified object.
wxAccStatus
KeyViewAx::GetName(int childId, wxString *name)
{
   int line;

   if (!IdToLine(childId, line))
   {
      *name = mView->GetName();
   }
   else
   {
      if (IdToLine(childId, line))
      {
         *name = mView->GetValue(line);
      }
   }

   return wxACC_OK;
}

wxAccStatus
KeyViewAx::GetParent(wxAccessible ** WXUNUSED(parent))
{
   return wxACC_NOT_IMPLEMENTED;
}

// Returns a role constant.
wxAccStatus
KeyViewAx::GetRole(int childId, wxAccRole *role)
{
   if (childId == wxACC_SELF)
   {
#if defined(__WXMSW__)
      *role = mView->GetViewType() == ViewByTree ? wxROLE_SYSTEM_OUTLINE : wxROLE_SYSTEM_LIST;
#endif

#if defined(__WXMAC__)
      *role = wxROLE_SYSTEM_GROUPING;
#endif
   }
   else
   {
#if defined(__WXMAC__)
      *role = wxROLE_SYSTEM_TEXT;
#else
      *role = mView->GetViewType() == ViewByTree ? wxROLE_SYSTEM_OUTLINEITEM : wxROLE_SYSTEM_LISTITEM;
#endif
   }

   return wxACC_OK;
}

// Gets a variant representing the selected children
// of this object.
// Acceptable values:
// - a null variant (IsNull() returns TRUE)
// - a list variant (GetType() == wxT("list"))
// - an integer representing the selected child element,
//   or 0 if this object is selected (GetType() == wxT("long"))
// - a "void*" pointer to a wxAccessible child object
wxAccStatus
KeyViewAx::GetSelections(wxVariant *selections)
{
   int id;

   LineToId(mView->GetSelection(), id);

   *selections = (long) id;

   return wxACC_OK;
}

// Returns a state constant.
wxAccStatus
KeyViewAx::GetState(int childId, long *state)
{
   int flag = wxACC_STATE_SYSTEM_FOCUSABLE;
   int line;

   if (!IdToLine(childId, line))
   {
      *state = wxACC_STATE_SYSTEM_FOCUSABLE; // |
               //mView->FindFocus() == mView ? wxACC_STATE_SYSTEM_FOCUSED : 0;
      return wxACC_OK;
   }

#if defined(__WXMSW__)
   int selected = mView->GetSelection();

   flag |= wxACC_STATE_SYSTEM_SELECTABLE;

   if (line == selected)
   {
      flag |= wxACC_STATE_SYSTEM_FOCUSED |
              wxACC_STATE_SYSTEM_SELECTED;
   }

   if (mView->HasChildren(line))
   {
      flag |= mView->IsExpanded(line) ?
         wxACC_STATE_SYSTEM_EXPANDED :
         wxACC_STATE_SYSTEM_COLLAPSED;
   }
#endif

#if defined(__WXMAC__1)
   if (mGrid->IsInSelection(row, col))
   {
      flag |= wxACC_STATE_SYSTEM_SELECTED;
   }

   if (mGrid->GetGridCursorRow() == row && mGrid->GetGridCursorCol() == col)
   {
       flag |= wxACC_STATE_SYSTEM_FOCUSED;
   }

   if (mGrid->IsReadOnly(row, col))
   {
      flag |= wxACC_STATE_SYSTEM_UNAVAILABLE;
   }
#endif

   *state = flag;

   return wxACC_OK;
}

// Returns a localized string representing the value for the object
// or child.
wxAccStatus
KeyViewAx::GetValue(int childId, wxString *strValue)
{
   int line;

   strValue->clear();

   if (!IdToLine(childId, line))
   {
      return wxACC_NOT_IMPLEMENTED;
   }

#if defined(__WXMSW__)
   if (mView->GetViewType() == ViewByTree)
   {
      KeyNode *node = mView->mLines[line];
      strValue->Printf(wxT("%d"), node->depth - 1);
   }

   // Don't set a value for the other view types
   return wxACC_NOT_IMPLEMENTED;
#endif

#if defined(__WXMAC__)
   return GetName(childId, strValue);
#endif
}

#if defined(__WXMAC__)
// Selects the object or child.
wxAccStatus
KeyViewAx::Select(int childId, wxAccSelectionFlags selectFlags)
{
#if 0
   int row;
   int col;

   if (GetRowCol(childId, row, col))
   {

      if (selectFlags & wxACC_SEL_TAKESELECTION)
      {
         mGrid->SetGridCursor(row, col);
      }

      mGrid->SelectBlock(row, col, row, col, selectFlags & wxACC_SEL_ADDSELECTION);
   }
#endif
   return wxACC_OK;
}
#endif

// Gets the window with the keyboard focus.
// If childId is 0 and child is NULL, no object in
// this subhierarchy has the focus.
// If this object has the focus, child should be 'this'.
wxAccStatus
KeyViewAx::GetFocus(int * WXUNUSED(childId), wxAccessible **child)
{
   *child = this;

   return wxACC_OK;
}

#endif // wxUSE_ACCESSIBILITY
