home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / dialogs.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  12.1 KB  |  310 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. """Handle dialog popups.
  19.  
  20. Simple Choices:
  21.     For dialogs where you just want to ask the user a question use the
  22.     ChoiceDialog class.  Pass it a title, description and a the buttons to
  23.     display.  The call dialog.run, passing it a callback.  Here's an example:
  24.  
  25.     dialog = dialog.ChoiceDialog("Do you like pizza?",
  26.         "Democracy would like to know if you enjoy eating pizza.",
  27.         dialog.BUTTON_YES, dialog.BUTTON_NO)
  28.     def handlePizzaAnswer(dialog):
  29.         if dialog.choice is None:
  30.             # handle the user closing the dialog windows
  31.         elif dialog.choice == dialog.BUTTON_YES:
  32.            # handle yes response
  33.         elif dialog.choice == dialag.BUTTON_NO:
  34.             # handle no respnose
  35.     dialog.run(handlePizzaAnswer)
  36.  
  37. Advanced usage:
  38.     For more advanced usage, check out the other Dialog subclasses.  They will
  39.     probably have different constructor arguments and may have attributes
  40.     other than choice that will be set.  For example, the HTTPAuthDialog has a
  41.     "username" and "password" attribute that store what the user entered in
  42.     the textboxes.
  43.  
  44. Frontend requirements:
  45.     Frontends should implement the runDialog method in UIBackendDelegate
  46.     class.  It inputs a Dialog subclass and displays it to the user.  When the
  47.     user clicks on a button, or closes the dialog window, the frontend must
  48.     call dialog.runCallback().
  49.  
  50.     As we add new dialog boxes, the frontend may run into Dialog subclasses
  51.     that it doesn't recognize.  In that case, call dialog.runCallback(None).
  52.  
  53.     The frontend can layout the window however it wants, in particular buttons
  54.     can be arranged with the default on the right or the left depending on the
  55.     platform (The default button is the 1st button in the list).  Frontends
  56.     should try to recognize standard buttons and display the stock icons for
  57.     them.  
  58.     """
  59.  
  60. import eventloop
  61. from gtcache import gettext as _
  62.  
  63. # Pass in a connection to the frontend
  64. def setDelegate(newDelegate):
  65.     global delegate
  66.     delegate = newDelegate
  67.  
  68. class DialogButton(object):
  69.     def __init__(self, text):
  70.         self.text = text
  71.     def __eq__(self, other):
  72.         return isinstance(other, DialogButton) and self.text == other.text
  73.     def __str__(self):
  74.         return "DialogButton(%r)" % self.text
  75.  
  76. BUTTON_OK = DialogButton(_("Ok"))
  77. BUTTON_CANCEL = DialogButton(_("Cancel"))
  78. BUTTON_YES = DialogButton(_("Yes"))
  79. BUTTON_NO = DialogButton(_("No"))
  80. BUTTON_QUIT = DialogButton(_("Quit"))
  81. BUTTON_IGNORE = DialogButton(_("Ignore"))
  82. BUTTON_SUBMIT_REPORT = DialogButton(_("Submit Crash Report"))
  83. BUTTON_MIGRATE = DialogButton(_("Migrate"))
  84. BUTTON_DONT_MIGRATE = DialogButton(_("Don't Migrate"))
  85. BUTTON_DOWNLOAD = DialogButton(_("Download"))
  86. BUTTON_REMOVE_ENTRY = DialogButton(_("Remove Entry"))
  87. BUTTON_DELETE_FILE = DialogButton(_("Delete File"))
  88. BUTTON_DELETE_FILES = DialogButton(_("Delete Files"))
  89. BUTTON_KEEP_VIDEOS = DialogButton(_("Keep Videos"))
  90. BUTTON_DELETE_VIDEOS = DialogButton(_("Delete Videos"))
  91. BUTTON_CREATE = DialogButton(_("Create"))
  92. BUTTON_CREATE_CHANNEL = DialogButton(_("Create Channel"))
  93. BUTTON_ADD = DialogButton(_("Add"))
  94. BUTTON_ADD_INTO_NEW_FOLDER = DialogButton(_("Add Into New Folder"))
  95. BUTTON_KEEP = DialogButton(_("Keep"))
  96. BUTTON_DELETE = DialogButton(_("Delete"))
  97. BUTTON_NOT_NOW = DialogButton(_("Not Now"))
  98. BUTTON_CLOSE_TO_TRAY = DialogButton(_("Close to Tray"))
  99. BUTTON_LAUNCH_MIRO = DialogButton(_("Launch Miro"))
  100. BUTTON_DOWNLOAD_ANYWAY = DialogButton(_("Download Anyway"))
  101.  
  102. class Dialog(object):
  103.     """Abstract base class for dialogs."""
  104.  
  105.     def __init__(self, title, description, buttons):
  106.         self.title = title
  107.         self.description = description
  108.         self.buttons = buttons
  109.  
  110.     def run(self, callback):
  111.         self.callback = callback
  112.         self.choice = None
  113.         delegate.runDialog(self)
  114.  
  115.     def runCallback(self, choice):
  116.         """Run the callback for this dialog.  Choice should be the button that
  117.         the user clicked, or None if the user closed the window without
  118.         makeing a selection.
  119.         """
  120.  
  121.         self.choice = choice
  122.         eventloop.addUrgentCall(self.callback, "%s callback" % self.__class__, 
  123.                 args=(self,))
  124.  
  125. class MessageBoxDialog(Dialog):
  126.     """Show the user some info in a dialog box.  The only button is Okay.  The
  127.     callback is optional for a message box dialog.  """
  128.  
  129.     def __init__(self, title, description):
  130.         Dialog.__init__(self, title, description, [BUTTON_OK])
  131.  
  132.     def run(self, callback=None):
  133.         Dialog.run(self, callback)
  134.  
  135.     def runCallback(self, choice):
  136.         if self.callback is not None:
  137.             Dialog.runCallback(self, choice)
  138.  
  139. class ChoiceDialog(Dialog):
  140.     """Give the user a choice of 2 options (Yes/No, Ok/Cancel,
  141.     Migrate/Don't Migrate, etc.)
  142.     """
  143.  
  144.     def __init__(self, title, description, defaultButton, otherButton):
  145.         super(ChoiceDialog, self).__init__(title, description,
  146.                 [defaultButton, otherButton])
  147.  
  148. class ThreeChoiceDialog(Dialog):
  149.     """Give the user a choice of 3 options (e.g. Remove entry/
  150.     Delete file/Cancel).
  151.     """
  152.  
  153.     def __init__(self, title, description, defaultButton, secondButton,
  154.             thirdButton):
  155.         super(ThreeChoiceDialog, self).__init__(title, description,
  156.                 [defaultButton, secondButton, thirdButton])
  157.  
  158. class HTTPAuthDialog(Dialog):
  159.     """Ask for a username and password for HTTP authorization.  Frontends
  160.     should create a dialog with text entries for a username and password.  Use
  161.     prefillUser and prefillPassword for the initial values of the entries.
  162.  
  163.     The buttons are always BUTTON_OK and BUTTON_CANCEL.
  164.     """
  165.  
  166.     def __init__(self, url, realm, prefillUser=None, prefillPassword=None):
  167.         desc = 'location %s requires a username and password for "%s".'  % \
  168.                 (url, realm)
  169.         super(HTTPAuthDialog, self).__init__("Login Required", desc,
  170.                 (BUTTON_OK, BUTTON_CANCEL))
  171.         self.prefillUser = prefillUser
  172.         self.prefillPassword = prefillPassword
  173.  
  174.     def runCallback(self, choice, username='', password=''):
  175.         self.username = username
  176.         self.password = password
  177.         super(HTTPAuthDialog, self).runCallback(choice)
  178.  
  179. class SearchChannelDialog(Dialog):
  180.     """Ask for information to create a new search channel.  Frontends
  181.     should create a dialog with all sorts of fields.  Use the given
  182.     values for the initial values and replace the values if the user
  183.     selects anything.
  184.  
  185.     The buttons are always BUTTON_CREATE_CHANNEL and BUTTON_CANCEL.
  186.     """
  187.     CHANNEL = 0
  188.     ENGINE = 1
  189.     URL = 2
  190.     def __init__(self, term=None, style=CHANNEL, location=None):
  191.         import views
  192.         import indexes
  193.         import util
  194.         from feed import RSSFeedImpl, ScraperFeedImpl
  195.         self.term = term
  196.         self.style = style
  197.         self.location = location
  198.  
  199.         self.channels = []
  200.  
  201.         def shorten(s):
  202.             if len(s) > 50:
  203.                 return s[:50] + u'...'
  204.             return s
  205.  
  206.         for feed in views.feeds:
  207.             if feed.actualFeed.__class__ in (RSSFeedImpl, ScraperFeedImpl):
  208.                 self.channels.append((feed.id, shorten(feed.getTitle())))
  209.  
  210.         self.engines = []
  211.         for engine in views.searchEngines:
  212.             self.engines.append((engine.name, engine.title))
  213.  
  214.         # For testing
  215.         if style == self.CHANNEL and self.location == -1:
  216.             self.location = self.channels[2][0]
  217.  
  218.         searchFeed = util.getSingletonDDBObject (views.feeds.filterWithIndex(indexes.feedsByURL, 'dtv:search'))
  219.         self.defaultEngine = searchFeed.lastEngine
  220.         
  221.         super(SearchChannelDialog, self).__init__(_("New Search Channel"), _("A search channel contains items that match a search term."),
  222.                 (BUTTON_CREATE_CHANNEL, BUTTON_CANCEL))
  223.  
  224.     def getURL(self):
  225.         from xhtmltools import urlencode
  226.         import searchengines
  227.         from database import defaultDatabase
  228.  
  229.         term = self.term
  230.         location = self.location
  231.         style = self.style
  232.         
  233.         if not term or not location:
  234.             return None
  235.  
  236.         if style == self.CHANNEL:
  237.             # Pull data from channel and switch style.
  238.             channel = defaultDatabase.getObjectByID(location)
  239.             if channel:
  240.                 style = self.URL
  241.                 location = channel.getBaseURL()
  242.  
  243.                 searchTerm = channel.getSearchTerm()
  244.                 if searchTerm is not None:
  245.                     term = searchTerm + " " + term
  246.  
  247.         # Do this after possibly pulling data from channel.
  248.         if type (term) == unicode:
  249.             term = term.encode("utf8")
  250.         if type (location) == unicode:
  251.             location = location.encode("utf8")
  252.  
  253.         if style == self.ENGINE:
  254.             return searchengines.getRequestURL (location, term)
  255.  
  256.         if style == self.URL:
  257.             return "dtv:searchTerm:%s?%s" % (urlencode(location), urlencode(term))
  258.  
  259.         return None
  260.  
  261. class TextEntryDialog(Dialog):
  262.     """Like the ChoiceDialog, but also contains a textbox for the user to
  263.     enter a value into.  This is used for things like the create playlist
  264.     dialog, the rename dialog, etc.
  265.     """
  266.  
  267.     def __init__(self, title, description, defaultButton, otherButton, prefillCallback=None, fillWithClipboardURL=False):
  268.         super(TextEntryDialog, self).__init__(title, description,
  269.                 [defaultButton, otherButton])
  270.         self.prefillCallback = prefillCallback
  271.         self.fillWithClipboardURL = fillWithClipboardURL
  272.  
  273.     def runCallback(self, choice, value=None):
  274.         self.value = value
  275.         super(TextEntryDialog, self).runCallback(choice)
  276.  
  277. class CheckboxDialog(Dialog):
  278.     """Like the ChoiceDialog, but also contains a checkbox for the user to
  279.     enter a value into.  This is used for things like asking whether to show
  280.     the dialog again.  There's also a mesage for the checkbox and an initial
  281.     value.
  282.     """
  283.  
  284.     def __init__(self, title, description, checkbox_text, checkbox_value, defaultButton, otherButton):
  285.         super(CheckboxDialog, self).__init__(title, description,
  286.                 [defaultButton, otherButton])
  287.         self.checkbox_text = checkbox_text
  288.         self.checkbox_value = checkbox_value
  289.  
  290.     def runCallback(self, choice, checkbox_value=False):
  291.         self.checkbox_value = checkbox_value
  292.         super(CheckboxDialog, self).runCallback(choice)
  293.  
  294. class CheckboxTextboxDialog(CheckboxDialog):
  295.     """Like CheckboxDialog but also with a text area. Used for
  296.     capturing bug report data"""
  297.  
  298.     def __init__(self, title, description, checkbox_text,
  299.         checkbox_value, textbox_value, defaultButton, otherButton):
  300.         super(CheckboxTextboxDialog, self).__init__(title, description,
  301.                                                     checkbox_text,
  302.                                                     checkbox_value,
  303.                                                     defaultButton,
  304.                                                     otherButton)
  305.         self.textbox_value = textbox_value
  306.  
  307.     def runCallback(self, choice, checkbox_value=False, textbox_value=""):
  308.         self.textbox_value = textbox_value
  309.         super(CheckboxTextboxDialog, self).runCallback(choice, checkbox_value)
  310.