Use debian 2.7 only
[fpbd-bostik.git] / pyfpdb / Hud.py
blob7b815d432885ba7747df1b007e38a9c413530dfd
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """Hud.py
5 Create and manage the hud overlays.
6 """
7 # Copyright 2008-2011 Ray E. Barker
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 ########################################################################
26 import L10n
27 _ = L10n.get_translation()
29 # Standard Library modules
30 import os
31 import sys
33 import logging
34 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
35 log = logging.getLogger("hud")
37 # pyGTK modules
38 import pygtk
39 import gtk
40 import pango
41 import gobject
43 # win32 modules -- only imported on windows systems
44 if os.name == 'nt':
45 import win32gui
46 import win32con
47 import win32api
49 # FreePokerTools modules
50 import Configuration
51 import Stats
52 import Mucked
53 import Database
54 #import HUD_main
57 def importName(module_name, name):
58 """Import a named object 'name' from module 'module_name'."""
59 # Recipe 16.3 in the Python Cookbook, 2nd ed. Thanks!!!!
61 try:
62 module = __import__(module_name, globals(), locals(), [name])
63 except:
64 return None
65 return(getattr(module, name))
68 class Hud:
69 def __init__(self, parent, table, max, poker_game, config, db_connection):
70 # __init__ is (now) intended to be called from the stdin thread, so it
71 # cannot touch the gui
72 if parent is None: # running from cli ..
73 self.parent = self
74 else:
75 self.parent = parent
76 self.table = table
77 self.config = config
78 self.poker_game = poker_game
79 self.max = max
80 self.db_connection = db_connection
81 self.deleted = False
82 self.stacked = True
83 self.site = table.site
84 self.mw_created = False
85 self.hud_params = parent.hud_params
86 self.repositioningwindows = False # used to keep reposition_windows from re-entering
88 self.stat_windows = {}
89 self.popup_windows = {}
90 self.aux_windows = []
92 # configure default font and colors from the configuration
93 (font, font_size) = config.get_default_font(self.table.site)
94 self.colors = config.get_default_colors(self.table.site)
95 self.hud_ui = config.get_hud_ui_parameters()
96 self.site_params = config.get_site_parameters(self.table.site)
98 self.backgroundcolor = gtk.gdk.color_parse(self.colors['hudbgcolor'])
99 self.foregroundcolor = gtk.gdk.color_parse(self.colors['hudfgcolor'])
101 self.font = pango.FontDescription("%s %s" % (font, font_size))
102 # do we need to add some sort of condition here for dealing with a request for a font that doesn't exist?
104 game_params = config.get_game_parameters(self.poker_game)
105 # if there are AUX windows configured, set them up (Ray knows how this works, if anyone needs info)
106 if not game_params['aux'] == [""]:
107 for aux in game_params['aux']:
108 aux_params = config.get_aux_parameters(aux)
109 my_import = importName(aux_params['module'], aux_params['class'])
110 if my_import == None:
111 continue
112 self.aux_windows.append(my_import(self, config, aux_params))
114 self.creation_attrs = None
116 # Set up a main window for this this instance of the HUD
117 def create_mw(self):
118 win = gtk.Window()
119 win.set_skip_taskbar_hint(True) # invisible to taskbar
120 win.set_gravity(gtk.gdk.GRAVITY_STATIC)
121 # give it a title that we can easily filter out in the window list when Table search code is looking
122 win.set_title("%s FPDBHUD" % (self.table.name))
123 win.set_decorated(False) # kill titlebars
124 win.set_opacity(self.colors["hudopacity"])
125 win.set_focus(None)
126 win.set_focus_on_map(False)
127 win.set_accept_focus(False)
129 eventbox = gtk.EventBox()
130 label = gtk.Label(self.hud_ui['label'])
132 win.add(eventbox)
133 eventbox.add(label)
135 # set it to the desired color of the HUD for this site
136 label.modify_bg(gtk.STATE_NORMAL, self.backgroundcolor)
137 label.modify_fg(gtk.STATE_NORMAL, self.foregroundcolor)
139 eventbox.modify_bg(gtk.STATE_NORMAL, self.backgroundcolor)
140 eventbox.modify_fg(gtk.STATE_NORMAL, self.foregroundcolor)
142 self.main_window = win
143 # move it to the table window's X/Y position (0,0 on the table window usually)
144 self.main_window.move(self.table.x, self.table.y)
146 # A popup menu for the main window
147 # This menu code has become extremely long - is there a better way to do this?
148 menu = gtk.Menu()
150 killitem = gtk.MenuItem(_('Kill This HUD'))
151 menu.append(killitem)
152 if self.parent is not None:
153 killitem.connect("activate", self.parent.kill_hud, self.table_name)
155 saveitem = gtk.MenuItem(_('Save HUD Layout'))
156 menu.append(saveitem)
157 saveitem.connect("activate", self.save_layout)
159 repositem = gtk.MenuItem(_('Reposition StatWindows'))
160 menu.append(repositem)
161 repositem.connect("activate", self.reposition_windows)
163 aggitem = gtk.MenuItem(_('Show Player Stats for'))
164 menu.append(aggitem)
165 self.aggMenu = gtk.Menu()
166 aggitem.set_submenu(self.aggMenu)
167 # set agg_bb_mult to 1 to stop aggregation
168 item = gtk.CheckMenuItem(_('For This Blind Level Only'))
169 self.aggMenu.append(item)
170 item.connect("activate", self.set_aggregation, ('P', 1))
171 setattr(self, 'h_aggBBmultItem1', item)
173 item = gtk.MenuItem(_('For Multiple Blind Levels:'))
174 self.aggMenu.append(item)
176 item = gtk.CheckMenuItem(_('%s to %s * Current Blinds') % (" 0.5", "2.0"))
177 self.aggMenu.append(item)
178 item.connect("activate", self.set_aggregation, ('P',2))
179 setattr(self, 'h_aggBBmultItem2', item)
181 item = gtk.CheckMenuItem(_('%s to %s * Current Blinds') % (" 0.33", "3.0"))
182 self.aggMenu.append(item)
183 item.connect("activate", self.set_aggregation, ('P',3))
184 setattr(self, 'h_aggBBmultItem3', item)
186 item = gtk.CheckMenuItem(_('%s to %s * Current Blinds') % (" 0.1", "10.0"))
187 self.aggMenu.append(item)
188 item.connect("activate", self.set_aggregation, ('P',10))
189 setattr(self, 'h_aggBBmultItem10', item)
191 item = gtk.CheckMenuItem(" " + _('All Levels'))
192 self.aggMenu.append(item)
193 item.connect("activate", self.set_aggregation, ('P',10000))
194 setattr(self, 'h_aggBBmultItem10000', item)
196 item = gtk.MenuItem(_('Number of Seats:'))
197 self.aggMenu.append(item)
199 item = gtk.CheckMenuItem(" " + _('Any Number'))
200 self.aggMenu.append(item)
201 item.connect("activate", self.set_seats_style, ('P','A'))
202 setattr(self, 'h_seatsStyleOptionA', item)
204 item = gtk.CheckMenuItem(" " + _('Custom'))
205 self.aggMenu.append(item)
206 item.connect("activate", self.set_seats_style, ('P','C'))
207 setattr(self, 'h_seatsStyleOptionC', item)
209 item = gtk.CheckMenuItem(" " + _('Exact'))
210 self.aggMenu.append(item)
211 item.connect("activate", self.set_seats_style, ('P','E'))
212 setattr(self, 'h_seatsStyleOptionE', item)
214 item = gtk.MenuItem(_('Since:'))
215 self.aggMenu.append(item)
217 item = gtk.CheckMenuItem(" " + _('All Time'))
218 self.aggMenu.append(item)
219 item.connect("activate", self.set_hud_style, ('P','A'))
220 setattr(self, 'h_hudStyleOptionA', item)
222 item = gtk.CheckMenuItem(" " + _('Session'))
223 self.aggMenu.append(item)
224 item.connect("activate", self.set_hud_style, ('P','S'))
225 setattr(self, 'h_hudStyleOptionS', item)
227 item = gtk.CheckMenuItem(" " + _('%s Days') % (self.hud_params['h_hud_days']))
228 self.aggMenu.append(item)
229 item.connect("activate", self.set_hud_style, ('P','T'))
230 setattr(self, 'h_hudStyleOptionT', item)
232 aggitem = gtk.MenuItem(_('Show Opponent Stats for'))
233 menu.append(aggitem)
234 self.aggMenu = gtk.Menu()
235 aggitem.set_submenu(self.aggMenu)
236 # set agg_bb_mult to 1 to stop aggregation
237 item = gtk.CheckMenuItem(_('For This Blind Level Only'))
238 self.aggMenu.append(item)
239 item.connect("activate", self.set_aggregation, ('O',1))
240 setattr(self, 'aggBBmultItem1', item)
242 item = gtk.MenuItem(_('For Multiple Blind Levels:'))
243 self.aggMenu.append(item)
245 item = gtk.CheckMenuItem(_('%s to %s * Current Blinds') % (" 0.5", "2.0"))
246 self.aggMenu.append(item)
247 item.connect("activate", self.set_aggregation, ('O',2))
248 setattr(self, 'aggBBmultItem2', item)
250 item = gtk.CheckMenuItem(_('%s to %s * Current Blinds') % (" 0.33", "3.0"))
251 self.aggMenu.append(item)
252 item.connect("activate", self.set_aggregation, ('O',3))
253 setattr(self, 'aggBBmultItem3', item)
255 item = gtk.CheckMenuItem(_('%s to %s * Current Blinds') % (" 0.1", "10.0"))
256 self.aggMenu.append(item)
257 item.connect("activate", self.set_aggregation, ('O',10))
258 setattr(self, 'aggBBmultItem10', item)
260 item = gtk.CheckMenuItem(" " + _('All Levels'))
261 self.aggMenu.append(item)
262 item.connect("activate", self.set_aggregation, ('O',10000))
263 setattr(self, 'aggBBmultItem10000', item)
265 item = gtk.MenuItem(_('Number of Seats:'))
266 self.aggMenu.append(item)
268 item = gtk.CheckMenuItem(" " + _('Any Number'))
269 self.aggMenu.append(item)
270 item.connect("activate", self.set_seats_style, ('O','A'))
271 setattr(self, 'seatsStyleOptionA', item)
273 item = gtk.CheckMenuItem(" " + _('Custom'))
274 self.aggMenu.append(item)
275 item.connect("activate", self.set_seats_style, ('O','C'))
276 setattr(self, 'seatsStyleOptionC', item)
278 item = gtk.CheckMenuItem(" " + _('Exact'))
279 self.aggMenu.append(item)
280 item.connect("activate", self.set_seats_style, ('O','E'))
281 setattr(self, 'seatsStyleOptionE', item)
283 item = gtk.MenuItem(_('Since:'))
284 self.aggMenu.append(item)
286 item = gtk.CheckMenuItem(" " + _('All Time'))
287 self.aggMenu.append(item)
288 item.connect("activate", self.set_hud_style, ('O','A'))
289 setattr(self, 'hudStyleOptionA', item)
291 item = gtk.CheckMenuItem(" " + _('Session'))
292 self.aggMenu.append(item)
293 item.connect("activate", self.set_hud_style, ('O','S'))
294 setattr(self, 'hudStyleOptionS', item)
296 item = gtk.CheckMenuItem(" " + _('%s Days') % (self.hud_params['hud_days']))
297 self.aggMenu.append(item)
298 item.connect("activate", self.set_hud_style, ('O','T'))
299 setattr(self, 'hudStyleOptionT', item)
301 # set active on current options:
302 if self.hud_params['h_agg_bb_mult'] == 1:
303 getattr(self, 'h_aggBBmultItem1').set_active(True)
304 elif self.hud_params['h_agg_bb_mult'] == 2:
305 getattr(self, 'h_aggBBmultItem2').set_active(True)
306 elif self.hud_params['h_agg_bb_mult'] == 3:
307 getattr(self, 'h_aggBBmultItem3').set_active(True)
308 elif self.hud_params['h_agg_bb_mult'] == 10:
309 getattr(self, 'h_aggBBmultItem10').set_active(True)
310 elif self.hud_params['h_agg_bb_mult'] > 9000:
311 getattr(self, 'h_aggBBmultItem10000').set_active(True)
313 if self.hud_params['agg_bb_mult'] == 1:
314 getattr(self, 'aggBBmultItem1').set_active(True)
315 elif self.hud_params['agg_bb_mult'] == 2:
316 getattr(self, 'aggBBmultItem2').set_active(True)
317 elif self.hud_params['agg_bb_mult'] == 3:
318 getattr(self, 'aggBBmultItem3').set_active(True)
319 elif self.hud_params['agg_bb_mult'] == 10:
320 getattr(self, 'aggBBmultItem10').set_active(True)
321 elif self.hud_params['agg_bb_mult'] > 9000:
322 getattr(self, 'aggBBmultItem10000').set_active(True)
324 if self.hud_params['h_seats_style'] == 'A':
325 getattr(self, 'h_seatsStyleOptionA').set_active(True)
326 elif self.hud_params['h_seats_style'] == 'C':
327 getattr(self, 'h_seatsStyleOptionC').set_active(True)
328 elif self.hud_params['h_seats_style'] == 'E':
329 getattr(self, 'h_seatsStyleOptionE').set_active(True)
331 if self.hud_params['seats_style'] == 'A':
332 getattr(self, 'seatsStyleOptionA').set_active(True)
333 elif self.hud_params['seats_style'] == 'C':
334 getattr(self, 'seatsStyleOptionC').set_active(True)
335 elif self.hud_params['seats_style'] == 'E':
336 getattr(self, 'seatsStyleOptionE').set_active(True)
338 if self.hud_params['h_hud_style'] == 'A':
339 getattr(self, 'h_hudStyleOptionA').set_active(True)
340 elif self.hud_params['h_hud_style'] == 'S':
341 getattr(self, 'h_hudStyleOptionS').set_active(True)
342 elif self.hud_params['h_hud_style'] == 'T':
343 getattr(self, 'h_hudStyleOptionT').set_active(True)
345 if self.hud_params['hud_style'] == 'A':
346 getattr(self, 'hudStyleOptionA').set_active(True)
347 elif self.hud_params['hud_style'] == 'S':
348 getattr(self, 'hudStyleOptionS').set_active(True)
349 elif self.hud_params['hud_style'] == 'T':
350 getattr(self, 'hudStyleOptionT').set_active(True)
352 eventbox.connect_object("button-press-event", self.on_button_press, menu)
354 debugitem = gtk.MenuItem(_('Debug Statistics Windows'))
355 menu.append(debugitem)
356 debugitem.connect("activate", self.debug_stat_windows)
358 item5 = gtk.MenuItem(_('Set max seats'))
359 menu.append(item5)
360 maxSeatsMenu = gtk.Menu()
361 item5.set_submenu(maxSeatsMenu)
362 for i in range(2, 11, 1):
363 item = gtk.MenuItem('%d-max' % i)
364 item.ms = i
365 maxSeatsMenu.append(item)
366 item.connect("activate", self.change_max_seats)
367 setattr(self, 'maxSeatsMenuItem%d' % (i - 1), item)
369 eventbox.connect_object("button-press-event", self.on_button_press, menu)
371 self.mw_created = True
372 self.label = label
373 menu.show_all()
374 self.main_window.show_all()
375 # self.topify_window(self.main_window)
377 def change_max_seats(self, widget):
378 if self.max != widget.ms:
379 #print 'change_max_seats', widget.ms
380 self.max = widget.ms
381 try:
382 self.kill()
383 self.create(*self.creation_attrs)
384 self.update(self.hand, self.config)
385 except Exception, e:
386 log.error("Exception:",str(e))
387 pass
389 def set_aggregation(self, widget, val):
390 (player_opp, num) = val
391 if player_opp == 'P':
392 # set these true all the time, set the multiplier to 1 to turn agg off:
393 self.hud_params['h_aggregate_ring'] = True
394 self.hud_params['h_aggregate_tour'] = True
396 if self.hud_params['h_agg_bb_mult'] != num \
397 and getattr(self, 'h_aggBBmultItem'+str(num)).get_active():
398 log.debug('set_player_aggregation %d', num)
399 self.hud_params['h_agg_bb_mult'] = num
400 for mult in ('1', '2', '3', '10', '10000'):
401 if mult != str(num):
402 getattr(self, 'h_aggBBmultItem'+mult).set_active(False)
403 else:
404 self.hud_params['aggregate_ring'] = True
405 self.hud_params['aggregate_tour'] = True
407 if self.hud_params['agg_bb_mult'] != num \
408 and getattr(self, 'aggBBmultItem'+str(num)).get_active():
409 log.debug('set_opponent_aggregation %d', num)
410 self.hud_params['agg_bb_mult'] = num
411 for mult in ('1', '2', '3', '10', '10000'):
412 if mult != str(num):
413 getattr(self, 'aggBBmultItem'+mult).set_active(False)
415 def set_seats_style(self, widget, val):
416 (player_opp, style) = val
417 if player_opp == 'P':
418 param = 'h_seats_style'
419 prefix = 'h_'
420 else:
421 param = 'seats_style'
422 prefix = ''
424 if style == 'A' and getattr(self, prefix+'seatsStyleOptionA').get_active():
425 self.hud_params[param] = 'A'
426 getattr(self, prefix+'seatsStyleOptionC').set_active(False)
427 getattr(self, prefix+'seatsStyleOptionE').set_active(False)
428 elif style == 'C' and getattr(self, prefix+'seatsStyleOptionC').get_active():
429 self.hud_params[param] = 'C'
430 getattr(self, prefix+'seatsStyleOptionA').set_active(False)
431 getattr(self, prefix+'seatsStyleOptionE').set_active(False)
432 elif style == 'E' and getattr(self, prefix+'seatsStyleOptionE').get_active():
433 self.hud_params[param] = 'E'
434 getattr(self, prefix+'seatsStyleOptionA').set_active(False)
435 getattr(self, prefix+'seatsStyleOptionC').set_active(False)
436 log.debug("setting self.hud_params[%s] = %s" % (param, style))
438 def set_hud_style(self, widget, val):
439 (player_opp, style) = val
440 if player_opp == 'P':
441 param = 'h_hud_style'
442 prefix = 'h_'
443 else:
444 param = 'hud_style'
445 prefix = ''
447 if style == 'A' and getattr(self, prefix+'hudStyleOptionA').get_active():
448 self.hud_params[param] = 'A'
449 getattr(self, prefix+'hudStyleOptionS').set_active(False)
450 getattr(self, prefix+'hudStyleOptionT').set_active(False)
451 elif style == 'S' and getattr(self, prefix+'hudStyleOptionS').get_active():
452 self.hud_params[param] = 'S'
453 getattr(self, prefix+'hudStyleOptionA').set_active(False)
454 getattr(self, prefix+'hudStyleOptionT').set_active(False)
455 elif style == 'T' and getattr(self, prefix+'hudStyleOptionT').get_active():
456 self.hud_params[param] = 'T'
457 getattr(self, prefix+'hudStyleOptionA').set_active(False)
458 getattr(self, prefix+'hudStyleOptionS').set_active(False)
459 log.debug("setting self.hud_params[%s] = %s" % (param, style))
461 def update_table_position(self):
462 # get table's X/Y position on the desktop, and relocate all of our child windows to accomodate
463 # In Windows, we can verify the existence of a Window, with win32gui.IsWindow(). In Linux, there doesn't seem to be a
464 # way to verify the existence of a Window, without trying to access it, which if it doesn't exist anymore, results in a
465 # big giant X trap and crash.
466 # People tell me this is a bad idea, because theoretically, IsWindow() could return true now, but not be true when we actually
467 # use it, but accessing a dead window doesn't result in a complete windowing system shutdown in Windows, whereas it does
468 # in X. - Eric
469 if os.name == 'nt':
470 if not win32gui.IsWindow(self.table.number):
471 self.parent.kill_hud(self, self.table.name)
472 self.parent.kill_hud(self, self.table.name.split(" ")[0])
473 #table.name is only a valid handle for ring games ! we are not killing tourney tables here.
474 return False
475 # anyone know how to do this in unix, or better yet, trap the X11 error that is triggered when executing the get_origin() for a closed window?
476 if self.table.gdkhandle is not None:
477 (oldx, oldy) = self.table.gdkhandle.get_origin() # In Windows, this call returns (0,0) if it's an invalid window. In X, the X server is immediately killed.
478 log.debug("update_table_position:"+str(oldx)+","+str(oldy))
479 #(x, y, width, height) = self.table.get_geometry()
480 #print "self.table.get_geometry=",x,y,width,height
481 if self.table.oldx != oldx or self.table.oldy != oldy: # If the current position does not equal the stored position, save the new position, and then move all the sub windows.
482 self.table.oldx = oldx
483 self.table.oldy = oldy
484 self.main_window.move(oldx + self.site_params['xshift'], oldy + self.site_params['yshift'])
485 adj = self.adj_seats(self.hand, self.config)
486 loc = self.config.get_locations(self.table.site, self.max)
487 # TODO: is stat_windows getting converted somewhere from a list to a dict, for no good reason?
488 for i, w in enumerate(self.stat_windows.itervalues()):
489 (oldx, oldy) = loc[adj[i+1]]
490 w.relocate(oldx, oldy)
492 # While we're at it, fix the positions of mucked cards too
493 for aux in self.aux_windows:
494 aux.update_card_positions()
496 self.reposition_windows()
497 # call reposition_windows, which apparently moves even hidden windows, where this function does not, even though they do the same thing, afaict
499 return True
501 def up_update_table_position(self):
502 # callback for table moved
504 # move the stat windows
505 (self.table.oldx, self.table.oldy) = self.table.gdkhandle.get_origin()
506 log.debug("up_update_table_position:"+str(self.table.oldx)+","+str(self.table.oldy))
507 adj = self.adj_seats(self.hand, self.config)
508 loc = self.config.get_locations(self.table.site, self.max)
509 for i, w in enumerate(self.stat_windows.itervalues()):
510 (x, y) = loc[adj[i+1]]
511 w.relocate(x, y)
512 # move the main window - use the "old" position as it's already updated by the time we get here.
513 self.main_window.move(self.table.oldx + self.site_params['xshift'], self.table.oldy + self.site_params['yshift'])
514 # and move any auxs
515 for aux in self.aux_windows:
516 aux.update_card_positions()
517 return True
519 def on_button_press(self, widget, event):
520 if event.button == 1: # if primary button, start movement
521 self.main_window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
522 return True
523 if event.button == 3: # if secondary button, popup our main popup window
524 widget.popup(None, None, None, event.button, event.time)
525 return True
526 return False
528 def kill(self, *args):
529 # kill all stat_windows, popups and aux_windows in this HUD
530 # heap dead, burnt bodies, blood 'n guts, veins between my teeth
531 for s in self.stat_windows.itervalues():
532 s.kill_popups()
533 try:
534 # throws "invalid window handle" in WinXP (sometimes?)
535 s.window.destroy()
536 except: # TODO: what exception?
537 pass
538 self.stat_windows = {}
539 # also kill any aux windows
540 for aux in self.aux_windows:
541 aux.destroy()
542 self.aux_windows = []
544 def resize_windows(self, *args):
545 log.debug("resize_windows old:"+str(self.table.oldwidth)+"x"+str(self.table.oldheight))
546 log.debug("resize_windows new:"+str(self.table.width)+"x"+str(self.table.height))
547 for w in self.stat_windows.itervalues():
548 if type(w) == int:
549 continue
550 rel_x = (w.x - self.table.x) * self.table.width / self.table.oldwidth
551 rel_y = (w.y - self.table.y) * self.table.height / self.table.oldheight
552 w.x = self.table.x + rel_x
553 w.y = self.table.y + rel_y
554 w.window.move(w.x, w.y)
556 def reposition_windows(self, *args):
557 if self.repositioningwindows is True:
558 return True
559 else:
560 self.repositioningwindows = True
562 self.update_table_position()
563 for w in self.stat_windows.itervalues():
564 if type(w) == int:
565 # print "in reposition, w =", w
566 continue
567 # print "in reposition, w =", w, w.x, w.y
568 w.window.move(w.x, w.y)
569 self.repositioningwindows = False
570 return True
572 def debug_stat_windows(self, *args):
573 # print self.table, "\n", self.main_window.window.get_transient_for()
574 for w in self.stat_windows:
575 try:
576 print self.stat_windows[w].window.window.get_transient_for()
577 except AttributeError:
578 print "this window doesnt have get_transient_for"
580 def save_layout(self, *args):
581 new_layout = [(0, 0)] * self.max
582 for sw in self.stat_windows:
583 loc = self.stat_windows[sw].window.get_position()
584 new_loc = (loc[0] - self.table.x, loc[1] - self.table.y)
585 new_layout[self.stat_windows[sw].adj - 1] = new_loc
586 self.config.edit_layout(self.table.site, self.max, locations=new_layout)
587 # ask each aux to save its layout back to the config object
588 [aux.save_layout() for aux in self.aux_windows]
589 # save the config object back to the file
590 print _("Updating config file")
591 self.config.save()
593 def adj_seats(self, hand, config):
594 # determine how to adjust seating arrangements, if a "preferred seat" is set in the hud layout configuration
595 # Need range here, not xrange -> need the actual list
596 adj = range(0, self.max + 1) # default seat adjustments = no adjustment
597 # does the user have a fav_seat?
598 if self.max not in config.supported_sites[self.table.site].layout:
599 sys.stderr.write(_("No layout found for %d-max games for site %s.") % (self.max, self.table.site))
600 return adj
601 if self.table.site != None and int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0:
602 try:
603 fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat
604 actual_seat = self.get_actual_seat(config.supported_sites[self.table.site].screen_name)
605 for i in xrange(0, self.max + 1):
606 j = actual_seat + i
607 if j > self.max:
608 j = j - self.max
609 adj[j] = fav_seat + i
610 if adj[j] > self.max:
611 adj[j] = adj[j] - self.max
612 except Exception, inst:
613 sys.stderr.write(_("Exception in %s") % "Hud.adj_seats")
614 sys.stderr.write("Error:" + (" %s") % inst) # __str__ allows args to printed directly
615 return adj
617 def get_actual_seat(self, name):
618 for key in self.stat_dict:
619 if self.stat_dict[key]['screen_name'] == name:
620 return self.stat_dict[key]['seat']
621 sys.stderr.write(_("Error finding actual seat."))
623 def create(self, hand, config, stat_dict, cards):
624 # update this hud, to the stats and players as of "hand"
625 # hand is the hand id of the most recent hand played at this table
627 # this method also manages the creating and destruction of stat
628 # windows via calls to the Stat_Window class
629 self.creation_attrs = hand, config, stat_dict, cards
631 self.hand = hand
632 if not self.mw_created:
633 self.create_mw()
635 self.stat_dict = stat_dict
636 self.cards = cards
637 log.info(_('Creating hud from hand %s') % str(hand))
638 adj = self.adj_seats(hand, config)
639 loc = self.config.get_locations(self.table.site, self.max)
640 if loc is None and self.max != 10:
641 loc = self.config.get_locations(self.table.site, 10)
642 if loc is None and self.max != 9:
643 loc = self.config.get_locations(self.table.site, 9)
645 # create the stat windows
646 for i in xrange(1, self.max + 1):
647 (x, y) = loc[adj[i]]
648 if i in self.stat_windows:
649 self.stat_windows[i].relocate(x, y)
650 else:
651 self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game],
652 parent = self,
653 table = self.table,
654 x = x,
655 y = y,
656 seat = i,
657 adj = adj[i],
658 player_id = 'fake',
659 font = self.font)
661 self.topify_window(self.main_window)
662 for i in xrange(1, self.max + 1):
663 self.topify_window(self.stat_windows[i].window, self.main_window)
665 game = config.supported_games[self.poker_game]
666 self.stats = [None for i in range (game.rows*game.cols)] # initialize to None for not present stats at [row][col]
667 for i in range (game.rows*game.cols):
668 if config.supported_games[self.poker_game].stats[i] is not None:
669 self.stats[i] = config.supported_games[self.poker_game].stats[i].stat_name
671 # if os.name == "nt": # we call update_table_position() regularly in Windows to see if we're moving around. See comments on that function for why this isn't done in X.
672 # gobject.timeout_add(500, self.update_table_position)
674 def update(self, hand, config):
675 self.hand = hand # this is the last hand, so it is available later
676 if os.name == 'nt':
677 if self.update_table_position() == False: # we got killed by finding our table was gone
678 return
680 self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
681 for s in self.stat_dict:
682 try:
683 statd = self.stat_dict[s]
684 except KeyError:
685 log.error(_("HUD process overloaded, skipping this hand."))
686 continue
687 try:
688 self.stat_windows[statd['seat']].player_id = statd['player_id']
689 #self.stat_windows[self.stat_dict[s]['seat']].player_id = self.stat_dict[s]['player_id']
690 except KeyError: # omg, we have more seats than stat windows .. damn poker sites with incorrect max seating info .. let's force 10 here
691 self.max = 10
692 self.create(hand, config, self.stat_dict, self.cards)
693 self.stat_windows[statd['seat']].player_id = statd['player_id']
695 unhidewindow = False
696 for r in xrange(0, config.supported_games[self.poker_game].rows):
697 for c in xrange(0, config.supported_games[self.poker_game].cols):
698 # stats may be None if the user hasn't configured a stat for this row,col
699 if self.stats[r*config.supported_games[self.poker_game].cols+c] is not None:
700 this_stat = config.supported_games[self.poker_game].stats[r*config.supported_games[self.poker_game].cols+c]
701 number = Stats.do_stat(self.stat_dict, player = statd['player_id'], stat = self.stats[r*config.supported_games[self.poker_game].cols+c])
702 statstring = "%s%s%s" % (this_stat.hudprefix, str(number[1]), this_stat.hudsuffix)
703 window = self.stat_windows[statd['seat']]
705 if this_stat.hudcolor != "":
706 window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor))
707 else:
708 window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
710 if this_stat.stat_loth != "":
711 if number[0] < (float(this_stat.stat_loth)/100):
712 window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.stat_locolor))
714 if this_stat.stat_hith != "":
715 if number[0] > (float(this_stat.stat_hith)/100):
716 window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.stat_hicolor))
718 window.label[r][c].set_text(statstring)
719 if statstring != "xxx": # is there a way to tell if this particular stat window is visible already, or no?
720 unhidewindow = True
721 tip = "%s\n%s\n%s, %s" % (statd['screen_name'], number[5], number[3], number[4])
722 Stats.do_tip(window.e_box[r][c], tip)
724 if unhidewindow: #and not window.window.visible: # there is no "visible" attribute in gtk.Window, although the docs seem to indicate there should be
725 window.window.show_all()
726 unhidewindow = False
728 def topify_window(self, window, parentwindow=None):
729 window.set_focus_on_map(False)
730 window.set_accept_focus(False)
731 # print "topify_window", window, parentwindow
733 if not self.table.gdkhandle:
734 self.table.gdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gtk handle to poker window
735 if parentwindow is not None:
736 window.window.set_transient_for(parentwindow.window)
737 else:
738 window.window.set_transient_for(self.table.gdkhandle)
739 window.set_destroy_with_parent(True)
741 class Stat_Window:
743 def button_press_cb(self, widget, event, *args):
744 # This handles all callbacks from button presses on the event boxes in
745 # the stat windows. There is a bit of an ugly kludge to separate single-
746 # and double-clicks.
747 self.window.show() #_all()
749 if event.button == 3: # right button event
750 newpopup = Popup_window(self.window, self)
751 #print "added popup", newpopup
752 # TODO: how should we go about making sure it doesn't open a dozen popups if you click?
753 self.popups.append(newpopup)
754 return True
756 if event.button == 2: # middle button event
757 self.window.hide()
758 return True
760 if event.button == 1: # left button event
761 # close on double click for a stat window
762 # for those that don't have a mouse with middle button
763 if event.type == gtk.gdk._2BUTTON_PRESS:
764 self.window.hide()
765 return True
766 # TODO: make position saving save sizes as well?
767 if event.state & gtk.gdk.SHIFT_MASK:
768 self.window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, event.button, int(event.x_root), int(event.y_root), event.time)
769 else:
770 self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
771 return True
772 return False
774 def noop(self, arga=None, argb=None): # i'm going to try to connect the focus-in and focus-out events here, to see if that fixes any of the focus problems.
775 return True
777 def kill_popup(self, popup):
778 #print "remove popup", popup
779 self.popups.remove(popup)
780 popup.window.destroy()
782 def kill_popups(self):
783 map(lambda x: x.window.destroy(), self.popups)
784 self.popups = { }
786 def relocate(self, x, y):
787 self.x = x + self.table.oldx
788 self.y = y + self.table.oldy
789 self.window.move(self.x, self.y)
791 def __init__(self, parent, game, table, seat, adj, x, y, player_id, font):
792 self.parent = parent # Hud object that this stat window belongs to
793 self.game = game # Configuration object for the curren
794 self.table = table # Table object where this is going
795 self.seat = seat # seat number of his player
796 self.adj = adj # the adjusted seat number for this player
797 self.x = x + table.x # table.x and y are the location of the table
798 self.y = y + table.y # x and y are the location relative to table.x & y
799 self.player_id = player_id # looks like this isn't used ;)
800 self.sb_click = 0 # used to figure out button clicks
801 self.popups = [] # list of open popups for this stat window
802 self.useframes = parent.config.get_frames(parent.site)
804 self.window = gtk.Window()
805 self.window.set_decorated(0)
806 self.window.set_property("skip-taskbar-hint", True)
807 self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
809 self.window.set_title("%s" % seat)
810 self.window.set_focus(None) # set gtk default focus widget for this window to None
811 self.window.set_focus_on_map(False)
812 self.window.set_accept_focus(False)
814 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
816 grid = gtk.Table(rows = game.rows, columns = game.cols, homogeneous = False)
817 self.grid = grid
818 self.window.add(grid)
819 self.window.modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
821 self.e_box = []
822 self.frame = []
823 self.label = []
824 usegtkframes = self.useframes
825 e_box = self.e_box
826 label = self.label
827 for r in xrange(game.rows):
828 if usegtkframes:
829 self.frame.append([])
830 e_box.append([])
831 label.append([])
832 for c in xrange(game.cols):
833 if usegtkframes:
834 self.frame[r].append( gtk.Frame() )
835 e_box[r].append( gtk.EventBox() )
837 e_box[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
838 e_box[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor)
840 Stats.do_tip(e_box[r][c], 'stuff')
841 if usegtkframes:
842 grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = game.xpad, ypadding = game.ypad)
843 self.frame[r][c].add(e_box[r][c])
844 else:
845 grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = game.xpad, ypadding = game.ypad)
846 label[r].append( gtk.Label(' ') )
848 if usegtkframes:
849 self.frame[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
850 label[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
851 label[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor)
853 e_box[r][c].add(self.label[r][c])
854 e_box[r][c].connect("button_press_event", self.button_press_cb)
855 e_box[r][c].connect("focus-in-event", self.noop)
856 e_box[r][c].connect("focus", self.noop)
857 e_box[r][c].connect("focus-out-event", self.noop)
858 label[r][c].modify_font(font)
860 self.window.set_opacity(parent.colors['hudopacity'])
861 self.window.connect("focus", self.noop)
862 self.window.connect("focus-in-event", self.noop)
863 self.window.connect("focus-out-event", self.noop)
864 self.window.connect("button_press_event", self.button_press_cb)
865 self.window.set_focus_on_map(False)
866 self.window.set_accept_focus(False)
869 self.window.move(self.x, self.y)
870 self.window.hide()
871 self.window.realize() # window must be realized before it has a gdkwindow so we can attach it to the table window..
872 # self.topify_window(self.window)
875 def destroy(*args): # call back for terminating the main eventloop
876 gtk.main_quit()
878 class Popup_window:
879 def __init__(self, parent, stat_window):
880 self.sb_click = 0
881 self.stat_window = stat_window
882 self.parent = parent
884 # create the popup window
885 self.window = gtk.Window()
886 self.window.set_decorated(0)
887 self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
888 self.window.set_title("popup")
889 self.window.set_property("skip-taskbar-hint", True)
890 self.window.set_focus_on_map(False)
891 self.window.set_accept_focus(False)
892 self.window.set_transient_for(parent.get_toplevel())
894 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
896 self.ebox = gtk.EventBox()
897 self.ebox.connect("button_press_event", self.button_press_cb)
898 self.lab = gtk.Label("stuff\nstuff\nstuff")
900 # need an event box so we can respond to clicks
901 self.window.add(self.ebox)
902 self.ebox.add(self.lab)
904 self.ebox.modify_bg(gtk.STATE_NORMAL, stat_window.parent.backgroundcolor)
905 self.ebox.modify_fg(gtk.STATE_NORMAL, stat_window.parent.foregroundcolor)
906 self.window.modify_bg(gtk.STATE_NORMAL, stat_window.parent.backgroundcolor)
907 self.window.modify_fg(gtk.STATE_NORMAL, stat_window.parent.foregroundcolor)
908 self.lab.modify_bg(gtk.STATE_NORMAL, stat_window.parent.backgroundcolor)
909 self.lab.modify_fg(gtk.STATE_NORMAL, stat_window.parent.foregroundcolor)
911 # figure out the row, col address of the click that activated the popup
912 row = 0
913 col = 0
914 for r in xrange(0, stat_window.game.rows):
915 for c in xrange(0, stat_window.game.cols):
916 if stat_window.e_box[r][c] == parent:
917 row = r
918 col = c
919 break
921 # figure out what popup format we're using
922 popup_format = "default"
923 if stat_window.game.stats[row*stat_window.game.rows+col] is not None:
924 popup_format = stat_window.game.stats[row*stat_window.game.rows+col].popup
926 # get the list of stats to be presented from the config
927 stat_list = []
928 for w in stat_window.parent.config.popup_windows:
929 if w == popup_format:
930 stat_list = stat_window.parent.config.popup_windows[w].pu_stats
931 break
933 # get a database connection
934 # db_connection = Database.Database(stat_window.parent.config, stat_window.parent.db_name, 'temp')
936 # calculate the stat_dict and then create the text for the pu
937 # stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand, stat_window.player_id)
938 # stat_dict = self.db_connection.get_stats_from_hand(stat_window.parent.hand)
939 # db_connection.close_connection()
940 stat_dict = stat_window.parent.stat_dict
941 pu_text = ""
942 mo_text = ""
943 for s in stat_list:
944 number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s, handid = int(stat_window.parent.hand))
945 mo_text += number[5] + " " + number[4] + "\n"
946 pu_text += number[3] + "\n"
949 self.lab.set_text(pu_text)
950 Stats.do_tip(self.lab, mo_text)
951 self.window.show_all()
953 self.window.set_transient_for(stat_window.window)
955 def button_press_cb(self, widget, event, *args):
956 # This handles all callbacks from button presses on the event boxes in
957 # the popup windows. There is a bit of an ugly kludge to separate single-
958 # and double-clicks. This is the same code as in the Stat_window class
959 if event.button == 1: # left button event
960 pass
962 if event.button == 2: # middle button event
963 pass
965 if event.button == 3: # right button event
966 self.stat_window.kill_popup(self)
967 return True
968 # self.window.destroy()
969 return False
971 def toggle_decorated(self, widget):
972 top = widget.get_toplevel()
973 (x, y) = top.get_position()
975 if top.get_decorated():
976 top.set_decorated(0)
977 top.move(x, y)
978 else:
979 top.set_decorated(1)
980 top.move(x, y)
982 def topify_window(self, window):
983 window.set_focus_on_map(False)
984 window.set_accept_focus(False)
986 if not self.table.gdkhandle:
987 self.table.gdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gtk handle to poker window
988 # window.window.reparent(self.table.gdkhandle, 0, 0)
989 window.window.set_transient_for(self.table.gdkhandle)
990 # window.present()