From 7da41a93be41bd00bbff0ba03495d47c4335e0c5 Mon Sep 17 00:00:00 2001 From: Shlomi Fish Date: Tue, 13 Oct 2020 17:18:36 +0300 Subject: [PATCH] Allow to swap the mouse keys using the ini file. See: https://sourceforge.net/p/pysolfc/discussion/503708/thread/5d77434ff8/ " PySolFC / Discussion / Open Discussion: Any way to remap mouse buttons? " We may not have covered all the affected calls to bind(). --- pysollib/game/__init__.py | 13 ++++++++--- pysollib/games/mahjongg/mahjongg.py | 21 ++++++++++++++--- pysollib/games/ultra/matrix.py | 15 +++++++++++-- pysollib/options.py | 34 ++++++++++++++++++++++++++++ pysollib/stack.py | 35 ++++++++++++++++++++--------- pysollib/tile/tktree.py | 17 +++++++++++--- pysollib/tile/tkwidget.py | 6 ++++- pysollib/tile/toolbar.py | 16 ++++++++++--- pysollib/tk/tkstats.py | 7 +++++- pysollib/tk/toolbar.py | 25 ++++++++++++++------- pysollib/ui/tktile/tkhtml.py | 6 ++++- 11 files changed, 159 insertions(+), 36 deletions(-) diff --git a/pysollib/game/__init__.py b/pysollib/game/__init__.py index db2ac6dc..377fcc1f 100644 --- a/pysollib/game/__init__.py +++ b/pysollib/game/__init__.py @@ -671,12 +671,19 @@ class Game(object): print_err('max_rounds <= 1, but talon.texts.rounds is not None: ' '%s' % class_name, 2) + def _calcMouseBind(self, binding_format): + """docstring for _calcMouseBind""" + return self.app.opt.calcCustomMouseButtonsBinding(binding_format) + def initBindings(self): # note: a Game is only allowed to bind self.canvas and not to self.top # bind(self.canvas, "", self.undoHandler) - bind(self.canvas, "<1>", self.undoHandler) - bind(self.canvas, "<2>", self.dropHandler) - bind(self.canvas, "<3>", self.redoHandler) + bind(self.canvas, + self._calcMouseBind("<{mouse_button1}>"), self.undoHandler) + bind(self.canvas, + self._calcMouseBind("<{mouse_button2}>"), self.dropHandler) + bind(self.canvas, + self._calcMouseBind("<{mouse_button3}>"), self.redoHandler) bind(self.canvas, '', self._unmapHandler) bind(self.canvas, '', self._configureHandler, add=True) diff --git a/pysollib/games/mahjongg/mahjongg.py b/pysollib/games/mahjongg/mahjongg.py index f62e145f..19e47f43 100644 --- a/pysollib/games/mahjongg/mahjongg.py +++ b/pysollib/games/mahjongg/mahjongg.py @@ -222,6 +222,9 @@ class Mahjongg_RowStack(OpenStack): for s in self.game.s.rows[self.id+1:]: s.group.tkraise() + def _calcMouseBind(self, binding_format): + return self.game.app.opt.calcCustomMouseButtonsBinding(binding_format) + # In Mahjongg games type there are a lot of stacks, so we optimize # and don't create bindings that are not used anyway. def initBindings(self): @@ -231,9 +234,21 @@ class Mahjongg_RowStack(OpenStack): # bind(group, "<3>", self._Stack__controlclickEventHandler) # bind(group, "", self._Stack__controlclickEventHandler) # - bind(group, "<1>", self.__clickEventHandler) - bind(group, "<3>", self.__controlclickEventHandler) - bind(group, "", self.__controlclickEventHandler) + bind( + group, + self._calcMouseBind("<{mouse_button1}>"), + self.__clickEventHandler + ) + bind( + group, + self._calcMouseBind("<{mouse_button3}>"), + self.__controlclickEventHandler + ) + bind( + group, + self._calcMouseBind(""), + self.__controlclickEventHandler + ) # bind(group, "", self._Stack__enterEventHandler) # bind(group, "", self._Stack__leaveEventHandler) diff --git a/pysollib/games/ultra/matrix.py b/pysollib/games/ultra/matrix.py index c9ce696a..9d4084a8 100644 --- a/pysollib/games/ultra/matrix.py +++ b/pysollib/games/ultra/matrix.py @@ -60,9 +60,20 @@ class Matrix_RowStack(OpenStack): # the tile (from Tk's stacking view) return len(self.cards) - 1 + def _calcMouseBind(self, binding_format): + return self.game.app.opt.calcCustomMouseButtonsBinding(binding_format) + def initBindings(self): - bind(self.group, "<1>", self._Stack__clickEventHandler) - bind(self.group, "", self._Stack__controlclickEventHandler) + bind( + self.group, + self._calcMouseBind("<{mouse_button1}>"), + self._Stack__clickEventHandler + ) + bind( + self.group, + self._calcMouseBind(""), + self._Stack__controlclickEventHandler, + ) def getBottomImage(self): return self.game.app.images.getBlankBottom() diff --git a/pysollib/options.py b/pysollib/options.py index 09b8727c..d822e302 100644 --- a/pysollib/options.py +++ b/pysollib/options.py @@ -43,6 +43,17 @@ import validate # * Options # ************************************************************************ +_global_settings = {} + + +def calcCustomMouseButtonsBinding(binding_format): + assert _global_settings['mouse_button1'] + return binding_format.format( + mouse_button1=_global_settings['mouse_button1'], + mouse_button2=_global_settings['mouse_button2'], + mouse_button3=_global_settings['mouse_button3'], + ) + configspec = ''' [general] @@ -253,6 +264,9 @@ class Options: ('solver_max_iterations', 'int'), ('solver_iterations_output_step', 'int'), ('solver_preset', 'string'), + ('mouse_button1', 'int'), + ('mouse_button2', 'int'), + ('mouse_button3', 'int'), # ('toolbar_vars', 'list'), # ('recent_gameid', 'list'), # ('favorite_gameid', 'list'), @@ -320,6 +334,9 @@ class Options: self.num_cards = False self.helpbar = False self.splashscreen = True + self.mouse_button1 = 1 + self.mouse_button2 = 2 + self.mouse_button3 = 3 self.mouse_type = 'drag-n-drop' # or 'sticky-mouse' or 'point-n-click' self.mouse_undo = False # use mouse for undo/redo self.negative_bottom = True @@ -751,3 +768,20 @@ class Options: self.offsets[key] = val except Exception: traceback.print_exc() + + # mouse buttons swap + def _positive(button): + return max([button, 1]) + _global_settings['mouse_button1'] = _positive(self.mouse_button1) + _global_settings['mouse_button2'] = _positive(self.mouse_button2) + _global_settings['mouse_button3'] = _positive(self.mouse_button3) + + def calcCustomMouseButtonsBinding(self, binding_format): + """docstring for calcCustomMouseButtonsBinding""" + def _positive(button): + return max([button, 1]) + return binding_format.format( + mouse_button1=_positive(self.mouse_button1), + mouse_button2=_positive(self.mouse_button2), + mouse_button3=_positive(self.mouse_button3), + ) diff --git a/pysollib/stack.py b/pysollib/stack.py index a43fefe5..5a867966 100644 --- a/pysollib/stack.py +++ b/pysollib/stack.py @@ -269,22 +269,35 @@ class Stack: if self.is_visible: self.initBindings() + def _calcMouseBind(self, binding_format): + return self.game.app.opt.calcCustomMouseButtonsBinding(binding_format) + # bindings {view widgets bind to controller} def initBindings(self): group = self.group - bind(group, "<1>", self.__clickEventHandler) + bind(group, self._calcMouseBind("<{mouse_button1}>"), + self.__clickEventHandler) # bind(group, "", self.__motionEventHandler) bind(group, "", self.__motionEventHandler) - bind(group, "", self.__releaseEventHandler) - bind(group, "", self.__controlclickEventHandler) - bind(group, "", self.__shiftclickEventHandler) - bind(group, "", self.__doubleclickEventHandler) - bind(group, "<3>", self.__rightclickEventHandler) - bind(group, "<2>", self.__middleclickEventHandler) - bind(group, "", self.__middleclickEventHandler) - # bind(group, "", self.__controlmiddleclickEventHandler) - # bind(group, "", self.__shiftrightclickEventHandler) - # bind(group, "", "") + bind(group, self._calcMouseBind(""), + self.__releaseEventHandler) + bind(group, self._calcMouseBind(""), + self.__controlclickEventHandler) + bind(group, self._calcMouseBind(""), + self.__shiftclickEventHandler) + bind(group, self._calcMouseBind(""), + self.__doubleclickEventHandler) + bind(group, self._calcMouseBind("<{mouse_button3}>"), + self.__rightclickEventHandler) + bind(group, self._calcMouseBind("<{mouse_button2}>"), + self.__middleclickEventHandler) + bind(group, self._calcMouseBind(""), + self.__middleclickEventHandler) + # bind(group, self._calcMouseBind( + # ""), self.__controlmiddleclickEventHandler) + # bind(group, self._calcMouseBind(""), + # self.__shiftrightclickEventHandler) + # bind(group, self._calcMouseBind(""), "") bind(group, "", self.__enterEventHandler) bind(group, "", self.__leaveEventHandler) diff --git a/pysollib/tile/tktree.py b/pysollib/tile/tktree.py index a55e6c85..ce0bfa08 100644 --- a/pysollib/tile/tktree.py +++ b/pysollib/tile/tktree.py @@ -244,9 +244,20 @@ class MfxTreeInCanvas(MfxScrolledCanvas): # self.style.text_normal_bg = self.canvas.option_get( # 'background', self.canvas.cget("background")) # - bind(self.canvas, "", self.singleClick) - bind(self.canvas, "", self.doubleClick) - # bind(self.canvas, "", xxx) + from pysollib.options import calcCustomMouseButtonsBinding + bind( + self.canvas, + calcCustomMouseButtonsBinding(""), + self.singleClick + ) + bind( + self.canvas, + calcCustomMouseButtonsBinding(""), + self.doubleClick + ) + # bind(self.canvas, + # calcCustomMouseButtonsBinding( + # ""), xxx) self.pack(fill='both', expand=True) def destroy(self): diff --git a/pysollib/tile/tkwidget.py b/pysollib/tile/tkwidget.py index c0ea0d9d..78c5e3b3 100644 --- a/pysollib/tile/tkwidget.py +++ b/pysollib/tile/tkwidget.py @@ -320,7 +320,11 @@ class PysolAboutDialog(MfxMessageDialog): url_label = ttk.Label(frame, text=kw.url, font=font, foreground='blue', cursor='hand2') url_label.pack() - url_label.bind('<1>', self._urlClicked) + from pysollib.options import calcCustomMouseButtonsBinding + url_label.bind( + calcCustomMouseButtonsBinding('<{mouse_button1}>'), + self._urlClicked + ) # focus = self.createButtons(bottom_frame, kw) self.mainloop(focus, kw.timeout) diff --git a/pysollib/tile/toolbar.py b/pysollib/tile/toolbar.py index 98c70134..0b32e495 100644 --- a/pysollib/tile/toolbar.py +++ b/pysollib/tile/toolbar.py @@ -158,6 +158,7 @@ class PysolToolbarTk: def __init__(self, top, menubar, dir, size=0, relief='flat', compound='none'): + from pysollib.options import calcCustomMouseButtonsBinding self.top = top self.menubar = menubar self.side = -1 @@ -192,7 +193,10 @@ class PysolToolbarTk: ): if label is None: sep = self._createSeparator() - sep.bind("<3>", self.rightclickHandler) + sep.bind( + calcCustomMouseButtonsBinding("<{mouse_button3}>"), + self.rightclickHandler + ) elif label == 'Pause': self._createButton(label, f, check=True, tooltip=t) else: @@ -209,8 +213,14 @@ class PysolToolbarTk: self._createLabel("player", label=n_('Player'), tooltip=_("Player options")) # - self.player_label.bind("<1>", self.mOptPlayerOptions) - self.frame.bind("<3>", self.rightclickHandler) + self.player_label.bind( + calcCustomMouseButtonsBinding("<{mouse_button1}>"), + self.mOptPlayerOptions + ) + self.frame.bind( + calcCustomMouseButtonsBinding("<{mouse_button3}>"), + self.rightclickHandler + ) # self.setCompound(compound, force=True) diff --git a/pysollib/tk/tkstats.py b/pysollib/tk/tkstats.py index 6cd35a65..8bcf669d 100644 --- a/pysollib/tk/tkstats.py +++ b/pysollib/tk/tkstats.py @@ -487,7 +487,12 @@ class AllGames_StatsDialog(MfxDialog): self.nodes = {} self.canvas = self.sc.canvas self.canvas.dialog = self - bind(self.canvas, "<1>", self.singleClick) + from pysollib.options import calcCustomMouseButtonsBinding + bind( + self.canvas, + calcCustomMouseButtonsBinding("<{mouse_button1}>"), + self.singleClick + ) self.fillCanvas(player, title) bbox = self.canvas.bbox("all") # print bbox diff --git a/pysollib/tk/toolbar.py b/pysollib/tk/toolbar.py index e030fde2..607076e5 100644 --- a/pysollib/tk/toolbar.py +++ b/pysollib/tk/toolbar.py @@ -171,7 +171,17 @@ class PysolToolbarTk: # self.frame = tkinter.Frame(top, relief=TkSettings.toolbar_relief, bd=TkSettings.toolbar_borderwidth) - # + + from pysollib.options import calcCustomMouseButtonsBinding + + def _bind2sep(sep): + sep.bind( + calcCustomMouseButtonsBinding("<{mouse_button1}>"), + self.clickHandler) + sep.bind( + calcCustomMouseButtonsBinding("<{mouse_button3}>"), + self.rightclickHandler) + for label, f, t in ( (n_("New"), self.mNewGame, _("New game")), (n_("Restart"), self.mRestart, _("Restart the\ncurrent game")), @@ -192,8 +202,7 @@ class PysolToolbarTk: ): if label is None: sep = self._createSeparator() - sep.bind("<1>", self.clickHandler) - sep.bind("<3>", self.rightclickHandler) + _bind2sep(sep) elif label == 'Pause': self._createButton(label, f, check=True, tooltip=t) else: @@ -201,17 +210,17 @@ class PysolToolbarTk: self.pause_button.config(variable=menubar.tkopt.pause) sep = self._createFlatSeparator() - sep.bind("<1>", self.clickHandler) - sep.bind("<3>", self.rightclickHandler) + _bind2sep(sep) self._createLabel("player", label=n_('Player'), tooltip=_("Player options")) # - self.player_label.bind("<1>", self.mOptPlayerOptions) + self.player_label.bind( + calcCustomMouseButtonsBinding("<{mouse_button1}>"), + self.mOptPlayerOptions) # self.player_label.bind("<3>",self.mOptPlayerOptions) self.popup = MfxMenu(master=None, label=n_('Toolbar'), tearoff=0) createToolbarMenu(menubar, self.popup) - self.frame.bind("<1>", self.clickHandler) - self.frame.bind("<3>", self.rightclickHandler) + _bind2sep(self.frame) # self.setCompound(compound, force=True) diff --git a/pysollib/ui/tktile/tkhtml.py b/pysollib/ui/tktile/tkhtml.py index 4e31b62d..acab5711 100644 --- a/pysollib/ui/tktile/tkhtml.py +++ b/pysollib/ui/tktile/tkhtml.py @@ -104,7 +104,11 @@ class tkHTMLWriter(formatter.NullWriter): url = self.anchor[0] tag = "href_" + url self.text.tag_add(tag, self.anchor_mark, "insert") - self.text.tag_bind(tag, "<1>", self.createCallback(url)) + from pysollib.options import calcCustomMouseButtonsBinding + self.text.tag_bind( + tag, + calcCustomMouseButtonsBinding("<{mouse_button1}>"), + self.createCallback(url)) self.text.tag_bind( tag, "", lambda e: self.anchor_enter(url)) self.text.tag_bind(tag, "", self.anchor_leave)