1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
|
13
|
|
14
|
|
15
|
|
16
|
|
17
|
|
18
|
|
19
|
|
20
|
|
21
|
|
22
|
|
23
|
|
24
|
|
25
|
|
26
|
|
27
|
|
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
|
|
60
|
fileMenu = wx.Menu()
|
61
|
gameMenu = wx.Menu()
|
62
|
|
63
|
|
64
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
178
|
sizer.Add(s_input)
|
179
|
sizer.Add(t_output, proportion=8)
|
180
|
|
181
|
|
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
|
|
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
|
|
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)
|