Annotation of previous/python-ui/uihelpers.py, revision 1.1

1.1     ! root        1: #!/usr/bin/env python
        !             2: #
        !             3: # Misc common helper classes and functions for the Hatari UI
        !             4: #
        !             5: # Copyright (C) 2008-2009 by Eero Tamminen <eerot at berlios>
        !             6: #
        !             7: # This program is free software; you can redistribute it and/or modify
        !             8: # it under the terms of the GNU General Public License as published by
        !             9: # the Free Software Foundation; either version 2 of the License, or
        !            10: # (at your option) any later version.
        !            11: #
        !            12: # This program is distributed in the hope that it will be useful,
        !            13: # but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            14: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            15: # GNU General Public License for more details.
        !            16: 
        !            17: import os
        !            18: import sys
        !            19: # use correct version of pygtk/gtk
        !            20: import pygtk
        !            21: pygtk.require('2.0')
        !            22: import gtk
        !            23: import gobject
        !            24: 
        !            25: 
        !            26: # leak debugging
        !            27: #import gc
        !            28: #gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
        !            29: 
        !            30: 
        !            31: # ---------------------
        !            32: # Hatari UI information
        !            33: 
        !            34: class UInfo:
        !            35:     """singleton constants for the UI windows,
        !            36:     one instance is needed to initialize these properly"""
        !            37:     version = "v1.0"
        !            38:     name = "Hatari UI"
        !            39:     logo = "hatari.png"
        !            40:     icon = "hatari-icon.png"
        !            41:     copyright = "UI copyright (C) 2008-2010 by Eero Tamminen"
        !            42: 
        !            43:     # path to the directory where the called script resides
        !            44:     path = os.path.dirname(sys.argv[0])
        !            45:     
        !            46:     def __init__(self, path = None):
        !            47:         "UIinfo([path]), set suitable paths for resources from CWD and path"
        !            48:         if path:
        !            49:             self.path = path
        !            50:         if not os.path.exists(UInfo.icon):
        !            51:             UInfo.icon = self._get_path(UInfo.icon)
        !            52:         if not os.path.exists(UInfo.logo):
        !            53:             UInfo.logo = self._get_path(UInfo.logo)
        !            54: 
        !            55:     def _get_path(self, filename):
        !            56:         sep = os.path.sep
        !            57:         testpath = "%s%s%s" % (self.path, sep, filename)
        !            58:         if os.path.exists(testpath):
        !            59:             return testpath
        !            60: 
        !            61: 
        !            62: # --------------------------------------------------------
        !            63: # functions for showing HTML files
        !            64: 
        !            65: class UIHelp:
        !            66:     def __init__(self):
        !            67:         """determine HTML viewer and where docs are"""
        !            68:         self._view = self.get_html_viewer()
        !            69:         self._path = self.get_doc_path()
        !            70: 
        !            71:     def get_html_viewer(self):
        !            72:         """return name of html viewer or None"""
        !            73:         path = self.get_binary_path("xdg-open")
        !            74:         if path:
        !            75:             return path
        !            76:         path = self.get_binary_path("firefox")
        !            77:         if path:
        !            78:             return path
        !            79:         return None
        !            80:         
        !            81:     def get_binary_path(self, name):
        !            82:         """return true if given binary is in path"""
        !            83:         # could also try running the binary with "--version" arg
        !            84:         # and check the exec return value
        !            85:         if os.sys.platform == "win32":
        !            86:             for i in os.environ['PATH'].split(';'):
        !            87:                 fname = os.path.join(i, name)
        !            88:                 if os.access(fname, os.X_OK) and not os.path.isdir(fname):
        !            89:                     return fname
        !            90:         else:
        !            91:             for i in os.environ['PATH'].split(':'):
        !            92:                 fname = os.path.join(i, name)
        !            93:                 if os.access(fname, os.X_OK) and not os.path.isdir(fname):
        !            94:                     return fname
        !            95:         return None
        !            96: 
        !            97:     def get_doc_path(self):
        !            98:         """return path or URL to Hatari docs or None"""
        !            99:         # first try whether there are local Hatari docs in standard place
        !           100:         # for this Hatari/UI version
        !           101:         sep = os.sep
        !           102:         path = self.get_binary_path("hatari")
        !           103:         path = sep.join(path.split(sep)[:-2]) # remove "bin/hatari"
        !           104:         path = path + sep + "share" + sep + "doc" + sep + "hatari" + sep
        !           105:         if os.path.exists(path + "manual.html"):
        !           106:             return path
        !           107:         # if not, point to latest Hatari HG version docs
        !           108:         print "WARNING: Hatari manual not found at:", path + "manual.html"
        !           109:         return "http://hg.berlios.de/repos/hatari/raw-file/tip/doc/"
        !           110: 
        !           111:     def set_mainwin(self, widget):
        !           112:         self.mainwin = widget
        !           113: 
        !           114:     def view_url(self, url, name):
        !           115:         """view given URL or file path, or error use 'name' as its name"""
        !           116:         if self._view and "://" in url or os.path.exists(url):
        !           117:             print "RUN: '%s' '%s'" % (self._view, url)
        !           118:             os.spawnlp(os.P_NOWAIT, self._view, self._view, url)
        !           119:             return
        !           120:         if not self._view:
        !           121:             msg = "Cannot view %s, HTML viewer missing" % name
        !           122:         else:
        !           123:             msg = "Cannot view %s,\n'%s' file is missing" % (name, url)
        !           124:         from dialogs import ErrorDialog
        !           125:         ErrorDialog(self.mainwin).run(msg)
        !           126: 
        !           127:     def view_hatari_manual(self, dummy=None):
        !           128:         self.view_url(self._path + "manual.html", "Hatari manual")
        !           129: 
        !           130:     def view_hatari_compatibility(self, dummy=None):
        !           131:         self.view_url(self._path + "compatibility.html", "Hatari compatibility list")
        !           132: 
        !           133:     def view_hatari_releasenotes(self, dummy=None):
        !           134:         self.view_url(self._path + "release-notes.txt", "Hatari release notes")
        !           135: 
        !           136:     def view_hatari_todo(self, dummy=None):
        !           137:         self.view_url(self._path + "todo.txt", "Hatari TODO items")
        !           138: 
        !           139:     def view_hatari_bugs(self, dummy=None):
        !           140:         self.view_url("http://developer.berlios.de/bugs/?group_id=10436", "Hatari bug tracker")
        !           141: 
        !           142:     def view_hatari_mails(self, dummy=None):
        !           143:         self.view_url("http://developer.berlios.de/mail/?group_id=10436", "Hatari mailing lists")
        !           144: 
        !           145:     def view_hatari_repository(self, dummy=None):
        !           146:         self.view_url("http://hg.berlios.de/repos/hatari", "latest Hatari changes")
        !           147: 
        !           148:     def view_hatari_authors(self, dummy=None):
        !           149:         self.view_url(self._path + "authors.txt", "Hatari authors")
        !           150: 
        !           151:     def view_hatari_page(self, dummy=None):
        !           152:         self.view_url("http://hatari.berlios.de/", "Hatari home page")
        !           153: 
        !           154:     def view_hatariui_page(self, dummy=None):
        !           155:         self.view_url("http://koti.mbnet.fi/tammat/hatari/hatari-ui.shtml", "Hatari UI home page")
        !           156: 
        !           157: 
        !           158: # --------------------------------------------------------
        !           159: # auxiliary class+callback to be used with the PasteDialog
        !           160: 
        !           161: class HatariTextInsert:
        !           162:     def __init__(self, hatari, text):
        !           163:         self.index = 0
        !           164:         self.text = text
        !           165:         self.pressed = False
        !           166:         self.hatari = hatari
        !           167:         print "OUTPUT '%s'" % text
        !           168:         gobject.timeout_add(100, _text_insert_cb, self)
        !           169: 
        !           170: # callback to insert text object to Hatari character at the time, at given interval
        !           171: def _text_insert_cb(textobj):
        !           172:     char = textobj.text[textobj.index]
        !           173:     if textobj.pressed:
        !           174:         textobj.pressed = False
        !           175:         textobj.hatari.insert_event("keyrelease %c" % char)
        !           176:         textobj.index += 1
        !           177:         if textobj.index >= len(textobj.text):
        !           178:             del(textobj)
        !           179:             return False
        !           180:     else:
        !           181:         textobj.pressed = True
        !           182:         textobj.hatari.insert_event("keypress %c" % char)
        !           183:     return True
        !           184: 
        !           185: 
        !           186: # ----------------------------
        !           187: # helper functions for buttons
        !           188: 
        !           189: def create_button(label, cb, data = None):
        !           190:     "create_button(label,cb[,data]) -> button widget"
        !           191:     button = gtk.Button(label)
        !           192:     if data == None:
        !           193:         button.connect("clicked", cb)
        !           194:     else:
        !           195:         button.connect("clicked", cb, data)
        !           196:     return button
        !           197: 
        !           198: def create_toolbutton(stock_id, cb, data = None):
        !           199:     "create_toolbutton(stock_id,cb[,data]) -> toolbar button with stock icon+label"
        !           200:     button = gtk.ToolButton(stock_id)
        !           201:     if data == None:
        !           202:         button.connect("clicked", cb)
        !           203:     else:
        !           204:         button.connect("clicked", cb, data)
        !           205:     return button
        !           206: 
        !           207: def create_toggle(label, cb, data = None):
        !           208:     "create_toggle(label,cb[,data]) -> toggle button widget"
        !           209:     button = gtk.ToggleButton(label)
        !           210:     if data == None:
        !           211:         button.connect("toggled", cb)
        !           212:     else:
        !           213:         button.connect("toggled", cb, data)
        !           214:     return button
        !           215: 
        !           216: 
        !           217: # -----------------------------
        !           218: # Table dialog helper functions
        !           219: 
        !           220: def create_table_dialog(parent, title, rows, cols, oktext = gtk.STOCK_APPLY):
        !           221:     "create_table_dialog(parent,title,rows, cols, oktext) -> (table,dialog)"
        !           222:     dialog = gtk.Dialog(title, parent,
        !           223:         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
        !           224:         (oktext,  gtk.RESPONSE_APPLY,
        !           225:         gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
        !           226: 
        !           227:     table = gtk.Table(rows, cols)
        !           228:     table.set_data("col_offset", 0)
        !           229:     table.set_col_spacings(8)
        !           230:     dialog.vbox.add(table)
        !           231:     return (table, dialog)
        !           232: 
        !           233: def table_set_col_offset(table, offset):
        !           234:     "set column offset for successive table_* ops on given table"
        !           235:     table.set_data("col_offset", offset)
        !           236: 
        !           237: def table_add_entry_row(table, row, label, size = None):
        !           238:     "table_add_entry_row(table,row,label,[entry size]) -> entry"
        !           239:     # add given label to given row in given table
        !           240:     # return entry for that line
        !           241:     label = gtk.Label(label)
        !           242:     align = gtk.Alignment(1) # right aligned
        !           243:     align.add(label)
        !           244:     col = table.get_data("col_offset")
        !           245:     table.attach(align, col, col+1, row, row+1, gtk.FILL)
        !           246:     col += 1
        !           247:     if size:
        !           248:         entry = gtk.Entry(size)
        !           249:         entry.set_width_chars(size)
        !           250:         align = gtk.Alignment(0) # left aligned (default is centered)
        !           251:         align.add(entry)
        !           252:         table.attach(align, col, col+1, row, row+1)
        !           253:     else:
        !           254:         entry = gtk.Entry()
        !           255:         table.attach(entry, col, col+1, row, row+1)
        !           256:     return entry
        !           257: 
        !           258: def table_add_widget_row(table, row, label, widget, fullspan = False):
        !           259:     "table_add_widget_row(table,row,label,widget) -> widget"
        !           260:     # add given label right aligned to given row in given table
        !           261:     # add given widget to the right column and returns it
        !           262:     # return entry for that line
        !           263:     if fullspan:
        !           264:         col = 0
        !           265:     else:
        !           266:         col = table.get_data("col_offset")
        !           267:     if label:
        !           268:         label = gtk.Label(label)
        !           269:         align = gtk.Alignment(1)
        !           270:         align.add(label)
        !           271:         table.attach(align, col, col+1, row, row+1, gtk.FILL)
        !           272:     if fullspan:
        !           273:         col = table.get_data("col_offset")
        !           274:         table.attach(widget, 1, col+2, row, row+1)
        !           275:     else:
        !           276:         table.attach(widget, col+1, col+2, row, row+1)
        !           277:     return widget
        !           278: 
        !           279: def table_add_radio_rows(table, row, label, texts, cb = None):
        !           280:     "table_add_radio_rows(table,row,label,texts[,cb]) -> [radios]"
        !           281:     # - add given label right aligned to given row in given table
        !           282:     # - create/add radio buttons with given texts to next row, set
        !           283:     #   the one given as "active" as active and set 'cb' as their
        !           284:     #   "toggled" callback handler
        !           285:     # - return array or radiobuttons
        !           286:     label = gtk.Label(label)
        !           287:     align = gtk.Alignment(1)
        !           288:     align.add(label)
        !           289:     col = table.get_data("col_offset")
        !           290:     table.attach(align, col, col+1, row, row+1, gtk.FILL)
        !           291: 
        !           292:     radios = []
        !           293:     radio = None
        !           294:     box = gtk.VBox()
        !           295:     for text in texts:
        !           296:         radio = gtk.RadioButton(radio, text)
        !           297:         if cb:
        !           298:             radio.connect("toggled", cb, text)
        !           299:         radios.append(radio)
        !           300:         box.add(radio)
        !           301:     table.attach(box, col+1, col+2, row, row+1)
        !           302:     return radios
        !           303: 
        !           304: def table_add_separator(table, row):
        !           305:     "table_add_separator(table,row)"
        !           306:     widget = gtk.HSeparator()
        !           307:     endcol = table.get_data("n-columns")
        !           308:     # separator for whole table width
        !           309:     table.attach(widget, 0, endcol, row, row+1, gtk.FILL)
        !           310: 
        !           311: 
        !           312: # -----------------------------
        !           313: # File selection helpers
        !           314: 
        !           315: def get_open_filename(title, parent, path = None):
        !           316:     buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        !           317:     fsel = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_OPEN, buttons)
        !           318:     fsel.set_local_only(True)
        !           319:     if path:
        !           320:         fsel.set_filename(path)
        !           321:     if fsel.run() == gtk.RESPONSE_OK:
        !           322:         filename = fsel.get_filename()
        !           323:     else:
        !           324:         filename = None
        !           325:     fsel.destroy()
        !           326:     return filename
        !           327: 
        !           328: def get_save_filename(title, parent, path = None):
        !           329:     buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        !           330:     fsel = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_SAVE, buttons)
        !           331:     fsel.set_local_only(True)
        !           332:     fsel.set_do_overwrite_confirmation(True)
        !           333:     if path:
        !           334:         fsel.set_filename(path)
        !           335:         if not os.path.exists(path):
        !           336:             # above set only folder, this is needed to set
        !           337:             # the file name when the file doesn't exist
        !           338:             fsel.set_current_name(os.path.basename(path))
        !           339:     if fsel.run() == gtk.RESPONSE_OK:
        !           340:         filename = fsel.get_filename()
        !           341:     else:
        !           342:         filename = None
        !           343:     fsel.destroy()
        !           344:     return filename
        !           345: 
        !           346: # Gtk is braindead, there's no way to set a default filename
        !           347: # for file chooser button unless it already exists
        !           348: # - set_filename() works only for files that already exist
        !           349: # - set_current_name() works only for SAVE action,
        !           350: #   but file chooser button doesn't support that
        !           351: # i.e. I had to do my own (less nice) container widget...
        !           352: class FselEntry():
        !           353:     def __init__(self, parent, validate = None, data = None):
        !           354:         self._parent = parent
        !           355:         self._validate = validate
        !           356:         self._validate_data = data
        !           357:         entry = gtk.Entry()
        !           358:         entry.set_width_chars(12)
        !           359:         entry.set_editable(False)
        !           360:         hbox = gtk.HBox()
        !           361:         hbox.add(entry)
        !           362:         button = create_button("Select...", self._select_file_cb)
        !           363:         hbox.pack_start(button, False, False)
        !           364:         self._entry = entry
        !           365:         self._hbox = hbox
        !           366:     
        !           367:     def _select_file_cb(self, widget):
        !           368:         fname = self._entry.get_text()
        !           369:         while True:
        !           370:             fname = get_save_filename("Select file", self._parent, fname)
        !           371:             if not fname:
        !           372:                 # assume cancel
        !           373:                 return
        !           374:             if self._validate:
        !           375:                 # filename needs validation and is valid?
        !           376:                 if not self._validate(self._validate_data, fname):
        !           377:                     continue
        !           378:             self._entry.set_text(fname)
        !           379:             return
        !           380:     
        !           381:     def set_filename(self, fname):
        !           382:         self._entry.set_text(fname)
        !           383:         
        !           384:     def get_filename(self):
        !           385:         return self._entry.get_text()
        !           386: 
        !           387:     def get_container(self):
        !           388:         return self._hbox

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.