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
12 # copy of this software and associated documentation files (the
13 # "Software"), to deal in the Software without restriction, including
14 # without limitation the rights to use, copy, modify, merge, publish,
15 # distribute, sublicense, and/or sell copies of the Software, and to permit
16 # persons to whom the Software is furnished to do so, subject to the
17 # following conditions:
19 # The above copyright notice and this permission notice shall be included
20 # in all copies or substantial portions of the Software.
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
26 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
27 # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
28 # THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 # Recommended line length or text width: 75 characters.
32 gtkgalcon_version
= (0,2,0)
33 gtkgalcon_version_str
= ".".join(map(str, gtkgalcon_version
))
40 from PyGTKShell
.Config
import _config
41 _config
["main-loop-integrate-twisted"] = True
42 from PyGTKShell
.RawConsole
import *
43 arrow_cursor
= gtk
.gdk
.Cursor(gtk
.gdk
.TOP_LEFT_ARROW
)
44 watch_cursor
= gtk
.gdk
.Cursor(gtk
.gdk
.WATCH
)
46 from twisted
.internet
import reactor
50 galcon
.net
.GalconClientRecvCmdLoggerMixin
,
51 galcon
.net
.GalconClientHousekeeperMixin
,
52 galcon
.net
.GalconClientUniverseTrackerMixin
,
53 galcon
.net
.GalconClientBase
,
57 ### CUSTOMIZED STUFF FROM PYGTK SHELL (RAWCONSOLE) [START]
60 class RawConsoleWithIntroMixin(RawConsoleBase
):
62 Raw console running an example script on initialization.
64 Customized for GtkGalcon.
67 def __init__(self
, *a
, **kw
):
68 super(RawConsoleWithIntroMixin
, self
).__init
__(*a
, **kw
)
69 buf
= self
.code_view
.get_buffer()
70 msg
= a_("Press F5 or Ctrl+E to execute this code.")
71 buf('# -*- coding: utf-8 -*-\n' +
73 'from PyGTKShell.API import *\n' +
74 'o = console.output_view.get_buffer()\n' +
76 'c = window.galcon_client\n' +
77 'c.send_commands((1, "message", "Hello, World!"))\n' +
81 class RawConsoleWithIntro(
82 RawConsoleCenterInitMixin
,
83 RawConsoleWithIntroMixin
,
88 class WindowWithRawConsole(Window
):
90 Window with a RawConsole in it and starting at a reasonable size.
93 def __init__(self
, *a
, **kw
):
94 super(WindowWithRawConsole
, self
).__init
__(*a
, **kw
)
95 self
.set_title("PyGTK Shell RawConsole")
96 self
.set_default_size(400, 400)
97 self
.raw_console
= self(RawConsoleWithIntro())
100 class WindowF12RawConsoleMixin(Window
):
102 Window opening a Window containing a RawConsole when F12 is pressed.
105 def __init__(self
, *a
, **kw
):
106 super(WindowF12RawConsoleMixin
, self
).__init
__(*a
, **kw
)
107 self
.connect("key-press-event", self
.__cb
_key
_press
_event
)
109 def __cb_key_press_event(self
, window
, event
):
110 if (KeyPressEval("F12"))(event
):
111 rc
= WindowWithRawConsole().raw_console
112 rc
.code_view
.textview_userexec_namespace
["window"] = self
117 class WindowF12RawConsole(
118 WindowF12RawConsoleMixin
,
123 ### CUSTOMIZED STUFF FROM PYGTK SHELL (RAWCONSOLE) [END]
126 def print_errback(failure
):
127 failure
.printTraceback()
131 class UniverseView(DrawingArea
):
137 super(UniverseView
, self
).__init
__()
139 self
.connect("expose-event", self
.__cb
_expose
_event
)
140 self
.set_size_request(640, 480)
143 self
.planet_info_list
= []
145 def __cb_expose_event(self
, widget
, event
):
146 x
, y
, w
, h
= self
.allocation
147 cr
= self
.window
.cairo_create()
150 cr
.rectangle(0, 0, w
, h
)
151 cr
.set_source_rgba(0, 0, 0, 1)
155 cr
.set_source_rgba(1, 1, 1, 1)
156 for planet_info
in self
.planet_info_list
:
157 cr
.arc(planet_info
.x
, planet_info
.y
, 10, 0, PI2
)
161 if self
.window
is not None:
162 self
.window
.invalidate_rect(self
.allocation
, False)
166 class GalconUniverseWindow(WindowF12RawConsoleMixin
):
168 A Window displaying a view of the universe.
172 super(GalconUniverseWindow
, self
).__init
__()
173 self
.set_title("Universe Window")
174 self
.universe_view
= self(UniverseView())
177 class GalconChatWindow(WindowF12RawConsoleMixin
):
179 A Window to allow chatting in Galcon, following the Galcon Client
183 def __init__(self
, options
, user_info
, server_info
):
184 self
.__options
= options
185 self
.__user
_info
= user_info
186 self
.__server
_info
= server_info
188 super(GalconChatWindow
, self
).__init
__()
189 self
.set_default_size(400, 300)
190 n
, o
= server_info
["name"], server_info
["owner"]
192 self
.set_title("GtkGalcon Chat (%s%s)" % (n
, o
))
194 outer_vbox
, inner_vbox
= gnome_hig(self
)
196 sw
= inner_vbox(Frame())(ScrolledWindow())
197 self
.output_view
= sw(TextView())
198 self
.__buf
= self
.output_view
.get_buffer()
199 self
.output_view
.set_editable(False)
200 hbox
= gnome_hig(inner_vbox(HBox(), False, False))
201 self
.input_entry
= hbox(Entry())
202 self
.send_button
= hbox(Button("Send"), False, False)
203 self
.send_button
.connect("clicked", self
.__cb
_send
)
204 self
.leave_button
= hbox(Button("Leave"), False, False)
205 self
.leave_button
.connect("clicked", self
.__cb
_leave
)
207 self
.send_button
.set_property("can-default", True)
208 gobject
.idle_add(self
.send_button
.grab_default
)
209 gobject
.idle_add(self
.input_entry
.grab_focus
)
212 self
.galcon_client
= C(self
.__options
,
213 self
.__user
_info
, self
.__server
_info
)
214 r
= self
.galcon_client
.register_command_callback
215 r("close", self
.__cmd
_close
)
216 r("message", self
.__cmd
_message
)
217 r("start", self
.__cmd
_start
)
218 r("stop", self
.__cmd
_stop
)
219 reactor
.listenUDP(0, self
.galcon_client
)
221 def f(): # TODO: implement this properly after login
222 self
.galcon_client
.send_commands((1, "status", "away"))
224 gobject
.timeout_add(500, f
)
226 def __cb_leave(self
, button
):
227 deferred
= self
.galcon_client
.logout()
228 deferred
.addCallback(self
.__cb
_left
)
230 def __cb_left(self
, no_args
):
231 # GalconLoginWindow._instance.show()
232 GalconServerListWindow
._instance
.show()
235 def __cb_send(self
, button
):
236 msg
= self
.input_entry
.get_text()
237 self
.galcon_client
.send_commands((1, "message", msg
))
238 self
.input_entry
.set_text("")
240 def __cmd_message(self
, command
):
241 sender
, message
= command
[3:]
243 if sender
: snd
= " " + sender
244 self
.__buf
("%s%s %s\n" % (time
.strftime("%X"), snd
, message
))
245 if "BOTQUIT" in message
:
246 self
.galcon_client
.logout()
248 w
.set_position(gtk
.WIN_POS_CENTER
)
251 i(Label("Bye!")).modify_font(pango
.FontDescription("sans 32"))
252 gobject
.timeout_add(2000, main_loop_quit
)
254 def __cmd_close(self
, command
):
255 self
.galcon_client
.send_commands((1, "[CLOSE]"))
256 gobject
.timeout_add(500, main_loop_quit
) # TODO: await ACK
258 def __cmd_stop(self
, command
):
259 self
.__buf
("(stop)\n")
261 def __cmd_start(self
, command
):
262 self
.__buf
("(start)\n")
265 class GalconServerListWindow(WindowF12RawConsoleMixin
):
267 Window displaying the list of Galcon servers.
270 def __init__(self
, options
, user_info
):
271 self
.__options
= options
272 super(GalconServerListWindow
, self
).__init
__()
273 self
.__user
_info
= user_info
274 self
.set_title("Galcon Server List")
275 self
.set_default_size(600, 400)
276 self
.set_position(gtk
.WIN_POS_CENTER
)
278 outer_vbox
, self
.__inner
_vbox
= gnome_hig(self
)
280 hbox
= gnome_hig(self
.__inner
_vbox
(HBox(), False, False))
281 self
.__version
_label
= hbox(LeftLabel(""), False, False)
282 self
.set_current_version()
283 self
.__version
_label
.set_sensitive(False)
285 self
.__refresh
_button
= hbox(Button("Refresh List"), False, False,
287 self
.__refresh
_button
.connect("clicked", self
.__cb
_refresh
)
289 sw
= self
.__inner
_vbox
(Frame())(ScrolledWindow())
290 self
.__treeview
= sw(TreeView())
291 self
.__treeview
.set_sensitive(False)
292 cb
= self
.__cb
_selection
_changed
293 self
.__treeview
.get_selection().connect("changed", cb
)
295 for i
, spec
in enumerate(galcon
.net
.ServerInfo
.COL_SPEC
):
296 if not spec
["visible"]: continue
297 col
= gtk
.TreeViewColumn(spec
["caption"])
298 col
.set_reorderable(True)
299 col
.set_sort_column_id(i
)
300 self
.__treeview
.append_column(col
)
301 cell
= gtk
.CellRendererText()
302 col
.pack_start(cell
, True)
303 col
.add_attribute(cell
, "text", i
)
304 col
.add_attribute(cell
, "sensitive", 0)
306 hbox
= gnome_hig(self
.__inner
_vbox
(HBox(), False, False))
307 self
.__join
_button
= hbox(Button("Join"))
308 self
.__join
_button
.set_sensitive(False)
309 self
.__join
_button
.connect("clicked", self
.__cb
_join
)
311 self
.back_button
= hbox(Button("Back"), False, False)
312 self
.back_button
.connect("clicked", self
.__cb
_back
)
314 self
.__statusbar
= outer_vbox(Statusbar(), False, False)
315 self
.__statusbar
_cid
= cid
= self
.__statusbar
.get_context_id("msg")
317 self
.__server
_password
_prompt
= dlg
= Dialog(
318 "Server Password Required", self
, gtk
.DIALOG_MODAL
,
319 (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
,
320 gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
)
322 dlg
.set_default_response(gtk
.RESPONSE_ACCEPT
)
323 dlg
.set_default_size(300, -1)
324 vbox
= gnome_hig(VBox()) # I want my own VBox here ;-)
325 vbox
.set_border_width(6)
326 dlg
.vbox
.pack_start(vbox
)
327 self
.__server
_password
_prompt
_label
= vbox(LeftLabel())
328 self
.__server
_password
_prompt
_entry
= vbox(Entry())
329 self
.__server
_password
_prompt
_entry
.set_visibility(False)
331 def set_current_version(self
, current_version
= None):
332 self
.__version
_label
.set_sensitive(False)
333 if current_version
is None:
334 self
.__version
_label
.set_text("Current version: ???")
336 self
.__version
_label
.set_text("Current version: %s" %
337 ".".join(current_version
))
338 self
.__version
_label
.set_sensitive(True)
340 def set_server_info_list(self
, server_info_list
= None):
341 types
= galcon
.net
.ServerInfo
.get_col_types()
342 model
= gtk
.ListStore(*types
)
343 self
.__treeview
.set_sensitive(False)
344 if server_info_list
is not None:
345 for server_info
in server_info_list
:
346 model
.append(server_info
.get_data_tuple())
347 self
.__treeview
.set_sensitive(True)
348 self
.__treeview
.set_model(model
)
350 def __cb_refresh(self
, button
):
351 self
.window
.set_cursor(watch_cursor
)
352 self
.__inner
_vbox
.set_sensitive(False)
353 self
.__statusbar
.push(self
.__statusbar
_cid
,
354 "Retrieving server list...")
355 deferred
= galcon
.net
.get_server_list(self
.__user
_info
)
356 deferred
.addCallback(self
.__server
_list
_callback
)
357 deferred
.addErrback(self
.__server
_list
_errback
)
359 def __server_list_callback(self
, (current_version
, server_info_list
)):
360 self
.set_current_version(current_version
)
361 self
.set_server_info_list(server_info_list
)
362 self
.__statusbar
.pop(self
.__statusbar
_cid
)
363 self
.__inner
_vbox
.set_sensitive(True)
364 self
.window
.set_cursor(arrow_cursor
)
366 def __server_list_errback(self
, failure
):
367 self
.set_sensitive(True)
368 self
.window
.set_cursor(arrow_cursor
)
369 cid
= self
.__statusbar
_cid
370 sbar
= self
.__statusbar
373 mid
= sbar
.push(cid
, "Retrieving server list failed")
374 gobject
.timeout_add(4000, lambda: sbar
.remove(cid
, mid
))
377 def __cb_selection_changed(self
, treesel
):
378 model
, iter = treesel
.get_selected()
382 data
= galcon
.net
.ServerInfo(*model
[iter])
383 ok_to_join
= data
.bots_ok
384 self
.__join
_button
.set_sensitive(ok_to_join
)
386 def __cb_join(self
, button
):
387 treesel
= self
.__treeview
.get_selection()
388 model
, iter = treesel
.get_selected()
389 server_info
= galcon
.net
.ServerInfo(*model
[iter])
390 if server_info
.pwd_protected
:
391 self
.__run
_server
_password
_prompt
(server_info
)
393 self
.join_with_server_info(server_info
)
395 def __cb_back(self
, button
):
396 GalconLoginWindow
._instance
.show()
399 def __run_server_password_prompt(self
, server_info
):
400 n
, o
= server_info
["name"], server_info
["owner"]
402 s
= "Password for %s%s:" % (n
, o
)
403 self
.__server
_password
_prompt
_label
.set_text(s
)
404 response_id
= self
.__server
_password
_prompt
.run()
405 if response_id
!= gtk
.RESPONSE_ACCEPT
: return None
406 passwd
= self
.__server
_password
_prompt
_entry
.get_text()
407 server_info
["passwd"] = passwd
408 self
.join_with_server_info(server_info
)
410 def join_with_server_info(self
, server_info
):
411 chat_window
= GalconChatWindow(self
.__options
,
412 self
.__user
_info
, server_info
)
413 gobject
.idle_add(self
.hide
)
416 class GalconLoginWindow(WindowF12RawConsoleMixin
):
418 A Window asking the user for Galcon login details and log in.
421 def __init__(self
, options
):
422 self
.__options
= options
423 super(GalconLoginWindow
, self
).__init
__()
424 self
.set_title("GtkGalcon Login")
425 self
.set_default_size(400, -1)
426 self
.set_position(gtk
.WIN_POS_CENTER
)
428 outer_vbox
, self
.inner_vbox
= gnome_hig(self
)
430 table
= gnome_hig(self
.inner_vbox(Table(), False, False))
432 xop
= {"xoptions": gtk
.FILL
}
434 table
.attach_cell(LeftLabel("Email Address:"), **xop
)
435 self
.email_entry
= table
.attach_cell(Entry())
438 table
.attach_cell(LeftLabel("Username:"), **xop
)
439 self
.name_entry
= table
.attach_cell(Entry())
440 self
.name_entry
.set_text("gtkgalcon")
443 table
.attach_cell(LeftLabel("Password:"), **xop
)
444 self
.passwd_entry
= table
.attach_cell(Entry())
445 self
.passwd_entry
.set_visibility(False)
448 hbox
= gnome_hig(table
.attach_row(HBox()))
449 self
.__login
_button
= hbox(Button("Sign In"))
450 self
.__login
_button
.connect("clicked", self
.__cb
_login
)
451 self
.__login
_button
.set_property("can-default", True)
452 gobject
.idle_add(self
.__login
_button
.grab_default
)
454 self
.__quit
_button
= hbox(Button("Quit"), False, False)
455 self
.__quit
_button
.connect("clicked", self
.__cb
_quit
)
457 self
.statusbar
= outer_vbox(Statusbar(), False, False)
458 self
.statusbar_cid
= cid
= self
.statusbar
.get_context_id("msg")
459 mid
= self
.statusbar
.push(cid
, "Enter your login details")
460 gobject
.timeout_add(4000, lambda: self
.statusbar
.remove(cid
, mid
))
462 def __get_user_info(self
):
463 email
= self
.email_entry
.get_text()
464 name
= self
.name_entry
.get_text()
465 passwd
= self
.passwd_entry
.get_text()
468 return galcon
.net
.UserInfo(email
, name
, passwd
, platform
, version
)
470 def __cb_login(self
, button
):
471 self
.window
.set_cursor(watch_cursor
)
472 self
.inner_vbox
.set_sensitive(False)
473 self
.statusbar
.push(self
.statusbar_cid
,
474 "Retrieving server list...")
475 user_info
= self
.__get
_user
_info
()
476 deferred
= galcon
.net
.get_server_list(user_info
)
477 deferred
.addCallbacks(self
.__server
_list
_callback
,
478 self
.__server
_list
_errback
)
479 deferred
.addErrback(print_errback
)
481 def __cb_quit(self
, button
):
484 def __server_list_callback(self
, (current_version
, server_info_list
)):
485 w
= GalconServerListWindow(self
.__options
, self
.__get
_user
_info
())
486 GalconServerListWindow
._instance
= w
487 w
.set_current_version(current_version
)
488 w
.set_server_info_list(server_info_list
)
489 gobject
.idle_add(self
.hide
)
490 self
.inner_vbox
.set_sensitive(True)
491 self
.window
.set_cursor(arrow_cursor
)
492 cid
= self
.statusbar_cid
493 self
.statusbar
.pop(cid
)
495 def __server_list_errback(self
, failure
):
496 failure
.trap(galcon
.net
.ServerListException
)
497 self
.inner_vbox
.set_sensitive(True)
498 self
.window
.set_cursor(arrow_cursor
)
499 cid
= self
.statusbar_cid
500 self
.statusbar
.pop(cid
)
502 mid
= self
.statusbar
.push(cid
, "Failure: %s" %
503 failure
.getErrorMessage())
504 gobject
.timeout_add(4000, lambda: self
.statusbar
.remove(cid
, mid
))
509 option_parser
= optparse
.OptionParser(prog
= "GtkGalcon",
510 version
= "%%prog %s" %
511 gtkgalcon_version_str
)
512 option_parser
= GalconClient
._modify
_option
_parser
(option_parser
)
513 options
, arguments
= option_parser
.parse_args(argv
[1:])
514 GalconLoginWindow
._instance
= GalconLoginWindow(options
)
518 if __name__
== "__main__":
520 sys
.exit(main(sys
.argv
))