Skip to content

Commit

Permalink
Merge pull request openstenoproject#183 from mighele/user-switchable-…
Browse files Browse the repository at this point in the history
…spaces

User switchable spaces
  • Loading branch information
balshetzer committed Mar 7, 2014
2 parents 3a1fd04 + c9ed5a3 commit 247d0c2
Show file tree
Hide file tree
Showing 6 changed files with 410 additions and 28 deletions.
9 changes: 9 additions & 0 deletions plover/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def init_engine(engine, config):

engine.enable_stroke_logging(config.get_enable_stroke_logging())
engine.enable_translation_logging(config.get_enable_translation_logging())
engine.set_space_placement(config.get_space_placement())

engine.set_is_running(config.get_auto_start())

Expand Down Expand Up @@ -110,6 +111,10 @@ def update_engine(engine, old, new):
if old.get_enable_translation_logging() != enable_translation_logging:
engine.enable_translation_logging(enable_translation_logging)

space_placement = new.get_space_placement()
if old.get_space_placement() != space_placement:
engine.set_space_placement(space_placement)

def same_thread_hook(fn, *args):
fn(*args)

Expand Down Expand Up @@ -239,6 +244,10 @@ def set_log_file_name(self, filename):
def enable_stroke_logging(self, b):
"""Turn stroke logging on or off."""
self.logger.enable_stroke_logging(b)

def set_space_placement(self, s):
"""Set whether spaces will be inserted before the output or after the output."""
self.formatter.set_space_placement(s)

def enable_translation_logging(self, b):
"""Turn translation logging on or off."""
Expand Down
13 changes: 12 additions & 1 deletion plover/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
STROKE_DISPLAY_Y_OPTION = 'y'
DEFAULT_STROKE_DISPLAY_Y = -1

OUTPUT_CONFIG_SECTION = 'Output Configuration'
OUTPUT_CONFIG_SPACE_PLACEMENT_OPTION = 'space_placement'
DEFAULT_OUTPUT_CONFIG_SPACE_PLACEMENT = 'Before Output'

CONFIG_FRAME_SECTION = 'Config Frame'
CONFIG_FRAME_X_OPTION = 'x'
DEFAULT_CONFIG_FRAME_X = -1
Expand Down Expand Up @@ -208,6 +212,13 @@ def get_show_stroke_display(self):
return self._get_bool(STROKE_DISPLAY_SECTION,
STROKE_DISPLAY_SHOW_OPTION, DEFAULT_STROKE_DISPLAY_SHOW)

def get_space_placement(self):
return self._get(OUTPUT_CONFIG_SECTION, OUTPUT_CONFIG_SPACE_PLACEMENT_OPTION,
DEFAULT_OUTPUT_CONFIG_SPACE_PLACEMENT)

def set_space_placement(self, s):
self._set(OUTPUT_CONFIG_SECTION, OUTPUT_CONFIG_SPACE_PLACEMENT_OPTION, s)

def set_stroke_display_on_top(self, b):
self._set(STROKE_DISPLAY_SECTION, STROKE_DISPLAY_ON_TOP_OPTION, b)

Expand Down Expand Up @@ -360,4 +371,4 @@ def _dict_entry_key(s):
try:
return int(s[len(DICTIONARY_FILE_OPTION):])
except ValueError:
return -1
return -1
167 changes: 159 additions & 8 deletions plover/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ def set_output(self, output):
fields = output_type._fields
self._output = output_type(*[getattr(output, f, noop) for f in fields])

def set_space_placement(self, s):
"""Set whether spaces will be inserted before the output or after the output"""
if s == 'After Output':
self.spaces_after = True
else:
self.spaces_after = False

def format(self, undo, do, prev):
"""Format the given translations.
Expand All @@ -68,9 +75,9 @@ def format(self, undo, do, prev):
for t in do:
last_action = _get_last_action(prev.formatting if prev else None)
if t.english:
t.formatting = _translation_to_actions(t.english, last_action)
t.formatting = _translation_to_actions(t.english, last_action, self.spaces_after)
else:
t.formatting = _raw_to_actions(t.rtfcre[0], last_action)
t.formatting = _raw_to_actions(t.rtfcre[0], last_action, self.spaces_after)
prev = t

old = [a for t in undo for a in t.formatting]
Expand Down Expand Up @@ -258,7 +265,7 @@ def __repr__(self):
# # doesn't contain unescaped { or }
# """, re.VERBOSE)

def _translation_to_actions(translation, last_action):
def _translation_to_actions(translation, last_action, spaces_after):
"""Create actions for a translation.
Arguments:
Expand All @@ -284,7 +291,7 @@ def _translation_to_actions(translation, last_action):
return [last_action.copy_state()]

for atom in atoms:
action = _atom_to_action(atom, last_action)
action = _atom_to_action(atom, last_action, spaces_after)
actions.append(action)
last_action = action

Expand All @@ -302,7 +309,7 @@ def _translation_to_actions(translation, last_action):
META_KEY_COMBINATION = '#'
META_COMMAND = 'PLOVER:'

def _raw_to_actions(stroke, last_action):
def _raw_to_actions(stroke, last_action, spaces_after):
"""Turn a raw stroke into actions.
Arguments:
Expand All @@ -319,11 +326,33 @@ def _raw_to_actions(stroke, last_action):
# output the raw stroke as is.
no_dash = stroke.replace('-', '', 1)
if no_dash.isdigit():
return _translation_to_actions(no_dash, last_action)
return _translation_to_actions(no_dash, last_action, spaces_after)
else:
return [_Action(text=(SPACE + stroke), word=stroke)]
if spaces_after:
return [_Action(text=(stroke + SPACE), word=stroke)]
else:
return [_Action(text=(SPACE + stroke), word=stroke)]

def _atom_to_action(atom, last_action, spaces_after):
"""Convert an atom into an action.
Arguments:
atom -- A string holding an atom. An atom is an irreducible string that is
either entirely a single meta command or entirely text containing no meta
commands.
last_action -- The context in which the new action takes place.
def _atom_to_action(atom, last_action):
Returns: An action for the atom.
"""
if spaces_after:
return _atom_to_action_spaces_after(atom, last_action)
else:
return _atom_to_action_spaces_before(atom, last_action)

def _atom_to_action_spaces_before(atom, last_action):
"""Convert an atom into an action.
Arguments:
Expand All @@ -337,6 +366,7 @@ def _atom_to_action(atom, last_action):
Returns: An action for the atom.
"""

action = _Action()
last_word = last_action.word
last_glue = last_action.glue
Expand Down Expand Up @@ -418,6 +448,127 @@ def _atom_to_action(atom, last_action):
action.word = _rightmost_word(text)
return action

def _atom_to_action_spaces_after(atom, last_action):
"""Convert an atom into an action.
Arguments:
atom -- A string holding an atom. An atom is an irreducible string that is
either entirely a single meta command or entirely text containing no meta
commands.
last_action -- The context in which the new action takes place.
Returns: An action for the atom.
"""

action = _Action()
last_word = last_action.word
last_glue = last_action.glue
last_attach = last_action.attach
last_capitalize = last_action.capitalize
last_lower = last_action.lower
last_orthography = last_action.orthography
last_space = SPACE if last_action.text.endswith(SPACE) else NO_SPACE
meta = _get_meta(atom)
if meta is not None:
meta = _unescape_atom(meta)
if meta in META_COMMAS:
action.text = meta + SPACE
if last_action.text != '':
action.replace = SPACE
if last_attach:
action.replace = NO_SPACE
elif meta in META_STOPS:
action.text = meta + SPACE
action.capitalize = True
action.lower = False
if last_action.text != '':
action.replace = SPACE
if last_attach:
action.replace = NO_SPACE
elif meta == META_CAPITALIZE:
action = last_action.copy_state()
action.capitalize = True
action.lower = False
elif meta == META_LOWER:
action = last_action.copy_state()
action.lower = True
action.capitalize = False
elif meta.startswith(META_COMMAND):
action = last_action.copy_state()
action.command = meta[len(META_COMMAND):]
elif meta.startswith(META_GLUE_FLAG):
action.glue = True
text = meta[len(META_GLUE_FLAG):]
if last_capitalize:
text = _capitalize(text)
if last_lower:
text = _lower(text)
action.text = text + SPACE
action.word = _rightmost_word(text)
if last_glue:
action.replace = SPACE
action.word = _rightmost_word(last_word + text)
if last_attach:
action.replace = NO_SPACE
action.word = _rightmost_word(last_word + text)
elif (meta.startswith(META_ATTACH_FLAG) or
meta.endswith(META_ATTACH_FLAG)):
begin = meta.startswith(META_ATTACH_FLAG)
end = meta.endswith(META_ATTACH_FLAG)
if begin:
meta = meta[len(META_ATTACH_FLAG):]
if end and len(meta) >= len(META_ATTACH_FLAG):
meta = meta[:-len(META_ATTACH_FLAG)]

space = NO_SPACE if end else SPACE
replace_space = NO_SPACE if last_attach else SPACE

if end:
action.attach = True
if begin and end and meta == '':
# We use an empty connection to indicate a "break" in the
# application of orthography rules. This allows the stenographer
# to tell plover not to auto-correct a word.
action.orthography = False
if last_action.text != '':
action.replace = replace_space
if (((begin and not end) or (begin and end and ' ' in meta)) and
last_orthography):
new = orthography.add_suffix(last_word.lower(), meta)
common = commonprefix([last_word.lower(), new])
if last_action.text == '':
replace_space = NO_SPACE
action.replace = last_word[len(common):] + replace_space
meta = new[len(common):]
if begin and end:
if last_action.text != '':
action.replace = replace_space
if last_capitalize:
meta = _capitalize(meta)
if last_lower:
meta = _lower(meta)
action.text = meta + space
action.word = _rightmost_word(
last_word[:len(last_word + last_space)-len(action.replace)] + meta)
if end and not begin and last_space == SPACE:
action.word = _rightmost_word(meta)
elif meta.startswith(META_KEY_COMBINATION):
action = last_action.copy_state()
action.combo = meta[len(META_KEY_COMBINATION):]
else:
text = _unescape_atom(atom)
if last_capitalize:
text = _capitalize(text)
if last_lower:
text = _lower(text)

action.text = text + SPACE
action.word = _rightmost_word(text)
return action

def _get_meta(atom):
"""Return the meta command, if any, without surrounding meta markups."""
if (atom is not None and
Expand Down
40 changes: 40 additions & 0 deletions plover/gui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
ADD_DICTIONARY_BUTTON_NAME = "Add Dictionary"
MACHINE_CONFIG_TAB_NAME = "Machine"
DISPLAY_CONFIG_TAB_NAME = "Display"
OUTPUT_CONFIG_TAB_NAME = "Output"
DICTIONARY_CONFIG_TAB_NAME = "Dictionary"
LOGGING_CONFIG_TAB_NAME = "Logging"
SAVE_CONFIG_BUTTON_NAME = "Save"
Expand All @@ -34,6 +35,10 @@
LOG_TRANSLATIONS_LABEL = "Log Translations"
LOG_FILE_DIALOG_TITLE = "Select a Log File"
CONFIG_BUTTON_NAME = "Configure..."
SPACE_PLACEMENTS_LABEL = "Space Placement:"
SPACE_PLACEMENT_BEFORE = "Before Output"
SPACE_PLACEMENT_AFTER = "After Output"
SPACE_PLACEMENTS = [SPACE_PLACEMENT_BEFORE, SPACE_PLACEMENT_AFTER]
CONFIG_PANEL_SIZE = (-1, -1)
UI_BORDER = 4
COMPONENT_SPACE = 3
Expand Down Expand Up @@ -89,12 +94,14 @@ def __init__(self, engine, config, parent):
notebook)
self.logging_config = LoggingConfig(self.config, notebook)
self.display_config = DisplayConfig(self.config, notebook)
self.output_config = OutputConfig(self.config, notebook)

# Adding each tab
notebook.AddPage(self.machine_config, MACHINE_CONFIG_TAB_NAME)
notebook.AddPage(self.dictionary_config, DICTIONARY_CONFIG_TAB_NAME)
notebook.AddPage(self.logging_config, LOGGING_CONFIG_TAB_NAME)
notebook.AddPage(self.display_config, DISPLAY_CONFIG_TAB_NAME)
notebook.AddPage(self.output_config, OUTPUT_CONFIG_TAB_NAME)

sizer.Add(notebook, proportion=1, flag=wx.EXPAND)

Expand Down Expand Up @@ -150,6 +157,7 @@ def _save(self, event):
self.dictionary_config.save()
self.logging_config.save()
self.display_config.save()
self.output_config.save()

try:
update_engine(self.engine, old_config, self.config)
Expand Down Expand Up @@ -459,3 +467,35 @@ def save(self):

def on_show_strokes(self, event):
StrokeDisplayDialog.display(self.GetParent(), self.config)

class OutputConfig(wx.Panel):

"""Display configuration graphical user interface."""
def __init__(self, config, parent):
"""Create a configuration component based on the given Config.
Arguments:
config -- A Config object.
parent -- This component's parent component.
"""
wx.Panel.__init__(self, parent, size=CONFIG_PANEL_SIZE)
self.config = config
sizer = wx.BoxSizer(wx.VERTICAL)

box = wx.BoxSizer(wx.HORIZONTAL)
box.Add(wx.StaticText(self, label=SPACE_PLACEMENTS_LABEL),
border=COMPONENT_SPACE,
flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
self.choice = wx.Choice(self, choices=SPACE_PLACEMENTS)
self.choice.SetStringSelection(self.config.get_space_placement())
box.Add(self.choice, proportion=1, flag=wx.EXPAND)
sizer.Add(box, border=UI_BORDER, flag=wx.ALL | wx.EXPAND)

self.SetSizer(sizer)

def save(self):
"""Write all parameters to the config."""
self.config.set_space_placement(self.choice.GetStringSelection())
3 changes: 3 additions & 0 deletions plover/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ def test_simple_fields(self):
('show_stroke_display', config.STROKE_DISPLAY_SECTION,
config.STROKE_DISPLAY_SHOW_OPTION, config.DEFAULT_STROKE_DISPLAY_SHOW,
True, False, True),
('space_placement', config.OUTPUT_CONFIG_SECTION,
config.OUTPUT_CONFIG_SPACE_PLACEMENT_OPTION, config.DEFAULT_OUTPUT_CONFIG_SPACE_PLACEMENT,
'Before Output', 'After Output', 'None'),
('stroke_display_on_top', config.STROKE_DISPLAY_SECTION,
config.STROKE_DISPLAY_ON_TOP_OPTION,
config.DEFAULT_STROKE_DISPLAY_ON_TOP, False, True, False),
Expand Down
Loading

0 comments on commit 247d0c2

Please sign in to comment.