2 # -*- coding: utf-8 -*-
6 Depends on PyGTK Shell.
8 # Copyright (C) 2007 Michael Carter
9 # Copyright (C) 2007 Felix Rabe <public@felixrabe.textdriven.com>
11 # Permission is hereby granted, free of charge, to any person obtaining a copy
12 # of this software and associated documentation files (the "Software"), to deal
13 # in the Software without restriction, including without limitation the rights
14 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 # copies of the Software, and to permit persons to whom the Software is
16 # furnished to do so, subject to the following conditions:
18 # The above copyright notice and this permission notice shall be included in
19 # all copies or substantial portions of the Software.
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 gtkgalcon_version
= (0,1,0)
33 from PyGTKShell
.Config
import _config
34 _config
["main-loop-integrate-twisted"] = True
35 from PyGTKShell
.RawConsole
import *
37 from twisted
.internet
import reactor
40 arrow_cursor
= gtk
.gdk
.Cursor(gtk
.gdk
.TOP_LEFT_ARROW
)
41 watch_cursor
= gtk
.gdk
.Cursor(gtk
.gdk
.WATCH
)
44 ### CUSTOMIZED STUFF FROM PYGTK SHELL (RAWCONSOLE) [START]
47 class RawConsoleWithIntroMixin(RawConsoleBase
):
49 Raw console running an example script on initialization.
51 Customized for GtkGalcon.
54 def __init__(self
, *a
, **kw
):
55 super(RawConsoleWithIntroMixin
, self
).__init
__(*a
, **kw
)
56 buf
= self
.code_view
.get_buffer()
57 msg
= a_("Press F5 or Ctrl+E to execute this code.")
58 buf('# -*- coding: utf-8 -*-\n' +
60 'from PyGTKShell.API import *\n' +
61 'o = console.output_view.get_buffer()\n' +
63 'c = window.galcon_client\n' +
64 'c.send_commands((1, "message", "Hello, World!"))\n' +
66 # buf.select_range(*buf.get_bounds())
69 class RawConsoleWithIntro(
70 RawConsoleCenterInitMixin
,
71 RawConsoleWithIntroMixin
,
76 class WindowWithRawConsole(Window
):
78 Window with a RawConsole in it and starting at a reasonable size.
81 def __init__(self
, *a
, **kw
):
82 super(WindowWithRawConsole
, self
).__init
__(*a
, **kw
)
83 self
.set_title("PyGTK Shell RawConsole")
84 self
.set_default_size(400, 400)
85 self
.raw_console
= self(RawConsoleWithIntro())
88 class WindowF12RawConsoleMixin(Window
):
90 Window opening a Window containing a RawConsole when F12 is pressed.
93 def __init__(self
, *a
, **kw
):
94 super(WindowF12RawConsoleMixin
, self
).__init
__(*a
, **kw
)
95 self
.connect("key-press-event", self
.__cb
_key
_press
_event
)
97 def __cb_key_press_event(self
, window
, event
):
98 if (KeyPressEval("F12"))(event
):
99 rc
= WindowWithRawConsole().raw_console
100 rc
.code_view
.textview_userexec_namespace
["window"] = self
105 class WindowF12RawConsole(
106 WindowF12RawConsoleMixin
,
111 ### CUSTOMIZED STUFF FROM PYGTK SHELL (RAWCONSOLE) [END]
114 def print_errback(failure
):
115 failure
.printTraceback()
119 class GalconChatWindow(WindowF12RawConsoleMixin
):
121 A Window to allow chatting in Galcon, following the Galcon Client
125 def __init__(self
, user_info
, server_info
):
126 self
.__user
_info
= user_info
127 self
.__server
_info
= server_info
129 super(GalconChatWindow
, self
).__init
__()
130 self
.set_default_size(400, 300)
131 n
, o
= server_info
["name"], server_info
["owner"]
133 self
.set_title("GtkGalcon Chat (%s%s)" % (n
, o
))
135 outer_vbox
, inner_vbox
= gnome_hig(self
)
137 sw
= inner_vbox(Frame())(ScrolledWindow())
138 self
.output_view
= sw(TextView())
139 self
.__buf
= self
.output_view
.get_buffer()
140 self
.output_view
.set_editable(False)
141 hbox
= gnome_hig(inner_vbox(HBox(), False, False))
142 self
.input_entry
= hbox(Entry())
143 self
.send_button
= hbox(Button("Send"), False, False)
144 self
.send_button
.connect("clicked", self
.__cb
_send
)
145 self
.leave_button
= hbox(Button("Leave"), False, False)
146 self
.leave_button
.connect("clicked", self
.__cb
_leave
)
148 self
.send_button
.set_property("can-default", True)
149 gobject
.idle_add(self
.send_button
.grab_default
)
150 gobject
.idle_add(self
.input_entry
.grab_focus
)
152 C
= galcon
.net
.GalconClient
153 self
.galcon_client
= C(self
.__user
_info
, self
.__server
_info
)
154 r
= self
.galcon_client
.register_command_callback
155 r("close", self
.__cmd
_close
)
156 r("message", self
.__cmd
_message
)
157 r("start", self
.__cmd
_start
)
158 r("stop", self
.__cmd
_stop
)
159 reactor
.listenUDP(0, self
.galcon_client
)
161 def f(): # TODO: implement this properly after login
162 self
.galcon_client
.send_commands((1, "status", "away"))
164 gobject
.timeout_add(500, f
)
166 def __cb_leave(self
, button
):
167 self
.galcon_client
.send_commands(
172 def __cb_send(self
, button
):
173 msg
= self
.input_entry
.get_text()
174 self
.galcon_client
.send_commands((1, "message", msg
))
175 self
.input_entry
.set_text("")
177 def __cmd_message(self
, command
):
178 sender
, message
= command
[3:]
180 if sender
: snd
= " " + sender
181 self
.__buf
("%s%s %s\n" % (time
.strftime("%X"), snd
, message
))
182 if "BOTQUIT" in message
:
183 self
.galcon_client
.send_commands(
188 w
.set_position(gtk
.WIN_POS_CENTER
)
191 i(Label("Bye!")).modify_font(pango
.FontDescription("sans 32"))
192 gobject
.timeout_add(2000, main_loop_quit
)
194 def __cmd_close(self
, command
):
195 self
.galcon_client
.send_commands((1, "[CLOSE]"))
196 gobject
.timeout_add(500, main_loop_quit
) # TODO: await ACK
198 def __cmd_stop(self
, command
):
199 self
.__buf
("(stop)\n")
201 def __cmd_start(self
, command
):
202 self
.__buf
("(start)\n")
205 class GalconServerListWindow(WindowF12RawConsoleMixin
):
207 Window displaying the list of Galcon servers.
210 def __init__(self
, user_info
):
211 super(GalconServerListWindow
, self
).__init
__()
212 self
.__user
_info
= user_info
213 self
.set_title("Galcon Server List")
214 self
.set_default_size(600, 400)
215 self
.set_position(gtk
.WIN_POS_CENTER
)
217 outer_vbox
, self
.__inner
_vbox
= gnome_hig(self
)
219 hbox
= gnome_hig(self
.__inner
_vbox
(HBox(), False, False))
220 self
.__version
_label
= hbox(LeftLabel(""), False, False)
221 self
.set_current_version()
222 self
.__version
_label
.set_sensitive(False)
224 self
.__refresh
_button
= hbox(Button("Refresh List"), False, False,
226 self
.__refresh
_button
.connect("clicked", self
.__cb
_refresh
)
228 sw
= self
.__inner
_vbox
(Frame())(ScrolledWindow())
229 self
.__treeview
= sw(TreeView())
230 self
.__treeview
.set_sensitive(False)
231 cb
= self
.__cb
_selection
_changed
232 self
.__treeview
.get_selection().connect("changed", cb
)
234 for i
, spec
in enumerate(galcon
.net
.ServerInfo
.COL_SPEC
):
235 if not spec
["visible"]: continue
236 col
= gtk
.TreeViewColumn(spec
["caption"])
237 col
.set_reorderable(True)
238 col
.set_sort_column_id(i
)
239 self
.__treeview
.append_column(col
)
240 cell
= gtk
.CellRendererText()
241 col
.pack_start(cell
, True)
242 col
.add_attribute(cell
, "text", i
)
243 col
.add_attribute(cell
, "sensitive", 0)
245 self
.__join
_button
= self
.__inner
_vbox
(Button("Join"), False,
247 self
.__join
_button
.set_sensitive(False)
248 self
.__join
_button
.connect("clicked", self
.__cb
_join
)
250 self
.__statusbar
= outer_vbox(Statusbar(), False, False)
251 self
.__statusbar
_cid
= cid
= self
.__statusbar
.get_context_id("msg")
253 self
.__server
_password
_prompt
= dlg
= Dialog(
254 "Server Password Required", self
, gtk
.DIALOG_MODAL
,
255 (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
,
256 gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
)
258 dlg
.set_default_response(gtk
.RESPONSE_ACCEPT
)
259 dlg
.set_default_size(300, -1)
260 vbox
= gnome_hig(VBox()) # I want my own VBox here ;-)
261 vbox
.set_border_width(6)
262 dlg
.vbox
.pack_start(vbox
)
263 self
.__server
_password
_prompt
_label
= vbox(LeftLabel())
264 self
.__server
_password
_prompt
_entry
= vbox(Entry())
265 self
.__server
_password
_prompt
_entry
.set_visibility(False)
267 def set_current_version(self
, current_version
= None):
268 self
.__version
_label
.set_sensitive(False)
269 if current_version
is None:
270 self
.__version
_label
.set_text("Current version: ???")
272 self
.__version
_label
.set_text("Current version: %s" %
273 ".".join(current_version
))
274 self
.__version
_label
.set_sensitive(True)
276 def set_server_info_list(self
, server_info_list
= None):
277 types
= galcon
.net
.ServerInfo
.get_col_types()
278 model
= gtk
.ListStore(*types
)
279 self
.__treeview
.set_sensitive(False)
280 if server_info_list
is not None:
281 for server_info
in server_info_list
:
282 model
.append(server_info
.get_data_tuple())
283 self
.__treeview
.set_sensitive(True)
284 self
.__treeview
.set_model(model
)
286 def __cb_refresh(self
, button
):
287 self
.window
.set_cursor(watch_cursor
)
288 self
.__inner
_vbox
.set_sensitive(False)
289 self
.__statusbar
.push(self
.__statusbar
_cid
,
290 "Retrieving server list...")
291 deferred
= galcon
.net
.get_server_list(self
.__user
_info
)
292 deferred
.addCallback(self
.__server
_list
_callback
)
293 deferred
.addErrback(self
.__server
_list
_errback
)
295 def __server_list_callback(self
, (current_version
, server_info_list
)):
296 self
.set_current_version(current_version
)
297 self
.set_server_info_list(server_info_list
)
298 self
.__statusbar
.pop(self
.__statusbar
_cid
)
299 self
.__inner
_vbox
.set_sensitive(True)
300 self
.window
.set_cursor(arrow_cursor
)
302 def __server_list_errback(self
, failure
):
303 self
.set_sensitive(True)
304 self
.window
.set_cursor(arrow_cursor
)
305 cid
= self
.__statusbar
_cid
306 sbar
= self
.__statusbar
309 mid
= sbar
.push(cid
, "Retrieving server list failed")
310 gobject
.timeout_add(4000, lambda: sbar
.remove(cid
, mid
))
313 def __cb_selection_changed(self
, treesel
):
314 model
, iter = treesel
.get_selected()
318 data
= galcon
.net
.ServerInfo(*model
[iter])
319 ok_to_join
= data
.bots_ok
320 self
.__join
_button
.set_sensitive(ok_to_join
)
322 def __cb_join(self
, button
):
323 treesel
= self
.__treeview
.get_selection()
324 model
, iter = treesel
.get_selected()
325 server_info
= galcon
.net
.ServerInfo(*model
[iter])
326 if server_info
.pwd_protected
:
327 self
.__run
_server
_password
_prompt
(server_info
)
329 self
.join_with_server_info(server_info
)
331 def __run_server_password_prompt(self
, server_info
):
332 n
, o
= server_info
["name"], server_info
["owner"]
334 s
= "Password for %s%s:" % (n
, o
)
335 self
.__server
_password
_prompt
_label
.set_text(s
)
336 response_id
= self
.__server
_password
_prompt
.run()
337 if response_id
!= gtk
.RESPONSE_ACCEPT
: return None
338 passwd
= self
.__server
_password
_prompt
_entry
.get_text()
339 server_info
["passwd"] = passwd
340 self
.join_with_server_info(server_info
)
342 def join_with_server_info(self
, server_info
):
343 chat_window
= GalconChatWindow(self
.__user
_info
, server_info
)
344 gobject
.idle_add(self
.destroy
)
347 class GalconLoginWindow(WindowF12RawConsoleMixin
):
349 A Window asking the user for Galcon login details and log in.
353 super(GalconLoginWindow
, self
).__init
__()
354 self
.set_title("GtkGalcon Login")
355 self
.set_default_size(400, -1)
356 self
.set_position(gtk
.WIN_POS_CENTER
)
358 outer_vbox
, self
.inner_vbox
= gnome_hig(self
)
360 table
= gnome_hig(self
.inner_vbox(Table(), False, False))
362 xop
= {"xoptions": gtk
.FILL
}
364 table
.attach_cell(LeftLabel("Email Address:"), **xop
)
365 self
.email_entry
= table
.attach_cell(Entry())
368 table
.attach_cell(LeftLabel("Username:"), **xop
)
369 self
.name_entry
= table
.attach_cell(Entry())
370 self
.name_entry
.set_text("gtkgalcon")
373 table
.attach_cell(LeftLabel("Password:"), **xop
)
374 self
.passwd_entry
= table
.attach_cell(Entry())
375 self
.passwd_entry
.set_visibility(False)
378 self
.login_button
= table
.attach_row(Button("Sign In"))
379 self
.login_button
.connect("clicked", self
.__login
)
380 self
.login_button
.set_property("can-default", True)
381 gobject
.idle_add(self
.login_button
.grab_default
)
383 self
.statusbar
= outer_vbox(Statusbar(), False, False)
384 self
.statusbar_cid
= cid
= self
.statusbar
.get_context_id("msg")
385 mid
= self
.statusbar
.push(cid
, "Enter your login details")
386 gobject
.timeout_add(4000, lambda: self
.statusbar
.remove(cid
, mid
))
388 def __get_user_info(self
):
389 email
= self
.email_entry
.get_text()
390 name
= self
.name_entry
.get_text()
391 passwd
= self
.passwd_entry
.get_text()
394 return galcon
.net
.UserInfo(email
, name
, passwd
, platform
, version
)
396 def __login(self
, button
):
397 self
.window
.set_cursor(watch_cursor
)
398 self
.inner_vbox
.set_sensitive(False)
399 self
.statusbar
.push(self
.statusbar_cid
,
400 "Retrieving server list...")
401 user_info
= self
.__get
_user
_info
()
402 deferred
= galcon
.net
.get_server_list(user_info
)
403 deferred
.addCallbacks(self
.__server
_list
_callback
,
404 self
.__server
_list
_errback
)
405 deferred
.addErrback(print_errback
)
407 def __server_list_callback(self
, (current_version
, server_info_list
)):
408 w
= GalconServerListWindow(self
.__get
_user
_info
())
409 w
.set_current_version(current_version
)
410 w
.set_server_info_list(server_info_list
)
411 gobject
.idle_add(self
.destroy
)
413 def __server_list_errback(self
, failure
):
414 failure
.trap(galcon
.net
.ServerListException
)
415 self
.inner_vbox
.set_sensitive(True)
416 self
.window
.set_cursor(arrow_cursor
)
417 cid
= self
.statusbar_cid
418 self
.statusbar
.pop(cid
)
420 mid
= self
.statusbar
.push(cid
, "Failure: %s" %
421 failure
.getErrorMessage())
422 gobject
.timeout_add(4000, lambda: self
.statusbar
.remove(cid
, mid
))
431 if __name__
== "__main__":
433 sys
.exit(main(sys
.argv
))