2014-11-08 16:42:34 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
KeyView.cpp
|
|
|
|
|
|
|
|
*******************************************************************//*!
|
|
|
|
|
|
|
|
\class KeyView
|
|
|
|
\brief Provides multiple views of keyboard shortcuts
|
|
|
|
|
|
|
|
*//*********************************************************************/
|
|
|
|
|
2021-05-09 15:16:56 +00:00
|
|
|
|
2018-11-10 19:47:12 +00:00
|
|
|
#include "KeyView.h"
|
2014-11-08 16:42:34 +00:00
|
|
|
|
2019-03-23 18:28:37 +00:00
|
|
|
#include <wx/setup.h> // for wxUSE_* macros
|
2014-11-08 16:42:34 +00:00
|
|
|
#include <wx/defs.h>
|
|
|
|
#include <wx/settings.h>
|
|
|
|
#include <wx/vlbox.h>
|
|
|
|
|
|
|
|
#include "../AColor.h"
|
|
|
|
#include "../ShuttleGui.h"
|
|
|
|
#include "../commands/CommandManager.h"
|
|
|
|
|
2016-11-22 18:16:03 +00:00
|
|
|
#include <wx/dc.h>
|
2019-03-23 14:53:24 +00:00
|
|
|
#include <wx/menu.h>
|
2014-11-08 16:42:34 +00:00
|
|
|
|
2019-03-31 16:49:49 +00:00
|
|
|
#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
|
|
|
|
|
2014-11-08 16:42:34 +00:00
|
|
|
// 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();
|
|
|
|
|
2020-05-06 15:56:16 +00:00
|
|
|
static wxString CommandTranslated = "Command";
|
2017-05-24 21:01:19 +00:00
|
|
|
|
|
|
|
|
2014-11-08 16:42:34 +00:00
|
|
|
// ============================================================================
|
|
|
|
// KeyView class
|
|
|
|
// ============================================================================
|
|
|
|
KeyView::KeyView(wxWindow *parent,
|
|
|
|
wxWindowID id,
|
|
|
|
const wxPoint & pos,
|
|
|
|
const wxSize & size)
|
2015-08-27 07:30:46 +00:00
|
|
|
: wxVListBox(parent, id, pos, size, wxBORDER_THEME | wxHSCROLL | wxVSCROLL),
|
2014-11-08 16:42:34 +00:00
|
|
|
mScrollX(0),
|
|
|
|
mWidth(0)
|
|
|
|
{
|
|
|
|
#if wxUSE_ACCESSIBILITY
|
|
|
|
// Create and set accessibility object
|
2016-05-07 01:03:37 +00:00
|
|
|
SetAccessible(mAx = safenew KeyViewAx(this));
|
2014-11-08 16:42:34 +00:00
|
|
|
#endif
|
|
|
|
|
2017-08-29 14:02:22 +00:00
|
|
|
SetMinSize({-1, 150});
|
|
|
|
|
2014-11-08 16:42:34 +00:00
|
|
|
// The default view
|
|
|
|
mViewType = ViewByTree;
|
2015-04-30 07:06:37 +00:00
|
|
|
|
|
|
|
// Calculate measurements used for columns and scrolling
|
|
|
|
RecalcExtents();
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2018-02-02 18:24:53 +00:00
|
|
|
if (index < 0 || index >= (int) mNodes.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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
|
2018-02-02 18:24:53 +00:00
|
|
|
if (index < 0 || index >= (int) mNodes.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxASSERT(false);
|
|
|
|
return wxEmptyString;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache the node and label
|
2018-02-02 18:24:53 +00:00
|
|
|
const KeyNode & node = mNodes[index];
|
2014-11-08 16:42:34 +00:00
|
|
|
wxString label = node.label;
|
|
|
|
|
|
|
|
// Prepend the prefix if available
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!node.prefix.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
label = node.prefix + wxT(" - ") + label;
|
|
|
|
}
|
|
|
|
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns the index for the given name
|
|
|
|
//
|
|
|
|
int
|
2019-03-11 02:05:37 +00:00
|
|
|
KeyView::GetIndexByName(const CommandID & name) const
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
int cnt = (int) mNodes.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Search the nodes for the key
|
|
|
|
for (int i = 0; i < cnt; i++)
|
|
|
|
{
|
2019-02-27 18:14:25 +00:00
|
|
|
if (name == mNodes[i].name)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
return mNodes[i].index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return wxNOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns the command manager name for the given index
|
|
|
|
//
|
2019-03-11 02:05:37 +00:00
|
|
|
CommandID
|
2014-11-08 16:42:34 +00:00
|
|
|
KeyView::GetName(int index) const
|
|
|
|
{
|
|
|
|
// Make sure index is valid
|
2018-02-02 18:24:53 +00:00
|
|
|
if (index < 0 || index >= (int) mNodes.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxASSERT(false);
|
2019-03-11 02:05:37 +00:00
|
|
|
return {};
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return mNodes[index].name;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns the command manager index for the given key combination
|
|
|
|
//
|
2019-03-11 02:05:37 +00:00
|
|
|
CommandID
|
2018-02-09 02:16:13 +00:00
|
|
|
KeyView::GetNameByKey(const NormalizedKeyString & key) const
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
int cnt = (int) mNodes.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Search the nodes for the key
|
|
|
|
for (int i = 0; i < cnt; i++)
|
|
|
|
{
|
2019-02-28 12:37:10 +00:00
|
|
|
if ( key == mNodes[i].key )
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
return mNodes[i].name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-11 02:05:37 +00:00
|
|
|
return {};
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns the index for the given key
|
|
|
|
//
|
|
|
|
int
|
2018-02-09 02:16:13 +00:00
|
|
|
KeyView::GetIndexByKey(const NormalizedKeyString & key) const
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
int cnt = (int) mNodes.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Search the nodes for the key
|
|
|
|
for (int i = 0; i < cnt; i++)
|
|
|
|
{
|
2019-02-28 12:37:10 +00:00
|
|
|
if ( key == mNodes[i].key )
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
return mNodes[i].index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return wxNOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns the key for the given index
|
|
|
|
//
|
2018-02-09 02:16:13 +00:00
|
|
|
NormalizedKeyString
|
2014-11-08 16:42:34 +00:00
|
|
|
KeyView::GetKey(int index) const
|
|
|
|
{
|
|
|
|
// Make sure index is valid
|
2018-02-02 18:24:53 +00:00
|
|
|
if (index < 0 || index >= (int) mNodes.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxASSERT(false);
|
2018-02-09 02:16:13 +00:00
|
|
|
return {};
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2018-02-02 18:24:53 +00:00
|
|
|
if (index < 0 || index >= (int) mNodes.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxASSERT(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parents can't be assigned keys
|
|
|
|
return !mNodes[index].isparent;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Sets the key for the given index
|
|
|
|
//
|
|
|
|
bool
|
2018-02-09 02:16:13 +00:00
|
|
|
KeyView::SetKey(int index, const NormalizedKeyString & key)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
// Make sure index is valid
|
2018-02-02 18:24:53 +00:00
|
|
|
if (index < 0 || index >= (int) mNodes.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxASSERT(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache the node
|
|
|
|
KeyNode & node = mNodes[index];
|
|
|
|
|
|
|
|
// Do not allow setting keys on branches
|
|
|
|
if (node.isparent)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
// Set the NEW key
|
2014-11-08 16:42:34 +00:00
|
|
|
node.key = key;
|
|
|
|
|
|
|
|
// Check to see if the key column needs to be expanded
|
|
|
|
int x, y;
|
2018-02-09 02:16:13 +00:00
|
|
|
GetTextExtent(node.key.Display(), &x, &y);
|
2014-11-08 16:42:34 +00:00
|
|
|
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
|
2019-03-11 02:05:37 +00:00
|
|
|
KeyView::SetKeyByName(const CommandID & name, const NormalizedKeyString & key)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
// Save NEW type
|
2014-11-08 16:42:34 +00:00
|
|
|
mViewType = type;
|
|
|
|
|
|
|
|
// Refresh the view lines
|
|
|
|
RefreshLines();
|
|
|
|
|
|
|
|
// Reselect old node (if possible)
|
|
|
|
if (index != wxNOT_FOUND)
|
|
|
|
{
|
|
|
|
SelectNode(index);
|
|
|
|
}
|
|
|
|
|
2019-07-05 08:25:02 +00:00
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
|
2014-11-08 16:42:34 +00:00
|
|
|
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);
|
|
|
|
}
|
2019-07-05 08:25:02 +00:00
|
|
|
|
|
|
|
// 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));
|
|
|
|
}
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Expand all branches
|
|
|
|
//
|
|
|
|
void
|
|
|
|
KeyView::ExpandAll()
|
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
int cnt = (int) mNodes.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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()
|
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
int cnt = (int) mNodes.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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
|
2018-02-02 18:24:53 +00:00
|
|
|
int cnt = (int) mNodes.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
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
|
2018-02-09 02:16:13 +00:00
|
|
|
GetTextExtent(node.key.Display(), &x, &y);
|
2014-11-08 16:42:34 +00:00
|
|
|
mLineHeight = wxMax(mLineHeight, y);
|
|
|
|
mKeyWidth = wxMax(mKeyWidth, x);
|
|
|
|
|
|
|
|
// Prepend prefix for view types other than tree
|
|
|
|
wxString label = node.label;
|
2019-02-12 00:10:48 +00:00
|
|
|
if (mViewType != ViewByTree && !node.prefix.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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 +
|
2017-08-19 15:48:45 +00:00
|
|
|
KV_COLUMN_SPACER +
|
|
|
|
mCommandWidth +
|
2014-11-08 16:42:34 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2016-02-13 15:43:16 +00:00
|
|
|
// Process a NEW set of bindings
|
2014-11-08 16:42:34 +00:00
|
|
|
//
|
|
|
|
void
|
2019-03-11 02:05:37 +00:00
|
|
|
KeyView::RefreshBindings(const CommandIDs & names,
|
2020-05-05 20:16:56 +00:00
|
|
|
const TranslatableStrings & categories,
|
2019-01-09 20:26:32 +00:00
|
|
|
const TranslatableStrings & prefixes,
|
2019-12-15 18:51:21 +00:00
|
|
|
const TranslatableStrings & labels,
|
2018-02-09 02:16:13 +00:00
|
|
|
const std::vector<NormalizedKeyString> & keys,
|
2017-05-25 14:45:47 +00:00
|
|
|
bool bSort
|
|
|
|
)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
// Start clean
|
2018-02-02 18:24:53 +00:00
|
|
|
mNodes.clear();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2020-05-06 15:56:16 +00:00
|
|
|
// lookup translation once only
|
|
|
|
CommandTranslated = _("Command");
|
|
|
|
|
2014-11-08 16:42:34 +00:00
|
|
|
// Examine all names...all arrays passed have the same indexes
|
2019-02-12 00:10:48 +00:00
|
|
|
int cnt = (int) names.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
for (int i = 0; i < cnt; i++)
|
|
|
|
{
|
2019-03-11 02:05:37 +00:00
|
|
|
auto name = names[i];
|
2014-11-08 16:42:34 +00:00
|
|
|
int x, y;
|
|
|
|
|
|
|
|
// Remove any menu code from the category and prefix
|
2020-05-05 20:16:56 +00:00
|
|
|
wxString cat = categories[i].Translation();
|
|
|
|
wxString pfx = prefixes[i].Translation();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Append "Menu" this node is for a menu title
|
2020-05-06 15:56:16 +00:00
|
|
|
if (cat != CommandTranslated)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
cat.Append(wxT(" "));
|
|
|
|
cat += _("Menu");
|
|
|
|
}
|
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
// Process a NEW category
|
2014-11-08 16:42:34 +00:00
|
|
|
if (cat != lastcat)
|
|
|
|
{
|
2016-02-13 15:43:16 +00:00
|
|
|
// A NEW category always finishes any current subtree
|
2014-11-08 16:42:34 +00:00
|
|
|
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;
|
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
// Add a NEW category node
|
2019-03-14 20:20:18 +00:00
|
|
|
if (!cat.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
KeyNode node;
|
|
|
|
|
|
|
|
// Fill in the node info
|
2019-03-11 02:05:37 +00:00
|
|
|
node.name = CommandID{}; // don't associate branches with a command
|
2014-11-08 16:42:34 +00:00
|
|
|
node.category = cat;
|
|
|
|
node.prefix = pfx;
|
|
|
|
node.label = cat;
|
|
|
|
node.index = nodecnt++;
|
|
|
|
node.iscat = true;
|
|
|
|
node.isparent = true;
|
|
|
|
node.depth = depth++;
|
2017-05-25 14:45:47 +00:00
|
|
|
node.isopen = true;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Add it to the tree
|
2018-02-02 18:24:53 +00:00
|
|
|
mNodes.push_back(node);
|
2014-11-08 16:42:34 +00:00
|
|
|
incat = true;
|
|
|
|
|
|
|
|
// Measure category
|
|
|
|
GetTextExtent(cat, &x, &y);
|
|
|
|
mLineHeight = wxMax(mLineHeight, y);
|
|
|
|
mCommandWidth = wxMax(mCommandWidth, x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
// Process a NEW prefix
|
2014-11-08 16:42:34 +00:00
|
|
|
if (pfx != lastpfx)
|
|
|
|
{
|
|
|
|
// Done with prefix branch
|
|
|
|
if (inpfx)
|
|
|
|
{
|
|
|
|
depth--;
|
|
|
|
inpfx = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remember for next iteration
|
|
|
|
lastpfx = pfx;
|
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
// Add a NEW prefix node
|
2019-03-14 20:20:18 +00:00
|
|
|
if (!pfx.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
KeyNode node;
|
|
|
|
|
|
|
|
// Fill in the node info
|
2019-03-11 02:05:37 +00:00
|
|
|
node.name = CommandID{}; // don't associate branches with a command
|
2014-11-08 16:42:34 +00:00
|
|
|
node.category = cat;
|
|
|
|
node.prefix = pfx;
|
|
|
|
node.label = pfx;
|
|
|
|
node.index = nodecnt++;
|
|
|
|
node.ispfx = true;
|
|
|
|
node.isparent = true;
|
|
|
|
node.depth = depth++;
|
2017-05-25 14:45:47 +00:00
|
|
|
node.isopen = true;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Add it to the tree
|
2018-02-02 18:24:53 +00:00
|
|
|
mNodes.push_back(node);
|
2014-11-08 16:42:34 +00:00
|
|
|
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
|
|
|
|
{
|
2020-01-31 15:11:18 +00:00
|
|
|
auto label = labels[i];
|
|
|
|
node.label = label.Strip().Translation();
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Fill in remaining info
|
|
|
|
node.name = name;
|
2018-02-09 02:16:13 +00:00
|
|
|
node.key = keys[i];
|
2014-11-08 16:42:34 +00:00
|
|
|
node.index = nodecnt++;
|
|
|
|
node.depth = depth;
|
|
|
|
|
|
|
|
// Add it to the tree
|
2018-02-02 18:24:53 +00:00
|
|
|
mNodes.push_back(node);
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Measure key
|
2018-02-09 02:16:13 +00:00
|
|
|
GetTextExtent(node.key.Display(), &x, &y);
|
2014-11-08 16:42:34 +00:00
|
|
|
mLineHeight = wxMax(mLineHeight, y);
|
|
|
|
mKeyWidth = wxMax(mKeyWidth, x);
|
|
|
|
|
|
|
|
// Prepend prefix for all view types to determine maximum
|
|
|
|
// column widths
|
|
|
|
wxString label = node.label;
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!node.prefix.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
label = node.prefix + wxT(" - ") + label;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Measure label
|
|
|
|
GetTextExtent(label, &x, &y);
|
|
|
|
mLineHeight = wxMax(mLineHeight, y);
|
|
|
|
mCommandWidth = wxMax(mCommandWidth, x);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// For debugging
|
2019-02-12 00:10:48 +00:00
|
|
|
for (int j = 0; j < mNodes.size(); j++)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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,
|
2017-10-09 05:03:14 +00:00
|
|
|
node.name,
|
|
|
|
node.category,
|
|
|
|
node.prefix,
|
|
|
|
node.label);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Update horizontal scrollbar
|
|
|
|
UpdateHScroll();
|
|
|
|
|
|
|
|
// Refresh the view lines
|
2017-05-25 14:45:47 +00:00
|
|
|
RefreshLines(bSort);
|
2014-11-08 16:42:34 +00:00
|
|
|
|
2017-05-25 14:45:47 +00:00
|
|
|
// Set the selected node if we've just reprepared the list and nothing was selected.
|
|
|
|
if ((GetSelection()==wxNOT_FOUND) && bSort )
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
SelectNode(LineToIndex(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Refresh the list of lines within the current view
|
|
|
|
//
|
|
|
|
void
|
2017-05-25 14:45:47 +00:00
|
|
|
KeyView::RefreshLines(bool bSort)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
int cnt = (int) mNodes.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
int linecnt = 0;
|
2018-02-02 18:24:53 +00:00
|
|
|
mLines.clear();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Process a filter if one is set
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!mFilter.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
// 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") +
|
2018-02-09 02:16:13 +00:00
|
|
|
node.key.Display().Lower();
|
2014-11-08 16:42:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ViewByName:
|
|
|
|
searchit = node.label.Lower();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ViewByKey:
|
2018-02-09 02:16:13 +00:00
|
|
|
searchit = node.key.Display().Lower();
|
2014-11-08 16:42:34 +00:00
|
|
|
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 +.
|
2019-02-28 13:54:36 +00:00
|
|
|
if ((mViewType == ViewByKey) &&
|
2019-03-06 19:53:39 +00:00
|
|
|
(mFilter.length() == 1) &&
|
2019-02-28 13:54:36 +00:00
|
|
|
(mFilter != searchit.Last() ||
|
2019-03-06 19:53:39 +00:00
|
|
|
((searchit.length() > 1) &&
|
|
|
|
((wxString)(searchit.GetChar(searchit.length() - 2)) != wxT("+")))))
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
// 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)
|
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
std::vector<KeyNode*> queue;
|
2014-11-08 16:42:34 +00:00
|
|
|
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;
|
2018-02-02 18:24:53 +00:00
|
|
|
for (int k = (int) mLines.size() - 1; k >= 0; k--)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
// 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)
|
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
queue.push_back(&mNodes[j]);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2018-02-02 18:24:53 +00:00
|
|
|
for (int j = (int) queue.size() - 1; j >= 0; j--)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
queue[j]->isopen = true;
|
|
|
|
queue[j]->line = linecnt++;
|
2018-02-02 18:24:53 +00:00
|
|
|
mLines.push_back(queue[j]);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally add the child node
|
|
|
|
node.line = linecnt++;
|
2018-02-02 18:24:53 +00:00
|
|
|
mLines.push_back(&node);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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++;
|
2018-02-02 18:24:53 +00:00
|
|
|
mLines.push_back(&node);
|
2014-11-08 16:42:34 +00:00
|
|
|
|
2021-01-12 11:56:09 +00:00
|
|
|
// If this node is not open, then skip all of its descendants
|
2014-11-08 16:42:34 +00:00
|
|
|
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++;
|
2018-02-02 18:24:53 +00:00
|
|
|
mLines.push_back(&node);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-25 14:45:47 +00:00
|
|
|
// 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 )
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
2017-05-25 14:45:47 +00:00
|
|
|
//To see how many lines are being sorted (and how often).
|
2019-02-12 00:10:48 +00:00
|
|
|
//wxLogDebug("Sorting %i lines for type %i", mLines.size(), mViewType);
|
2017-05-25 14:45:47 +00:00
|
|
|
|
|
|
|
// Speed up the comparison function used in sorting
|
|
|
|
// by only translating this string once.
|
|
|
|
CommandTranslated = _("Command");
|
2014-11-08 16:42:34 +00:00
|
|
|
|
2017-05-25 14:45:47 +00:00
|
|
|
// Sort list based on type
|
|
|
|
switch (mViewType)
|
|
|
|
{
|
|
|
|
case ViewByTree:
|
2018-02-02 18:24:53 +00:00
|
|
|
std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByTree);
|
2017-05-25 14:45:47 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ViewByName:
|
2018-02-02 18:24:53 +00:00
|
|
|
std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByName);
|
2017-05-25 14:45:47 +00:00
|
|
|
break;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
2017-05-25 14:45:47 +00:00
|
|
|
case ViewByKey:
|
2018-02-02 18:24:53 +00:00
|
|
|
std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByKey);
|
2017-05-25 14:45:47 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now, reassign the line numbers
|
2018-02-02 18:24:53 +00:00
|
|
|
for (int i = 0; i < (int) mLines.size(); i++)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
mLines[i]->line = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// For debugging
|
2019-02-12 00:10:48 +00:00
|
|
|
for (int j = 0; j < mLines.size(); j++)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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,
|
2017-10-09 05:03:14 +00:00
|
|
|
node.name,
|
|
|
|
node.category,
|
|
|
|
node.prefix,
|
|
|
|
node.label);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
// Tell listbox the NEW count and refresh the entire view
|
2018-02-02 18:24:53 +00:00
|
|
|
SetItemCount(mLines.size());
|
2014-11-08 16:42:34 +00:00
|
|
|
RefreshAll();
|
|
|
|
|
|
|
|
#if wxUSE_ACCESSIBILITY
|
|
|
|
// Let accessibility know that the list has changed
|
2017-05-25 14:45:47 +00:00
|
|
|
if( bSort )
|
|
|
|
mAx->ListUpdated();
|
2014-11-08 16:42:34 +00:00
|
|
|
#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
|
|
|
|
//
|
2020-04-11 07:08:33 +00:00
|
|
|
// Must do this ourselves because we want to send notifications
|
2014-11-08 16:42:34 +00:00
|
|
|
// 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
|
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
if (line < 0 || line >= (int) mLines.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
return wxNOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mLines[line]->index;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Converts a node index to a line index
|
|
|
|
//
|
|
|
|
int
|
|
|
|
KeyView::IndexToLine(int index) const
|
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
if (index < 0 || index >= (int) mNodes.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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;
|
2017-08-19 15:48:45 +00:00
|
|
|
wxRect r2 = rect; // for just the key shortcut.
|
2014-11-08 16:42:34 +00:00
|
|
|
wxCoord indent = 0;
|
|
|
|
|
|
|
|
// When in tree view mode, each younger branch gets indented by the
|
|
|
|
// width of the open/close bitmaps
|
|
|
|
if (mViewType == ViewByTree)
|
|
|
|
{
|
2017-08-19 15:48:45 +00:00
|
|
|
indent += mKeyWidth + KV_COLUMN_SPACER + node->depth * KV_BITMAP_SIZE;
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Offset left side by the indentation (if any) and scroll amounts
|
|
|
|
r.x = indent - mScrollX;
|
2017-08-19 15:48:45 +00:00
|
|
|
r2.x = -mScrollX;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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);
|
2017-08-19 15:48:45 +00:00
|
|
|
r2.width = mKeyWidth;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Selected lines get a solid background
|
|
|
|
if (IsSelected(line))
|
|
|
|
{
|
|
|
|
if (FindFocus() == this)
|
|
|
|
{
|
|
|
|
// Focused lines get highlighted background
|
2017-07-22 10:05:08 +00:00
|
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
2014-11-08 16:42:34 +00:00
|
|
|
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
|
|
|
|
dc.DrawRectangle(r);
|
2017-08-19 15:48:45 +00:00
|
|
|
|
2017-07-22 10:05:08 +00:00
|
|
|
// 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);
|
2017-08-19 15:48:45 +00:00
|
|
|
|
|
|
|
if (mViewType == ViewByTree){
|
|
|
|
dc.DrawRectangle(r2);
|
|
|
|
AColor::DrawFocus(dc, r2);
|
|
|
|
}
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-08-24 01:25:01 +00:00
|
|
|
// Non focused lines get a light background
|
2014-11-08 16:42:34 +00:00
|
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
2017-07-22 10:05:08 +00:00
|
|
|
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)));
|
2014-11-08 16:42:34 +00:00
|
|
|
dc.DrawRectangle(r);
|
2017-08-19 15:48:45 +00:00
|
|
|
if (mViewType == ViewByTree)
|
|
|
|
dc.DrawRectangle(r2);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Non-selected lines get a thin bottom border
|
|
|
|
dc.SetPen(wxColour(240, 240, 240));
|
Fix cross-platform one-pixel differences in line drawing...
... Make all line drawing go through our AColor which wraps the wxDC
line-drawing functions.
Despite what wxWidgets documentation says, the functions still don't
consistently include the first point and omit the last point of a line. I
observed inclusion of both on Mac, while on Windows, omission sometimes of the
first point instead of the last.
Discrepancies could be observed in 2.3.0 between Windows and Mac, using the
Window magnifier or command+alt+ +, zooming in closely on the ends of the
shadow lines below and right of tracks, or at the bottom-right corners of the
bevels drawn around vertical rulers.
So where there is an observable one-pixel difference of drawing between
platforms, there is the question, which was the intent when the drawing code
was written? Should the coordinates be corrected by one or not?
I reviewed each case and used my judgment.
Most of the calls are in drawing rulers. Major tick lines were drawn five
pixels long, and minor, three, on Mac. I keep this behavior, but you might
argue for making them four and two.
On the other hand the drawing of ruler grid lines, which you can see in
the equalization and frequency analysis windows, did need a one-pixel correction
to avoid straying out of bounds.
2018-11-17 14:25:26 +00:00
|
|
|
AColor::Line(dc, r.GetLeft(), r.GetBottom(), r.GetRight(), r.GetBottom());
|
2017-08-19 15:48:45 +00:00
|
|
|
if (mViewType == ViewByTree )
|
Fix cross-platform one-pixel differences in line drawing...
... Make all line drawing go through our AColor which wraps the wxDC
line-drawing functions.
Despite what wxWidgets documentation says, the functions still don't
consistently include the first point and omit the last point of a line. I
observed inclusion of both on Mac, while on Windows, omission sometimes of the
first point instead of the last.
Discrepancies could be observed in 2.3.0 between Windows and Mac, using the
Window magnifier or command+alt+ +, zooming in closely on the ends of the
shadow lines below and right of tracks, or at the bottom-right corners of the
bevels drawn around vertical rulers.
So where there is an observable one-pixel difference of drawing between
platforms, there is the question, which was the intent when the drawing code
was written? Should the coordinates be corrected by one or not?
I reviewed each case and used my judgment.
Most of the calls are in drawing rulers. Major tick lines were drawn five
pixels long, and minor, three, on Mac. I keep this behavior, but you might
argue for making them four and two.
On the other hand the drawing of ruler grid lines, which you can see in
the equalization and frequency analysis windows, did need a one-pixel correction
to avoid straying out of bounds.
2018-11-17 14:25:26 +00:00
|
|
|
AColor::Line(dc, r2.GetLeft(), r2.GetBottom(), r2.GetRight(), r2.GetBottom());
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
|
2015-08-24 01:25:01 +00:00
|
|
|
if (node->iscat || node->ispfx)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
2017-08-19 15:48:45 +00:00
|
|
|
wxCoord bx = x + mKeyWidth + KV_COLUMN_SPACER;
|
2015-08-24 01:25:01 +00:00
|
|
|
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);
|
|
|
|
}
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Indent text
|
|
|
|
x += KV_LEFT_MARGIN;
|
|
|
|
|
2017-08-19 15:48:45 +00:00
|
|
|
// Draw the key and command columns
|
2018-02-09 02:16:13 +00:00
|
|
|
dc.DrawText(node->key.Display(), x , rect.y);
|
2017-08-19 15:48:45 +00:00
|
|
|
dc.DrawText(label, x + mKeyWidth + KV_COLUMN_SPACER + node->depth * KV_BITMAP_SIZE, rect.y);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Adjust left edge by margin and account for scrolling
|
|
|
|
wxCoord x = rect.x + KV_LEFT_MARGIN - mScrollX;
|
|
|
|
|
|
|
|
// Prepend prefix if available
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!node->prefix.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
label = node->prefix + wxT(" - ") + label;
|
|
|
|
}
|
|
|
|
|
2017-08-19 15:48:45 +00:00
|
|
|
// don't swap the columns based on view type
|
|
|
|
if((mViewType == ViewByName) || (mViewType == ViewByKey))
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
// Draw key columnd and then command column
|
2018-02-09 02:16:13 +00:00
|
|
|
dc.DrawText(node->key.Display(), x, rect.y);
|
2014-11-08 16:42:34 +00:00
|
|
|
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();
|
|
|
|
|
2019-07-05 08:25:02 +00:00
|
|
|
// 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.
|
2014-11-08 16:42:34 +00:00
|
|
|
if (GetSelection() != wxNOT_FOUND)
|
|
|
|
{
|
2015-04-13 05:52:32 +00:00
|
|
|
RefreshRow(GetSelection());
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
2019-07-05 08:25:02 +00:00
|
|
|
|
2014-11-08 16:42:34 +00:00
|
|
|
#if wxUSE_ACCESSIBILITY
|
2019-07-05 08:25:02 +00:00
|
|
|
// Tell accessibility of the change
|
|
|
|
mAx->SetCurrentLine(GetSelection());
|
2014-11-08 16:42:34 +00:00
|
|
|
#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)
|
|
|
|
{
|
2015-04-13 05:52:32 +00:00
|
|
|
RefreshRow(GetSelection());
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2016-02-13 15:43:16 +00:00
|
|
|
// Get NEW scroll position and scroll the view
|
2014-11-08 16:42:34 +00:00
|
|
|
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
|
2015-08-19 05:37:03 +00:00
|
|
|
ScrollToRow(topline);
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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
|
2018-02-02 18:24:53 +00:00
|
|
|
if (line < (int) mLines.size() - 1)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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
|
2015-08-19 05:37:03 +00:00
|
|
|
ScrollToRow(topline);
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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:
|
|
|
|
{
|
2018-02-02 18:24:53 +00:00
|
|
|
int cnt = (int) mLines.size();
|
2014-11-08 16:42:34 +00:00
|
|
|
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)
|
|
|
|
{
|
2018-02-09 02:16:13 +00:00
|
|
|
label = GetKey(LineToIndex(i)).Display();
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
2018-02-09 02:16:13 +00:00
|
|
|
label = GetKey(LineToIndex(i)).Display();
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2015-08-19 05:37:03 +00:00
|
|
|
int line = VirtualHitTest(pos.y);
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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
|
2015-08-19 05:37:03 +00:00
|
|
|
ScrollToRow(topline);
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// And make sure current line is still selected
|
|
|
|
SelectNode(LineToIndex(line));
|
2019-07-30 10:26:46 +00:00
|
|
|
|
|
|
|
// 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;
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
//
|
2020-04-12 22:48:14 +00:00
|
|
|
// To accomplish this, we prepend each label with its line number
|
2014-11-08 16:42:34 +00:00
|
|
|
// (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.
|
|
|
|
//
|
2018-02-02 18:24:53 +00:00
|
|
|
bool
|
|
|
|
KeyView::CmpKeyNodeByTree(KeyNode *t1, KeyNode *t2)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
2017-05-24 21:01:19 +00:00
|
|
|
unsigned int k1UInt= 0xffffffff;
|
|
|
|
unsigned int k2UInt= 0xffffffff;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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.
|
2017-05-24 21:01:19 +00:00
|
|
|
if (t1->category != CommandTranslated || t1->isparent)
|
|
|
|
k1UInt = (unsigned int) t1->line;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// See above for explanation
|
2017-05-24 21:01:19 +00:00
|
|
|
if (t2->category != CommandTranslated || t2->isparent)
|
|
|
|
k2UInt = (unsigned int) t2->line;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
2017-05-24 21:01:19 +00:00
|
|
|
if( k1UInt < k2UInt )
|
2018-02-02 18:24:53 +00:00
|
|
|
return true;
|
2017-05-24 21:01:19 +00:00
|
|
|
if( k1UInt > k2UInt )
|
2018-02-02 18:24:53 +00:00
|
|
|
return false;
|
2014-11-08 16:42:34 +00:00
|
|
|
|
2018-02-02 18:24:53 +00:00
|
|
|
return ( t1->label < t2->label );
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Sort compare function for command view
|
|
|
|
//
|
|
|
|
// Nothing special here, just a standard ascending sort.
|
|
|
|
//
|
2018-02-02 18:24:53 +00:00
|
|
|
bool
|
|
|
|
KeyView::CmpKeyNodeByName(KeyNode *t1, KeyNode *t2)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxString k1 = t1->label;
|
|
|
|
wxString k2 = t2->label;
|
|
|
|
|
|
|
|
// Prepend prefix if available
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!t1->prefix.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
k1 = t1->prefix + wxT(" - ") + k1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepend prefix if available
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!t2->prefix.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
k2 = t2->prefix + wxT(" - ") + k2;
|
|
|
|
}
|
|
|
|
|
2018-02-02 18:24:53 +00:00
|
|
|
return (k1 < k2);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2020-04-11 07:08:33 +00:00
|
|
|
// We accomplish this by prefixing all non-assigned entries with 0xff.
|
2014-11-08 16:42:34 +00:00
|
|
|
// 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.
|
|
|
|
//
|
2018-02-02 18:24:53 +00:00
|
|
|
bool
|
|
|
|
KeyView::CmpKeyNodeByKey(KeyNode *t1, KeyNode *t2)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
2018-02-09 02:16:13 +00:00
|
|
|
wxString k1 = t1->key.Display();
|
|
|
|
wxString k2 = t2->key.Display();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Left node is unassigned, so prefix it
|
2019-02-12 00:10:48 +00:00
|
|
|
if(k1.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
k1 = wxT("\xff");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Right node is unassigned, so prefix it
|
2019-02-12 00:10:48 +00:00
|
|
|
if(k2.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
k2 = wxT("\xff");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add prefix if available
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!t1->prefix.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
k1 += t1->prefix + wxT(" - ");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add prefix if available
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!t2->prefix.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
k2 += t2->prefix + wxT(" - ");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add labels
|
|
|
|
k1 += t1->label;
|
|
|
|
k2 += t2->label;
|
|
|
|
|
2018-02-02 18:24:53 +00:00
|
|
|
return (k1 < k2);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if wxUSE_ACCESSIBILITY
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return parenthood state of line
|
|
|
|
//
|
|
|
|
bool
|
|
|
|
KeyView::HasChildren(int line)
|
|
|
|
{
|
|
|
|
// Make sure line is valid
|
2018-02-02 18:24:53 +00:00
|
|
|
if (line < 0 || line >= (int) mLines.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxASSERT(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mLines[line]->isparent;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns espanded/collapsed state of line
|
|
|
|
//
|
|
|
|
bool
|
|
|
|
KeyView::IsExpanded(int line)
|
|
|
|
{
|
|
|
|
// Make sure line is valid
|
2018-02-02 18:24:53 +00:00
|
|
|
if (line < 0 || line >= (int) mLines.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxASSERT(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mLines[line]->isopen;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Returns the height of the line
|
|
|
|
//
|
|
|
|
wxCoord
|
|
|
|
KeyView::GetLineHeight(int line)
|
|
|
|
{
|
|
|
|
// Make sure line is valid
|
2018-02-02 18:24:53 +00:00
|
|
|
if (line < 0 || line >= (int) mLines.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
wxASSERT(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-13 05:52:32 +00:00
|
|
|
return OnGetRowHeight(line);
|
2014-11-08 16:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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
|
2018-02-02 18:24:53 +00:00
|
|
|
if (line < 0 || line >= (int) mLines.size())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2018-02-09 02:16:13 +00:00
|
|
|
wxString key = GetKey(index).Display();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// Add the key if it isn't empty
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!key.empty())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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)
|
2018-04-03 12:45:30 +00:00
|
|
|
: WindowAccessible(view)
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2016-02-13 15:43:16 +00:00
|
|
|
// Inform accessibility a NEW line has been selected and/or a previously
|
2014-11-08 16:42:34 +00:00
|
|
|
// 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
|
2019-05-17 07:33:33 +00:00
|
|
|
if (mView == wxWindow::FindFocus()) {
|
|
|
|
NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
|
|
|
|
mView,
|
|
|
|
wxOBJID_CLIENT,
|
|
|
|
mLastId);
|
|
|
|
}
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
// 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
|
2015-04-13 05:52:32 +00:00
|
|
|
if (line < 0 || line >= (int) mView->GetItemCount())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
// 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
|
2015-04-13 05:52:32 +00:00
|
|
|
if (line < 0 || line >= (int) mView->GetItemCount())
|
2014-11-08 16:42:34 +00:00
|
|
|
{
|
|
|
|
// 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)
|
|
|
|
{
|
2015-04-13 05:52:32 +00:00
|
|
|
*childCount = (int) mView->GetItemCount();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2019-02-12 00:10:48 +00:00
|
|
|
actionName->clear();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
return wxACC_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the description for this object or a child.
|
|
|
|
wxAccStatus
|
|
|
|
KeyViewAx::GetDescription(int WXUNUSED(childId), wxString *description)
|
|
|
|
{
|
2019-02-12 00:10:48 +00:00
|
|
|
description->clear();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
return wxACC_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns help text for this object or a child, similar to tooltip text.
|
|
|
|
wxAccStatus
|
|
|
|
KeyViewAx::GetHelpText(int WXUNUSED(childId), wxString *helpText)
|
|
|
|
{
|
2019-02-12 00:10:48 +00:00
|
|
|
helpText->clear();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2019-02-12 00:10:48 +00:00
|
|
|
shortcut->clear();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-02-12 00:10:48 +00:00
|
|
|
strValue->clear();
|
2014-11-08 16:42:34 +00:00
|
|
|
|
|
|
|
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
|