Project

Profile

Help

How to connect?
Download (10.6 KB) Statistics View on GitHub Reload from mirrored respository
| Branch: | Tag: | Revision:

github / src / ui / window.py @ 9083fa1b

1
# 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

    
31
import wx
32

    
33
from ytranslate.tools import t
34

    
35
from scripting.key import key_name
36
from ui.dialogs.connection import ConnectionDialog
37
from ui.dialogs.macro import MacroDialog
38
from ui.dialogs.preferences import PreferencesDialog
39
from ui.event import EVT_FOCUS, FocusEvent, myEVT_FOCUS
40

    
41
class ClientWindow(wx.Frame):
42

    
43
    def __init__(self, engine, world=None):
44
        super(ClientWindow, self).__init__(None)
45
        self.engine = engine
46
        self.CreateMenuBar()
47
        self.InitUI(world)
48

    
49
    def _get_client(self):
50
        return self.panel.client
51
    def _set_client(self, client):
52
        self.panel.client = client
53
    client = property(_get_client, _set_client)
54

    
55
    def CreateMenuBar(self):
56
        """Create the GUI menu bar and hierarchy of menus."""
57
        menubar = wx.MenuBar()
58

    
59
        # Differemtn menus
60
        fileMenu = wx.Menu()
61
        gameMenu = wx.Menu()
62

    
63
        # File menu
64
        ## Preferences
65
        preferences = wx.MenuItem(fileMenu, -1, t("ui.menu.preferences"))
66
        self.Bind(wx.EVT_MENU, self.OnPreferences, preferences)
67
        fileMenu.AppendItem(preferences)
68

    
69
        ## Quit
70
        quit = wx.MenuItem(fileMenu, -1, t("ui.menu.quit"))
71
        self.Bind(wx.EVT_MENU, self.OnQuit, quit)
72
        fileMenu.AppendItem(quit)
73

    
74
        # Game menu
75
        macro = wx.MenuItem(fileMenu, -1, t("ui.menu.macro"))
76
        self.Bind(wx.EVT_MENU, self.OnMacro, macro)
77
        gameMenu.AppendItem(macro)
78

    
79
        menubar.Append(fileMenu, t("ui.menu.file"))
80
        menubar.Append(gameMenu, t("ui.menu.game"))
81

    
82
        self.SetMenuBar(menubar)
83

    
84
    def InitUI(self, world=None):
85
        if world is None:
86
            dialog = ConnectionDialog(self.engine)
87
            dialog.ShowModal()
88
            world = self.engine.default_world
89

    
90
        self.panel = MUDPanel(self, self.engine, world)
91
        self.SetTitle("CocoMUD client")
92
        self.Maximize()
93
        self.Show()
94
        self.Bind(wx.EVT_CLOSE, self.OnClose)
95

    
96
    def OnPreferences(self, e):
97
        """Open the preferences dialog box."""
98
        dialog = PreferencesDialog(self.engine)
99
        dialog.ShowModal()
100
        dialog.Destroy()
101

    
102
    def OnMacro(self, e):
103
        """Open the macro dialog box."""
104
        dialog = MacroDialog(self.engine)
105
        dialog.ShowModal()
106
        dialog.Destroy()
107

    
108
    def OnQuit(self, e):
109
        self.OnClose(e)
110

    
111
    def OnClose(self, e):
112
        if self.panel.client:
113
            self.panel.client.running = False
114
        self.Destroy()
115

    
116
    # Methods to handle client's events
117
    def handle_message(self, message):
118
        """The client has just received a message."""
119
        pos = self.panel.output.GetInsertionPoint()
120
        self.panel.output.write(message)
121
        self.panel.output.SetInsertionPoint(pos)
122

    
123
    def handle_option(self, command):
124
        """Handle the specified option.
125

126
        The command is a string representing the received option.
127
        The following options are supported:
128
            "hide":  the input should be hidden
129
            "show":  the input should be shown
130

131
        """
132
        if command == "hide":
133
            evt = FocusEvent(myEVT_FOCUS, -1, "password")
134
            wx.PostEvent(self.panel, evt)
135
        elif command == "show":
136
            evt = FocusEvent(myEVT_FOCUS, -1, "input")
137
            wx.PostEvent(self.panel, evt)
138

    
139
class MUDPanel(wx.Panel):
140

    
141
    def __init__(self, parent, engine, world):
142
        wx.Panel.__init__(self, parent)
143
        self.engine = engine
144
        self.client = None
145
        self.world = world
146
        self.index = -1
147
        self.history = []
148
        sizer = wx.BoxSizer(wx.VERTICAL)
149
        self.SetSizer(sizer)
150

    
151
        # Input
152
        s_input = wx.BoxSizer(wx.HORIZONTAL)
153
        l_input = wx.StaticText(self, -1, t("ui.client.input"))
154
        t_input = wx.TextCtrl(self, -1, "", size=(125, -1),
155
                style=wx.TE_PROCESS_ENTER)
156
        self.input = t_input
157

    
158
        # Password
159
        l_password = wx.StaticText(self, -1, t("ui.client.password"))
160
        t_password = wx.TextCtrl(self, -1, "", size=(20, -1),
161
                style=wx.TE_PROCESS_ENTER | wx.TE_PASSWORD)
162
        self.password = t_password
163
        t_password.Hide()
164

    
165
        # Add the input field in the sizer
166
        s_input.Add(l_input)
167
        s_input.Add(t_input, proportion=4)
168
        s_input.Add(l_password)
169
        s_input.Add(t_password, proportion=2)
170

    
171
        # Ouput
172
        l_output = wx.StaticText(self, -1, t("ui.client.output"))
173
        t_output = wx.TextCtrl(self, -1, "",
174
                size=(600, 400), style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_PROCESS_TAB)
175
        self.output = t_output
176

    
177
        # Add the output fields in the sizer
178
        sizer.Add(s_input)
179
        sizer.Add(t_output, proportion=8)
180

    
181
        # Event handler
182
        t_input.Bind(wx.EVT_TEXT_ENTER, self.EvtText)
183
        t_password.Bind(wx.EVT_TEXT_ENTER, self.EvtText)
184
        self.Bind(EVT_FOCUS, self.OnFocus)
185
        t_input.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
186
        t_input.Bind(wx.EVT_SET_FOCUS, self.OnInputFocused)
187
        t_password.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
188
        t_output.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
189

    
190
    def EvtText(self, event):
191
        """One of the input fields is sending text."""
192
        self.input.Clear()
193
        self.password.Clear()
194
        encoding = self.engine.settings["options.general.encoding"]
195
        msg = event.GetString().encode(encoding, "replace")
196
        self.client.write(msg + "\r\n")
197

    
198
        # Write in the history
199
        if event.GetEventObject() == self.input:
200
            if self.index == -1 and msg:
201
                self.history.append(msg)
202

    
203
    def OnFocus(self, evt):
204
        """The GUIClient requires a change of focus.
205

206
        This event is triggered when the GUIClient asks a change of
207
        focus in the input field (hiding the password field) or in
208
        the password field (hiding the input field).
209
        """
210
        val = evt.GetValue()
211
        if val == "input":
212
            self.input.Show()
213
            self.input.SetFocus()
214
            self.password.Hide()
215
        elif val == "password":
216
            self.password.Show()
217
            self.password.SetFocus()
218
            self.input.Hide()
219

    
220
    def OnInputFocused(self, e):
221
        """Input gains the focus."""
222
        message = self.input.GetValue()
223
        if message:
224
            self.input.SetInsertionPoint(len(message) + 1)
225
        e.Skip()
226

    
227
    def OnKeyDown(self, e):
228
        """A key is pressed in the window."""
229
        skip = True
230
        modifiers = e.GetModifiers()
231
        key = e.GetUnicodeKey()
232
        if not key:
233
            key = e.GetKeyCode()
234

    
235
        if e.GetEventObject() == self.input:
236
            skip = self.HandleHistory(modifiers, key)
237

    
238
        # Look for matching macros
239
        for code, macro in self.engine.macros.items():
240
            if code == (key, modifiers):
241
                macro.execute(self.engine, self.client)
242

    
243
        if e.GetEventObject() is self.output:
244
            shortcut = key_name(key, modifiers)
245
            if shortcut:
246
                if len(shortcut) == 1:
247
                    self.input.AppendText(shortcut.lower())
248
                    self.input.SetFocus()
249
                elif shortcut.startswith("Shift + ") and len(shortcut) == 9:
250
                    self.input.AppendText(shortcut[8])
251
                    self.input.SetFocus()
252
                elif shortcut == "Backspace":
253
                    message = self.input.GetValue()
254
                    if message:
255
                        message = message[:-1]
256
                        self.input.SetValue(message)
257
                    self.input.SetFocus()
258
                    self.input.SetInsertionPoint(len(message) + 1)
259
        elif e.GetEventObject() == self.input:
260
            if key == wx.WXK_TAB:
261
                if self.engine.settings["options.accessibility.tab_end"]:
262
                    message = self.output.GetValue()
263
                    self.output.SetInsertionPoint(-1)
264

    
265
        if skip:
266
            e.Skip()
267

    
268
    def HandleHistory(self, modifiers, key):
269
        """Handle the history commands."""
270
        if modifiers == 0:
271
            if key == wx.WXK_UP:
272
                self.HistoryGoUp()
273
                return False
274
            elif key == wx.WXK_DOWN:
275
                self.HistoryGoDown()
276
                return False
277

    
278
        return True
279

    
280
    def HistoryGoUp(self):
281
        """Go up in the history."""
282
        if self.index == -1:
283
            self.index = len(self.history) - 1
284
        elif self.index > 0:
285
            self.index -= 1
286
        else:
287
            return
288

    
289
        message = self.history[self.index]
290
        encoding = self.engine.settings["options.general.encoding"]
291
        message = message.decode(encoding, "replace")
292
        self.input.Clear()
293
        self.input.SetValue(message)
294
        self.input.SetInsertionPoint(-1)
295

    
296
    def HistoryGoDown(self):
297
        """Go down in the history."""
298
        if self.index == -1:
299
            return
300
        elif self.index >= len(self.history) - 1:
301
            self.index = -1
302
            message = ""
303
        else:
304
            self.index += 1
305
            message = self.history[self.index]
306

    
307
        encoding = self.engine.settings["options.general.encoding"]
308
        message = message.decode(encoding, "replace")
309
        self.input.Clear()
310
        self.input.SetValue(message)
311
        self.input.SetInsertionPoint(-1)
(3-3/3)