Project

Profile

Help

Download (10.9 KB) Statistics View on GitHub Reload from mirrored respository
| Branch: | Tag: | Revision:

github / src / ui / window.py @ a5c338e8

1 2e7e9634 Vincent Le Goff
# Copyright (c) 2016, LE GOFF Vincent
2
# All rights reserved.
3
4
# Redistribution and use in source and binary forms, with or without
5
# modification, are permitted provided that the following conditions are met:
6
7
# * Redistributions of source code must retain the above copyright notice, this
8
#   list of conditions and the following disclaimer.
9
10
# * Redistributions in binary form must reproduce the above copyright notice,
11
#   this list of conditions and the following disclaimer in the documentation
12
#   and/or other materials provided with the distribution.
13
14
# * Neither the name of ytranslate nor the names of its
15
#   contributors may be used to endorse or promote products derived from
16
#   this software without specific prior written permission.
17
18
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
"""This file contains the ClientWindow class."""
30 e1d2d0dc Vincent Le Goff
31
import wx
32
33 45e6bcc7 Vincent Le Goff
from ytranslate.tools import t
34
35 5e0c1c3a Vincent Le Goff
from scripting.key import key_name
36 9ebb426f Vincent Le Goff
from ui.dialogs.connection import ConnectionDialog
37
from ui.dialogs.macro import MacroDialog
38
from ui.dialogs.preferences import PreferencesDialog
39 bf6b6eb0 Vincent Le Goff
from ui.event import EVT_FOCUS, FocusEvent, myEVT_FOCUS
40 e1d2d0dc Vincent Le Goff
41 0ce60917 Vincent Le Goff
class ClientWindow(wx.Frame):
42 e1d2d0dc Vincent Le Goff
43 9ebb426f Vincent Le Goff
    def __init__(self, engine, world=None):
44 0ce60917 Vincent Le Goff
        super(ClientWindow, self).__init__(None)
45 bf6b6eb0 Vincent Le Goff
        self.engine = engine
46 833f56ad Vincent Le Goff
        self.focus = True
47 e1d2d0dc Vincent Le Goff
        self.CreateMenuBar()
48 9ebb426f Vincent Le Goff
        self.InitUI(world)
49 e1d2d0dc Vincent Le Goff
50 4d2b7a46 Vincent Le Goff
    @property
51
    def world(self):
52
        return self.panel and self.panel.world or None
53
54 bf6b6eb0 Vincent Le Goff
    def _get_client(self):
55
        return self.panel.client
56
    def _set_client(self, client):
57
        self.panel.client = client
58
    client = property(_get_client, _set_client)
59
60 e1d2d0dc Vincent Le Goff
    def CreateMenuBar(self):
61
        """Create the GUI menu bar and hierarchy of menus."""
62
        menubar = wx.MenuBar()
63 bf6b6eb0 Vincent Le Goff
64
        # Differemtn menus
65 e1d2d0dc Vincent Le Goff
        fileMenu = wx.Menu()
66 bf6b6eb0 Vincent Le Goff
        gameMenu = wx.Menu()
67
68
        # File menu
69
        ## Preferences
70 07b2525b Vincent Le Goff
        preferences = wx.MenuItem(fileMenu, -1, t("ui.menu.preferences"))
71 bf6b6eb0 Vincent Le Goff
        self.Bind(wx.EVT_MENU, self.OnPreferences, preferences)
72 e1d2d0dc Vincent Le Goff
        fileMenu.AppendItem(preferences)
73
74 bf6b6eb0 Vincent Le Goff
        ## Quit
75 07b2525b Vincent Le Goff
        quit = wx.MenuItem(fileMenu, -1, t("ui.menu.quit"))
76 e1d2d0dc Vincent Le Goff
        self.Bind(wx.EVT_MENU, self.OnQuit, quit)
77 bf6b6eb0 Vincent Le Goff
        fileMenu.AppendItem(quit)
78
79
        # Game menu
80 07b2525b Vincent Le Goff
        macro = wx.MenuItem(fileMenu, -1, t("ui.menu.macro"))
81 7e3e3813 Vincent Le Goff
        self.Bind(wx.EVT_MENU, self.OnMacro, macro)
82
        gameMenu.AppendItem(macro)
83 e1d2d0dc Vincent Le Goff
84 07b2525b Vincent Le Goff
        menubar.Append(fileMenu, t("ui.menu.file"))
85
        menubar.Append(gameMenu, t("ui.menu.game"))
86 e1d2d0dc Vincent Le Goff
87
        self.SetMenuBar(menubar)
88
89 9ebb426f Vincent Le Goff
    def InitUI(self, world=None):
90
        if world is None:
91
            dialog = ConnectionDialog(self.engine)
92
            dialog.ShowModal()
93
            world = self.engine.default_world
94
95
        self.panel = MUDPanel(self, self.engine, world)
96 1719fbd5 Vincent Le Goff
        self.SetTitle("{} [CocoMUD]".format(world.name))
97 806da0b1 Vincent Le Goff
        self.Maximize()
98 e1d2d0dc Vincent Le Goff
        self.Show()
99 6fa6bfa5 Vincent Le Goff
        self.Bind(wx.EVT_CLOSE, self.OnClose)
100 833f56ad Vincent Le Goff
        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
101 e1d2d0dc Vincent Le Goff
102
    def OnPreferences(self, e):
103
        """Open the preferences dialog box."""
104 bf6b6eb0 Vincent Le Goff
        dialog = PreferencesDialog(self.engine)
105
        dialog.ShowModal()
106
        dialog.Destroy()
107
108 7e3e3813 Vincent Le Goff
    def OnMacro(self, e):
109
        """Open the macro dialog box."""
110 a5c338e8 Vincent Le Goff
        dialog = MacroDialog(self.engine, self.world)
111 e1d2d0dc Vincent Le Goff
        dialog.ShowModal()
112
        dialog.Destroy()
113 bf6b6eb0 Vincent Le Goff
114 e1d2d0dc Vincent Le Goff
    def OnQuit(self, e):
115 6fa6bfa5 Vincent Le Goff
        self.OnClose(e)
116
117
    def OnClose(self, e):
118
        if self.panel.client:
119
            self.panel.client.running = False
120
        self.Destroy()
121 e1d2d0dc Vincent Le Goff
122 833f56ad Vincent Le Goff
    def OnActivate(self, e):
123
        """The window gains focus."""
124
        self.focus = e.GetActive()
125
        e.Skip()
126
127 bf6b6eb0 Vincent Le Goff
    # Methods to handle client's events
128
    def handle_message(self, message):
129
        """The client has just received a message."""
130
        pos = self.panel.output.GetInsertionPoint()
131 907751f9 Vincent Le Goff
        lines = message.splitlines()
132
        lines = [line for line in lines if line]
133
        message = "\n".join(lines)
134
        output = self.panel.output.GetValue()
135
        if output and not output.endswith("\n"):
136
            message = "\n" + message
137
138 a235c8cc Vincent Le Goff
        if self.engine.settings["options.accessibility.nl_end"]:
139
            message += "\n"
140
141 bf6b6eb0 Vincent Le Goff
        self.panel.output.write(message)
142
        self.panel.output.SetInsertionPoint(pos)
143
144
    def handle_option(self, command):
145
        """Handle the specified option.
146

147
        The command is a string representing the received option.
148
        The following options are supported:
149
            "hide":  the input should be hidden
150
            "show":  the input should be shown
151

152
        """
153
        if command == "hide":
154
            evt = FocusEvent(myEVT_FOCUS, -1, "password")
155
            wx.PostEvent(self.panel, evt)
156
        elif command == "show":
157
            evt = FocusEvent(myEVT_FOCUS, -1, "input")
158
            wx.PostEvent(self.panel, evt)
159 e1d2d0dc Vincent Le Goff
160
class MUDPanel(wx.Panel):
161
162 9ebb426f Vincent Le Goff
    def __init__(self, parent, engine, world):
163 e1d2d0dc Vincent Le Goff
        wx.Panel.__init__(self, parent)
164 7e3e3813 Vincent Le Goff
        self.engine = engine
165 e1d2d0dc Vincent Le Goff
        self.client = None
166 9ebb426f Vincent Le Goff
        self.world = world
167 947e7664 Vincent Le Goff
        self.index = -1
168
        self.history = []
169 833f56ad Vincent Le Goff
        self.focused = True
170 177f07f8 Vincent Le Goff
        sizer = wx.BoxSizer(wx.VERTICAL)
171
        self.SetSizer(sizer)
172 e1d2d0dc Vincent Le Goff
173
        # Input
174 177f07f8 Vincent Le Goff
        s_input = wx.BoxSizer(wx.HORIZONTAL)
175 424e22f2 Vincent Le Goff
        l_input = wx.StaticText(self, -1, t("ui.client.input"))
176 e1d2d0dc Vincent Le Goff
        t_input = wx.TextCtrl(self, -1, "", size=(125, -1),
177
                style=wx.TE_PROCESS_ENTER)
178
        self.input = t_input
179
180
        # Password
181 424e22f2 Vincent Le Goff
        l_password = wx.StaticText(self, -1, t("ui.client.password"))
182 e1d2d0dc Vincent Le Goff
        t_password = wx.TextCtrl(self, -1, "", size=(20, -1),
183
                style=wx.TE_PROCESS_ENTER | wx.TE_PASSWORD)
184
        self.password = t_password
185
        t_password.Hide()
186
187
        # Add the input field in the sizer
188 177f07f8 Vincent Le Goff
        s_input.Add(l_input)
189
        s_input.Add(t_input, proportion=4)
190
        s_input.Add(l_password)
191
        s_input.Add(t_password, proportion=2)
192 e1d2d0dc Vincent Le Goff
193
        # Ouput
194 424e22f2 Vincent Le Goff
        l_output = wx.StaticText(self, -1, t("ui.client.output"))
195 e1d2d0dc Vincent Le Goff
        t_output = wx.TextCtrl(self, -1, "",
196 3541bd46 Vincent Le Goff
                size=(600, 400), style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_PROCESS_TAB)
197 e1d2d0dc Vincent Le Goff
        self.output = t_output
198
199
        # Add the output fields in the sizer
200 177f07f8 Vincent Le Goff
        sizer.Add(s_input)
201
        sizer.Add(t_output, proportion=8)
202 e1d2d0dc Vincent Le Goff
203
        # Event handler
204
        t_input.Bind(wx.EVT_TEXT_ENTER, self.EvtText)
205
        t_password.Bind(wx.EVT_TEXT_ENTER, self.EvtText)
206
        self.Bind(EVT_FOCUS, self.OnFocus)
207 7e3e3813 Vincent Le Goff
        t_input.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
208 3541bd46 Vincent Le Goff
        t_input.Bind(wx.EVT_SET_FOCUS, self.OnInputFocused)
209 7e3e3813 Vincent Le Goff
        t_password.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
210
        t_output.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
211 e1d2d0dc Vincent Le Goff
212
    def EvtText(self, event):
213
        """One of the input fields is sending text."""
214
        self.input.Clear()
215
        self.password.Clear()
216 7b7e38e9 Vincent Le Goff
        encoding = self.engine.settings["options.general.encoding"]
217
        msg = event.GetString().encode(encoding, "replace")
218 e1d2d0dc Vincent Le Goff
        self.client.write(msg + "\r\n")
219
220 947e7664 Vincent Le Goff
        # Write in the history
221
        if event.GetEventObject() == self.input:
222
            if self.index == -1 and msg:
223
                self.history.append(msg)
224
225 e1d2d0dc Vincent Le Goff
    def OnFocus(self, evt):
226 424e22f2 Vincent Le Goff
        """The GUIClient requires a change of focus.
227

228
        This event is triggered when the GUIClient asks a change of
229
        focus in the input field (hiding the password field) or in
230
        the password field (hiding the input field).
231
        """
232 e1d2d0dc Vincent Le Goff
        val = evt.GetValue()
233
        if val == "input":
234
            self.input.Show()
235
            self.input.SetFocus()
236
            self.password.Hide()
237
        elif val == "password":
238
            self.password.Show()
239
            self.password.SetFocus()
240
            self.input.Hide()
241
242 3541bd46 Vincent Le Goff
    def OnInputFocused(self, e):
243
        """Input gains the focus."""
244
        message = self.input.GetValue()
245
        if message:
246
            self.input.SetInsertionPoint(len(message) + 1)
247
        e.Skip()
248
249 7e3e3813 Vincent Le Goff
    def OnKeyDown(self, e):
250
        """A key is pressed in the window."""
251 947e7664 Vincent Le Goff
        skip = True
252 a5035561 Vincent Le Goff
        modifiers = e.GetModifiers()
253 e1d2d0dc Vincent Le Goff
        key = e.GetUnicodeKey()
254 a5035561 Vincent Le Goff
        if not key:
255
            key = e.GetKeyCode()
256
257 947e7664 Vincent Le Goff
        if e.GetEventObject() == self.input:
258
            skip = self.HandleHistory(modifiers, key)
259
260 7e3e3813 Vincent Le Goff
        # Look for matching macros
261 a5c338e8 Vincent Le Goff
        if self.world:
262
            for macro in self.world.macros:
263
                code = (macro.key, macro.modifiers)
264
                if code == (key, modifiers):
265
                    macro.execute(self.engine, self.client)
266 7e3e3813 Vincent Le Goff
267 3541bd46 Vincent Le Goff
        if e.GetEventObject() is self.output:
268 5e0c1c3a Vincent Le Goff
            shortcut = key_name(key, modifiers)
269
            if shortcut:
270 1eaa501a Vincent Le Goff
                if shortcut == "Backspace" or len(shortcut) == 1 or (
271
                        shortcut.startswith("Shift +") and len(shortcut) == 9):
272
                    self.input.EmulateKeyPress(e)
273 3541bd46 Vincent Le Goff
        elif e.GetEventObject() == self.input:
274
            if key == wx.WXK_TAB:
275 9083fa1b Vincent Le Goff
                if self.engine.settings["options.accessibility.tab_end"]:
276
                    message = self.output.GetValue()
277
                    self.output.SetInsertionPoint(-1)
278 3541bd46 Vincent Le Goff
279
        if skip:
280 889d066e Vincent Le Goff
            e.Skip()
281 947e7664 Vincent Le Goff
282
    def HandleHistory(self, modifiers, key):
283
        """Handle the history commands."""
284
        if modifiers == 0:
285
            if key == wx.WXK_UP:
286
                self.HistoryGoUp()
287
                return False
288
            elif key == wx.WXK_DOWN:
289
                self.HistoryGoDown()
290
                return False
291
292
        return True
293
294
    def HistoryGoUp(self):
295
        """Go up in the history."""
296
        if self.index == -1:
297
            self.index = len(self.history) - 1
298
        elif self.index > 0:
299
            self.index -= 1
300
        else:
301
            return
302
303
        message = self.history[self.index]
304
        encoding = self.engine.settings["options.general.encoding"]
305
        message = message.decode(encoding, "replace")
306
        self.input.Clear()
307
        self.input.SetValue(message)
308 9083fa1b Vincent Le Goff
        self.input.SetInsertionPoint(-1)
309 947e7664 Vincent Le Goff
310
    def HistoryGoDown(self):
311
        """Go down in the history."""
312
        if self.index == -1:
313
            return
314
        elif self.index >= len(self.history) - 1:
315
            self.index = -1
316
            message = ""
317
        else:
318
            self.index += 1
319
            message = self.history[self.index]
320
321
        encoding = self.engine.settings["options.general.encoding"]
322
        message = message.decode(encoding, "replace")
323
        self.input.Clear()
324
        self.input.SetValue(message)
325 9083fa1b Vincent Le Goff
        self.input.SetInsertionPoint(-1)