Project

Profile

Help

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

github / src / ui / window.py @ 947e7664

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 ui.dialogs.connection import ConnectionDialog
36
from ui.dialogs.macro import MacroDialog
37
from ui.dialogs.preferences import PreferencesDialog
38
from ui.event import EVT_FOCUS, FocusEvent, myEVT_FOCUS
39

    
40
class ClientWindow(wx.Frame):
41

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

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

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

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

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

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

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

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

    
81
        self.SetMenuBar(menubar)
82

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

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

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

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

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

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

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

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

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

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

    
138
class MUDPanel(wx.Panel):
139

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

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

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

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

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

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

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

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

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

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

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

    
218
    def OnKeyDown(self, e):
219
        """A key is pressed in the window."""
220
        skip = True
221
        modifiers = e.GetModifiers()
222
        key = e.GetUnicodeKey()
223
        if not key:
224
            key = e.GetKeyCode()
225

    
226
        if e.GetEventObject() == self.input:
227
            skip = self.HandleHistory(modifiers, key)
228

    
229
        # Look for matching macros
230
        for code, macro in self.engine.macros.items():
231
            if code == (key, modifiers):
232
                macro.execute(self.engine, self.client)
233

    
234
        if not modifiers and e.GetEventObject() is self.output:
235
            print "Trying to redirect..."
236
            #self.input.SetFocus()
237
            e.SetEventObject(self.input)
238
            #wx.PostEvent(self.input, e)
239
            e.Skip()
240
        elif skip:
241
            e.Skip()
242

    
243
    def HandleHistory(self, modifiers, key):
244
        """Handle the history commands."""
245
        if modifiers == 0:
246
            if key == wx.WXK_UP:
247
                self.HistoryGoUp()
248
                return False
249
            elif key == wx.WXK_DOWN:
250
                self.HistoryGoDown()
251
                return False
252

    
253
        return True
254

    
255
    def HistoryGoUp(self):
256
        """Go up in the history."""
257
        if self.index == -1:
258
            self.index = len(self.history) - 1
259
        elif self.index > 0:
260
            self.index -= 1
261
        else:
262
            return
263

    
264
        message = self.history[self.index]
265
        encoding = self.engine.settings["options.general.encoding"]
266
        message = message.decode(encoding, "replace")
267
        self.input.Clear()
268
        self.input.SetValue(message)
269
        self.input.SetInsertionPoint(len(message) + 1)
270

    
271
    def HistoryGoDown(self):
272
        """Go down in the history."""
273
        if self.index == -1:
274
            return
275
        elif self.index >= len(self.history) - 1:
276
            self.index = -1
277
            message = ""
278
        else:
279
            self.index += 1
280
            message = self.history[self.index]
281

    
282
        encoding = self.engine.settings["options.general.encoding"]
283
        message = message.decode(encoding, "replace")
284
        self.input.Clear()
285
        self.input.SetValue(message)
286
        self.input.SetInsertionPoint(len(message) + 1)
(3-3/3)