From 91a83e6f1fda6483bfd4b449a1ef7903a00af0ab Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 13 Sep 2009 02:14:27 -0700 Subject: [PATCH] Reworked actions api and actions objects: Created standardized Action object for all gui actions. Actions module constants are actual Action objects (not strings). Keypresses, labels, tooltips, stock icons, etc all associate in the Action constructor. Usage of the action's signaling call () eliminated the need for a reference to handle_states. --- grc/gui/ActionHandler.py | 151 ++++++++-------- grc/gui/Actions.py | 359 ++++++++++++++++++++++++--------------- grc/gui/Bars.py | 12 +- grc/gui/FlowGraph.py | 12 +- grc/gui/MainWindow.py | 20 +-- grc/gui/NotebookPage.py | 5 +- grc/gui/StateCache.py | 6 +- 7 files changed, 319 insertions(+), 246 deletions(-) diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index 901619e6..59e535bd 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -52,10 +52,10 @@ class ActionHandler: @param platform platform module """ self.clipboard = None - for action in Actions.get_all_actions(): action.connect('activate', self._handle_actions) + for action in Actions.get_all_actions(): action.connect('activate', self._handle_action) #setup the main window - self.main_window = MainWindow(self.handle_states, platform) - self.main_window.connect('delete_event', self._quit) + self.main_window = MainWindow(platform) + self.main_window.connect('delete-event', self._quit) self.main_window.connect('key-press-event', self._handle_key_press) self.get_page = self.main_window.get_page self.get_flow_graph = self.main_window.get_flow_graph @@ -65,7 +65,7 @@ class ActionHandler: Messages.send_init(platform) #initialize self.init_file_paths = file_paths - self.handle_states(Actions.APPLICATION_INITIALIZE) + Actions.APPLICATION_INITIALIZE() #enter the mainloop gtk.main() @@ -81,7 +81,7 @@ class ActionHandler: """ try: assert self.get_focus_flag() except AssertionError: return False - try: self.handle_states(Actions.get_action_name_from_key_press(event)) + try: Actions.get_action_from_key_press(event)() except KeyError: return False return True #handled by this method @@ -92,42 +92,24 @@ class ActionHandler: This method in turns calls the state handler to quit. @return true """ - self.handle_states(Actions.APPLICATION_QUIT) + Actions.APPLICATION_QUIT() return True - def _handle_actions(self, action): - """ - Handle all of the activate signals from the gtk actions. - The action signals derive from clicking on a toolbar or menu bar button. - Forward the action to the state handler. - """ - self.handle_states(action.get_name()) - return True - - def handle_states(self, state=''): - """ - Handle the state changes in the GUI. - Handle all of the state changes that arise from the action handler or other gui and - inputs in the application. The state passed to the handle_states method is a string descriping - the change. A series of if/elif statements handle the state by greying out action buttons, causing - changes in the flow graph, saving/opening files... The handle_states method is passed to the - contructors of many of the classes used in this application enabling them to report any state change. - @param state a string describing the state change - """ - #print state + def _handle_action(self, action): + #print action ################################################## # Initalize/Quit ################################################## - if state == Actions.APPLICATION_INITIALIZE: + if action == Actions.APPLICATION_INITIALIZE: for action in Actions.get_all_actions(): action.set_sensitive(False) #set all actions disabled - # enable a select few actions + #enable a select few actions for action in ( Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW, Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS, Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY, Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY, Actions.TYPES_WINDOW_DISPLAY, - ): Actions.get_action_from_name(action).set_sensitive(True) + ): action.set_sensitive(True) if not self.init_file_paths: self.init_file_paths = Preferences.files_open() if not self.init_file_paths: self.init_file_paths = [''] @@ -136,26 +118,26 @@ class ActionHandler: if Preferences.file_open() in self.init_file_paths: self.main_window.new_page(Preferences.file_open(), show=True) if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists - elif state == Actions.APPLICATION_QUIT: + elif action == Actions.APPLICATION_QUIT: if self.main_window.close_pages(): gtk.main_quit() exit(0) ################################################## # Selections ################################################## - elif state == Actions.ELEMENT_SELECT: + elif action == Actions.ELEMENT_SELECT: pass #do nothing, update routines below - elif state == Actions.NOTHING_SELECT: + elif action == Actions.NOTHING_SELECT: self.get_flow_graph().unselect() ################################################## # Enable/Disable ################################################## - elif state == Actions.BLOCK_ENABLE: + elif action == Actions.BLOCK_ENABLE: if self.get_flow_graph().enable_selected(True): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.BLOCK_DISABLE: + elif action == Actions.BLOCK_DISABLE: if self.get_flow_graph().enable_selected(False): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) @@ -163,12 +145,12 @@ class ActionHandler: ################################################## # Cut/Copy/Paste ################################################## - elif state == Actions.BLOCK_CUT: - self.handle_states(Actions.BLOCK_COPY) - self.handle_states(Actions.ELEMENT_DELETE) - elif state == Actions.BLOCK_COPY: + elif action == Actions.BLOCK_CUT: + Actions.BLOCK_COPY() + Actions.ELEMENT_DELETE() + elif action == Actions.BLOCK_COPY: self.clipboard = self.get_flow_graph().copy_to_clipboard() - elif state == Actions.BLOCK_PASTE: + elif action == Actions.BLOCK_PASTE: if self.clipboard: self.get_flow_graph().paste_from_clipboard(self.clipboard) self.get_flow_graph().update() @@ -177,46 +159,46 @@ class ActionHandler: ################################################## # Move/Rotate/Delete/Create ################################################## - elif state == Actions.BLOCK_MOVE: + elif action == Actions.BLOCK_MOVE: self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.BLOCK_ROTATE_CCW: + elif action == Actions.BLOCK_ROTATE_CCW: if self.get_flow_graph().rotate_selected(90): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.BLOCK_ROTATE_CW: + elif action == Actions.BLOCK_ROTATE_CW: if self.get_flow_graph().rotate_selected(-90): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.ELEMENT_DELETE: + elif action == Actions.ELEMENT_DELETE: if self.get_flow_graph().remove_selected(): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.handle_states(Actions.NOTHING_SELECT) + Actions.NOTHING_SELECT() self.get_page().set_saved(False) - elif state == Actions.ELEMENT_CREATE: + elif action == Actions.ELEMENT_CREATE: self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.handle_states(Actions.NOTHING_SELECT) + Actions.NOTHING_SELECT() self.get_page().set_saved(False) - elif state == Actions.BLOCK_INC_TYPE: + elif action == Actions.BLOCK_INC_TYPE: if self.get_flow_graph().type_controller_modify_selected(1): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.BLOCK_DEC_TYPE: + elif action == Actions.BLOCK_DEC_TYPE: if self.get_flow_graph().type_controller_modify_selected(-1): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.PORT_CONTROLLER_INC: + elif action == Actions.PORT_CONTROLLER_INC: if self.get_flow_graph().port_controller_modify_selected(1): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) - elif state == Actions.PORT_CONTROLLER_DEC: + elif action == Actions.PORT_CONTROLLER_DEC: if self.get_flow_graph().port_controller_modify_selected(-1): self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) @@ -224,16 +206,16 @@ class ActionHandler: ################################################## # Window stuff ################################################## - elif state == Actions.ABOUT_WINDOW_DISPLAY: + elif action == Actions.ABOUT_WINDOW_DISPLAY: Dialogs.AboutDialog(self.get_flow_graph().get_parent()) - elif state == Actions.HELP_WINDOW_DISPLAY: + elif action == Actions.HELP_WINDOW_DISPLAY: Dialogs.HelpDialog() - elif state == Actions.TYPES_WINDOW_DISPLAY: + elif action == Actions.TYPES_WINDOW_DISPLAY: Dialogs.TypesDialog(self.get_flow_graph().get_parent()) ################################################## # Param Modifications ################################################## - elif state == Actions.BLOCK_PARAM_MODIFY: + elif action == Actions.BLOCK_PARAM_MODIFY: selected_block = self.get_flow_graph().get_selected_block() if selected_block: if PropsDialog(selected_block).run(): @@ -249,14 +231,14 @@ class ActionHandler: ################################################## # Undo/Redo ################################################## - elif state == Actions.FLOW_GRAPH_UNDO: + elif action == Actions.FLOW_GRAPH_UNDO: n = self.get_page().get_state_cache().get_prev_state() if n: self.get_flow_graph().unselect() self.get_flow_graph().import_data(n) self.get_flow_graph().update() self.get_page().set_saved(False) - elif state == Actions.FLOW_GRAPH_REDO: + elif action == Actions.FLOW_GRAPH_REDO: n = self.get_page().get_state_cache().get_next_state() if n: self.get_flow_graph().unselect() @@ -266,19 +248,19 @@ class ActionHandler: ################################################## # New/Open/Save/Close ################################################## - elif state == Actions.FLOW_GRAPH_NEW: + elif action == Actions.FLOW_GRAPH_NEW: self.main_window.new_page() - elif state == Actions.FLOW_GRAPH_OPEN: + elif action == Actions.FLOW_GRAPH_OPEN: file_paths = OpenFlowGraphFileDialog(self.get_page().get_file_path()).run() if file_paths: #open a new page for each file, show only the first for i,file_path in enumerate(file_paths): self.main_window.new_page(file_path, show=(i==0)) - elif state == Actions.FLOW_GRAPH_CLOSE: + elif action == Actions.FLOW_GRAPH_CLOSE: self.main_window.close_page() - elif state == Actions.FLOW_GRAPH_SAVE: + elif action == Actions.FLOW_GRAPH_SAVE: #read-only or undefined file path, do save-as if self.get_page().get_read_only() or not self.get_page().get_file_path(): - self.handle_states(Actions.FLOW_GRAPH_SAVE_AS) + Actions.FLOW_GRAPH_SAVE_AS() #otherwise try to save else: try: @@ -287,12 +269,12 @@ class ActionHandler: except IOError: Messages.send_fail_save(self.get_page().get_file_path()) self.get_page().set_saved(False) - elif state == Actions.FLOW_GRAPH_SAVE_AS: + elif action == Actions.FLOW_GRAPH_SAVE_AS: file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run() if file_path is not None: self.get_page().set_file_path(file_path) - self.handle_states(Actions.FLOW_GRAPH_SAVE) - elif state == Actions.FLOW_GRAPH_SCREEN_CAPTURE: + Actions.FLOW_GRAPH_SAVE() + elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE: file_path = SaveImageFileDialog(self.get_page().get_file_path()).run() if file_path is not None: pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf() @@ -300,10 +282,10 @@ class ActionHandler: ################################################## # Gen/Exec/Stop ################################################## - elif state == Actions.FLOW_GRAPH_GEN: + elif action == Actions.FLOW_GRAPH_GEN: if not self.get_page().get_pid(): if not self.get_page().get_saved() or not self.get_page().get_file_path(): - self.handle_states(Actions.FLOW_GRAPH_SAVE) #only save if file path missing or not saved + Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved if self.get_page().get_saved() and self.get_page().get_file_path(): generator = self.get_page().get_generator() try: @@ -311,37 +293,37 @@ class ActionHandler: generator.write() except Exception,e: Messages.send_fail_gen(e) else: self.generator = None - elif state == Actions.FLOW_GRAPH_EXEC: + elif action == Actions.FLOW_GRAPH_EXEC: if not self.get_page().get_pid(): - self.handle_states(Actions.FLOW_GRAPH_GEN) + Actions.FLOW_GRAPH_GEN() if self.get_page().get_saved() and self.get_page().get_file_path(): ExecFlowGraphThread(self) - elif state == Actions.FLOW_GRAPH_KILL: + elif action == Actions.FLOW_GRAPH_KILL: if self.get_page().get_pid(): try: os.kill(self.get_page().get_pid(), signal.SIGKILL) except: print "could not kill pid: %s"%self.get_page().get_pid() - elif state == '': #pass and run the global actions + elif action == Actions.PAGE_CHANGE: #pass and run the global actions pass - else: print '!!! State "%s" not handled !!!'%state + else: print '!!! Action "%s" not handled !!!'%action ################################################## # Global Actions for all States ################################################## #update general buttons - Actions.get_action_from_name(Actions.ELEMENT_DELETE).set_sensitive(bool(self.get_flow_graph().get_selected_elements())) - Actions.get_action_from_name(Actions.BLOCK_PARAM_MODIFY).set_sensitive(bool(self.get_flow_graph().get_selected_block())) - Actions.get_action_from_name(Actions.BLOCK_ROTATE_CCW).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.get_action_from_name(Actions.BLOCK_ROTATE_CW).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements())) + Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block())) + Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) #update cut/copy/paste - Actions.get_action_from_name(Actions.BLOCK_CUT).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.get_action_from_name(Actions.BLOCK_COPY).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.get_action_from_name(Actions.BLOCK_PASTE).set_sensitive(bool(self.clipboard)) + Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard)) #update enable/disable - Actions.get_action_from_name(Actions.BLOCK_ENABLE).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.get_action_from_name(Actions.BLOCK_DISABLE).set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) #set the exec and stop buttons self.update_exec_stop() #saved status - Actions.get_action_from_name(Actions.FLOW_GRAPH_SAVE).set_sensitive(not self.get_page().get_saved()) + Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved()) self.main_window.update() try: #set the size of the flow graph area (if changed) new_size = self.get_flow_graph().get_option('window_size') @@ -351,6 +333,7 @@ class ActionHandler: #draw the flow graph self.get_flow_graph().update_selected() self.get_flow_graph().queue_draw() + return True #action was handled def update_exec_stop(self): """ @@ -358,9 +341,9 @@ class ActionHandler: Lock and unlock the mutex for race conditions with exec flow graph threads. """ sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_pid() - Actions.get_action_from_name(Actions.FLOW_GRAPH_GEN).set_sensitive(sensitive) - Actions.get_action_from_name(Actions.FLOW_GRAPH_EXEC).set_sensitive(sensitive) - Actions.get_action_from_name(Actions.FLOW_GRAPH_KILL).set_sensitive(self.get_page().get_pid() != None) + Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive) + Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive) + Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_pid() != None) class ExecFlowGraphThread(Thread): """Execute the flow graph as a new process and wait on it to finish.""" diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index 531888ac..90017987 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -21,155 +21,248 @@ import pygtk pygtk.require('2.0') import gtk -###################################################################################################### -# Action Names -###################################################################################################### -APPLICATION_INITIALIZE = 'app init' -APPLICATION_QUIT = 'app quit' -PARAM_MODIFY = 'param modify' -BLOCK_MOVE = 'block move' -BLOCK_ROTATE_CCW = 'block rotate ccw' -BLOCK_ROTATE_CW = 'block rotate cw' -BLOCK_PARAM_MODIFY = 'block param modify' -BLOCK_INC_TYPE = 'block increment type' -BLOCK_DEC_TYPE = 'block decrement type' -BLOCK_ENABLE = 'block enable' -BLOCK_DISABLE = 'block disable' -BLOCK_CUT = 'block cut' -BLOCK_COPY = 'block copy' -BLOCK_PASTE = 'block paste' -PORT_CONTROLLER_INC = 'port controller increment' -PORT_CONTROLLER_DEC = 'port controller decrement' -ELEMENT_CREATE = 'element create' -ELEMENT_DELETE = 'element delete' -ELEMENT_SELECT = 'element select' -NOTHING_SELECT = 'nothing select' -FLOW_GRAPH_OPEN = 'flow graph open' -FLOW_GRAPH_UNDO = 'flow graph undo' -FLOW_GRAPH_REDO = 'flow graph redo' -FLOW_GRAPH_SAVE = 'flow graph save' -FLOW_GRAPH_SAVE_AS = 'flow graph save as' -FLOW_GRAPH_CLOSE = 'flow graph close' -FLOW_GRAPH_NEW = 'flow graph new' -FLOW_GRAPH_GEN = 'flow graph gen' -FLOW_GRAPH_EXEC = 'flow graph exec' -FLOW_GRAPH_KILL = 'flow graph kill' -FLOW_GRAPH_SCREEN_CAPTURE = 'flow graph screen capture' -ABOUT_WINDOW_DISPLAY = 'about window display' -HELP_WINDOW_DISPLAY = 'help window display' -TYPES_WINDOW_DISPLAY = 'types window display' - -###################################################################################################### -# Action Key Map -###################################################################################################### NO_MODS_MASK = 0 -_actions_key_list = ( - #action name, key value, mod mask - (FLOW_GRAPH_NEW, gtk.keysyms.n, gtk.gdk.CONTROL_MASK), - (FLOW_GRAPH_OPEN, gtk.keysyms.o, gtk.gdk.CONTROL_MASK), - (FLOW_GRAPH_SAVE, gtk.keysyms.s, gtk.gdk.CONTROL_MASK), - (FLOW_GRAPH_SAVE_AS, gtk.keysyms.s, gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK), - (FLOW_GRAPH_CLOSE, gtk.keysyms.w, gtk.gdk.CONTROL_MASK), - (APPLICATION_QUIT, gtk.keysyms.q, gtk.gdk.CONTROL_MASK), - (FLOW_GRAPH_UNDO, gtk.keysyms.z, gtk.gdk.CONTROL_MASK), - (FLOW_GRAPH_REDO, gtk.keysyms.y, gtk.gdk.CONTROL_MASK), - (ELEMENT_DELETE, gtk.keysyms.Delete, NO_MODS_MASK), - (BLOCK_ROTATE_CCW, gtk.keysyms.Left, NO_MODS_MASK), - (BLOCK_ROTATE_CW, gtk.keysyms.Right, NO_MODS_MASK), - (BLOCK_DEC_TYPE, gtk.keysyms.Up, NO_MODS_MASK), - (BLOCK_INC_TYPE, gtk.keysyms.Down, NO_MODS_MASK), - (BLOCK_PARAM_MODIFY, gtk.keysyms.Return, NO_MODS_MASK), - (BLOCK_ENABLE, gtk.keysyms.e, NO_MODS_MASK), - (BLOCK_DISABLE, gtk.keysyms.d, NO_MODS_MASK), - (BLOCK_CUT, gtk.keysyms.x, gtk.gdk.CONTROL_MASK), - (BLOCK_COPY, gtk.keysyms.c, gtk.gdk.CONTROL_MASK), - (BLOCK_PASTE, gtk.keysyms.v, gtk.gdk.CONTROL_MASK), - (FLOW_GRAPH_GEN, gtk.keysyms.F5, NO_MODS_MASK), - (FLOW_GRAPH_EXEC, gtk.keysyms.F6, NO_MODS_MASK), - (FLOW_GRAPH_KILL, gtk.keysyms.F7, NO_MODS_MASK), - (FLOW_GRAPH_SCREEN_CAPTURE, gtk.keysyms.Print, NO_MODS_MASK), - (HELP_WINDOW_DISPLAY, gtk.keysyms.F1, NO_MODS_MASK), - #the following have no associated gtk.Action - (PORT_CONTROLLER_INC, gtk.keysyms.equal, NO_MODS_MASK), - (PORT_CONTROLLER_INC, gtk.keysyms.plus, NO_MODS_MASK), - (PORT_CONTROLLER_INC, gtk.keysyms.KP_Add, NO_MODS_MASK), - (PORT_CONTROLLER_DEC, gtk.keysyms.minus, NO_MODS_MASK), - (PORT_CONTROLLER_DEC, gtk.keysyms.KP_Subtract, NO_MODS_MASK), -) -_actions_key_dict = dict(((keyval, mod_mask), action_name) for action_name, keyval, mod_mask in _actions_key_list) -_used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for action_name, keyval, mod_mask in _actions_key_list], NO_MODS_MASK) +######################################################################## +# Actions API +######################################################################## +_actions_keypress_dict = dict() _keymap = gtk.gdk.keymap_get_default() -def get_action_name_from_key_press(event): +_used_mods_mask = NO_MODS_MASK +def get_action_from_key_press(event): """ - Get the action name associated with the key press event. + Get the action associated with the key press event. Both the key value and the mask must have a match. @param event a gtk key press event - @return the action name or blank string + @throws a key error when no action matches + @return the action object """ + _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK) #extract the key value and the consumed modifiers keyval, egroup, level, consumed = _keymap.translate_keyboard_state( event.hardware_keycode, event.state, event.group) #get the modifier mask and ignore irrelevant modifiers mod_mask = event.state & ~consumed & _used_mods_mask - try: return _actions_key_dict[(keyval, mod_mask)] + try: return _actions_keypress_dict[(keyval, mod_mask)] except KeyError: raise KeyError, 'Keypress: "%s, %s" does not have an associated action'%(gtk.gdk.keyval_name(keyval), mod_mask) -###################################################################################################### -# Actions -###################################################################################################### -_actions_list = ( - gtk.Action(FLOW_GRAPH_NEW, '_New', 'Create a new flow graph', gtk.STOCK_NEW), - gtk.Action(FLOW_GRAPH_OPEN, '_Open', 'Open an existing flow graph', gtk.STOCK_OPEN), - gtk.Action(FLOW_GRAPH_SAVE, '_Save', 'Save the current flow graph', gtk.STOCK_SAVE), - gtk.Action(FLOW_GRAPH_SAVE_AS, 'Save _As', 'Save the current flow graph as...', gtk.STOCK_SAVE_AS), - gtk.Action(FLOW_GRAPH_CLOSE, '_Close', 'Close the current flow graph', gtk.STOCK_CLOSE), - gtk.Action(APPLICATION_QUIT, '_Quit', 'Quit program', gtk.STOCK_QUIT), - gtk.Action(FLOW_GRAPH_UNDO, '_Undo', 'Undo a change to the flow graph', gtk.STOCK_UNDO), - gtk.Action(FLOW_GRAPH_REDO, '_Redo', 'Redo a change to the flow graph', gtk.STOCK_REDO), - gtk.Action(ELEMENT_DELETE, '_Delete', 'Delete the selected blocks', gtk.STOCK_DELETE), - gtk.Action(BLOCK_ROTATE_CCW, 'Rotate Counterclockwise', 'Rotate the selected blocks 90 degrees to the left', gtk.STOCK_GO_BACK), - gtk.Action(BLOCK_ROTATE_CW, 'Rotate Clockwise', 'Rotate the selected blocks 90 degrees to the right', gtk.STOCK_GO_FORWARD), - gtk.Action(BLOCK_PARAM_MODIFY, '_Properties', 'Modify params for the selected block', gtk.STOCK_PROPERTIES), - gtk.Action(BLOCK_ENABLE, 'E_nable', 'Enable the selected blocks', gtk.STOCK_CONNECT), - gtk.Action(BLOCK_DISABLE, 'D_isable', 'Disable the selected blocks', gtk.STOCK_DISCONNECT), - gtk.Action(BLOCK_CUT, 'Cu_t', 'Cut', gtk.STOCK_CUT), - gtk.Action(BLOCK_COPY, '_Copy', 'Copy', gtk.STOCK_COPY), - gtk.Action(BLOCK_PASTE, '_Paste', 'Paste', gtk.STOCK_PASTE), - gtk.Action(ABOUT_WINDOW_DISPLAY, '_About', 'About this program', gtk.STOCK_ABOUT), - gtk.Action(HELP_WINDOW_DISPLAY, '_Help', 'Usage Tips', gtk.STOCK_HELP), - gtk.Action(TYPES_WINDOW_DISPLAY, '_Types', 'Types Color Mapping', gtk.STOCK_DIALOG_INFO), - gtk.Action(FLOW_GRAPH_GEN, '_Generate', 'Generate the flow graph', gtk.STOCK_CONVERT), - gtk.Action(FLOW_GRAPH_EXEC, '_Execute', 'Execute the flow graph', gtk.STOCK_EXECUTE), - gtk.Action(FLOW_GRAPH_KILL, '_Kill', 'Kill the flow graph', gtk.STOCK_STOP), - gtk.Action(FLOW_GRAPH_SCREEN_CAPTURE, 'S_creen Capture', 'Create a screen capture of the flow graph', gtk.STOCK_PRINT), -) -def get_all_actions(): return _actions_list +_all_actions_list = list() +def get_all_actions(): return _all_actions_list + +_accel_group = gtk.AccelGroup() +def get_accel_group(): return _accel_group -_actions_dict = dict((action.get_name(), action) for action in get_all_actions()) -def get_action_from_name(action_name): +class Action(gtk.Action): """ - Retrieve the action from the action list. - Search the list and find an action with said name. - @param action_name the action name(string) - @throw KeyError bad action name - @return a gtk action object + A custom Action class based on gtk.Action. + Pass additional arguments such as keypresses. + Register actions and keypresses with this module. """ - try: return _actions_dict[action_name] - except KeyError: raise KeyError, 'Action Name: "%s" does not exist'%action_name -###################################################################################################### -# Accelerators -###################################################################################################### -_accel_group = gtk.AccelGroup() -def get_accel_group(): return _accel_group + def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None): + """ + Create a new Action instance. + @param key_presses a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...) + @param the regular gtk.Action parameters (defaults to None) + """ + if name is None: name = label + gtk.Action.__init__(self, + name=name, label=label, + tooltip=tooltip, stock_id=stock_id, + ) + #register this action + _all_actions_list.append(self) + for i in range(len(keypresses)/2): + keyval, mod_mask = keypresses[i*2:(i+1)*2] + #register this keypress + assert not _actions_keypress_dict.has_key((keyval, mod_mask)) + _actions_keypress_dict[(keyval, mod_mask)] = self + #set the accelerator group, and accelerator path + #register the key name and mod mask with the accelerator path + if label is None: continue #dont register accel + accel_path = '
/'+self.get_name() + self.set_accel_group(get_accel_group()) + self.set_accel_path(accel_path) + gtk.accel_map_add_entry(accel_path, keyval, mod_mask) + + def __str__(self): + """ + The string representation should be the name of the action id. + Try to find the action id for this action by searching this module. + """ + try: + import Actions + return filter(lambda attr: getattr(Actions, attr) == self, dir(Actions))[0] + except: return self.get_name() + + def __repr__(self): return str(self) -#set the accelerator group, and accelerator path -#register the key name and mod mask with the accelerator path -for action_name, keyval, mod_mask in _actions_key_list: - try: - accel_path = '
/'+action_name - get_action_from_name(action_name).set_accel_group(get_accel_group()) - get_action_from_name(action_name).set_accel_path(accel_path) - gtk.accel_map_add_entry(accel_path, keyval, mod_mask) - except KeyError: pass #no action was created for this action name + def __call__(self): + """ + Emit the activate signal when called with (). + """ + self.emit('activate') + +######################################################################## +# Actions +######################################################################## +PAGE_CHANGE = Action() +FLOW_GRAPH_NEW = Action( + label='_New', + tooltip='Create a new flow graph', + stock_id=gtk.STOCK_NEW, + keypresses=(gtk.keysyms.n, gtk.gdk.CONTROL_MASK), +) +FLOW_GRAPH_OPEN = Action( + label='_Open', + tooltip='Open an existing flow graph', + stock_id=gtk.STOCK_OPEN, + keypresses=(gtk.keysyms.o, gtk.gdk.CONTROL_MASK), +) +FLOW_GRAPH_SAVE = Action( + label='_Save', + tooltip='Save the current flow graph', + stock_id=gtk.STOCK_SAVE, + keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK), +) +FLOW_GRAPH_SAVE_AS = Action( + label='Save _As', + tooltip='Save the current flow graph as...', + stock_id=gtk.STOCK_SAVE_AS, + keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK), +) +FLOW_GRAPH_CLOSE = Action( + label='_Close', + tooltip='Close the current flow graph', + stock_id=gtk.STOCK_CLOSE, + keypresses=(gtk.keysyms.w, gtk.gdk.CONTROL_MASK), +) +APPLICATION_INITIALIZE = Action() +APPLICATION_QUIT = Action( + label='_Quit', + tooltip='Quit program', + stock_id=gtk.STOCK_QUIT, + keypresses=(gtk.keysyms.q, gtk.gdk.CONTROL_MASK), +) +FLOW_GRAPH_UNDO = Action( + label='_Undo', + tooltip='Undo a change to the flow graph', + stock_id=gtk.STOCK_UNDO, + keypresses=(gtk.keysyms.z, gtk.gdk.CONTROL_MASK), +) +FLOW_GRAPH_REDO = Action( + label='_Redo', + tooltip='Redo a change to the flow graph', + stock_id=gtk.STOCK_REDO, + keypresses=(gtk.keysyms.y, gtk.gdk.CONTROL_MASK), +) +NOTHING_SELECT = Action() +ELEMENT_SELECT = Action() +ELEMENT_CREATE = Action() +ELEMENT_DELETE = Action( + label='_Delete', + tooltip='Delete the selected blocks', + stock_id=gtk.STOCK_DELETE, + keypresses=(gtk.keysyms.Delete, NO_MODS_MASK), +) +BLOCK_MOVE = Action() +BLOCK_ROTATE_CCW = Action( + label='Rotate Counterclockwise', + tooltip='Rotate the selected blocks 90 degrees to the left', + stock_id=gtk.STOCK_GO_BACK, + keypresses=(gtk.keysyms.Left, NO_MODS_MASK), +) +BLOCK_ROTATE_CW = Action( + label='Rotate Clockwise', + tooltip='Rotate the selected blocks 90 degrees to the right', + stock_id=gtk.STOCK_GO_FORWARD, + keypresses=(gtk.keysyms.Right, NO_MODS_MASK), +) +BLOCK_PARAM_MODIFY = Action( + label='_Properties', + tooltip='Modify params for the selected block', + stock_id=gtk.STOCK_PROPERTIES, + keypresses=(gtk.keysyms.Return, NO_MODS_MASK), +) +BLOCK_ENABLE = Action( + label='E_nable', + tooltip='Enable the selected blocks', + stock_id=gtk.STOCK_CONNECT, + keypresses=(gtk.keysyms.e, NO_MODS_MASK), +) +BLOCK_DISABLE = Action( + label='D_isable', + tooltip='Disable the selected blocks', + stock_id=gtk.STOCK_DISCONNECT, + keypresses=(gtk.keysyms.d, NO_MODS_MASK), +) +BLOCK_CUT = Action( + label='Cu_t', + tooltip='Cut', + stock_id=gtk.STOCK_CUT, + keypresses=(gtk.keysyms.x, gtk.gdk.CONTROL_MASK), +) +BLOCK_COPY = Action( + label='_Copy', + tooltip='Copy', + stock_id=gtk.STOCK_COPY, + keypresses=(gtk.keysyms.c, gtk.gdk.CONTROL_MASK), +) +BLOCK_PASTE = Action( + label='_Paste', + tooltip='Paste', + stock_id=gtk.STOCK_PASTE, + keypresses=(gtk.keysyms.v, gtk.gdk.CONTROL_MASK), +) +ABOUT_WINDOW_DISPLAY = Action( + label='_About', + tooltip='About this program', + stock_id=gtk.STOCK_ABOUT, +) +HELP_WINDOW_DISPLAY = Action( + label='_Help', + tooltip='Usage Tips', + stock_id=gtk.STOCK_HELP, + keypresses=(gtk.keysyms.F1, NO_MODS_MASK), +) +TYPES_WINDOW_DISPLAY = Action( + label='_Types', + tooltip='Types Color Mapping', + stock_id=gtk.STOCK_DIALOG_INFO, +) +FLOW_GRAPH_GEN = Action( + label='_Generate', + tooltip='Generate the flow graph', + stock_id=gtk.STOCK_CONVERT, + keypresses=(gtk.keysyms.F5, NO_MODS_MASK), +) +FLOW_GRAPH_EXEC = Action( + label='_Execute', + tooltip='Execute the flow graph', + stock_id=gtk.STOCK_EXECUTE, + keypresses=(gtk.keysyms.F6, NO_MODS_MASK), +) +FLOW_GRAPH_KILL = Action( + label='_Kill', + tooltip='Kill the flow graph', + stock_id=gtk.STOCK_STOP, + keypresses=(gtk.keysyms.F7, NO_MODS_MASK), +) +FLOW_GRAPH_SCREEN_CAPTURE = Action( + label='S_creen Capture', + tooltip='Create a screen capture of the flow graph', + stock_id=gtk.STOCK_PRINT, + keypresses=(gtk.keysyms.Print, NO_MODS_MASK), +) +PORT_CONTROLLER_DEC = Action( + keypresses=(gtk.keysyms.minus, NO_MODS_MASK, gtk.keysyms.KP_Subtract, NO_MODS_MASK), +) +PORT_CONTROLLER_INC = Action( + keypresses=(gtk.keysyms.plus, NO_MODS_MASK, gtk.keysyms.KP_Add, NO_MODS_MASK), +) +BLOCK_INC_TYPE = Action( + keypresses=(gtk.keysyms.Down, NO_MODS_MASK), +) +BLOCK_DEC_TYPE = Action( + keypresses=(gtk.keysyms.Up, NO_MODS_MASK), +) diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py index 697d48a3..fff5ebc0 100644 --- a/grc/gui/Bars.py +++ b/grc/gui/Bars.py @@ -104,9 +104,8 @@ class Toolbar(gtk.Toolbar): """ gtk.Toolbar.__init__(self) self.set_style(gtk.TOOLBAR_ICONS) - for action_name in TOOLBAR_LIST: - if action_name: #add a tool item - action = Actions.get_action_from_name(action_name) + for action in TOOLBAR_LIST: + if action: #add a tool item self.add(action.create_tool_item()) #this reset of the tooltip property is required (after creating the tool item) for the tooltip to show action.set_property('tooltip', action.get_property('tooltip')) @@ -123,16 +122,15 @@ class MenuBar(gtk.MenuBar): Add the submenu to the menu bar. """ gtk.MenuBar.__init__(self) - for main_action,action_names in MENU_BAR_LIST: + for main_action, actions in MENU_BAR_LIST: #create the main menu item main_menu_item = main_action.create_menu_item() self.append(main_menu_item) #create the menu main_menu = gtk.Menu() main_menu_item.set_submenu(main_menu) - for action_name in action_names: - if action_name: #append a menu item - action = Actions.get_action_from_name(action_name) + for action in actions: + if action: #append a menu item main_menu.append(action.create_menu_item()) else: main_menu.append(gtk.SeparatorMenuItem()) main_menu.show_all() #this show all is required for the separators to show diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 35ccf5e2..c90071f2 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -86,7 +86,7 @@ class FlowGraph(Element): block.set_coordinate(coor) block.set_rotation(0) block.get_param('id').set_value(id) - self.handle_states(ELEMENT_CREATE) + ELEMENT_CREATE() ########################################################################### # Copy Paste @@ -409,7 +409,7 @@ class FlowGraph(Element): self._old_selected_port is not self._new_selected_port: try: self.connect(self._old_selected_port, self._new_selected_port) - self.handle_states(ELEMENT_CREATE) + ELEMENT_CREATE() except: Messages.send_fail_connection() self._old_selected_port = None self._new_selected_port = None @@ -424,7 +424,7 @@ class FlowGraph(Element): self._selected_elements = list( set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements) ) - self.handle_states(ELEMENT_SELECT) + ELEMENT_SELECT() ########################################################################## ## Event Handlers @@ -446,7 +446,7 @@ class FlowGraph(Element): #double click detected, bring up params dialog if possible if double_click and self.get_selected_block(): self.mouse_pressed = False - self.handle_states(BLOCK_PARAM_MODIFY) + BLOCK_PARAM_MODIFY() def handle_mouse_button_release(self, left_click, coordinate): """ @@ -457,7 +457,7 @@ class FlowGraph(Element): self.time = 0 self.mouse_pressed = False if self.element_moved: - self.handle_states(BLOCK_MOVE) + BLOCK_MOVE() self.element_moved = False self.update_selected_elements() @@ -487,7 +487,7 @@ class FlowGraph(Element): adj.emit('changed') #remove the connection if selected in drag event if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection(): - self.handle_states(ELEMENT_DELETE) + ELEMENT_DELETE() #move the selected elements and record the new coordinate X, Y = self.get_coordinate() if not self.get_ctrl_mask(): self.move_selected((int(x - X), int(y - Y))) diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py index 6d36f4cf..39cd84da 100644 --- a/grc/gui/MainWindow.py +++ b/grc/gui/MainWindow.py @@ -1,5 +1,5 @@ """ -Copyright 2008 Free Software Foundation, Inc. +Copyright 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -21,7 +21,8 @@ from Constants import \ NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH from Actions import \ APPLICATION_QUIT, FLOW_GRAPH_KILL, \ - FLOW_GRAPH_SAVE, get_accel_group + FLOW_GRAPH_SAVE, PAGE_CHANGE, \ + get_accel_group import pygtk pygtk.require('2.0') import gtk @@ -67,14 +68,13 @@ PAGE_TITLE_MARKUP_TMPL = """\ class MainWindow(gtk.Window): """The topmost window with menus, the tool bar, and other major windows.""" - def __init__(self, handle_states, platform): + def __init__(self, platform): """ - MainWindow contructor. - @param handle_states the callback function + MainWindow contructor + Setup the menu, toolbar, flowgraph editor notebook, block selection window... """ self._platform = platform #setup window - self.handle_states = handle_states gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) vbox = gtk.VBox() self.hpaned = gtk.HPaned() @@ -123,7 +123,7 @@ class MainWindow(gtk.Window): This method in turns calls the state handler to quit. @return true """ - self.handle_states(APPLICATION_QUIT) + APPLICATION_QUIT() return True def _handle_page_change(self, notebook, page, page_num): @@ -137,7 +137,7 @@ class MainWindow(gtk.Window): """ self.current_page = self.notebook.get_nth_page(page_num) Messages.send_page_switch(self.current_page.get_file_path()) - self.handle_states() + PAGE_CHANGE() ############################################################ # Report Window @@ -223,12 +223,12 @@ class MainWindow(gtk.Window): self._set_page(self.page_to_be_closed) #unsaved? ask the user if not self.page_to_be_closed.get_saved() and self._save_changes(): - self.handle_states(FLOW_GRAPH_SAVE) #try to save + FLOW_GRAPH_SAVE() #try to save if not self.page_to_be_closed.get_saved(): #still unsaved? self.page_to_be_closed = None #set the page to be closed back to None return #stop the flow graph if executing - if self.page_to_be_closed.get_pid(): self.handle_states(FLOW_GRAPH_KILL) + if self.page_to_be_closed.get_pid(): FLOW_GRAPH_KILL() #remove the page self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed)) if ensure and self.notebook.get_n_pages() == 0: self.new_page() #no pages, make a new one diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py index cb6b7ed3..645af3f7 100644 --- a/grc/gui/NotebookPage.py +++ b/grc/gui/NotebookPage.py @@ -80,9 +80,8 @@ class NotebookPage(gtk.HBox): self.drawing_area = DrawingArea(self.get_flow_graph()) self.scrolled_window.add_with_viewport(self.get_drawing_area()) self.pack_start(self.scrolled_window) - #inject drawing area and handle states into flow graph + #inject drawing area into flow graph self.get_flow_graph().drawing_area = self.get_drawing_area() - self.get_flow_graph().handle_states = main_window.handle_states self.show_all() def get_drawing_area(self): return self.drawing_area @@ -104,7 +103,7 @@ class NotebookPage(gtk.HBox): @param the button """ self.main_window.page_to_be_closed = self - self.main_window.handle_states(FLOW_GRAPH_CLOSE) + FLOW_GRAPH_CLOSE() def set_markup(self, markup): """ diff --git a/grc/gui/StateCache.py b/grc/gui/StateCache.py index 04b18b18..60ab3a6b 100644 --- a/grc/gui/StateCache.py +++ b/grc/gui/StateCache.py @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from Actions import FLOW_GRAPH_UNDO, FLOW_GRAPH_REDO, get_action_from_name +from Actions import FLOW_GRAPH_UNDO, FLOW_GRAPH_REDO from Constants import STATE_CACHE_SIZE class StateCache(object): @@ -88,5 +88,5 @@ class StateCache(object): """ Update the undo and redo actions based on the number of next and prev states. """ - get_action_from_name(FLOW_GRAPH_REDO).set_sensitive(self.num_next_states != 0) - get_action_from_name(FLOW_GRAPH_UNDO).set_sensitive(self.num_prev_states != 0) + FLOW_GRAPH_REDO.set_sensitive(self.num_next_states != 0) + FLOW_GRAPH_UNDO.set_sensitive(self.num_prev_states != 0) -- 2.30.2