1
# -*- coding: utf-8 -*-
13
class GtkNetworkAddDialog:
15
def __init__(self, networkDialog, parent):
16
self.__networkDialog = networkDialog
19
self.__gui = gtkui.loadUIFile('network_new_server.ui')
20
self.__gui.connect_signals(self)
22
self.__gui.get_object('add_account_dialog').set_transient_for(parent)
24
# FIXME: Hard-coded servers
26
self.serverModel = gtk.ListStore(str, str, int)
27
# Translators: Add Network Profile Dialog: Connect to the GGZ Gaming Zone server (the default)
28
self.serverModel.set(self.serverModel.append(), 0, _("GGZ Gaming Zone"), 1, "gnome.ggzgamingzone.org", 2, 5688)
29
# Translators: Add Network Profile Dialog: Use a custom server
30
self.serverModel.set(self.serverModel.append(), 0, _("Custom"), 1, "", 2, 5688)
32
widget = self.__gui.get_object('server_combo')
33
widget.set_model(self.serverModel)
34
cell = gtk.CellRendererText()
35
widget.pack_start(cell, False)
36
widget.add_attribute(cell, 'text', 0)
37
widget.set_model(self.serverModel)
40
def setVisible(self, isVisible):
41
widget = self.__gui.get_object('add_account_dialog')
49
self.__gui.get_object('server_combo').set_active(0)
50
self.__gui.get_object('username_entry').set_text('')
52
def _on_server_changed(self, widget):
53
widget = self.__gui.get_object('server_combo')
54
model = widget.get_model()
55
iter = widget.get_active_iter()
56
(host,) = model.get(iter, 1)
57
(port,) = model.get(iter, 2)
58
self.__gui.get_object('host_entry').set_text(host)
59
self.__gui.get_object('port_spin').set_value(port)
60
table = self.__gui.get_object('custom_server_table')
67
username = self.__gui.get_object('username_entry').get_text()
68
host = self.__gui.get_object('host_entry').get_text()
69
return username != '' and host != ''
71
def _on_input_changed(self, widget):
72
self.__gui.get_object('add_button').set_sensitive(self.have_data())
74
def _on_username_activate(self, widget):
76
self._on_response(None, gtk.RESPONSE_OK)
78
def _on_response(self, widget, response_id):
79
username = self.__gui.get_object('username_entry').get_text()
80
host = self.__gui.get_object('host_entry').get_text()
81
port = self.__gui.get_object('port_spin').get_value_as_int()
82
name = '%s@%s' % (username, host) # FIXME
84
if response_id == gtk.RESPONSE_OK:
85
profile = self.__networkDialog.feedback.addProfile((name, username, host, port))
86
self.__networkDialog.addProfile(profile, profile.name, useNow = True)
88
self.__gui.get_object('add_account_dialog').hide()
91
def _on_delete(self, widget, event):
92
# Hide; don't delete this window
95
class GtkNetworkGameDialog(glchess.ui.NetworkController):
99
def __init__(self, mainUI, feedback):
100
"""Constructor for a new game dialog.
102
'mainUI' is the main UI.
103
'feedback' is the object to feedback events with.
105
self.__mainUI = mainUI
106
self.feedback = feedback
109
self.__gui = gtkui.loadUIFile('network_game.ui')
111
self.infobar = gtk.InfoBar()
113
self.infobar.add(vbox)
115
self.infoTitleLabel = gtk.Label()
116
vbox.pack_start(self.infoTitleLabel, False, True, 0)
117
self.infoTitleLabel.show()
118
self.infoDescriptionLabel = gtk.Label()
119
vbox.pack_start(self.infoDescriptionLabel, False, True, 0);
120
self.infoDescriptionLabel.show()
122
self.__gui.get_object('network_game_dialog').get_content_area().pack_start(self.infobar, False, False, 0)
124
self.__gui.connect_signals(self)
127
self.__profile = None
129
self.profileModel = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, str)
130
# Translators: Server Combo Box: Not connected to a server
131
self.profileModel.set(self.profileModel.append(), 0, None, 1, self._set_profile, 2, _('Disconnected'))
132
self.profileModelSuffixCount = 0
133
self.profileModel.set(self.profileModel.append(), 1, None)
134
self.profileModelSuffixCount += 1
135
# Translators: Server Combo Box: Add new profile
136
self.profileModel.set(self.profileModel.append(), 0, None, 1, self._new_profile, 2, _('New profile...'))
137
self.profileModelSuffixCount += 1
139
widget = self.__gui.get_object('server_combo')
140
widget.set_model(self.profileModel)
142
widget.set_row_separator_func(self._is_profile_model_separator)
143
cell = gtk.CellRendererText()
144
widget.pack_start(cell, False)
145
widget.add_attribute(cell, 'text', 2)
147
# room object, index, name, num players, description, font weight, font style, icon_name
148
self.roomModel = gtk.TreeStore(gobject.TYPE_PYOBJECT, int, str, str, str, int, int, str)
149
self.firstNonChessIter = None
151
view = self.__gui.get_object('room_list')
152
view.set_model(self.roomModel)
153
cell = gtk.CellRendererText()
154
column = gtk.TreeViewColumn('', cell)
155
column.add_attribute(cell, 'text', 3)
156
view.append_column(column)
157
cell = gtk.CellRendererPixbuf()
158
column = gtk.TreeViewColumn('', cell)
159
column.add_attribute(cell, 'icon-name', 7)
160
view.append_column(column)
161
cell = gtk.CellRendererText()
162
column = gtk.TreeViewColumn('', cell)
163
column.add_attribute(cell, 'text', 2)
164
column.add_attribute(cell, 'weight', 5)
165
column.add_attribute(cell, 'style', 6)
166
view.append_column(column)
167
cell = gtk.CellRendererText()
168
column = gtk.TreeViewColumn('', cell)
169
column.add_attribute(cell, 'text', 4)
170
#view.append_column(column)
171
view.connect('row-activated', self._on_room_changed)
174
self.playerModel = gtk.ListStore(gobject.TYPE_PYOBJECT, str, str)
175
view = self.__gui.get_object('player_list')
176
view.set_model(self.playerModel)
177
cell = gtk.CellRendererPixbuf()
178
column = gtk.TreeViewColumn('', cell)
179
column.add_attribute(cell, 'icon-name', 2)
180
view.append_column(column)
181
cell = gtk.CellRendererText()
182
column = gtk.TreeViewColumn('', cell)
183
column.add_attribute(cell, 'text', 1)
184
view.append_column(column)
186
# table, number, seats, description, seat model, can connect
187
self.tableModel = gtk.ListStore(gobject.TYPE_PYOBJECT, str, str, str, gobject.TYPE_PYOBJECT, gobject.TYPE_BOOLEAN)
190
view = self.__gui.get_object('table_list')
191
view.get_selection().connect('changed', self._on_table_selected)
192
view.set_model(self.tableModel)
194
cell = gtk.CellRendererText()
195
# Translators: Available GGZ Tables: Table name column title
196
column = gtk.TreeViewColumn(_('Table'), cell)
197
column.add_attribute(cell, 'text', 1)
198
view.append_column(column)
199
cell = gtk.CellRendererText()
200
# Translators: Available GGZ Tables: Seat status column title
201
column = gtk.TreeViewColumn(_('Seats'), cell)
202
column.add_attribute(cell, 'text', 2)
203
view.append_column(column)
204
cell = gtk.CellRendererText()
205
# Translators: Available GGZ Tables: Table description column title
206
column = gtk.TreeViewColumn(_('Description'), cell)
207
column.add_attribute(cell, 'text', 3)
208
view.append_column(column)
210
view = self.__gui.get_object('seat_list')
211
cell = gtk.CellRendererText()
212
# Translators: Current GGZ Table: Seat name column title
213
column = gtk.TreeViewColumn(_('Seat'), cell)
214
column.add_attribute(cell, 'text', 2)
215
view.append_column(column)
216
# Translators: Current GGZ Table: Player name column title
217
column = gtk.TreeViewColumn(_('Player'), cell)
218
column.add_attribute(cell, 'text', 3)
219
column.add_attribute(cell, 'style', 4)
220
view.append_column(column)
222
self.__loadThrobber()
224
# Create styles for the buffer
225
buffer = self.__gui.get_object('chat_textview').get_buffer()
226
buffer.create_tag('motd', family='Monospace', foreground = 'red')
227
buffer.create_tag('chat', family='Monospace')
228
#buffer.create_tag('output', family='Monospace', weight = pango.WEIGHT_BOLD)
229
#buffer.create_tag('move', family='Monospace', foreground = 'blue')
230
buffer.create_tag('info', family='Monospace', foreground = 'gray')
231
#buffer.create_tag('error', family='Monospace', foreground = 'red')
232
buffer.create_mark('end', buffer.get_end_iter())
234
self.__addProfileDialog = GtkNetworkAddDialog(self, self.__gui.get_object('network_game_dialog'))
238
def setVisible(self, isVisible):
239
"""Called by glchess.ui.NetworkController"""
240
widget = self.__gui.get_object('network_game_dialog')
244
# Prompt for new profile if none configured
245
# FIXME: Make this clearer this is the count of non-profile elements in the combo
246
if len(self.profileModel) <= (self.profileModelSuffixCount + 1):
247
self.__addProfileDialog.setVisible(True)
249
self.__addProfileDialog.setVisible(False)
250
self.__editProfileDialog.setVisible(False)
253
def setSensitive(self, isSensitive):
254
widget = self.__gui.get_object('controls_box')
255
widget.set_sensitive(isSensitive)
257
def setError(self, title, description):
258
self.infobar.set_message_type(gtk.MESSAGE_ERROR);
259
self.infoTitleLabel.set_markup('<big><b>%s</b></big>' % title)
260
self.infoDescriptionLabel.set_markup('<i>%s</i>' % description)
263
def clearError(self):
266
def addProfile(self, profile, name, useNow = False):
267
"""Called by glchess.ui.UIController"""
268
iter = self.profileModel.insert(len(self.profileModel) - self.profileModelSuffixCount)
269
self.profileModel.set(iter, 0, profile, 1, self._set_profile, 2, name)
270
if self.__profile is None and useNow:
271
self.__gui.get_object('server_combo').set_active_iter(iter)
273
def setBusy(self, isBusy):
274
"""Called by glchess.ui.UIController"""
275
if self._throbberTimer is not None:
276
gobject.source_remove(self._throbberTimer)
277
self._throbberTimer = None
279
# Disable room buttons when busy
280
widget = self.__gui.get_object('room_button_box')
281
widget.set_sensitive(not isBusy)
283
# Display animating frames if busy or idle frame if not
285
self._throbberFrame = 1
286
self._throbberTimer = gobject.timeout_add(25, self._updateThrobber)
288
self._throbberFrame = 0
289
self._updateThrobber()
291
def addRoom(self, index, name, nPlayers, description, room, protocol):
292
"""Called by glchess.ui.UIController"""
294
(icon, isSupported) = {None: ('stock_people', True),
295
'Chess': ('gnome-glchess', True),
296
'Reversi': ('gnome-iagno', False),
297
'GGZCards': ('gnome-aisleriot', False),
298
'Gnect': ('gnome-gnect', False),
299
'Gnibbles': ('gnome-gnibbles', False),
300
'Freeciv': ('civclient', False)}[protocol]
306
iter = self.roomModel.insert_before(None, self.firstNonChessIter)
307
style = pango.STYLE_NORMAL
309
iter = self.roomModel.append(None)
310
if self.firstNonChessIter is None:
311
self.firstNonChessIter = iter
312
style = pango.STYLE_ITALIC
314
self.roomIters[room] = iter
315
self.roomModel.set(iter, 0, room, 1, index, 2, name, 3, nPlayers, 4, description, 5, pango.WEIGHT_NORMAL, 6, style, 7, icon)
317
def enterRoom(self, room):
318
"""Called by glchess.ui.UIController"""
319
for (r, iter) in self.roomIters.iteritems():
321
weight = pango.WEIGHT_BOLD
323
weight = pango.WEIGHT_NORMAL
324
self.roomModel.set(iter, 5, weight)
326
def updateRoom(self, room, nPlayers):
328
iter = self.roomIters[room]
330
print 'Unknown room!'
332
self.roomModel.set(iter, 3, nPlayers)
334
def removeRoom(self, room):
335
"""Called by glchess.ui.UIController"""
336
iter = self.roomModel.get_iter_first()
337
while iter is not None:
338
if room is self.roomModel.get_value(iter, 0):
339
self.roomModel.remove(iter)
341
iter = self.roomModel.iter_next(iter)
343
def clearRooms(self):
344
"""Called by glchess.ui.UIController"""
345
self.firstNonChessIter = None
347
self.roomModel.clear()
349
def updateTable(self, table, name, seats, description, canConnect):
350
"""Called by glchess.ui.UIController"""
352
iter = self.tableIters[table]
353
seatModel = self.tableModel.get_value(iter, 4)
355
iter = self.tableModel.append()
356
self.tableIters[table] = iter
357
seatModel = gtk.ListStore(int, str, str, str, int) # number, type, name, occupant, font style
358
self.tableModel.set(iter, 0, table, 1, name, 2, seats, 3, description, 4, seatModel, 5, canConnect)
360
def updateSeat(self, table, number, type, name):
361
"""Called by glchess.ui.UIController"""
362
iter = self.tableIters[table]
363
seatModel = self.tableModel.get_value(iter, 4)
364
iter = seatModel.get_iter_first()
365
while iter is not None:
366
if number == seatModel.get_value(iter, 0):
368
iter = seatModel.iter_next(iter)
370
iter = seatModel.append()
373
# Translators: GGZ seat is occupied by the white player
374
seatName = _('White')
376
# Translators: GGZ seat is occupied by the black player
377
seatName = _('Black')
379
# Translators: GGZ seat is occupied by a spectator
380
seatName = _('Spectator')
382
style = pango.STYLE_ITALIC
385
style = pango.STYLE_NORMAL
386
elif type == 'reserved':
387
# Translators: GGZ seat status: This seat is reserved. %s is replaced with
388
# the name of the player the seat is reserved for.
389
occupant = _('Reserved for %s') % name
391
# Translators: GGZ seat status: This seat is not taken
392
occupant = _('Seat empty')
394
# Translators: GGZ seat status: This seat contains an AI player.
395
# %s is replaced with the name of the AI.
396
occupant = _('AI (%s)') % name
398
seatModel.set(iter, 0, number, 1, type, 2, seatName, 3, occupant, 4, style)
400
def removeTable(self, table):
401
"""Called by glchess.ui.UIController"""
402
iter = self.tableIters[table]
403
self.tableModel.remove(iter)
405
def joinTable(self, table):
406
"""Called by glchess.ui.UIController"""
407
gameFrame = self.__gui.get_object('game_frame')
408
roomFrame = self.__gui.get_object('room_frame')
413
iter = self.tableIters[table]
415
seatModel = self.tableModel.get_value(iter, 4)
416
self.__gui.get_object('seat_list').set_model(seatModel)
418
name = self.tableModel.get_value(iter, 3)
419
self.__gui.get_object('game_name_label').set_text(name)
423
def clearTables(self):
424
"""Called by glchess.ui.UIController"""
426
self.tableModel.clear()
428
def addPlayer(self, player, name, icon):
429
"""Called by glchess.ui.UIController"""
430
iter = self.playerModel.append()
431
self.playerModel.set(iter, 0, player, 1, name, 2, icon)
433
def removePlayer(self, player):
434
"""Called by glchess.ui.UIController"""
435
iter = self.playerModel.get_iter_first()
436
while iter is not None:
437
if player is self.playerModel.get_value(iter, 0):
438
self.playerModel.remove(iter)
440
iter = self.playerModel.iter_next(iter)
442
def clearPlayers(self):
443
"""Called by glchess.ui.UIController"""
444
self.playerModel.clear()
446
def addText(self, text, style):
447
"""Called by glchess.ui.UIController"""
448
view = self.__gui.get_object('chat_textview')
449
scroll = self.__gui.get_object('chat_scroll_window')
450
adj = scroll.get_vadjustment()
451
atBottom = adj.value >= adj.upper - adj.page_size
452
buffer = view.get_buffer()
453
mark = buffer.get_mark('end')
454
buffer.insert_with_tags_by_name(buffer.get_iter_at_mark(mark), text, style)
456
view.scroll_mark_onscreen(mark)
459
buffer = self.__gui.get_object('chat_textview').get_buffer()
460
buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
463
"""Called by glchess.ui.UIController"""
464
self.__gui.get_object('network_game_dialog').hide()
468
def __loadThrobber(self):
469
self._throbberTimer = None
470
theme = gtk.icon_theme_get_default()
472
self._throbberFrames = []
475
icon = theme.load_icon('process-idle', size, 0)
476
except gobject.GError:
479
self._throbberFrames.append(icon)
482
icon = theme.load_icon('process-working', size, 0)
483
except gobject.GError:
486
# If a smaller icon was received than expected then use that size
487
height = icon.get_height()
488
width = icon.get_width()
489
size = min(size, height, width)
491
for i in xrange(height / size):
492
for j in xrange(width / size):
493
frame = icon.subpixbuf(j * size, i * size, size, size)
494
self._throbberFrames.append(frame)
497
self._throbberFrame = 0
498
self._updateThrobber()
500
def _updateThrobber(self):
501
widget = self.__gui.get_object('throbber_image')
503
icon = self._throbberFrames[self._throbberFrame]
507
widget.set_from_pixbuf(icon)
509
# Move to next frame restarting animation after idle frame
510
self._throbberFrame += 1
511
if self._throbberFrame >= len(self._throbberFrames):
512
self._throbberFrame = 1
515
def __setCombo(self, comboName, key):
516
widget = self.__gui.get_object(comboName)
517
iter = self.__getIter(widget.get_model(), key)
519
widget.set_active_iter(iter)
521
def __getIter(self, model, key, default = None):
524
iter = model.get_iter_first()
526
if model.get_value(iter, 0) == key:
529
iter = model.iter_next(iter)
532
def __getComboData(self, comboBox, index):
535
model = comboBox.get_model()
536
iter = comboBox.get_active_iter()
540
data = model.get(iter, index)
543
def __startGame(self):
544
game = glchess.ui.Game()
545
game.name = self.__gui.get_object('game_name_entry').get_text()
546
game.allowSpectators = True
549
game.white.type = self.__getComboData(self.__gui.get_object('white_type_combo'), 0)
550
if game.white.type == '':
551
game.white.name = _('White')
553
game.white.name = self.__getComboData(self.__gui.get_object('white_type_combo'), 2)
554
game.white.level = self.__getComboData(self.__gui.get_object('white_difficulty_combo'), 0)
555
game.black.type = self.__getComboData(self.__gui.get_object('black_type_combo'), 0)
556
if game.black.type == '':
557
game.black.name = _('Black')
559
game.black.name = self.__getComboData(self.__gui.get_object('black_type_combo'), 2)
560
game.black.level = self.__getComboData(self.__gui.get_object('black_difficulty_combo'), 0)
562
game.duration = self.__getComboData(self.__gui.get_object('time_combo'), 1)
563
if game.duration < 0:
564
multiplier = self.__getComboData(self.__gui.get_object('custom_time_units_combo'), 1)
565
game.duration = self.__getComboData(self.__gui.get_object('custom_time_spin'), 1) * multiplier
568
glchess.config.set('new_game_dialog/white/type', game.white.type)
569
glchess.config.set('new_game_dialog/white/difficulty', game.white.level)
570
glchess.config.set('new_game_dialog/black/type', game.black.type)
571
glchess.config.set('new_game_dialog/black/difficulty', game.black.level)
573
# Inform the child class
574
self.__mainUI.feedback.onGameStart(game)
576
def _is_profile_model_separator(self, model, iter):
577
return model.get(iter, 1)[0] is None
579
def _set_profile(self, profile):
580
if profile != self.__profile:
581
self.__profile = profile
582
self.feedback.setProfile(profile)
584
def __selectActiveProfile(self):
585
iter = self.profileModel.get_iter_first()
586
while iter is not None:
587
if self.__profile == self.profileModel.get_value(iter, 0):
589
iter = self.profileModel.iter_next(iter)
590
self.__gui.get_object('server_combo').set_active_iter(iter)
592
def _new_profile(self, profile):
593
self.__selectActiveProfile()
594
self.__addProfileDialog.setVisible(True)
596
# Gtk+ signal handlers
598
def _on_table_selected(self, selection):
599
(model, iter) = selection.get_selected()
603
isSensitive = model.get_value(iter, 5)
605
widget = self.__gui.get_object('table_join_button')
606
widget.set_sensitive(isSensitive)
608
def _on_table_list_activated(self):
611
def _on_server_combo_changed(self, widget):
613
model = widget.get_model()
614
iter = widget.get_active_iter()
615
(method,) = model.get(iter, 1)
616
(profile,) = model.get(iter, 0)
619
def _on_chat_entry_activate(self, widget):
621
text = widget.get_text()
623
self.feedback.sendChat(text)
625
def _on_delete(self, widget, event):
626
# Hide; don't delete this window
629
def _on_response(self, widget, response_id):
631
self.__gui.get_object('network_game_dialog').hide()
633
def _on_room_changed(self, widget, path, column):
635
# FIXME: Only if allowed to enter room (state machine)
636
model = self.__gui.get_object('room_list').get_model()
637
iter = model.get_iter(path)
640
room = model.get_value(iter, 0)
642
self.feedback.enterRoom(room)
645
def _on_table_join_button_clicked(self, widget):
647
(model, iter) = self.__gui.get_object('table_list').get_selection().get_selected()
650
table = model.get_value(iter, 0)
651
self.feedback.joinTable(table)
653
def _on_table_leave_button_clicked(self, widget):
655
self.feedback.leaveTable()
657
def _on_table_new_button_clicked(self, widget):
659
self.feedback.startTable()