1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 23:59:37 +02:00

Improve key/character event handling, esp. for Unicode entry on Windows.

This commit is contained in:
BusinessmanProgrammerSteve 2010-05-11 22:10:44 +00:00
parent 26b59b363b
commit 23ed04bc6a
2 changed files with 143 additions and 123 deletions

View File

@ -1583,35 +1583,19 @@ bool LabelTrack::CaptureKey(wxKeyEvent & event)
{ {
// Cache the keycode // Cache the keycode
int keyCode = event.GetKeyCode(); int keyCode = event.GetKeyCode();
wxChar charCode = keyCode; // Check for modifiers -- this does what wxKeyEvent::HasModifiers() should
#if wxUSE_UNICODE // do (it checks Control instead of CMD on Mac)
charCode = event.GetUnicodeKey(); bool hasMods = event.GetModifiers() & (wxMOD_CMD | wxMOD_ALT);
#endif
if (mSelIndex >= 0) { if (mSelIndex >= 0) {
switch (keyCode) if (IsGoodLabelEditKey(keyCode) && !hasMods) {
{
case WXK_BACK:
case WXK_DELETE:
case WXK_HOME:
case WXK_END:
case WXK_LEFT:
case WXK_RIGHT:
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
case WXK_ESCAPE:
case WXK_TAB:
return true;
break;
}
if( IsGoodLabelCharacter(keyCode, charCode) && !event.CmdDown()) {
return true; return true;
} }
} }
else else
{ {
if( IsGoodLabelFirstCharacter(keyCode, charCode) && !event.CmdDown() ){ if( IsGoodLabelFirstKey(keyCode) && !hasMods)
{
AudacityProject * pProj = GetActiveProject(); AudacityProject * pProj = GetActiveProject();
// If we're playing, don't capture if the selection is the same as the // If we're playing, don't capture if the selection is the same as the
@ -1630,21 +1614,10 @@ bool LabelTrack::CaptureKey(wxKeyEvent & event)
if( GetLabelIndex( pProj->mViewInfo.sel0, pProj->mViewInfo.sel1) != wxNOT_FOUND ) if( GetLabelIndex( pProj->mViewInfo.sel0, pProj->mViewInfo.sel1) != wxNOT_FOUND )
return false; return false;
// Add a label
SetSelected(true);
AddLabel(pProj->mViewInfo.sel0, pProj->mViewInfo.sel1);
pProj->PushState(_("Added label"), _("Label"));
return true; return true;
} }
} }
#if 0
if (IsGoodLabelFirstCharacter(keyCode, charCode) && !event.CmdDown()) {
return true;
}
#endif
return false; return false;
} }
@ -1656,10 +1629,7 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even
// Cache the keycode // Cache the keycode
int keyCode = event.GetKeyCode(); int keyCode = event.GetKeyCode();
wxChar charCode = keyCode; int mods = event.GetModifiers();
#if wxUSE_UNICODE
charCode = event.GetUnicodeKey();
#endif
// All editing keys are only active if we're currently editing a label // All editing keys are only active if we're currently editing a label
if (mSelIndex >= 0) { if (mSelIndex >= 0) {
@ -1696,6 +1666,7 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even
break; break;
case WXK_DELETE: case WXK_DELETE:
case WXK_NUMPAD_DELETE:
{ {
int len = mLabels[mSelIndex]->title.Length(); int len = mLabels[mSelIndex]->title.Length();
@ -1725,59 +1696,86 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even
break; break;
case WXK_HOME: case WXK_HOME:
case WXK_NUMPAD_HOME:
// Move cursor to beginning of label // Move cursor to beginning of label
mCurrentCursorPos = 0; if (mods == wxMOD_SHIFT) {
if (event.ShiftDown()) { mCurrentCursorPos = 0;
mDragXPos = 0; mDragXPos = 0;
} }
else else if (mods == wxMOD_NONE)
{ {
mCurrentCursorPos = 0;
mDragXPos = -1; mDragXPos = -1;
mInitialCursorPos = mCurrentCursorPos; mInitialCursorPos = mCurrentCursorPos;
} }
else {
// Not handled
event.Skip();
}
break; break;
case WXK_END: case WXK_END:
case WXK_NUMPAD_END:
// Move cursor to end of label // Move cursor to end of label
mCurrentCursorPos = (int)mLabels[mSelIndex]->title.length(); if (mods == wxMOD_SHIFT) {
if (event.ShiftDown()) { mCurrentCursorPos = (int)mLabels[mSelIndex]->title.length();
mDragXPos = 0; mDragXPos = 0;
} }
else else if (mods == wxMOD_NONE)
{ {
mCurrentCursorPos = (int)mLabels[mSelIndex]->title.length();
mDragXPos = -1; mDragXPos = -1;
mInitialCursorPos = mCurrentCursorPos; mInitialCursorPos = mCurrentCursorPos;
} }
else {
// Not handled
event.Skip();
}
break; break;
case WXK_LEFT: case WXK_LEFT:
case WXK_NUMPAD_LEFT:
// Moving cursor left // Moving cursor left
if (mCurrentCursorPos > 0) { if (mods == wxMOD_SHIFT) {
mCurrentCursorPos--; if (mCurrentCursorPos > 0) {
if (event.ShiftDown()) { mCurrentCursorPos--;
mDragXPos = 0; mDragXPos = 0;
} }
else }
{ else if (mods == wxMOD_NONE) {
if (mCurrentCursorPos > 0) {
mCurrentCursorPos--;
mDragXPos = -1; mDragXPos = -1;
mInitialCursorPos = mCurrentCursorPos; mInitialCursorPos = mCurrentCursorPos;
} }
} }
else {
// Not handled
event.Skip();
}
break; break;
case WXK_RIGHT: case WXK_RIGHT:
case WXK_NUMPAD_RIGHT:
// Moving cursor right // Moving cursor right
if (mCurrentCursorPos < (int)mLabels[mSelIndex]->title.length()) { if (mods == wxMOD_SHIFT) {
mCurrentCursorPos++; if (mCurrentCursorPos < (int)mLabels[mSelIndex]->title.length()) {
if (event.ShiftDown()) { mCurrentCursorPos++;
mDragXPos = 0; mDragXPos = 0;
} }
else }
{ else if (mods == wxMOD_NONE)
{
if (mCurrentCursorPos < (int)mLabels[mSelIndex]->title.length()) {
mCurrentCursorPos++;
mDragXPos = -1; mDragXPos = -1;
mInitialCursorPos = mCurrentCursorPos; mInitialCursorPos = mCurrentCursorPos;
} }
} }
else {
// Not handled
event.Skip();
}
break; break;
case WXK_RETURN: case WXK_RETURN:
@ -1788,6 +1786,7 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even
break; break;
case WXK_TAB: case WXK_TAB:
case WXK_NUMPAD_TAB:
if (event.ShiftDown()) { if (event.ShiftDown()) {
mSelIndex--; mSelIndex--;
} else { } else {
@ -1815,6 +1814,7 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even
switch (keyCode) { switch (keyCode) {
case WXK_TAB: case WXK_TAB:
case WXK_NUMPAD_TAB:
if (!mLabels.IsEmpty()) { if (!mLabels.IsEmpty()) {
if (event.ShiftDown()) { if (event.ShiftDown()) {
mSelIndex = (int)mLabels.Count() - 1; mSelIndex = (int)mLabels.Count() - 1;
@ -1841,7 +1841,8 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even
return updated; return updated;
} }
/// KeyEvent is called for every keypress when over the label track. /// OnChar is called for incoming characters -- that's any keypress not handled
/// by OnKeyDown.
bool LabelTrack::OnChar(double & newSel0, double & newSel1, wxKeyEvent & event) bool LabelTrack::OnChar(double & newSel0, double & newSel1, wxKeyEvent & event)
{ {
// Only track true changes to the label // Only track true changes to the label
@ -1854,68 +1855,78 @@ bool LabelTrack::OnChar(double & newSel0, double & newSel1, wxKeyEvent & event)
charCode = event.GetUnicodeKey(); charCode = event.GetUnicodeKey();
#endif #endif
if (mSelIndex >= 0) { // We still have some filtering to do. Character events can be generated for,
if (IsGoodLabelCharacter(keyCode, charCode)) { // i.e., the F keys, and if they aren't handled in OnKeyDown() or in the
// Test if cursor is in the end of string or not // command manager we get them here.
if (mLabels[mSelIndex]->highlighted) {
RemoveSelectedText();
}
if (mCurrentCursorPos < (int)mLabels[mSelIndex]->title.length()) { // AWD: the following behavior is not really documented (I figured it out by
// Get substring on the righthand side of cursor // entering lots of Unicode characters on various OSes), and it's possible
wxString rightPart = mLabels[mSelIndex]->title.Mid(mCurrentCursorPos); // that different versions of wxWidgets act differently. It's unfortunately
// Set title to substring on the lefthand side of cursor // the only way I can find to allow input of full Unicode ranges without
mLabels[mSelIndex]->title = mLabels[mSelIndex]->title.Left(mCurrentCursorPos); // breaking other stuff (Audacity's command manager, keyboard menu
//append charcode // navigation, Windows' Alt-+-xxxx arbitrary Unicode entry, etc.)
mLabels[mSelIndex]->title += charCode; bool bogusChar =
//append the right part substring #if defined(__WXMSW__) && wxUSE_UNICODE
mLabels[mSelIndex]->title += rightPart; // In Windows Unicode builds, these have keyCode not matching charCode
} (keyCode != (int)charCode) ||
else #else
{ // In Windows non-unicode, GTK+, and Mac builds the keyCode comes in the
//append charCode // WXK_* range
mLabels[mSelIndex]->title += charCode; (keyCode >= WXK_START && keyCode <= WXK_COMMAND) ||
} #endif
//moving cursor position forward // Avoid modified characters, but allow Alt (option) on Mac because
mCurrentCursorPos++; // several legit characters come in with it set.
mInitialCursorPos = mCurrentCursorPos; (event.CmdDown()) ||
updated = true; #if !defined(__WXMAC__)
} (event.AltDown()) ||
else #endif
event.Skip(); // Avoid control characters on all platforms; Casting to wxUChar to avoid
} // assertions in Windows non-Unicode builds...
else //if( !IsGoodLabelFirstCharacter(keyCode, charCode)) (wxIscntrl((wxUChar)charCode));
{
// Don't automatically start a label unless its one of the accepted if (bogusChar) {
// characters.
// We can always create the label and then type the forbidden character
// as our first character.
event.Skip(); event.Skip();
return false;
}
// If we've reached this point and aren't currently editing, add new label
if (mSelIndex < 0) {
SetSelected(true);
AudacityProject *p = GetActiveProject();
AddLabel(p->mViewInfo.sel0, p->mViewInfo.sel1);
p->PushState(_("Added label"), _("Label"));
}
//
// Now we are definitely in a label; append the incoming character
//
// Test if cursor is in the end of string or not
if (mLabels[mSelIndex]->highlighted) {
RemoveSelectedText();
}
if (mCurrentCursorPos < (int)mLabels[mSelIndex]->title.length()) {
// Get substring on the righthand side of cursor
wxString rightPart = mLabels[mSelIndex]->title.Mid(mCurrentCursorPos);
// Set title to substring on the lefthand side of cursor
mLabels[mSelIndex]->title = mLabels[mSelIndex]->title.Left(mCurrentCursorPos);
//append charcode
mLabels[mSelIndex]->title += charCode;
//append the right part substring
mLabels[mSelIndex]->title += rightPart;
} }
#if 0
else else
{ {
// Create new label //append charCode
wxString rightPart; mLabels[mSelIndex]->title += charCode;
LabelStruct *l = new LabelStruct();
l->t = newSel0;
l->t1 = newSel1;
l->title += wxChar(charCode);
mCurrentCursorPos = 1;
mInitialCursorPos = mCurrentCursorPos;
int len = mLabels.Count();
int pos = 0;
while (pos < len && l->t > mLabels[pos]->t)
pos++;
mLabels.Insert(l, pos);
mSelIndex = pos;
updated = true;
} }
#endif //moving cursor position forward
mCurrentCursorPos++;
mInitialCursorPos = mCurrentCursorPos;
updated = true;
// Make sure the caret is visible // Make sure the caret is visible
mDrawCursor = true; mDrawCursor = true;
@ -2595,21 +2606,30 @@ void LabelTrack::CreateCustomGlyphs()
mbGlyphsReady=true; mbGlyphsReady=true;
} }
/// The 'typing at selection creates label' feature /// Returns true for keys we capture to start a label.
/// will only accept these charCodes as a first label bool LabelTrack::IsGoodLabelFirstKey(int keyCode)
/// character.
bool LabelTrack::IsGoodLabelFirstCharacter(int keyCode, wxChar charCode)
{ {
// The WXK_DELETE is because Windows doesn't convert DELETE to a proper // Allow everything before WXK_START except space and delete, the numpad keys
// unicode value. (Or at least one we can't use...0x46) // when numlock is on, and everything after WXK_COMMAND
return (keyCode < WXK_START && !wxIscntrl(charCode) && (keyCode != WXK_SPACE) && (keyCode != WXK_DELETE)) || return (keyCode < WXK_START
keyCode > WXK_COMMAND; && keyCode != WXK_SPACE && keyCode != WXK_DELETE) ||
(keyCode >= WXK_NUMPAD0 && keyCode <= WXK_DIVIDE) ||
(keyCode >= WXK_NUMPAD_EQUAL && keyCode <= WXK_NUMPAD_DIVIDE) ||
(keyCode > WXK_COMMAND);
} }
/// We'll only accept these characters within a label /// This returns true for keys we capture for label editing.
bool LabelTrack::IsGoodLabelCharacter(int keyCode, wxChar charCode) bool LabelTrack::IsGoodLabelEditKey(int keyCode)
{ {
return (keyCode < WXK_START && !wxIscntrl(charCode)) || // Accept everything outside of WXK_START through WXK_COMMAND, plus the keys
// within that range that are usually printable, plus the ones we use for
// keyboard navigation.
return keyCode < WXK_START ||
(keyCode >= WXK_END && keyCode <= WXK_DOWN) ||
(keyCode >= WXK_NUMPAD0 && keyCode <= WXK_DIVIDE) ||
(keyCode >= WXK_NUMPAD_SPACE && keyCode <= WXK_NUMPAD_ENTER) ||
(keyCode >= WXK_NUMPAD_HOME && keyCode <= WXK_NUMPAD_END) ||
(keyCode >= WXK_NUMPAD_DELETE && keyCode <= WXK_NUMPAD_DIVIDE) ||
keyCode > WXK_COMMAND; keyCode > WXK_COMMAND;
} }

View File

@ -79,8 +79,8 @@ class LabelTrack:public Track {
friend class LabelStruct; friend class LabelStruct;
public: public:
bool IsGoodLabelCharacter(int keyCode, wxChar charCode); bool IsGoodLabelFirstKey(int keyCode);
bool IsGoodLabelFirstCharacter(int keyCode, wxChar charCode); bool IsGoodLabelEditKey(int keyCode);
bool IsTextSelected(); bool IsTextSelected();
void CreateCustomGlyphs(); void CreateCustomGlyphs();