diff --git a/pysollib/actions.py b/pysollib/actions.py index 5967a3c2..04df3482 100644 --- a/pysollib/actions.py +++ b/pysollib/actions.py @@ -305,7 +305,7 @@ class PysolMenubarActions: self.setMenuState(ms.redo, "edit.redo") self.setMenuState(ms.redo, "edit.redoall") self.updateBookmarkMenuState() - self.setMenuState(ms.restart, "edit.restartgame") + self.setMenuState(ms.restart, "edit.restart") # Game menu self.setMenuState(ms.deal, "game.dealcards") self.setMenuState(ms.autodrop, "game.autodrop") diff --git a/pysollib/game.py b/pysollib/game.py index 4b1353d1..fc8e7eac 100644 --- a/pysollib/game.py +++ b/pysollib/game.py @@ -1001,7 +1001,7 @@ class Game: if self.app.debug and not self.top.winfo_ismapped(): return #self.top.busyUpdate() - self.canvas.after(200) + ##self.canvas.after(200) self.canvas.update_idletasks() old_a = self.app.opt.animations if old_a == 0: diff --git a/pysollib/help.py b/pysollib/help.py index c42f6403..40ef2287 100644 --- a/pysollib/help.py +++ b/pysollib/help.py @@ -160,9 +160,7 @@ def helpHTML(app, document, dir_, top=None): wm_set_icon(top, app.dataloader.findIcon()) except: pass - viewer = tkHTMLViewer(top) - viewer.app = app - viewer.home = help_html_index + viewer = tkHTMLViewer(top, app, help_html_index) viewer.display(doc) #wm_map(top, maximized=maximized) viewer.parent.tkraise() diff --git a/pysollib/pysolgtk/menubar.py b/pysollib/pysolgtk/menubar.py index 0fe33aab..905784b0 100644 --- a/pysollib/pysolgtk/menubar.py +++ b/pysollib/pysolgtk/menubar.py @@ -78,116 +78,130 @@ class PysolMenubar(PysolMenubarActions): def createMenubar(self): entries = ( - ('New', gtk.STOCK_NEW, '_New', 'N', 'New game', self.mNewGame), - ('Open', gtk.STOCK_OPEN, '_Open', 'O', 'Open a\nsaved game', self.mOpen), - ('Restart', gtk.STOCK_REFRESH, '_Restart', 'G', 'Restart the\ncurrent game', self.mRestart), - ('Save', gtk.STOCK_SAVE, '_Save', 'S', 'Save game', self.mSave), - ('Undo', gtk.STOCK_UNDO, 'Undo', 'Z', 'Undo', self.mUndo), - ('Redo', gtk.STOCK_REDO, 'Redo', 'R', 'Redo', self.mRedo), - ('Autodrop',gtk.STOCK_JUMP_TO, '_Auto drop', 'A', 'Auto drop', self.mDrop), - ('Stats', gtk.STOCK_EXECUTE, 'Stats', None, 'Statistics', self.mStatus), - ('Rules', gtk.STOCK_HELP, 'Rules', None, 'Rules', self.mHelpRules), - ('Quit', gtk.STOCK_QUIT, 'Quit', 'Q', 'Quit PySol', self.mQuit), + ('new', gtk.STOCK_NEW, '_New', 'N', 'New game', self.mNewGame), + ('open', gtk.STOCK_OPEN, '_Open', 'O', 'Open a\nsaved game', self.mOpen), + ('restart', gtk.STOCK_REFRESH, '_Restart', 'G', 'Restart the\ncurrent game', self.mRestart), + ('save', gtk.STOCK_SAVE, '_Save', 'S', 'Save game', self.mSave), + ('undo', gtk.STOCK_UNDO, 'Undo', 'Z', 'Undo', self.mUndo), + ('redo', gtk.STOCK_REDO, 'Redo', 'R', 'Redo', self.mRedo), + ('autodrop',gtk.STOCK_JUMP_TO, '_Auto drop', 'A', 'Auto drop', self.mDrop), + ('stats', gtk.STOCK_HOME, 'Stats', None, 'Statistics', self.mStatus), + ('rules', gtk.STOCK_HELP, 'Rules', 'F1', 'Rules', self.mHelpRules), + ('quit', gtk.STOCK_QUIT, 'Quit', 'Q', 'Quit PySol', self.mQuit), - ("FileMenu", None, "_File" ), - ("SelectGame", None, "Select _game"), - ("EditMenu", None, '_Edit'), - ("GameMenu", None, "_Game"), - ("AssistMenu", None, "_Assist"), - ("OptionsMenu", None, "_Options"), - ('AnimationsMenu', None, '_Animations'), - ("HelpMenu", None, "_Help"), + ('file', None, '_File' ), + ('selectgame', None, 'Select _game'), + ('edit', None, '_Edit'), + ('game', None, '_Game'), + ('assist', None, '_Assist'), + ('options', None, '_Options'), + ("automaticplay", None, "_Automatic play"), - ('SelectGameByNumber', None, "Select game by number...", None, None, self.mSelectGameById), - ("SaveAs", None, 'Save _as...', None, None, self.m), - ("RedoAll", None, 'Redo _all', None, None, self.mRedoAll), - ("DealCards", None, '_Deal cards', "D", None, self.mDeal), - ("Status", None, 'S_tatus...', "T", None, self.mStatus), - ("Hint", None, '_Hint', "H", None, self.mHint), - ("HighlightPiles", None, 'Highlight _piles', None, None, self.mHighlightPiles), - ("Demo", None, '_Demo', "D", None, self.mDemo), - ("DemoAllGames", None, 'Demo (all games)', None, None, self.mMixedDemo), - ("TableTile", None, "Table t_ile...", None, None, self.mOptTableTile), - ("Contents", None, '_Contents', 'F1', None, self.mHelp), - ("About", None, '_About PySol...', None, None, self.mHelpAbout), + ('animations', None, '_Animations'), + ('help', None, '_Help'), + + ('selectgamebynumber', None, 'Select game by number...', None, None, self.mSelectGameById), + ('saveas', None, 'Save _as...', None, None, self.m), + ('redoall', None, 'Redo _all', None, None, self.mRedoAll), + ('dealcards', None, '_Deal cards', 'D', None, self.mDeal), + ('status', None, 'S_tatus...', 'T', None, self.mStatus), + ('hint', None, '_Hint', 'H', None, self.mHint), + ('highlightpiles', None, 'Highlight _piles', None, None, self.mHighlightPiles), + ('demo', None,'_Demo', 'D',None,self.mDemo), + ('demoallgames', None,'Demo (all games)', None,None,self.mMixedDemo), + ('playeroptions',None,'_Player options...',None,None,self.mOptPlayerOptions), + ('tabletile', None,'Table t_ile...', None,None,self.mOptTableTile), + ('contents', None,'_Contents','F1',None,self.mHelp), + ('aboutpysol', None,'_About PySol...', None,None,self.mHelpAbout), ) + # toggle_entries = ( - ("Confirm", None, '_Confirm', None, None, self.mOptConfirm), - ("Autoplay", None, 'Auto_play', "P", None, self.mOptAutoDrop), - ("AutomaticFaceUp", None,'_Automatic _face up', "F", None, self.mOptAutoFaceUp), - ("HighlightMatchingCards", None, 'Highlight _matching cards', None, None, self.mOptEnableHighlightCards), - ("CardShadow", None, 'Card shadow', None, None, self.mOptShadow), - ("ShadeLegalMoves", None, 'Shade legal moves', None, None, self.mOptShade), + ('pause', gtk.STOCK_STOP, '_Pause', 'P', 'Pause game', self.mPause), + ('optautodrop', None, 'A_uto drop', None, None, self.mOptAutoDrop), + ('autofaceup', None, 'Auto _face up', None, None, self.mOptAutoFaceUp), + ("autodeal", None, "Auto _deal", None, None, self.mOptAutoDeal), + ("quickplay", None, '_Quick play', None, None, self.mOptQuickPlay), + + ('highlightmatchingcards', None, 'Highlight _matching cards', None, None, self.mOptEnableHighlightCards), + ('cardshadow', None, 'Card shadow', None, None, self.mOptShadow), + ('shadelegalmoves', None, 'Shade legal moves', None, None, self.mOptShade), ) + # animations_entries = ( - ("AnimationNone", None, "_None", None, None, 0), - ("AnimationFast", None, "_Fast", None, None, 1), - ("AnimationTimer", None, "_Timer based", None, None, 2), - ("AnimationSlow", None, "_Slow", None, None, 3), - ("AnimationVerySlow", None, "_Very slow", None, None, 4), + ('animationnone', None, '_None', None, None, 0), + ('animationfast', None, '_Fast', None, None, 1), + ('animationtimer', None, '_Timer based', None, None, 2), + ('animationslow', None, '_Slow', None, None, 3), + ('animationveryslow', None, '_Very slow', None, None, 4), ) # ui_info = ''' - + - - - + + + - - - + + + - + - - - - + + + + - + - - - + + + + - + - - - - - + + + + + - - - - - - - - - - - - - + + + + + + + + - - + + + + + + + + + + + + - - - - + + + + @@ -197,7 +211,7 @@ class PysolMenubar(PysolMenubarActions): ui_manager = gtk.UIManager() ui_manager_id = ui_manager.add_ui_from_string(ui_info) - action_group = gtk.ActionGroup("PySolActions") + action_group = gtk.ActionGroup('PySolActions') action_group.add_actions(entries) action_group.add_toggle_actions(toggle_entries) action_group.add_radio_actions(animations_entries, @@ -207,11 +221,11 @@ class PysolMenubar(PysolMenubarActions): ui_manager.insert_action_group(action_group, 0) self.top.add_accel_group(ui_manager.get_accel_group()) self.top.ui_manager = ui_manager - menubar = ui_manager.get_widget("/MenuBar") + menubar = ui_manager.get_widget('/menubar') games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName()) - menu_item = ui_manager.get_widget("/MenuBar/FileMenu/SelectGame") + menu_item = ui_manager.get_widget('/menubar/file/selectgame') menu_item.show() menu = gtk.Menu() menu_item.set_submenu(menu) @@ -238,7 +252,7 @@ class PysolMenubar(PysolMenubarActions): menu_item.connect('toggled', command, g.id) def _addSelectAllGameSubMenu(self, games, menu, command): - cb_max = gdk.screen_height()/20 + cb_max = gdk.screen_height()/24 n, d = 0, cb_max i = 0 group = None @@ -259,15 +273,29 @@ class PysolMenubar(PysolMenubarActions): # menu updates # +## WARNING: setMenuState: not found: /menubar/file/holdandquit +## WARNING: setMenuState: not found: /menubar/assist/findcard def setMenuState(self, state, path): - return - w = self.__menubar.get_widget(path) - w.set_sensitive(state) + path_map = {'help.rulesforthisgame': '/menubar/help/rules',} + if path_map.has_key(path): + path = path_map[path] + else: + path = '/menubar/'+path.replace('.', '/') + menuitem = self.top.ui_manager.get_widget(path) + if not menuitem: + ##print 'WARNING: setMenuState: not found:', path + return + menuitem.set_sensitive(state) + + def setToolbarState(self, state, path): - ##~ w = getattr(self.app.toolbar, path + "_button") - ##~ w.set_sensitive(state) - pass + path = '/toolbar/'+path + button = self.top.ui_manager.get_widget(path) + if not button: + print 'WARNING: setToolbarState: not found:', path + else: + button.set_sensitive(state) # @@ -289,7 +317,7 @@ class PysolMenubar(PysolMenubarActions): if key <= 0: key = self.app.opt.table_color.lower() d = SelectTileDialogWithPreview(self.top, app=self.app, - title=_("Select table background"), + title=_('Select table background'), manager=self.app.tabletile_manager, key=key) if d.status == 0 and d.button in (0, 1): @@ -299,9 +327,6 @@ class PysolMenubar(PysolMenubarActions): self._mOptTableTile(d.key) - def mOptConfirm(self, *args): - pass - def mOptHintOptions(self, *args): pass diff --git a/pysollib/pysolgtk/playeroptionsdialog.py b/pysollib/pysolgtk/playeroptionsdialog.py index 32dd4982..352ad045 100644 --- a/pysollib/pysolgtk/playeroptionsdialog.py +++ b/pysollib/pysolgtk/playeroptionsdialog.py @@ -36,17 +36,78 @@ __all__ = ['PlayerOptionsDialog'] # imports -import gtk +import gobject, gtk # PySol imports # Toolkit imports from tkwidget import MfxDialog +from pysollib.mfxutil import kwdefault + # /*********************************************************************** # // # ************************************************************************/ class PlayerOptionsDialog(MfxDialog): - pass + def __init__(self, parent, title, app, **kw): + kw = self.initKw(kw) + MfxDialog.__init__(self, parent, title, **kw) + # + top_box, bottom_box = self.createVBox() + # + label = gtk.Label('Please enter your name') + label.show() + top_box.pack_start(label) + self.player_entry = gtk.Entry() + self.player_entry.show() + top_box.pack_start(self.player_entry, expand=False) + completion = gtk.EntryCompletion() + self.player_entry.set_completion(completion) + model = gtk.ListStore(gobject.TYPE_STRING) + print '>>', app.getAllUserNames() + for name in app.getAllUserNames(): + iter = model.append() + model.set(iter, 0, name) + completion.set_model(model) + completion.set_text_column(0) + self.player_entry.set_text(app.opt.player) + # + self.confirm_quit_check = gtk.CheckButton(_('Confirm quit')) + self.confirm_quit_check.show() + top_box.pack_start(self.confirm_quit_check) + self.confirm_quit_check.set_active(app.opt.confirm != 0) + # + self.update_stats_check = gtk.CheckButton(_('Update statistics and logs')) + self.update_stats_check.show() + top_box.pack_start(self.update_stats_check) + self.update_stats_check.set_active(app.opt.update_player_stats != 0) + # + self.createButtons(bottom_box, kw) + self.show_all() + gtk.main() + + + def initKw(self, kw): + kwdefault(kw, + strings=(_('&OK'), _('&Cancel'),), + default=0, + #resizable=1, + #font=None, + padx=10, pady=10, + #width=600, height=400, + ##~ buttonpadx=10, buttonpady=5, + ) + return MfxDialog.initKw(self, kw) + + + def done(self, button): + self.button = button.get_data('user_data') + self.player = self.player_entry.get_text() + self.confirm = self.confirm_quit_check.get_active() + self.update_stats = self.update_stats_check.get_active() + self.win_animation = False + self.quit() + + diff --git a/pysollib/pysolgtk/selecttile.py b/pysollib/pysolgtk/selecttile.py index 3239a634..0bf43a24 100644 --- a/pysollib/pysolgtk/selecttile.py +++ b/pysollib/pysolgtk/selecttile.py @@ -44,7 +44,7 @@ class SelectTileDialogWithPreview(MfxDialog): kw = self.initKw(kw) MfxDialog.__init__(self, parent, title, **kw) # - top_box, bottom_box = self.createBox() + top_box, bottom_box = self.createHBox() # if key is None: key = manager.getSelected() diff --git a/pysollib/pysolgtk/tkcanvas.py b/pysollib/pysolgtk/tkcanvas.py index 2947ecae..a66a95fa 100644 --- a/pysollib/pysolgtk/tkcanvas.py +++ b/pysollib/pysolgtk/tkcanvas.py @@ -80,6 +80,7 @@ class _CanvasItem: ##print self, 'addtag' ##~ assert isinstance(group._item, CanvasGroup) self._item.reparent(group._item) + def dtag(self, group): pass ##print self, 'dtag' @@ -88,9 +89,11 @@ class _CanvasItem: def bind(self, sequence, func, add=None): bind(self._item, sequence, func, add) + def bbox(self): ## FIXME return (0, 0, 0, 0) + def delete(self): if self._item is not None: self._item.destroy() @@ -120,15 +123,18 @@ class _CanvasItem: moveTo = move def show(self): - self._item.show() + if self._item: + self._item.show() def hide(self): - self._item.hide() + if self._item: + self._item.hide() def connect(self, signal, func, args): ##print signal self._item.connect('event', func, args) + class MfxCanvasGroup(_CanvasItem): def __init__(self, canvas): _CanvasItem.__init__(self, canvas) @@ -153,19 +159,6 @@ class MfxCanvasImage(_CanvasItem): self._item.set(pixbuf=image.pixbuf) - -## arrow = MfxCanvasLine(self.canvas, x1, y1, x2, y2, width=7, -## fill=self.app.opt.hintarrow_color, -## arrow="last", arrowshape=(30,30,10)) -## arrow = MfxCanvasLine(game.canvas, -## coords, -## {'width': w, -## 'fill': game.app.opt.hintarrow_color, -## ##'arrow': 'last', -## ##'arrowshape': (s1, s1, s2) -## } -## ) - class MfxCanvasLine(_CanvasItem): def __init__(self, canvas, *points, **kw): _CanvasItem.__init__(self, canvas) @@ -192,7 +185,6 @@ class MfxCanvasLine(_CanvasItem): canvas.show_all() - class MfxCanvasRectangle(_CanvasItem): def __init__(self, canvas, x1, y1, x2, y2, width, fill, outline): _CanvasItem.__init__(self, canvas) @@ -259,6 +251,7 @@ class MfxCanvas(gnome.canvas.Canvas): # private self.__tileimage = None self.__tiles = [] + self.__topimage = None # friend MfxCanvasText self._text_color = '#000000' # @@ -346,6 +339,14 @@ class MfxCanvas(gnome.canvas.Canvas): self.__tileimage.destroy() self.__tileimage = None + def hideAllItems(self): + for i in self._all_items: + i.hide() + + def showAllItems(self): + for i in self._all_items: + i.show() + # PySol extension def findCard(self, stack, event): # FIXME @@ -415,11 +416,13 @@ class MfxCanvas(gnome.canvas.Canvas): if not filename: return False if not self.window: # not realized yet - return False + self.realize() + ##return False self.setBackgroundImage(filename, stretch) def setBackgroundImage(self, filename, stretch): + print 'setBackgroundImage', filename width, height = self.get_size() pixbuf = gtk.gdk.pixbuf_new_from_file(filename) @@ -456,8 +459,23 @@ class MfxCanvas(gnome.canvas.Canvas): def setTopImage(self, image, cw=0, ch=0): - ## FIXME - pass + if self.__topimage: + self.__topimage.destroy() + self.__topimage = None + if not image: + return + if type(image) is str: + pixbuf = gtk.gdk.pixbuf_new_from_file(image) + else: + pixbuf = image.pixbuf + w, h = self.get_size() + iw, ih = pixbuf.get_width(), pixbuf.get_height() + x, y = (w-iw)/2, (h-ih)/2 + dx, dy = self.world_to_window(0, 0) + dx, dy = int(dx), int(dy) + self.__topimage = self.root().add(gnome.canvas.CanvasPixbuf, + pixbuf=pixbuf, x=x-dx, y=y-dy) + def update_idletasks(self): ##print 'MfxCanvas.update_idletasks' @@ -465,6 +483,7 @@ class MfxCanvas(gnome.canvas.Canvas): #self.show_now() self.update_now() + def grid(self, *args, **kw): self.top.table.attach(self, 0, 1, 2, 3, @@ -485,7 +504,7 @@ class MfxCanvas(gnome.canvas.Canvas): self.set_size_request(width, height) #self.set_size(width, height) #self.queue_resize() - gobject.idle_add(self._resize, priority=gobject.PRIORITY_HIGH_IDLE) + #gobject.idle_add(self._resize, priority=gobject.PRIORITY_HIGH_IDLE) class MfxScrolledCanvas(MfxCanvas): diff --git a/pysollib/pysolgtk/tkhtml.py b/pysollib/pysolgtk/tkhtml.py index be7d3ba6..607463d5 100644 --- a/pysollib/pysolgtk/tkhtml.py +++ b/pysollib/pysolgtk/tkhtml.py @@ -4,9 +4,13 @@ ## ## PySol -- a Python Solitaire game ## +## Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer +## Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer +## Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ## Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ## Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ## Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer +## All Rights Reserved. ## ## 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 @@ -24,19 +28,169 @@ ## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ## ## Markus F.X.J. Oberhumer -## -## http://wildsau.idv.uni-linz.ac.at/mfx/pysol.html +## +## http://www.oberhumer.com/pysol ## ##---------------------------------------------------------------------------## +__all__ = ['tkHTMLViewer'] # imports -import os, sys +import os, sys, re, types +import htmllib, formatter +import traceback + +import gtk, pango, gobject +from gtk import gdk + +if __name__ == '__main__': + d = os.path.abspath(os.path.join(sys.path[0], '..', '..')) + sys.path.append(d) + import gettext + gettext.install('pysol', d, unicode=True) # PySol imports +from pysollib.mfxutil import Struct, openURL +from pysollib.settings import PACKAGE # Toolkit imports -from tkwidget import MfxDialog +from tkutil import bind, unbind_destroy, loadImage +from tkwidget import MfxMessageDialog + + +REMOTE_PROTOCOLS = ('ftp:', 'gopher:', 'http:', 'mailto:', 'news:', 'telnet:') + + +# /*********************************************************************** +# // +# ************************************************************************/ + +class tkHTMLWriter(formatter.NullWriter): + def __init__(self, text, viewer, app): + formatter.NullWriter.__init__(self) + + self.text = text # gtk.TextBuffer + self.viewer = viewer # tkHTMLViewer + + self.anchor = None + self.anchor_mark = None + + self.font = None + self.font_mark = None + self.indent = '' + + + def write(self, data): + data = unicode(data) + self.text.insert(self.text.get_end_iter(), data, len(data)) + + def anchor_bgn(self, href, name, type): + if href: + ##self.text.update_idletasks() # update display during parsing + self.anchor = (href, name, type) + self.anchor_mark = self.text.get_end_iter().get_offset() + + def anchor_end(self): + if self.anchor: + href = self.anchor[0] + tag_name = 'href_' + href + if self.viewer.anchor_tags.has_key(tag_name): + tag = self.viewer.anchor_tags[tag_name][0] + else: + tag = self.text.create_tag(tag_name, foreground='blue', + underline=pango.UNDERLINE_SINGLE) + self.viewer.anchor_tags[tag_name] = (tag, href) + tag.connect('event', self.viewer.anchor_event, href) + u = self.viewer.normurl(href, with_protocol=False) + if u in self.viewer.visited_urls: + tag.set_property('foreground', '#660099') + start = self.text.get_iter_at_offset(self.anchor_mark) + end = self.text.get_end_iter() + ##print 'apply_tag href >>', start.get_offset(), end.get_offset() + self.text.apply_tag(tag, start, end) + + self.anchor = None + + def new_font(self, font): + # end the current font + if self.font: + ##print 'end_font(%s)' % `self.font` + start = self.text.get_iter_at_offset(self.font_mark) + end = self.text.get_end_iter() + ##print 'apply_tag font >>', start.get_offset(), end.get_offset() + self.text.apply_tag_by_name(self.font, start, end) + self.font = None + # start the new font + if font: + ##print 'start_font(%s)' % `font` + self.font_mark = self.text.get_end_iter().get_offset() + if self.viewer.fontmap.has_key(font[0]): + self.font = font[0] + elif font[3]: + self.font = 'pre' + elif font[2]: + self.font = 'bold' + elif font[1]: + self.font = 'italic' + else: + self.font = None + + def new_margin(self, margin, level): + self.indent = ' ' * level + + def send_label_data(self, data): + ##self.write(self.indent + data + ' ') + self.write(self.indent) + if data == '*': #
  • + img = self.viewer.symbols_img.get('disk') + if img: + self.text.insert_pixbuf(self.text.get_end_iter(), img) + else: + self.write('*') ##unichr(0x2022) + else: + self.write(data) + self.write(' ') + + def send_paragraph(self, blankline): + self.write('\n' * blankline) + + def send_line_break(self): + self.write('\n') + + def send_hor_rule(self, *args): + ##~ width = int(int(self.text['width']) * 0.9) + width = 70 + self.write('_' * width) + self.write('\n') + + def send_literal_data(self, data): + self.write(data) + + def send_flowing_data(self, data): + self.write(data) + + +# /*********************************************************************** +# // +# ************************************************************************/ + +class tkHTMLParser(htmllib.HTMLParser): + def anchor_bgn(self, href, name, type): + self.formatter.flush_softspace() + htmllib.HTMLParser.anchor_bgn(self, href, name, type) + self.formatter.writer.anchor_bgn(href, name, type) + + def anchor_end(self): + if self.anchor: + self.anchor = None + self.formatter.writer.anchor_end() + + def do_dt(self, attrs): + self.formatter.end_paragraph(1) + self.ddpop() + + def handle_image(self, src, alt, ismap, align, width, height): + self.formatter.writer.viewer.showImage(src, alt, ismap, align, width, height) # /*********************************************************************** @@ -44,13 +198,397 @@ from tkwidget import MfxDialog # ************************************************************************/ class tkHTMLViewer: - symbols_fn = {} + symbols_fn = {} # filenames, loaded in Application.loadImages3 + symbols_img = {} - def __init__(self, parent): + def __init__(self, parent, app=None, home=None): self.parent = parent - self.parent.wm_deiconify() + self.app = app + self.home = home + self.url = None + self.history = Struct( + list = [], + index = 0, + ) + self.visited_urls = [] + self.images = {} + self.anchor_tags = {} - def display(self, url, add=1, relpath=1): - pass + # create buttons + button_width = 8 + vbox = gtk.VBox() + parent.table.attach(vbox, + 0, 1, 0, 1, + gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL | gtk.SHRINK, + 0, 0) + + buttons_box = gtk.HBox() + vbox.pack_start(buttons_box, fill=True, expand=False) + for name, label, callback in ( + ('homeButton', _('Index'), self.goHome), + ('backButton', _('Back'), self.goBack), + ('forwardButton', _('Forward'), self.goForward), + ('closeButton', _('Close'), self.destroy) ): + button = gtk.Button(label) + button.show() + button.connect('clicked', callback) + buttons_box.pack_start(button, fill=True, expand=False) + button.set_property('can-focus', False) + setattr(self, name, button) + + # create text widget + self.textview = gtk.TextView() + self.textview.show() + self.textview.set_left_margin(10) + self.textview.set_right_margin(10) + self.textview.set_cursor_visible(False) + self.textview.set_editable(False) + self.textview.set_wrap_mode(gtk.WRAP_WORD) + self.textbuffer = self.textview.get_buffer() + + sw = gtk.ScrolledWindow() + sw.set_property('hscrollbar-policy', gtk.POLICY_AUTOMATIC) + sw.set_property('vscrollbar-policy', gtk.POLICY_AUTOMATIC) + sw.set_property('border-width', 0) + sw.add(self.textview) + sw.show() + vbox.pack_start(sw, fill=True, expand=True) + self.vadjustment = sw.get_vadjustment() + self.hadjustment = sw.get_hadjustment() + + # statusbar + self.statusbar = gtk.Statusbar() + self.statusbar.show() + vbox.pack_start(self.statusbar, fill=True, expand=False) + + # load images + for name, fn in self.symbols_fn.items(): + self.symbols_img[name] = self.getImage(fn) + + # bindings + parent.connect('key-press-event', self.key_press_event) + parent.connect('destroy', self.destroy) + self.textview.connect('motion-notify-event', self.motion_notify_event) + self.textview.connect('leave-notify-event', self.leave_event) + self.textview.connect('enter-notify-event', self.motion_notify_event) + + self._changed_cursor = False + + self.createFontMap() + + # cursor + self.defcursor = gdk.XTERM + self.handcursor = gdk.HAND2 + ##self.textview.realize() + ##window = self.textview.get_window(gtk.TEXT_WINDOW_TEXT) + ##window.set_cursor(gdk.Cursor(self.defcursor)) + + parent.set_default_size(600, 440) + parent.show_all() + + + def motion_notify_event(self, widget, event): + x, y, _ = widget.window.get_pointer() + x, y = widget.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, x, y) + tags = widget.get_iter_at_location(x, y).get_tags() + is_over_anchor = False + for tag, href in self.anchor_tags.values(): + if tag in tags: + is_over_anchor = True + break + if is_over_anchor: + if not self._changed_cursor: + ##print 'set cursor hand' + window = widget.get_window(gtk.TEXT_WINDOW_TEXT) + window.set_cursor(gdk.Cursor(self.handcursor)) + self._changed_cursor = True + self.statusbar.pop(0) + href = url = self.normurl(href) + self.statusbar.push(0, href) + else: + if self._changed_cursor: + ##print 'set cursor xterm' + window = widget.get_window(gtk.TEXT_WINDOW_TEXT) + window.set_cursor(gdk.Cursor(self.defcursor)) + self._changed_cursor = False + self.statusbar.pop(0) + return False + + def leave_event(self, widget, event): + if self._changed_cursor: + ##print 'set cursor xterm' + window = widget.get_window(gtk.TEXT_WINDOW_TEXT) + window.set_cursor(gdk.Cursor(self.defcursor)) + self._changed_cursor = False + self.statusbar.pop(0) + + def anchor_event(self, tag, textview, event, iter, href): + #print 'anchor_event:', args + if event.type == gdk.BUTTON_PRESS and event.button == 1: + self.updateHistoryXYView() + self.display(href) + return True + return False + + def key_press_event(self, w, e): + if gdk.keyval_name(e.keyval) == 'Escape': + self.destroy() + + + def createFontMap(self): + try: ## if app + default_font = self.app.getFont('sans') + fixed_font = self.app.getFont('fixed') + except: + traceback.print_exc() + default_font = ('times new roman', 12) + fixed_font = ('courier', 12) + size = default_font[1] + sign = 1 + if size < 0: sign = -1 + self.fontmap = { + 'h1' : (default_font[0], size + 12*sign, 'bold'), + 'h2' : (default_font[0], size + 8*sign, 'bold'), + 'h3' : (default_font[0], size + 6*sign, 'bold'), + 'h4' : (default_font[0], size + 4*sign, 'bold'), + 'h5' : (default_font[0], size + 2*sign, 'bold'), + 'h6' : (default_font[0], size + 1*sign, 'bold'), + 'bold' : (default_font[0], size, 'bold'), + } + + for tag_name in self.fontmap.keys(): + font = self.fontmap[tag_name] + font = font[0]+' '+str(font[1]) + tag = self.textbuffer.create_tag(tag_name, font=font) + tag.set_property('weight', pango.WEIGHT_BOLD) + + font = font[0]+' '+str(font[1]) + tag = self.textbuffer.create_tag('italic', style=pango.STYLE_ITALIC) + self.fontmap['italic'] = (font[0], size, 'italic') + font = fixed_font[0]+' '+str(fixed_font[1]) + self.textbuffer.create_tag('pre', font=font) + self.fontmap['pre'] = fixed_font + # set default font + fd = pango.FontDescription(default_font[0]+' '+str(default_font[1])) + if 'bold' in default_font: + fd.set_weight(pango.WEIGHT_BOLD) + if 'italic' in default_font: + fd.set_style(pango.STYLE_ITALIC) + self.textview.modify_font(fd) + + def destroy(self, *event): + self.parent.destroy() + self.parent = None + + def get_position(self): + pos = self.hadjustment.get_value(), self.vadjustment.get_value() + return pos + + def set_position(self, pos): + def callback(pos, hadj, vadj): + hadj.set_value(pos[0]) + vadj.set_value(pos[1]) + gobject.idle_add(callback, pos, self.hadjustment, self.vadjustment) + + # locate a file relative to the current self.url + def basejoin(self, url, baseurl=None, relpath=1): + if baseurl is None: + baseurl = self.url + if 0: + import urllib + url = urllib.pathname2url(url) + if relpath and self.url: + url = urllib.basejoin(baseurl, url) + else: + url = os.path.normpath(url) + if relpath and baseurl and not os.path.isabs(url): + h1, t1 = os.path.split(url) + h2, t2 = os.path.split(baseurl) + if h1 != h2: + url = os.path.join(h2, h1, t1) + url = os.path.normpath(url) + return url + + def normurl(self, url, with_protocol=True): + for p in REMOTE_PROTOCOLS: + if url.startswith(p): + break + else: + url = self.basejoin(url) + if with_protocol: + if os.name == 'nt': + url = url.replace('\\', '/') + url = 'file://'+url + return url + + def openfile(self, url): + if url[-1:] == '/' or os.path.isdir(url): + url = os.path.join(url, 'index.html') + url = os.path.normpath(url) + return open(url, 'rb'), url + + def display(self, url, add=1, relpath=1, position=(0,0)): + ##print 'display:', url, position + # for some reason we have to stop the PySol demo + # (is this a multithread problem with Tkinter ?) + try: + ##self.app.game.stopDemo() + ##self.app.game._cancelDrag() + pass + except: + pass + + # ftp: and http: would work if we use urllib, but this widget is + # far too limited to display anything but our documentation... + for p in REMOTE_PROTOCOLS: + if url.startswith(p): + if not openURL(url): + self.errorDialog(PACKAGE + _('''HTML limitation: +The %s protocol is not supported yet. + +Please use your standard web browser +to open the following URL: +%s +''') % (p, url)) + return + + # locate the file relative to the current url + url = self.basejoin(url, relpath=relpath) + + # read the file + try: + file = None + if 0: + import urllib + file = urllib.urlopen(url) + else: + file, url = self.openfile(url) + data = file.read() + file.close() + file = None + except Exception, ex: + if file: file.close() + self.errorDialog(_('Unable to service request:\n') + url + '\n\n' + str(ex)) + return + except: + if file: file.close() + self.errorDialog(_('Unable to service request:\n') + url) + return + + self.url = url + if self.home is None: + self.home = self.url + if add: + self.addHistory(self.url, position=position) + + ##print self.history.index, self.history.list + if self.history.index > 1: + self.backButton.set_sensitive(True) + else: + self.backButton.set_sensitive(False) + if self.history.index < len(self.history.list): + self.forwardButton.set_sensitive(True) + else: + self.forwardButton.set_sensitive(False) + + start, end = self.textbuffer.get_bounds() + self.textbuffer.delete(start, end) + + writer = tkHTMLWriter(self.textbuffer, self, self.app) + fmt = formatter.AbstractFormatter(writer) + parser = tkHTMLParser(fmt) + parser.feed(data) + parser.close() + + self.set_position(position) + + self.parent.set_title(parser.title) + + + def addHistory(self, url, position=(0,0)): + if not url in self.visited_urls: + self.visited_urls.append(url) + if self.history.index > 0: + u, pos = self.history.list[self.history.index-1] + if u == url: + self.updateHistoryXYView() + return + del self.history.list[self.history.index : ] + self.history.list.append((url, position)) + self.history.index = self.history.index + 1 + + def updateHistoryXYView(self): + if self.history.index > 0: + url, position = self.history.list[self.history.index-1] + position = self.get_position() + self.history.list[self.history.index-1] = (url, position) + + def goBack(self, *event): + if self.history.index > 1: + self.updateHistoryXYView() + self.history.index = self.history.index - 1 + url, position = self.history.list[self.history.index-1] + self.display(url, add=0, relpath=0, position=position) + + def goForward(self, *event): + if self.history.index < len(self.history.list): + self.updateHistoryXYView() + url, position = self.history.list[self.history.index] + self.history.index = self.history.index + 1 + self.display(url, add=0, relpath=0, position=position) + + def goHome(self, *event): + if self.home and self.home != self.url: + self.updateHistoryXYView() + self.display(self.home, relpath=0) + + def errorDialog(self, msg): + d = MfxMessageDialog(self.parent, title=PACKAGE+' HTML Problem', + text=msg, bitmap='warning', + strings=(_('&OK'),), default=0) + + def getImage(self, fn): + if self.images.has_key(fn): + return self.images[fn] + try: + img = gdk.pixbuf_new_from_file(fn) + except: + img = None + self.images[fn] = img + return img + + def showImage(self, src, alt, ismap, align, width, height): + url = self.basejoin(src) + img = self.getImage(url) + if img: + iter = self.textbuffer.get_end_iter() + self.textbuffer.insert_pixbuf(iter, img) + + + +# /*********************************************************************** +# // +# ************************************************************************/ + + +def tkhtml_main(args): + try: + url = args[1] + except: + url = os.path.join(os.pardir, os.pardir, 'data', 'html', 'index.html') + top = gtk.Window() + table = gtk.Table() + table.show() + top.add(table) + top.table = table + viewer = tkHTMLViewer(top) + viewer.app = None + viewer.display(url) + top.connect('destroy', lambda w: gtk.main_quit()) + gtk.main() + return 0 + +if __name__ == '__main__': + sys.exit(tkhtml_main(sys.argv)) diff --git a/pysollib/pysolgtk/tkwidget.py b/pysollib/pysolgtk/tkwidget.py index 3fb56765..34a7f8b8 100644 --- a/pysollib/pysolgtk/tkwidget.py +++ b/pysollib/pysolgtk/tkwidget.py @@ -60,6 +60,7 @@ class _MyDialog(gtk.Dialog): self.__dict__[name] = value def quit(self, *args): + self.status = 0 self.hide() self.destroy() gtk.main_quit() @@ -88,7 +89,6 @@ class MfxDialog(_MyDialog): if modal: setTransient(self, parent) - # settings if width > 0 or height > 0: self.set_size_request(width, height) @@ -96,14 +96,22 @@ class MfxDialog(_MyDialog): self.set_title(title) # self.connect('key-press-event', self._keyPressEvent) - self.show() - def createBox(self): - hbox = gtk.HBox(spacing=5) - hbox.set_border_width(5) - self.vbox.pack_start(hbox) - hbox.show() - return hbox, self.action_area + + def createBox(self, widget_class=gtk.HBox): + box = widget_class(spacing=5) + box.set_border_width(5) + self.vbox.pack_start(box) + box.show() + return box, self.action_area + + createHBox = createBox + + def createVBox(self): + return self.createBox(widget_class=gtk.VBox) + + def createTable(self): + return self.createBox(widget_class=gtk.Table) def createBitmaps(self, box, kw): if kw['bitmap']: @@ -133,10 +141,10 @@ class MfxDialog(_MyDialog): continue text = text.replace('&', '_') b = gtk.Button(text) - b.set_flags(gtk.CAN_DEFAULT) + b.set_property('can-default', True) if i == default: b.grab_focus() - ##~ b.grab_default() + #b.grab_default() b.set_data("user_data", i) b.connect("clicked", self.done) box.pack_start(b) @@ -181,17 +189,22 @@ class MfxMessageDialog(MfxDialog): label = gtk.Label(kw['text']) label.set_justify(gtk.JUSTIFY_CENTER) - label.xpad, label.ypad = kw['padx'], kw['pady'] + label.set_property('xpad', kw['padx']) + label.set_property('ypad', kw['pady']) top_box.pack_start(label) self.createButtons(bottom_box, kw) label.show() + self.set_position(gtk.WIN_POS_CENTER_ON_PARENT) + ##self.set_position(gtk.WIN_POS_CENTER) + + self.show_all() gtk.main() def initKw(self, kw): - if kw.has_key('bitmap'): - kwdefault(kw, width=250, height=150) + #if kw.has_key('bitmap'): + # kwdefault(kw, width=250, height=150) return MfxDialog.initKw(self, kw) @@ -260,13 +273,13 @@ class MfxSimpleEntry(_MyDialog): self.entry.grab_focus() button = gtk.Button("OK") button.connect("clicked", self.done) - button.set_flags(CAN_DEFAULT) + button.set_flags(gtk.CAN_DEFAULT) self.action_area.pack_start(button) button.show() button.grab_default() button = gtk.Button("Cancel") button.connect("clicked", self.quit) - button.set_flags(CAN_DEFAULT) + button.set_flags(gtk.CAN_DEFAULT) self.action_area.pack_start(button) button.show() diff --git a/pysollib/pysolgtk/tkwrap.py b/pysollib/pysolgtk/tkwrap.py index d0c64400..abf38f50 100644 --- a/pysollib/pysolgtk/tkwrap.py +++ b/pysollib/pysolgtk/tkwrap.py @@ -110,7 +110,7 @@ class _MfxToplevel(gtk.Window): def __init__(self, *args, **kw): gtk.Window.__init__(self, type=gtk.WINDOW_TOPLEVEL) ##~ self.style = self.get_style().copy() - self.set_style(self.style) + ##~ self.set_style(self.style) #self.vbox = gtk.VBox() #self.vbox.show() #self.add(self.vbox) @@ -145,7 +145,7 @@ class _MfxToplevel(gtk.Window): print "Toplevel configure:", k, v raise AttributeError, k if height > 0 and width > 0: - print 'configure: size:', width, height + ##print 'configure: size:', width, height ## FIXME #self.set_default_size(width, height) #self.set_size_request(width, height) @@ -202,12 +202,15 @@ class _MfxToplevel(gtk.Window): def wm_geometry(self, newGeometry=None): ##print 'wm_geometry', newGeometry - if newGeometry == '': - self.reshow_with_initial_size() + print 'allow_shrink:', self.allow_shrink + if not newGeometry: + pass + ##self.reshow_with_initial_size() ##self.resize(1, 1) else: - w, h = newGeometry - self.resize(w, h) + pass + ##w, h = newGeometry + ##self.resize(w, h) @@ -227,7 +230,8 @@ class _MfxToplevel(gtk.Window): ##~ self.set_icon_name(name) def wm_minsize(self, width, height): - self.set_geometry_hints(min_width=width, min_height=height) + pass + ##~ self.set_geometry_hints(min_width=width, min_height=height) def wm_protocol(self, name=None, func=None): if name == 'WM_DELETE_WINDOW': diff --git a/pysollib/pysolgtk/toolbar.py b/pysollib/pysolgtk/toolbar.py index 92053988..046a39f3 100644 --- a/pysollib/pysolgtk/toolbar.py +++ b/pysollib/pysolgtk/toolbar.py @@ -57,31 +57,31 @@ class PysolToolbar(PysolToolbarActions): self.toolbar = gtk.Toolbar(gtk.ORIENTATION_HORIZONTAL, gtk.TOOLBAR_ICONS) - #self.bg = top.get_style().bg[gtk.STATE_NORMAL] ui_info = ''' - - - + + + - - + + - - - + + + + - - + + - + ''' ui_manager = self.top.ui_manager # created in menubar.py ui_manager_id = ui_manager.add_ui_from_string(ui_info) - toolbar = ui_manager.get_widget("/ToolBar") + toolbar = ui_manager.get_widget("/toolbar") toolbar.set_tooltips(True) toolbar.set_style(gtk.TOOLBAR_ICONS) toolbar.show() @@ -93,36 +93,6 @@ class PysolToolbar(PysolToolbarActions): toolbar.show() - - # no longer needed - self.bg = None - # - - - - # util - def _createButton(self, name, command, padx=0, stock=None, tooltip=None): - ##button = self.toolbar.append_item(name, tooltip, "", stock, command) - ##button = self.toolbar.insert_stock(stock, tooltip, '', command, None, -1) - image = gtk.Image() - image.set_from_stock(stock, gtk.ICON_SIZE_SMALL_TOOLBAR) - - button = gtk.ToolButton(None, name) - button.set_tooltip(tooltip) - #button.set_relief(gtk.RELIEF_NONE) - #button.connect('activate', command) - self.toolbar.insert(button, -1) - setattr(self, name + "_button", button) - - - def _createLabel(self, name, padx=0, side='IGNORE', tooltip=None): - ## FIXME: append_widget - pass - - def _createSeparator(self): - self.toolbar.append_space() - - # # wrappers # diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py index 62ed3d0d..56b26082 100644 --- a/pysollib/tk/menubar.py +++ b/pysollib/tk/menubar.py @@ -300,7 +300,7 @@ class PysolMenubar(PysolMenubarActions): menu.add_command(label=n_("&Clear bookmarks"), command=self.mClearBookmarks) menu.add_separator() - menu.add_command(label=n_("Restart &game"), command=self.mRestart, accelerator=m+"G") + menu.add_command(label=n_("Restart"), command=self.mRestart, accelerator=m+"G") menu = MfxMenu(self.__menubar, label=n_("&Game")) menu.add_command(label=n_("&Deal cards"), command=self.mDeal, accelerator="D") diff --git a/pysollib/tk/tkhtml.py b/pysollib/tk/tkhtml.py index 5bba1d91..5761cda0 100644 --- a/pysollib/tk/tkhtml.py +++ b/pysollib/tk/tkhtml.py @@ -232,9 +232,10 @@ class tkHTMLViewer: symbols_fn = {} # filenames, loaded in Application.loadImages3 symbols_img = {} - def __init__(self, parent): + def __init__(self, parent, app=None, home=None): self.parent = parent - self.home = None + self.app = app + self.home = home self.url = None self.history = Struct( list = [], @@ -371,11 +372,10 @@ class tkHTMLViewer: def display(self, url, add=1, relpath=1, xview=0, yview=0): # for some reason we have to stop the PySol demo # (is this a multithread problem with Tkinter ?) - if self.__dict__.get("app"): - if self.app and self.app.game: - self.app.game.stopDemo() - ##self.app.game._cancelDrag() - ##pass + if self.app and self.app.game: + self.app.game.stopDemo() + ##self.app.game._cancelDrag() + ##pass # ftp: and http: would work if we use urllib, but this widget is # far too limited to display anything but our documentation...