173 lines
4.5 KiB
Python
173 lines
4.5 KiB
Python
"tests a set of controls for repeated hotkey errors"
|
|
|
|
__revision__ = "0.0.1"
|
|
|
|
from win32defines import SS_NOPREFIX
|
|
|
|
#-----------------------------------------------------------------------------
|
|
def RepeatedHotkeyTest(windows):
|
|
"Return the repeated hotkey errors"
|
|
|
|
hotkeyControls, allChars, hotkeys = CollectDialogInfo(windows)
|
|
|
|
#if windows[0].Class == "MenuItem":
|
|
# print "=-" * 20
|
|
# import pprint
|
|
# pprint.pprint (windows)
|
|
|
|
# get the available characters in the dialog
|
|
dlgAvailable = allChars.difference(hotkeys)
|
|
|
|
# remove some characters that aren't good choices for hotkeys
|
|
dlgAvailable.difference_update(set("-& _"))
|
|
|
|
|
|
bugs = []
|
|
# for each hotkey
|
|
for char, controls in hotkeyControls.items():
|
|
|
|
# if there is more than one control associated then it is a bug
|
|
if len(controls) > 1:
|
|
|
|
ctrlsAvailableChars = ""
|
|
|
|
# build up the available characters for each control
|
|
for c in controls:
|
|
controlChars = ""
|
|
controlChars = set(c.Text.lower())
|
|
|
|
controlAvailableChars = controlChars.intersection(dlgAvailable)
|
|
controlAvailableChars = "<%s>" % SetAsString(controlAvailableChars)
|
|
|
|
ctrlsAvailableChars += controlAvailableChars
|
|
|
|
refCtrls = [ctrl.ref for ctrl in controls if ctrl.ref]
|
|
refHotkeyControls, refAllChars, refHotkeys = CollectDialogInfo(refCtrls)
|
|
|
|
isInRef = -1
|
|
if len(refHotkeys) > 1:
|
|
isInRef = 1
|
|
else:
|
|
isInRef = 0
|
|
|
|
bugs.append((
|
|
controls,
|
|
{
|
|
"RepeatedHotkey" : char,
|
|
"CharsUsedInDialog" : SetAsString(hotkeys),
|
|
"AllCharsInDialog" : SetAsString(allChars),
|
|
"AvailableInControls" : ctrlsAvailableChars,
|
|
},
|
|
"RepeatedHotkey",
|
|
isInRef)
|
|
)
|
|
|
|
# # What is the algorithm to try and do all that is necessary to find
|
|
# # if it is possible to get a fix character if none of the current
|
|
# # characters are free
|
|
# for bug in bugs:
|
|
# for c, chars in bug.bugData:
|
|
# # control has no possibilities
|
|
# if not chars:
|
|
# # check if there are any other hotkey controls
|
|
# # in the dialog that could be used
|
|
# others = set(c.Title.lower()).intersection(unUsedChars)
|
|
|
|
|
|
return bugs
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
def CollectDialogInfo(windows):
|
|
hotkeyControls = {}
|
|
allChars = ''
|
|
|
|
for win in windows:
|
|
# skip it if it doesn't implement hotkey functionality
|
|
if not ImplementsHotkey(win):
|
|
continue
|
|
|
|
# get the hotkey
|
|
pos, char = GetHotkey(win.Text)
|
|
|
|
# if no hotkey for this control
|
|
# then continue
|
|
if not char:
|
|
continue
|
|
|
|
# store hotkey with list of used hotkeys
|
|
# map this hotkey to the list of controls that have it
|
|
hotkeyControls.setdefault(char.lower(), []).append(win)
|
|
|
|
|
|
# Add the title of this control to the list of available
|
|
# characters for the dialog
|
|
allChars += win.Text.lower()
|
|
|
|
|
|
allChars = set(allChars)
|
|
hotkeys = set(hotkeyControls.keys())
|
|
|
|
return hotkeyControls, allChars, hotkeys
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# get hokey position
|
|
def GetHotkey(text):
|
|
"Return the position and character of the hotkey"
|
|
|
|
# find the last & character that is not followed
|
|
# by & or by the end of the string
|
|
|
|
curEnd = len(text) + 1
|
|
text = text.replace("&&", "__")
|
|
while True:
|
|
pos = text.rfind("&", 0, curEnd)
|
|
|
|
# One found was at the end of the text or
|
|
# no (more) & were found
|
|
# so return the None value
|
|
if pos == -1 or pos == len(text):
|
|
return (-1, '')
|
|
|
|
# One found but was prededed by non valid hotkey character
|
|
# so continue, as that can't be a shortcut
|
|
if text[pos - 1] == '&':
|
|
curEnd = pos - 2
|
|
continue
|
|
|
|
# 2 ampersands in a row - so skip
|
|
# the 1st one and continue
|
|
return (pos, text[pos+1])
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
def SetAsString(s):
|
|
return "".join(sorted(s))
|
|
|
|
|
|
#=============================================================================
|
|
def ImplementsHotkey(win):
|
|
"checks whether a control interprets & character to be a hotkey"
|
|
|
|
# buttons always implement hotkey
|
|
if win.Class == "Button":
|
|
return True
|
|
|
|
# Statics do if they don't have SS_NOPREFIX style
|
|
elif win.Class == "Static" and not win.HasStyle(SS_NOPREFIX):
|
|
return True
|
|
|
|
if win.Class == "MenuItem" and win.State != "2048":
|
|
return True
|
|
|
|
# Most controls don't - so just return false if
|
|
# neither of the above condition hold
|
|
return False
|
|
|
|
|
|
RepeatedHotkeyTest.TestsMenus = True
|
|
|