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) |
- « Previous
- 1
- 2
- 3
- Next »