From: <umg...@us...> - 2007-07-09 14:10:27
|
Revision: 463 http://svn.sourceforge.net/pybridge/?rev=463&view=rev Author: umgangee Date: 2007-07-09 07:10:29 -0700 (Mon, 09 Jul 2007) Log Message: ----------- Split bridgetable window into a generic table window, and a subclass bridge-specific table. Re-implement table widge ts and windows in PyGTK, instead of Glade, for flexibility and modularity. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_bidbox.py trunk/pybridge/pybridge/ui/window_bridgetable.py trunk/pybridge/pybridge/ui/window_main.py Added Paths: ----------- trunk/pybridge/pybridge/ui/window_gametable.py Modified: trunk/pybridge/pybridge/ui/window_bidbox.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bidbox.py 2007-07-09 14:00:02 UTC (rev 462) +++ trunk/pybridge/pybridge/ui/window_bidbox.py 2007-07-09 14:10:29 UTC (rev 463) @@ -13,19 +13,18 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import gtk -from wrapper import GladeWrapper +from pybridge.ui.config import config +from pybridge.ui.eventhandler import SimpleEventHandler +from pybridge.ui.vocabulary import * + import pybridge.bridge.call as Call -from config import config -from eventhandler import SimpleEventHandler -from vocabulary import * - class WindowBidbox(object): """The bidding box is presented to a player, during bidding. Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-07-09 14:00:02 UTC (rev 462) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-07-09 14:10:29 UTC (rev 463) @@ -17,100 +17,238 @@ import gtk -from wrapper import GladeWrapper from pybridge.network.client import client from pybridge.network.error import GameError -from cardarea import CardArea -from config import config -from eventhandler import SimpleEventHandler -from manager import WindowManager -from vocabulary import * +from pybridge.ui.cardarea import CardArea +from pybridge.ui.config import config +from pybridge.ui.eventhandler import SimpleEventHandler +from pybridge.ui.manager import WindowManager +from pybridge.ui.vocabulary import * +from pybridge.ui.window_gametable import WindowGameTable from window_bidbox import WindowBidbox -class WindowBridgetable(GladeWrapper): +class BiddingView(gtk.TreeView): + """A view of all calls made in an auction.""" - glade_name = 'window_bridgetable' + def __init__(self): + gtk.TreeView.__init__(self) + self.set_rules_hint(True) - def setUp(self): - self.children = WindowManager() - self.eventHandler = SimpleEventHandler(self) - - self.table = None # Table currently displayed in window. - self.player, self.position = None, None - - # Set up "Take Seat" menu. - self.takeseat_items = {} - menu = gtk.Menu() - for position in Direction: - item = gtk.MenuItem(DIRECTION_NAMES[position], True) - item.connect('activate', self.on_seat_activated, position) - item.show() - menu.append(item) - self.takeseat_items[position] = item - self.takeseat.set_menu(menu) - - # Set up CardArea widget. - self.cardarea = CardArea() - self.cardarea.on_card_clicked = self.on_card_clicked - self.cardarea.on_hand_clicked = self.on_hand_clicked - self.cardarea.set_size_request(640, 480) - self.scrolled_cardarea.add_with_viewport(self.cardarea) - self.cardarea.show() - + self.store = gtk.ListStore(str, str, str, str) + self.set_model(self.store) + self.clear = self.store.clear renderer = gtk.CellRendererText() renderer.set_property('size-points', 12) renderer.set_property('xalign', 0.5) - # Set up bidding history and column display. - self.call_store = gtk.ListStore(str, str, str, str) - self.biddingview.set_model(self.call_store) + # Set up columns, each corresponding to a position. for index, position in enumerate(Direction): title = DIRECTION_NAMES[position] column = gtk.TreeViewColumn(title, renderer, markup=index) - column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - column.set_fixed_width(50) - self.biddingview.append_column(column) + self.append_column(column) - # Set up trick history and column display. - self.trick_store = gtk.ListStore(str, str, str, str) - self.trickview.set_model(self.trick_store) - for index, position in enumerate(Direction): - title = DIRECTION_NAMES[position] - column = gtk.TreeViewColumn(str(title), renderer, markup=index) - column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - column.set_fixed_width(50) - self.trickview.append_column(column) + def add_call(self, call, position): + """Adds call from specified position.""" + column = position.index + if column == 0 or self.store.get_iter_first() == None: + iter = self.store.append() + else: # Get bottom row. There must be a better way than this... + iter = self.store.get_iter_first() + while self.store.iter_next(iter) != None: + iter = self.store.iter_next(iter) + + format = render_call(call) + self.store.set(iter, column, format) + + + + +class ScoreView(gtk.TreeView): + """A display of contracts bid, their results and their scores.""" + + + def __init__(self): + gtk.TreeView.__init__(self) + self.set_rules_hint(True) + + self.store = gtk.ListStore(str, str, str, str) + self.set_model(self.store) + self.clear = self.store.clear renderer = gtk.CellRendererText() - # Set up score sheet and column display. - self.score_store = gtk.ListStore(str, str, str, str) - self.scoresheet.set_model(self.score_store) for index, title in enumerate([_('Contract'), _('Made'), _('N/S'), _('E/W')]): column = gtk.TreeViewColumn(title, renderer, markup=index) - self.scoresheet.append_column(column) + self.append_column(column) - # Set up observer listing. - self.observer_store = gtk.ListStore(str) - self.treeview_observers.set_model(self.observer_store) - column = gtk.TreeViewColumn(None, renderer, text=0) - self.observer_store.set_sort_column_id(0, gtk.SORT_ASCENDING) - self.treeview_observers.append_column(column) + def add_score(self, game): + declarerWon, defenceWon = game.play.getTrickCount() + score = game.getScore() - def tearDown(self): - # Close all child windows. - for window in self.children.values(): - self.children.close(window) + textContract = render_contract(game.contract) + textMade = str(declarerWon) + if game.contract['declarer'] in (Direction.North, Direction.South) and score > 0 \ + or game.contract['declarer'] in (Direction.East, Direction.West) and score < 0: + textNS, textEW = str(abs(score)), '' + else: + textNS, textEW = '', str(abs(score)) - self.table = None # Dereference table. + self.store.prepend([textContract, textMade, textNS, textEW]) + + +class BridgeDashboard(gtk.VBox): + """An at-a-glance display of the state of a bridge game.""" + + + def __init__(self): + gtk.VBox.__init__(self) + self.set_spacing(4) + + self.contract = gtk.Label() + self.pack_start(self.contract) + + hbox = gtk.HBox() + hbox.set_homogeneous(True) + hbox.set_spacing(6) + self.declarer_tricks = gtk.Label() + frame = gtk.Frame() + frame.set_label(_('Declarer')) + frame.set_label_align(0.5, 0.5) + frame.add(self.declarer_tricks) + hbox.pack_start(frame) + self.defence_tricks = gtk.Label() + frame = gtk.Frame() + frame.set_label(_('Defence')) + frame.set_label_align(0.5, 0.5) + frame.add(self.defence_tricks) + hbox.pack_start(frame) + self.pack_start(hbox) + + hbox = gtk.HBox() + hbox.set_homogeneous(True) + hbox.set_spacing(6) + # TODO: display board number? + self.dealer = gtk.Label() + self.dealer.set_alignment(0, 0.5) + hbox.pack_start(self.dealer) + self.vulnerability = gtk.Label() + self.vulnerability.set_alignment(0, 0.5) + hbox.pack_start(self.vulnerability) + self.pack_start(hbox) + + + def set_contract(self, game): + if game.contract: + text = render_contract(game.contract) + else: + text = _('No contract') + self.contract.set_markup("<span size='x-large'>%s</span>" % text) + + + def set_trickcount(self, game): + if game.play: + declarer, defence = game.play.getTrickCount() + required = game.contract['bid'].level.index + 7 + declarerNeeds = max(0, required - declarer) + defenceNeeds = max(0, 13 + 1 - required - defence) + else: + declarer, defence, declarerNeeds, defenceNeeds = 0, 0, 0, 0 + format = "<span size='x-large'><b>%s</b> (%s)</span>" + self.declarer_tricks.set_markup(format % (declarer, declarerNeeds)) + self.defence_tricks.set_markup(format % (defence, defenceNeeds)) + + + def set_dealer(self, game): + if game.inProgress(): + dealertext = "<b>%s</b>" % DIRECTION_NAMES[game.board['dealer']] + else: + dealertext = '' + self.dealer.set_markup(_('Dealer') + ': ' + dealertext) + + + def set_vulnerability(self, game): + if game.inProgress(): + vulntext = "<b>%s</b>" % VULN_SYMBOLS[game.board['vuln']] + else: + vulntext = '' + self.vulnerability.set_markup(_('Vuln') + ': ' + vulntext) + + + + +class WindowBridgeTable(WindowGameTable): + + gametype = _('Contract Bridge') + + stockdirections = [gtk.STOCK_GO_UP, gtk.STOCK_GO_FORWARD, + gtk.STOCK_GO_DOWN, gtk.STOCK_GO_BACK] + + + def setUp(self): + super(WindowBridgeTable, self).setUp() + + # Set up menu attached to 'Take Seat' toolbar button. + self.takeseat_menuitems = {} + menu = gtk.Menu() + for position, stock in zip(Direction, self.stockdirections): + item = gtk.ImageMenuItem(DIRECTION_NAMES[position], True) + item.set_image(gtk.image_new_from_stock(stock, gtk.ICON_SIZE_MENU)) + item.connect('activate', self.on_takeseat_clicked, position) + item.show() + menu.append(item) + self.takeseat_menuitems[position] = item + self.takeseat.set_menu(menu) + + # Set up CardArea widget. + self.cardarea = CardArea() + self.cardarea.on_card_clicked = self.on_card_clicked + self.cardarea.on_hand_clicked = self.on_hand_clicked + self.cardarea.set_size_request(640, 480) + self.gamearea.add(self.cardarea) + + # Set up sidebar. + self.dashboard = BridgeDashboard() + self.sidebar.pack_start(self.dashboard, expand=False) + + self.biddingview = BiddingView() + sw = gtk.ScrolledWindow() + sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + sw.add(self.biddingview) + frame = gtk.Frame() + frame.add(sw) + exp = gtk.Expander(_('Bidding')) + exp.set_expanded(True) + exp.add(frame) + self.sidebar.pack_start(exp) + +# self.lasttrick = CardArea() +# frame = gtk.Frame() +# frame.add(self.lasttrick) +# exp = gtk.Expander(_('Last Trick')) +# exp.set_expanded(True) +# exp.add(frame) +# self.sidebar.pack_start(exp) + + self.scoreview = ScoreView() + sw = gtk.ScrolledWindow() + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + sw.add(self.scoreview) + frame = gtk.Frame() + frame.add(sw) + exp = gtk.Expander(_('Score Sheet')) + exp.set_expanded(False) + exp.add(frame) + self.sidebar.pack_start(exp) + + def errback(self, failure): print "Error: %s" % failure.getErrorMessage() #print failure.getBriefTraceback() @@ -121,12 +259,9 @@ @param table: the (now) focal table. """ - self.table = table - self.table.attach(self.eventHandler) - self.table.game.attach(self.eventHandler) - self.player, self.position = None, None + super(WindowBridgeTable, self).setTable(table) - self.window.set_title(_('Table %s (Bridge)') % self.table.id) + self.table.game.attach(self.eventHandler) self.resetGame() for position in Direction: @@ -140,21 +275,13 @@ for call in self.table.game.bidding.calls: position = self.table.game.bidding.whoCalled(call) - self.addCall(call, position) + self.biddingview.add_call(call, position) - self.setDealer() - self.setVulnerability() - - # If contract, set contract. - if self.table.game.bidding.isComplete(): - self.setContract() - # If playing, set trick counts. if self.table.game.play: for position, cards in self.table.game.play.played.items(): for card in cards: self.addCard(card, position) - self.setTrickCount() # If user is a player and bidding in progress, open bidding box. if self.player and not self.table.game.bidding.isComplete(): @@ -167,71 +294,41 @@ for position in Direction: player = self.table.players.get(position) # Player name or None. - available = player is None or position == self.position - self.takeseat_items[position].set_property('sensitive', available) - if player: - self.event_joinGame(player, position) - else: # Position vacant. - self.event_leaveGame(None, position) + avail = player is None or position == self.position + self.takeseat_menuitems[position].set_property('sensitive', avail) + # If player == None, this unsets player name. + self.cardarea.set_player_name(position, player) - # Initialise observer listing. - self.observer_store.clear() - for observer in self.table.observers: - self.event_addObserver(observer) - def resetGame(self): - """Clears bidding history, contract, trick counts.""" -# self.cardarea.clear() - self.call_store.clear() # Reset bidding history. - self.trick_store.clear() # Reset trick history. - self.setContract() # Reset contract. - self.setTrickCount() # Reset trick counts. + """Clear bidding history, contract, trick counts.""" + self.cardarea.clear() + self.biddingview.clear() # Reset bidding history. + self.dashboard.set_contract(self.table.game) + self.dashboard.set_trickcount(self.table.game) + self.dashboard.set_dealer(self.table.game) + self.dashboard.set_vulnerability(self.table.game) - def addCall(self, call, position): - """Adds call from specified player, to bidding tab.""" - column = position.index - if column == 0 or self.call_store.get_iter_first() == None: - iter = self.call_store.append() - else: # Get bottom row. There must be a better way than this... - iter = self.call_store.get_iter_first() - while self.call_store.iter_next(iter) != None: - iter = self.call_store.iter_next(iter) - format = render_call(call) - self.call_store.set(iter, column, format) - - def addCard(self, card, position): """""" - position = self.table.game.play.whoPlayed(card) - column = position.index - row = self.table.game.play.played[position].index(card) +# position = self.table.game.play.whoPlayed(card) +# column = position.index +# row = self.table.game.play.played[position].index(card) +# +# if self.trick_store.get_iter_first() == None: +# self.trick_store.append() +# iter = self.trick_store.get_iter_first() +# for i in range(row): +# iter = self.trick_store.iter_next(iter) +# if iter is None: +# iter = self.trick_store.append() +# +# format = render_card(card) +# self.trick_store.set(iter, column, format) - if self.trick_store.get_iter_first() == None: - self.trick_store.append() - iter = self.trick_store.get_iter_first() - for i in range(row): - iter = self.trick_store.iter_next(iter) - if iter is None: - iter = self.trick_store.append() - format = render_card(card) - self.trick_store.set(iter, column, format) - - - def addScore(self, contract, made, score): - textContract = render_contract(contract) - textMade = '%s' % made - if contract['declarer'] in (Direction.North, Direction.South) and score > 0 \ - or contract['declarer'] in (Direction.East, Direction.West) and score < 0: - textNS, textEW = '%s' % abs(score), '' - else: - textNS, textEW = '', '%s' % abs(score) - self.score_store.prepend([textContract, textMade, textNS, textEW]) - - def gameComplete(self): # Display all previously revealed hands - the server will reveal the others. for position in self.table.game.visibleHands: @@ -242,13 +339,14 @@ dialog = gtk.MessageDialog(parent=self.window, type=gtk.MESSAGE_INFO) dialog.set_title(_('Game result')) - # Determine and display score in dialog box. + # Determine and display score in dialog box and score sheet. if self.table.game.contract: + self.scoreview.add_score(self.table.game) + declarerWon, defenceWon = self.table.game.play.getTrickCount() required = self.table.game.contract['bid'].level.index + 7 offset = declarerWon - required score = self.table.game.getScore() - self.addScore(self.table.game.contract, declarerWon, score) fields = {'contract': render_contract(self.table.game.contract), 'offset': abs(offset) } @@ -336,54 +434,6 @@ self.cardarea.set_trick(trick) -# Methods to set information displayed on side panel. - - - def setContract(self): - """Sets the contract label from contract.""" - format = "<span size=\"x-large\">%s</span>" - - if self.table.game.contract: - text = render_contract(self.table.game.contract) - self.label_contract.set_markup(format % text) - self.label_contract.set_property('sensitive', True) - else: - self.label_contract.set_markup(format % _('No contract')) - self.label_contract.set_property('sensitive', False) - - - def setDealer(self): - format = "<b>%s</b>" - - dealer = '' - if self.table.game.inProgress(): - dealer = DIRECTION_NAMES[self.table.game.board['dealer']] - - self.label_dealer.set_markup(format % dealer) - - - def setTrickCount(self): - """Sets the trick counter labels for declarer and defence.""" - format = "<span size=\"x-large\"><b>%s</b> (%s)</span>" - - if self.table.game.play: - declarer, defence = self.table.game.play.getTrickCount() - required = self.table.game.contract['bid'].level.index + 7 - declarerNeeds = max(0, required - declarer) - defenceNeeds = max(0, 13 + 1 - required - defence) - - self.label_declarer.set_markup(format % (declarer, declarerNeeds)) - self.label_defence.set_markup(format % (defence, defenceNeeds)) - self.frame_declarer.set_property('sensitive', True) - self.frame_defence.set_property('sensitive', True) - - else: # Reset trick counters. - self.label_declarer.set_markup(format % (0, 0)) - self.label_defence.set_markup(format % (0, 0)) - self.frame_declarer.set_property('sensitive', False) - self.frame_defence.set_property('sensitive', False) - - def setTurnIndicator(self): """Sets the statusbar text to indicate which player is on turn.""" context = self.statusbar.get_context_id('turn') @@ -412,38 +462,14 @@ self.statusbar.push(context, text) - def setVulnerability(self): - """Sets the vulnerability indicators.""" - format = "<b>%s</b>" - vulnerable = '' - if self.table.game.inProgress(): - vulnerable = VULN_SYMBOLS[self.table.game.board['vuln']] - - self.label_vuln.set_markup(format % vulnerable) - - # Registered event handlers. - def event_addObserver(self, observer): - self.observer_store.append([observer]) - - - def event_removeObserver(self, observer): - - def func(model, path, iter, user_data): - if model.get_value(iter, 0) in user_data: - model.remove(iter) - return True - - self.observer_store.foreach(func, observer) - - def event_joinGame(self, player, position): self.cardarea.set_player_name(position, player) # Disable menu item corresponding to position. - widget = self.takeseat_items[position] + widget = self.takeseat_menuitems[position] widget.set_property('sensitive', False) # If all positions occupied, disable Take Seat. @@ -458,7 +484,7 @@ def event_leaveGame(self, player, position): self.cardarea.set_player_name(position, None) # Enable menu item corresponding to position. - widget = self.takeseat_items[position] + widget = self.takeseat_menuitems[position] widget.set_property('sensitive', True) # If we are not seated, ensure Take Seat is enabled. @@ -467,34 +493,38 @@ def event_start(self, board): - #self.children.close('dialog_gameresult') self.resetGame() + # Re-initialise player labels. + # TODO: shouldn't need to do this. + for position in Direction: + player = self.table.players.get(position) # Player name or None. + self.cardarea.set_player_name(position, player) + self.redrawTrick() # Clear trick. for position in Direction: self.redrawHand(position) self.setTurnIndicator() - self.setDealer() - self.setVulnerability() + self.dashboard.set_dealer(self.table.game) + self.dashboard.set_vulnerability(self.table.game) if self.player: d = self.player.callRemote('getHand') - # When player's hand is returned by server, reveal it to client-side Game. - # TODO: is there a better way of synchronising hands? + # When user's hand is returned, reveal it to client-side Game. d.addCallbacks(self.table.game.revealHand, self.errback, - callbackKeywords={'position' : self.position}) + callbackKeywords={'position': self.position}) bidbox = self.children.open(WindowBidbox, parent=self) bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) def event_makeCall(self, call, position): - self.addCall(call, position) + self.biddingview.add_call(call, position) self.setTurnIndicator() if self.table.game.bidding.isComplete(): - self.setContract() + self.dashboard.set_contract(self.table.game) if self.children.get(WindowBidbox): # If a player. self.children.close(self.children[WindowBidbox]) @@ -507,7 +537,7 @@ playfrom = self.table.game.play.whoPlayed(card) self.addCard(card, playfrom) self.setTurnIndicator() - self.setTrickCount() + self.dashboard.set_trickcount(self.table.game) self.redrawTrick() self.redrawHand(playfrom) @@ -516,17 +546,10 @@ def event_revealHand(self, hand, position): - all = not self.table.game.inProgress() # Show all cards if game has finished. - self.redrawHand(position, all) + all = not self.table.game.inProgress() + self.redrawHand(position, all) # Show all cards if game has finished. - def event_sendMessage(self, message, sender, recipients): - buffer = self.chat_messagehistory.get_buffer() - iter = buffer.get_end_iter() - buffer.insert(iter, '\n' + _('%(sender)s: %(message)s' % {'sender': sender, 'message': message})) - self.chat_messagehistory.scroll_to_iter(iter, 0) - - # Signal handlers. @@ -539,7 +562,7 @@ def on_hand_clicked(self, position): if not self.player and not self.table.players.get(position): # Join game at position. - self.on_seat_activated(self.cardarea, position) + self.on_takeseat_clicked(self.cardarea, position) def on_card_clicked(self, card, position): @@ -549,110 +572,34 @@ d.addErrback(self.errback) - def on_seat_activated(self, widget, position): - - def success(player): - self.player = player # RemoteReference to BridgePlayer object. - self.position = position + def on_takeseat_clicked(self, widget, position=None): - self.takeseat.set_property('sensitive', False) - self.leaveseat.set_property('sensitive', True) - + def success(r): self.cardarea.set_player_mapping(self.position) - if self.table.game.inProgress(): d = self.player.callRemote('getHand') d.addCallbacks(self.table.game.revealHand, self.errback, callbackKeywords={'position' : self.position}) - # If game is running and bidding is active, open bidding box. if not self.table.game.bidding.isComplete(): bidbox = self.children.open(WindowBidbox, parent=self) bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) - d = self.table.joinGame(position) - d.addCallbacks(success, self.errback) - - - def on_takeseat_clicked(self, widget, *args): # TODO: match user up with preferred partner. - for position in Direction: - if position not in self.table.players: # Position is vacant. - self.on_seat_activated(widget, position) # Take position. - break + if position is None: + # No position specified by user: choose an arbitary position. + position = [p for p in Direction if p not in self.table.players][0] + d = super(WindowBridgeTable, self).on_takeseat_clicked(widget, position) + d.addCallback(success) def on_leaveseat_clicked(self, widget, *args): def success(r): - self.player = None - self.position = None - self.takeseat.set_property('sensitive', True) - self.leaveseat.set_property('sensitive', False) if self.children.get(WindowBidbox): self.children.close(self.children[WindowBidbox]) - d = self.table.leaveGame(self.position) - d.addCallbacks(success, self.errback) + d = super(WindowBridgeTable, self).on_leaveseat_clicked(widget, *args) + d.addCallback(success) - - def on_toggle_gameinfo_clicked(self, widget, *args): - visible = self.toggle_gameinfo.get_active() - self.gameinfo.set_property('visible', visible) - - - def on_toggle_chat_clicked(self, widget, *args): - visible = self.toggle_chat.get_active() - self.chatbox.set_property('visible', visible) - - - def on_toggle_fullscreen_clicked(self, widget, *args): - if self.toggle_fullscreen.get_active(): - self.window.fullscreen() - else: - self.window.unfullscreen() - - - def on_leavetable_clicked(self, widget, *args): - # If user is currently playing a game, request confirmation. - if self.player and self.table.game.inProgress(): - dialog = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_MODAL, - type=gtk.MESSAGE_QUESTION) - dialog.set_title(_('Leave table?')) - dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) - dialog.add_button(_('Leave Table'), gtk.RESPONSE_OK) - dialog.set_markup(_('Are you sure you wish to leave this table?')) - dialog.format_secondary_text(_('You are currently playing a game. Leaving may forfeit the game, or incur penalties.')) - - def dialog_response_cb(dialog, response_id): - dialog.destroy() - if response_id == gtk.RESPONSE_OK: - d = client.leaveTable(self.table.id) - d.addErrback(self.errback) - - dialog.connect('response', dialog_response_cb) - dialog.show() - - else: - d = client.leaveTable(self.table.id) - d.addErrback(self.errback) - - - def on_chat_message_changed(self, widget, *args): - sensitive = self.chat_message.get_text() != '' - self.chat_send.set_property('sensitive', sensitive) - - - def on_chat_send_clicked(self, widget, *args): - message = self.chat_message.get_text() - if message: # Don't send a null message. - self.chat_send.set_property('sensitive', False) - self.chat_message.set_text('') # Clear message. - self.table.sendMessage(message) - - - def on_window_delete_event(self, widget, *args): - self.on_leavetable_clicked(widget, *args) - return True # Stops window deletion taking place. - Added: trunk/pybridge/pybridge/ui/window_gametable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_gametable.py (rev 0) +++ trunk/pybridge/pybridge/ui/window_gametable.py 2007-07-09 14:10:29 UTC (rev 463) @@ -0,0 +1,215 @@ +# PyBridge -- online contract bridge made easy. +# Copyright (C) 2004-2007 PyBridge Project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +import gtk + +from pybridge.network.client import client + +from eventhandler import SimpleEventHandler +from manager import WindowManager, wm +from wrapper import ICON_PATH + +from window_chat import WindowChat + + +class WindowGameTable(object): + """A generic table display window. + + This exposes core table functionality to the user, and may be subclassed to + support individual games. + """ + + gametype = _('Unnamed Game') + + + def __init__(self, parent=None): + self.window = gtk.Window() + if parent: + self.window.set_transient_for(parent.window) + self.window.connect('delete_event', self.on_window_delete_event) + self.window.set_icon_from_file(ICON_PATH) + self.window.set_title(_('Table')) + + self.setUp() + self.window.show_all() + + + def setUp(self): + self.children = WindowManager() + self.eventHandler = SimpleEventHandler(self) + + self.player = None + self.position = None + self.table = None # Table currently displayed in window. + + # Set up widget layout. + vbox = gtk.VBox() + self.toolbar = gtk.Toolbar() + self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ) + vbox.pack_start(self.toolbar, expand=False) + hbox = gtk.HBox() + self.gamearea = gtk.Viewport() # Use self.gamearea.add(...) + hbox.pack_start(self.gamearea) + self.sidebar = gtk.VBox(spacing=6) # Use self.sidebar.pack_start(...) + self.sidebar.set_border_width(6) + hbox.pack_start(self.sidebar, expand=False) + vbox.pack_start(hbox) + self.statusbar = gtk.Statusbar() + vbox.pack_start(self.statusbar, expand=False) + self.window.add(vbox) + + # Set up toolbar buttons. + self.takeseat = gtk.MenuToolButton(gtk.STOCK_MEDIA_PLAY) + self.takeseat.set_label(_('Take Seat')) + self.takeseat.set_is_important(True) + self.takeseat.connect('clicked', self.on_takeseat_clicked) + self.toolbar.insert(self.takeseat, -1) + + self.leaveseat = gtk.ToolButton(gtk.STOCK_MEDIA_STOP) + self.leaveseat.set_label(_('Leave Seat')) + self.leaveseat.connect('clicked', self.on_leaveseat_clicked) + self.leaveseat.set_property('sensitive', False) + self.toolbar.insert(self.leaveseat, -1) + + self.toolbar.insert(gtk.SeparatorToolItem(), -1) + + self.fullscreen = gtk.ToggleToolButton(gtk.STOCK_FULLSCREEN) + self.fullscreen.connect('clicked', self.on_fullscreen_clicked) + #self.fullscreen.set_tooltip() + self.toolbar.insert(self.fullscreen, -1) + + self.toolbar.insert(gtk.SeparatorToolItem(), -1) + + self.leavetable = gtk.ToolButton(gtk.STOCK_QUIT) + self.leavetable.set_label(_('Leave Table')) + self.leavetable.connect('clicked', self.on_leavetable_clicked) + self.toolbar.insert(self.leavetable, -1) + + + def tearDown(self): + # Close all child windows. + for window in self.children.values(): + self.children.close(window) + + # If table has chat session, remove it from chat window. + if self.table.chat: + chatwin = wm.get(WindowChat) + if chatwin: + chatwin.removeChat(self.table.chat) + + self.table = None # Dereference table. + + + def setTable(self, table): + """Set display to follow specified table object. + + @param table: the focal table. + """ +# if self.table: +# self.table.detach(self.eventHandler) + self.table = table + self.table.attach(self.eventHandler) + + fields = {'tablename': self.table.id, 'gametype': self.gametype} + title = _('%(tablename)s (%(gametype)s)') % fields + self.window.set_title(title) + + if self.table.chat: + chatwin = wm.get(WindowChat) or wm.open(WindowChat) + chatwin.addChat(self.table.chat, title=title) + + +# Registered event handlers. + + +# Signal handlers. + + + def on_takeseat_clicked(self, widget, position): + # Subclasses should override this method, and call it using super(...) + # with the position argument guaranteed to be specified. + assert position is not None + + self.takeseat.set_property('sensitive', False) + + def success(player): + self.leaveseat.set_property('sensitive', True) + self.player = player + self.position = position + + def failure(reason): + self.takeseat.set_property('sensitive', True) # Re-enable. + + d = self.table.joinGame(position) + d.addCallbacks(success, failure) + return d + + + def on_leaveseat_clicked(self, widget, *args): + self.leaveseat.set_property('sensitive', False) + + def success(r): + self.takeseat.set_property('sensitive', True) + self.player = None + self.position = None + + def failure(reason): + self.leaveseat.set_property('sensitive', True) # Re-enable. + + d = self.table.leaveGame(self.position) + d.addCallbacks(success, failure) + return d + + + def on_leavetable_clicked(self, widget, *args): + # If user is currently playing a game, request confirmation. + if self.player and self.table.game.inProgress(): + dialog = gtk.MessageDialog(parent=self.window, + flags=gtk.DIALOG_MODAL, + type=gtk.MESSAGE_QUESTION) + dialog.set_title(_('Leave table?')) + dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) + dialog.add_button(_('Leave Table'), gtk.RESPONSE_OK) + dialog.set_markup(_('Are you sure you wish to leave this table?')) + dialog.format_secondary_text(_('You are currently playing a game. Leaving may forfeit the game, or incur penalties.')) + + def dialog_response_cb(dialog, response_id): + dialog.destroy() + if response_id == gtk.RESPONSE_OK: + d = client.leaveTable(self.table.id) + #d.addErrback(self.errback) + + dialog.connect('response', dialog_response_cb) + dialog.show() + + else: + d = client.leaveTable(self.table.id) + #d.addErrback(self.errback) + + + def on_fullscreen_clicked(self, widget, *args): + if self.fullscreen.get_active(): + self.window.fullscreen() + else: + self.window.unfullscreen() + + + def on_window_delete_event(self, widget, *args): + self.on_leavetable_clicked(widget, *args) + return True # Stops window deletion taking place. + Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-07-09 14:00:02 UTC (rev 462) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-07-09 14:10:29 UTC (rev 463) @@ -32,8 +32,10 @@ from dialog_connection import DialogConnection from dialog_newtable import DialogNewtable from dialog_preferences import DialogPreferences -from window_bridgetable import WindowBridgetable +# TODO: import all Window*Table classes automatically. +from pybridge.bridge.ui.window_bridgetable import WindowBridgeTable + TABLE_ICON = env.find_pixmap("table.png") USER_ICON = env.find_pixmap("user.png") @@ -49,14 +51,14 @@ def setUp(self): # Use a private WindowManager for table window instances. self.tables = WindowManager() - + # Set up table model and icon view. self.tableview.set_text_column(0) self.tableview.set_pixbuf_column(1) self.tableview_model = gtk.ListStore(str, gtk.gdk.Pixbuf) self.tableview_model.set_sort_column_id(0, gtk.SORT_ASCENDING) self.tableview.set_model(self.tableview_model) - + # Set up people model and icon view. # TODO: allow users to provide their own "avatar" icons. self.peopleview.set_text_column(0) @@ -64,7 +66,7 @@ self.peopleview_model = gtk.ListStore(str, gtk.gdk.Pixbuf) self.peopleview_model.set_sort_column_id(0, gtk.SORT_ASCENDING) self.peopleview.set_model(self.peopleview_model) - + # Attach event handler to listen for events. self.eventHandler = SimpleEventHandler(self) client.attach(self.eventHandler) @@ -152,7 +154,7 @@ def event_joinTable(self, tableid, table): - window = self.tables.open(WindowBridgetable, id=tableid) + window = self.tables.open(WindowBridgeTable, id=tableid) window.setTable(table) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |