summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/wx-3.0-msw/wx/tools/Editra/src/plugdlg.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/wx-3.0-msw/wx/tools/Editra/src/plugdlg.py')
-rw-r--r--lib/python2.7/site-packages/wx-3.0-msw/wx/tools/Editra/src/plugdlg.py1206
1 files changed, 1206 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/tools/Editra/src/plugdlg.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/tools/Editra/src/plugdlg.py
new file mode 100644
index 0000000..f4a6888
--- /dev/null
+++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/tools/Editra/src/plugdlg.py
@@ -0,0 +1,1206 @@
+###############################################################################
+# Name: plugdlg.py #
+# Purpose: User interface into the PluginManager, also provides interface for #
+# downloading and installing plugins. #
+# Author: Cody Precord <cprecord@editra.org> #
+# Copyright: (c) 2008 Cody Precord <staff@editra.org> #
+# License: wxWindows License #
+###############################################################################
+
+"""
+Provides a dialog for downloading, installing and configuring plugins or Editra.
+
+@todo: refactor list population and list item creation
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__cvsid__ = "$Id: plugdlg.py 70229 2012-01-01 01:27:10Z CJP $"
+__revision__ = "$Revision: 70229 $"
+
+#-----------------------------------------------------------------------------#
+# Imports
+import os
+import sys
+import re
+import urllib2
+import wx
+import wx.lib.delayedresult as delayedresult
+
+# Local Imports
+import ed_glob
+import plugin
+import ed_event
+import ed_msg
+import util
+import ed_txt
+from profiler import Profile_Get, Profile_Set
+import ed_basewin
+import eclib
+
+#-----------------------------------------------------------------------------#
+# Globals
+
+CONFIG_PG = 0
+DOWNLOAD_PG = 1
+INSTALL_PG = 2
+PY_VER = str(sys.version_info[0]) + str(sys.version_info[1])
+PLUGIN_REPO = "http://editra.org/plugins.php?list=True&py=" + PY_VER
+
+# Panel Display Modes
+MODE_CONFIG = 0
+MODE_ERROR = 1
+
+# Image list indexes
+IMG_CONFIG = 0
+IMG_DOWNLOAD = 1
+IMG_INSTALL = 2
+IMG_ERROR = 3
+IMG_PLUGIN = 4
+
+_ = wx.GetTranslation
+
+#-----------------------------------------------------------------------------#
+
+def MakeThemeTool(tool_id):
+ """Makes a themed bitmap for the tool book of the plugin dialog.
+ @param tool_id: An art identifier id
+ @return: 32x32 bitmap
+ @todo: why does drawing a bitmap overlay on gtk not draw on transparent area
+
+ """
+ osize = Profile_Get('ICON_SZ', default=(24, 24))
+ Profile_Set('ICON_SZ', (32, 32))
+ base = wx.ArtProvider.GetBitmap(str(tool_id), wx.ART_TOOLBAR)
+ Profile_Set('ICON_SZ', osize)
+ if not base.IsOk():
+ base = wx.ArtProvider.GetBitmap(wx.ART_WARNING,
+ wx.ART_TOOLBAR,
+ size=(32, 32))
+
+ over = wx.ArtProvider.GetBitmap(str(ed_glob.ID_PLUGMGR), wx.ART_MENU)
+ if over.IsOk():
+ # Draw overlay onto button
+ mdc = wx.MemoryDC()
+ mdc.SelectObject(base)
+ mdc.SetBrush(wx.TRANSPARENT_BRUSH)
+ mdc.SetPen(wx.TRANSPARENT_PEN)
+ mdc.DrawBitmap(over, 15, 15, False)
+ mdc.SelectObject(wx.NullBitmap)
+
+ return base
+
+#-----------------------------------------------------------------------------#
+
+class PluginDialog(wx.Frame):
+ """Defines a Plugin manager Dialog that can be used to download plugins
+ from a defined repository, offers services to install plugins that
+ where downloaded with or without the dialog, as well as configure
+ already installed plugins. It is instanciated as a standalone window
+ when the show method is called so that if downloads are taking along time
+ it does not interfere with usage of the editor.
+
+ """
+ def __init__(self, parent, id=wx.ID_ANY, title=u'', size=wx.DefaultSize):
+ super(PluginDialog, self).__init__(parent, title=title, size=size,
+ style=wx.DEFAULT_FRAME_STYLE)
+ util.SetWindowIcon(self)
+
+ # Attributes
+ bstyle = eclib.SEGBOOK_STYLE_NO_DIVIDERS|eclib.SEGBOOK_STYLE_LABELS
+ self._nb = eclib.SegmentBook(self, style=bstyle)
+ self._cfg_pg = ConfigPanel(self._nb, style=wx.BORDER_SUNKEN)
+ self._dl_pg = DownloadPanel(self._nb)
+ self._inst_pg = InstallPanel(self._nb)
+ self._imglst = list()
+
+ # Setup
+ self.__InitImageList()
+
+ self._nb.AddPage(self._cfg_pg, _("Configure"), img_id=IMG_CONFIG)
+ self._nb.AddPage(self._dl_pg, _("Download"), img_id=IMG_DOWNLOAD)
+ self._nb.AddPage(self._inst_pg, _("Install"), img_id=IMG_INSTALL)
+
+ # Check for plugins with error conditions and if any are found
+ # Add the error page.
+ pmgr = wx.GetApp().GetPluginManager()
+ if len(pmgr.GetIncompatible()):
+ self._nb.AddPage(ConfigPanel(self._nb, style=wx.NO_BORDER,
+ mode=MODE_ERROR),
+ _("Errors"), img_id=IMG_ERROR)
+
+ # Layout
+ sizer = wx.BoxSizer(wx.VERTICAL)
+ sizer.Add(self._nb, 1, wx.EXPAND)
+ self.SetSizer(sizer)
+ self.SetStatusBar(eclib.ProgressStatusBar(self, style=wx.SB_FLAT))
+ self.SetInitialSize(size)
+
+ # Event Handlers
+ self.Bind(eclib.EVT_SB_PAGE_CHANGING, self.OnPageChanging)
+ self.Bind(wx.EVT_CLOSE, self.OnClose)
+ self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy, self)
+
+ # Message Handlers
+ ed_msg.Subscribe(self.OnThemeChange, ed_msg.EDMSG_THEME_CHANGED)
+
+ def OnDestroy(self, evt):
+ """Cleanup message handlers on delete"""
+ if evt.GetId() == self.GetId():
+ ed_msg.Unsubscribe(self.OnThemeChange)
+ evt.Skip()
+
+ def __InitImageList(self):
+ """Initialize the segmentbooks image list"""
+ dorefresh = False
+ if len(self._imglst):
+ del self._imglst
+ self._imglst = list()
+ dorefresh = True
+
+ self._imglst.append(MakeThemeTool(ed_glob.ID_PREF))
+ self._imglst.append(MakeThemeTool(ed_glob.ID_WEB))
+ self._imglst.append(MakeThemeTool(ed_glob.ID_PACKAGE))
+ bmp = wx.ArtProvider.GetBitmap(wx.ART_ERROR, wx.ART_TOOLBAR, (32, 32))
+ self._imglst.append(bmp)
+ bmp = wx.ArtProvider.GetBitmap(str(ed_glob.ID_PREF),
+ wx.ART_TOOLBAR, (32, 32))
+ self._imglst.append(bmp)
+ self._nb.SetImageList(self._imglst)
+ self._nb.SetUsePyImageList(True)
+
+ if dorefresh:
+ self._nb.Refresh()
+
+ def OnClose(self, evt):
+ """Handles closing the dialog and unregistering it from the mainloop.
+ @param evt: wx.EVT_CLOSE
+
+ """
+ if self._dl_pg.IsDownloading():
+ dlg = wx.MessageDialog(self, _("Downloads are incomplete"),
+ _("Do you wish to exit?"),
+ style=wx.YES_NO|wx.ICON_EXCLAMATION| \
+ wx.CENTER)
+ result = dlg.ShowModal()
+ dlg.Destroy()
+ if result == wx.ID_NO:
+ return
+ else:
+ pass
+
+ if self._cfg_pg.ConfigChanged():
+ wx.MessageBox(_("You must restart Editra before your "
+ "changes will take full affect."),
+ _("Configuration Changes Made"),
+ wx.ICON_INFORMATION|wx.OK)
+
+
+ wx.GetApp().UnRegisterWindow(repr(self))
+ evt.Skip()
+
+ def OnThemeChange(self, msg):
+ """Update icons on theme change message"""
+ self.__InitImageList()
+
+ def Show(self, show=True):
+ """Shows the dialog
+ @postcondition: Dialog is registered with the main loop and shown
+
+ """
+ wx.GetApp().RegisterWindow(repr(self), self, True)
+ wx.Frame.Show(self, show)
+
+ def Busy(self, busy=True):
+ """Set the status of the frame to be busy or not
+ @keyword busy: Start or Stop being busy
+
+ """
+ if busy:
+ self.GetStatusBar().StartBusy()
+ else:
+ self.GetStatusBar().StopBusy()
+
+ def OnPageChanging(self, evt):
+ """Updates pages as they are being changed to.
+ @param evt: segmentbk.EVT_SB_PAGE_CHANGING
+
+ """
+ cur_pg = evt.GetSelection()
+ self.SetTitle(self.GetTitle().split(" | ")[0] + \
+ " | " + self._nb.GetPageText(cur_pg))
+ if cur_pg == CONFIG_PG:
+ self._cfg_pg.PopulateCtrl()
+ self.SetStatusText(_("Changes will take affect once the"
+ " program has been restarted"), 0)
+ elif cur_pg == DOWNLOAD_PG:
+ self._dl_pg.UpdateList()
+ elif cur_pg == INSTALL_PG:
+ pass
+ else:
+ page = self._nb.GetPage(cur_pg)
+ size = page.GetBestSize()
+ s2 = self.GetSize()
+ segbar = self._nb.GetSegmentBar()
+ segsize = segbar.GetBestSize()
+ self.SetClientSize((s2.GetWidth(),
+ size.GetHeight() + segsize.GetHeight()))
+ self.SetStatusText("", 0)
+
+ evt.Skip()
+
+#-----------------------------------------------------------------------------#
+
+class ConfigPanel(eclib.ControlBox):
+ """Creates a panel for configuring plugins."""
+ def __init__(self, parent, style=wx.NO_BORDER, mode=MODE_CONFIG):
+ """Build config panel"""
+ eclib.ControlBox.__init__(self, parent, style=style)
+
+ # Attrtibutes
+ self._mode = mode
+ self._changed = False
+ self._list = eclib.PanelBox(self)
+
+ # Layout Panel
+ self.SetWindow(self._list)
+
+ if self._mode == MODE_CONFIG:
+ self.PopulateCtrl()
+ else:
+ self.PopulateErrors()
+
+ # Event Handlers
+ self.Bind(ed_event.EVT_NOTIFY, self.OnNotify)
+
+ def ConfigChanged(self):
+ """Did the configuration change?
+ @return: bool
+
+ """
+ return self._changed
+
+ def GetItemIdentifier(self, name):
+ """Gets the named item and returns its identifier. The
+ identifier is the combination of the name and version
+ strings.
+ @param name: name of item in list
+ @return: identifier for the named list item
+
+ """
+ identifer = None
+ for item in self._list.GetItems():
+ if item.GetPluginName().lower() == name.lower():
+ identifer = (name, item.GetVersionString())
+ return identifer
+
+ def OnNotify(self, evt):
+ """Handles the notification events that are
+ posted from the list control.
+ @param evt: ed_event.NotificationEvent
+
+ """
+ e_id = evt.GetId()
+ if e_id == ed_glob.ID_PREF:
+ # TODO: check for an open existing instance of this config objects
+ # page.
+ cfg_obj = evt.GetValue()
+ parent = self.GetParent() # SegmentBook
+
+ bmp = cfg_obj.GetBitmap()
+ if bmp.IsNull():
+ idx = IMG_PLUGIN
+ else:
+ imglst = parent.GetImageList()
+ imglst.append(bmp)
+ idx = len(imglst) - 1
+
+ label = cfg_obj.GetLabel()
+ panel = cfg_obj.GetConfigPanel(parent)
+ parent.AddPage(panel, label, True, idx)
+ parent.SetSegmentCanClose(parent.GetPageCount() - 1, True)
+ else:
+ pname, enabled = evt.GetValue()
+ pmgr = wx.GetApp().GetPluginManager()
+ pmgr.EnablePlugin(pname, enabled)
+ self._changed = True
+
+ def PopulateCtrl(self):
+ """Populates the list of plugins and sets the
+ values of their states. Any successive calls to
+ this function will clear the list and Repopulate it
+ with current config values. Returns the number of
+ items populated to the list
+ @postcondition: list is populated with all plugins that are
+ currently loaded and sets the checkmarks accordingly
+ @return: number of items added to list
+
+ """
+ p_mgr = wx.GetApp().GetPluginManager()
+ if self._list.GetItemCount():
+ self._list.DeleteAllItems()
+
+ p_mgr.ReInit()
+ config = p_mgr.GetConfig()
+ keys = sorted([ ed_txt.DecodeString(name)
+ for name in config.keys() ],
+ key=unicode.lower)
+ uninstalled = Profile_Get('UNINSTALL_PLUGINS', default=list())
+
+ with eclib.Freezer(self._list) as _tmp:
+ for item in keys:
+ val = config[item]
+ mod = sys.modules.get(item)
+ dist = p_mgr.GetPluginDistro(item)
+ if dist is not None:
+ item = dist.project_name
+ version = dist.version
+ else:
+ version = str(getattr(mod, '__version__', _("Unknown")))
+
+ pdata = PluginData()
+ pdata.SetName(item)
+ desc = getattr(mod, '__doc__', None)
+ if not isinstance(desc, basestring):
+ desc = _("No Description Available")
+ pdata.SetDescription(desc.strip())
+ pdata.SetAuthor(getattr(mod, '__author__', _("Unknown")))
+ pdata.SetVersion(version)
+ pdata.SetDist(dist)
+ pbi = PBPluginItem(self._list, mod, pdata, None)
+
+ pbi.SetChecked(val)
+ util.Log("[pluginmgr][info] Adding %s to list" % item)
+ self._list.AppendItem(pbi)
+ if pbi.GetInstallPath() in uninstalled:
+ pbi.Enable(False)
+
+ self._list.SendSizeEvent()
+ return self._list.GetItemCount()
+
+ def PopulateErrors(self):
+ """Populates the list of plugins and sets the
+ values of their states. Any successive calls to
+ this function will clear the list and Repopulate it
+ with current config values. Returns the number of
+ items populated to the list
+ @postcondition: list is populated with all plugins that are
+ currently loaded and sets the checkmarks accordingly
+ @return: number of items added to list
+
+ """
+ p_mgr = wx.GetApp().GetPluginManager()
+ if self._list.GetItemCount():
+ self._list.DeleteAllItems()
+
+ p_mgr.ReInit()
+ errors = p_mgr.GetIncompatible()
+ keys = sorted([ ed_txt.DecodeString(name)
+ for name in errors.keys() ],
+ key=unicode.lower)
+ bmp = wx.ArtProvider.GetBitmap(wx.ART_ERROR, wx.ART_TOOLBAR, (32, 32))
+ msg = _("This plugin requires a newer version of Editra.")
+
+ with eclib.Freezer(self._list) as _tmp:
+ for item in keys:
+ val = errors[item]
+ mod = sys.modules.get(val)
+ dist = p_mgr.GetPluginDistro(item)
+ if dist is not None:
+ item = dist.project_name
+ version = dist.version
+ else:
+ version = unicode(getattr(mod, '__version__', _("Unknown")))
+
+ pin = PluginData()
+ pin.SetName(item)
+ pin.SetAuthor(getattr(mod, '__author__', _("Unknown")))
+ pin.SetVersion(version)
+ pin.SetDist(dist)
+ pbi = PluginErrorItem(self._list, pin, msg, bmp=bmp)
+
+ self._list.AppendItem(pbi)
+
+ self._list.SendSizeEvent()
+ return self._list.GetItemCount()
+
+#-----------------------------------------------------------------------------#
+
+class DownloadPanel(ed_basewin.EdBaseCtrlBox):
+ """Creates a panel with controls for downloading plugins."""
+ ID_DOWNLOAD = wx.NewId()
+ EGG_PATTERN = re.compile(r"(?P<name>[^-]+)"
+ r"( -(?P<ver>[^-]+) (-py(?P<pyver>[^-]+) (-(?P<plat>.+))? )? )?",
+ re.VERBOSE | re.IGNORECASE
+ ).match
+
+ def __init__(self, parent):
+ """Initializes the panel"""
+ super(DownloadPanel, self).__init__(parent)
+
+ # Attributes
+ self._p_list = dict() # list of available plugins/meta
+ self._dl_list = dict() # List of download urls
+ self._eggcount = 0 # Number of plugins to download
+ self._eggbasket = dict() # Basket of downloaded eggs
+ self._list = eclib.PanelBox(self)
+
+ # Layout Panel
+ cbar = self.CreateControlBar(wx.BOTTOM)
+ cbar.AddStretchSpacer()
+ self._downlb = wx.Button(cbar, DownloadPanel.ID_DOWNLOAD, _("Download"))
+ self._downlb.Disable()
+ cbar.AddControl(self._downlb, wx.ALIGN_RIGHT)
+ self.SetWindow(self._list)
+
+ # Event Handlers
+ self.Bind(wx.EVT_BUTTON, self.OnButton)
+ self.Bind(ed_event.EVT_NOTIFY, self.OnNotify)
+
+ def _ResultCatcher(self, delayedResult):
+ """Catches the results from the download worker threads"""
+ # Check if result has come after the window is dead
+ try:
+ frame = self.GetGrandParent()
+ except wx.PyDeadObjectError:
+ return
+
+ self._eggcount = self._eggcount - 1
+ try:
+ result = delayedResult.get()
+ plug = result[0]
+ if result[1]:
+ self._eggbasket[plug] = result[2]
+ frame.SetStatusText(_("Downloaded") + ": " + plug, 0)
+ finally:
+ if not self._eggcount:
+ frame.SetStatusText(_("Finshed downloading plugins"), 0)
+ wx.CallAfter(frame.Busy, False)
+ inst_pg = self.GetParent().GetPage(INSTALL_PG)
+ for key in self._eggbasket:
+ inst_pg.AddItemToInstall(key)
+ self.GetParent().SetSelection(INSTALL_PG)
+
+ def _UpdateCatcher(self, delayedResult):
+ """Catches the results from the download worker threads"""
+ try:
+ frame = self.GetGrandParent()
+ result = delayedResult.get()
+ if len(result):
+ self._p_list = self.FormatPluginList(result)
+ self.PopulateList()
+ frame.SetStatusText(_("Select plugins to download"), 0)
+ except wx.PyDeadObjectError:
+ return
+ except Exception, msg:
+ util.Log("[plugdlg][err] Download failed " + str(msg))
+ frame.SetStatusText(_("Unable to retrieve plugin list"), 0)
+ wx.CallAfter(frame.Busy, False)
+
+ def FormatPluginList(self, data):
+ """Formats a list of plugin data served by the server into
+ PluginData objects for usage in the list view.
+ @return: PluginData of all available plugins
+
+ """
+ plugins = data
+ p_list = dict()
+ if len(plugins) < 2:
+ return p_list
+
+ for meta in plugins:
+ data = meta.split("\n")
+
+ if len(data) < 4:
+ continue
+
+ tmpdat = PluginData()
+ set_map = {'author' : tmpdat.SetAuthor,
+ 'version' : tmpdat.SetVersion,
+ 'name' : tmpdat.SetName,
+ 'description' : tmpdat.SetDescription,
+ 'url' : tmpdat.SetUrl}
+
+ for attr in data:
+ tmp = attr.split("=")
+ if len(tmp) != 2:
+ continue
+
+ funct = set_map.get(tmp[0].lower(), None)
+ if funct:
+ funct(ed_txt.DecodeString(tmp[1].strip()))
+
+ if tmpdat.GetName() != u'':
+ p_list[ed_txt.DecodeString(tmpdat.GetName())] = tmpdat
+
+ # Remove items that have already been installed
+ config_pg = self.GetParent().GetPage(CONFIG_PG)
+ to_clean = list()
+ for pin in p_list:
+ cfg_id = config_pg.GetItemIdentifier(pin.lower())
+ if cfg_id is not None:
+ try:
+ cur_id = [int(v) for v in cfg_id[1].split(".")]
+ dl_id = [int(v) for v in p_list[pin].GetVersion().split(".")]
+ except:
+ continue
+ if cur_id >= dl_id: # Installed version is >= avail dl
+ to_clean.append(pin)
+
+ for item in to_clean:
+ del p_list[item]
+
+ return p_list
+
+ def GetDownloadedData(self):
+ """Returns the dictionary of downloaded data or an
+ empty dictionary if no data has been downloaded.
+ @return: set of all successfully downloaded plugins
+
+ """
+ return self._eggbasket
+
+ def IsDownloading(self):
+ """Returns whether the panel has active download
+ threads or not.
+ @return: bool
+
+ """
+ if self._eggcount:
+ return True
+ else:
+ return False
+
+ def OnButton(self, evt):
+ """Handles the Button Events.
+ @param evt: wx.EVT_BUTTON
+
+ """
+ e_id = evt.GetId()
+ if e_id == self.ID_DOWNLOAD:
+ urls = list()
+ for item in self._dl_list:
+ if self._dl_list[item] and item in self._p_list:
+ urls.append(self._p_list[item].GetUrl())
+ self._eggcount = len(urls)
+
+ # Start a separate thread to download each selection
+ for egg in range(len(urls)):
+ self.GetGrandParent().SetStatusText(_("Downloading") + "...", 0)
+ self.GetGrandParent().Busy(True)
+ delayedresult.startWorker(self._ResultCatcher, _DownloadPlugin,
+ wargs=(urls[egg]), jobID=egg)
+ else:
+ evt.Skip()
+
+ def OnNotify(self, evt):
+ """Handles the notification events that are posted by the
+ list control when items are checked.
+ @param evt: ed_event.NotificationEvent
+
+ """
+ pin, enable = evt.GetValue()
+ self._dl_list[pin] = enable
+
+ if enable:
+ self._downlb.Enable()
+ else:
+ for item in self._dl_list:
+ if self._dl_list[item]:
+ self._downlb.Enable()
+ break
+ else:
+ self._downlb.Disable()
+
+ if pin in self._dl_list:
+ del self._dl_list[pin]
+
+ def PopulateList(self):
+ """Populates the list control based off data in the plugin data
+ list. The plugin data list is set as a result of calling UpdateList
+ it is not recommended to call this directly.
+
+ @return: number of items added to control
+
+ """
+ if self._list.GetItemCount():
+ self._list.DeleteAllItems()
+ pins = sorted([ name for name in self._p_list.keys() ], key=unicode.lower)
+ with eclib.Freezer(self) as _tmp:
+ for item in pins:
+ pbi = PBDownloadItem(self._list, self._p_list[item], None)
+ self._list.AppendItem(pbi)
+ self._list.SendSizeEvent()
+ return self._list.GetItemCount()
+
+ def RemoveDownloadedItem(self, item):
+ """Remove an item from the download cache
+ @param item: Name of item to remove
+
+ """
+ # Removed downloaded data
+ if item in self._eggbasket:
+ del self._eggbasket[item]
+
+ # Remove download entry data
+ match = self.EGG_PATTERN(item)
+ if match:
+ plugin_name = match.group('name').lower()
+ if plugin_name in self._dl_list:
+ del self._dl_list[plugin_name]
+
+ def UpdateList(self, url=PLUGIN_REPO):
+ """Update the list of available downloads
+ @param url: url to fetch update list from
+ @postcondition: Worker thread is started that will update list when it
+ finishes.
+
+ """
+ if self._list.GetItemCount():
+ self._list.DeleteAllItems()
+ frame = self.GetGrandParent()
+ frame.SetStatusText(_("Retrieving Plugin List") + "...", 0)
+ frame.Busy(True)
+ delayedresult.startWorker(self._UpdateCatcher, _GetPluginListData,
+ wkwargs={'url' : url}, jobID='update')
+
+#-----------------------------------------------------------------------------#
+# Download utility functions
+
+# The obtained meta data must be served as a file that is formatted
+# as follows. Each meta data item must be on a single line with
+# each set of meta data for different plugins separated by three
+# hash marks '###'.
+def _GetPluginListData(url=PLUGIN_REPO):
+ """Gets the list of plugins and their related meta data
+ as a string and returns it.
+ @return: list of data of available plugins from website
+
+ """
+ text = u''
+ try:
+ try:
+ if Profile_Get('USE_PROXY', default=False):
+ proxy_set = Profile_Get('PROXY_SETTINGS',
+ default=dict(uname='', url='',
+ port='80', passwd=''))
+ proxy = util.GetProxyOpener(proxy_set)
+ h_file = proxy.open(url)
+ else:
+ h_file = urllib2.urlopen(url)
+
+ text = h_file.read()
+ h_file.close()
+ except (IOError, OSError), msg:
+ util.Log("[plugdlg][err] %s" % str(msg))
+ finally:
+ return text.split("###")
+
+def _DownloadPlugin(*args):
+ """Downloads the plugin at the given url.
+ @note: *args is really a string that has been exploded
+ @return: name, completed, egg data
+ @rtype: tuple
+
+ """
+ url = "".join(args)
+ egg = None
+ try:
+ try:
+ if Profile_Get('USE_PROXY', default=False):
+ proxy_set = Profile_Get('PROXY_SETTINGS',
+ default=dict(uname='', url='',
+ port='80', passwd=''))
+ proxy = util.GetProxyOpener(proxy_set)
+ h_file = proxy.open(url)
+ else:
+ h_file = urllib2.urlopen(url)
+
+ egg = h_file.read()
+ h_file.close()
+ except (IOError, OSError), msg:
+ util.Log("[plugdlg][err] %s" % str(msg))
+ finally:
+ return (url.split("/")[-1], True, egg)
+
+#-----------------------------------------------------------------------------#
+
+class InstallPanel(ed_basewin.EdBaseCtrlBox):
+ """Creates a panel for installing plugins."""
+ ID_INSTALL = wx.NewId()
+ ID_USER = wx.NewId()
+ ID_SYS = wx.NewId()
+ ID_REMOVE_ITEM = wx.NewId()
+
+ def __init__(self, parent):
+ """Initializes the panel"""
+ super(InstallPanel, self).__init__(parent)
+
+ # Attributes
+ bbar = self.CreateControlBar(wx.BOTTOM)
+ toolt = wx.ToolTip(_("To add a new item drag and drop the plugin file "
+ "into the list.\n\nTo remove an item select it "
+ "and hit Delete or Backspace."))
+ self._install = wx.ListBox(self, wx.ID_ANY,
+ style=wx.LB_SORT|wx.BORDER_NONE)
+ self._install.SetToolTip(toolt)
+ self._install.SetDropTarget(util.DropTargetFT(self._install,
+ None, self.OnDrop))
+
+ self._instb = wx.Button(bbar, self.ID_INSTALL, _("Install"))
+ self._instb.Disable()
+ self._usercb = wx.CheckBox(bbar, self.ID_USER, _("User Directory"))
+ self._usercb.SetValue(True)
+ toolt = wx.ToolTip(_("Install the plugins only for the current user"))
+ self._usercb.SetToolTip(toolt)
+ self._syscb = wx.CheckBox(bbar, self.ID_SYS, _("System Directory"))
+ toolt = wx.ToolTip(_("Install the plugins for all users\n"
+ " **requires administrative privileges**"))
+ self._syscb.SetToolTip(toolt)
+ if not os.access(ed_glob.CONFIG['SYS_PLUGIN_DIR'], os.R_OK | os.W_OK):
+ self._syscb.Disable()
+
+ # Layout Panel
+ self.SetWindow(self._install)
+ bbar.AddControl(self._usercb)
+ bbar.AddControl(self._syscb)
+ bbar.AddStretchSpacer()
+ bbar.AddControl(self._instb, wx.ALIGN_RIGHT)
+ self.SendSizeEvent()
+
+ # Event Handlers
+ self.Bind(wx.EVT_BUTTON, self.OnButton)
+ self.Bind(wx.EVT_CHECKBOX, self.OnCheckBox)
+ self._install.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+
+ def _Install(self):
+ """Install the plugins in the list.
+ @postcondition: all plugins listed in the list are installed and loaded
+
+ """
+ items = self._install.GetItems()
+ inst_loc = ed_glob.CONFIG['PLUGIN_DIR']
+ if self._syscb.GetValue():
+ inst_loc = ed_glob.CONFIG['SYS_PLUGIN_DIR']
+
+ for item in items:
+ egg_name = item.split(os.sep)[-1]
+ if os.path.isabs(item):
+ try:
+ reader = file(item, "rb")
+ egg = reader.read()
+ reader.close()
+ except (IOError, SystemError, OSError):
+ continue
+ else:
+ dl_pg = self.GetParent().GetPage(DOWNLOAD_PG)
+ egg = dl_pg.GetDownloadedData().get(item, None)
+ if not egg:
+ continue
+
+ try:
+ writer = file(inst_loc + egg_name, "wb")
+ writer.write(egg)
+ writer.close()
+ except (IOError, OSError):
+ continue
+ else:
+ # If successfully installed remove from list
+ ind = self._install.FindString(item)
+ dl_pg = self.GetParent().GetPage(DOWNLOAD_PG)
+ if ind != wx.NOT_FOUND:
+ self._install.Delete(ind)
+ dl_pg.RemoveDownloadedItem(item)
+
+ if not len(self._install.GetItems()):
+ # All plugins installed correctly
+ grand_p = self.GetTopLevelParent()
+ grand_p.SetStatusText(_("Successfully Installed Plugins"), 0)
+ # Note: need to do this because SetSelection doesn't fire a
+ # page change.
+ wx.GetApp().GetPluginManager().ReInit()
+ self.GetParent().SetSelection(CONFIG_PG)
+ self._instb.Disable()
+ else:
+ self.GetGrandParent().SetStatusText(_("Error"), 1)
+ dlg = wx.MessageDialog(self,
+ _("Failed to install %d plugins") % \
+ self._install.GetCount(),
+ _("Installation Error"),
+ style = wx.OK | wx.CENTER | wx.ICON_ERROR)
+ dlg.ShowModal()
+ dlg.Destroy()
+
+ def AddItemToInstall(self, item):
+ """Adds an item to the install list, the item
+ should be a string of the path to the item or
+ the items name if it is an in memory file from the
+ download page.
+ @param item: path or name of plugin item
+
+ """
+ if self._install.FindString(item) == wx.NOT_FOUND:
+ self._instb.Enable()
+ self._install.Append(item)
+ else:
+ pass
+
+ def OnButton(self, evt):
+ """Handles button events generated by the panel.
+ @param evt: wx.EVT_BUTTON
+
+ """
+ if evt.GetId() == self.ID_INSTALL:
+ self._Install()
+ else:
+ evt.Skip()
+
+ def OnCheckBox(self, evt):
+ """Handles the checkbox events to make sure that
+ only one of the two check boxes is checked at a time
+ @param evt: wx.EVT_CHECKBOX
+
+ """
+ e_id = evt.GetId()
+ val = evt.GetEventObject().GetValue()
+ u_cb = self.FindWindowById(self.ID_USER)
+ s_cb = self.FindWindowById(self.ID_SYS)
+ if e_id == self.ID_USER:
+ if not s_cb.IsEnabled():
+ u_cb.SetValue(True)
+ else:
+ s_cb.SetValue(not val)
+ elif e_id == self.ID_SYS:
+ u_cb.SetValue(not val)
+ else:
+ pass
+ evt.Skip()
+
+ def OnDrop(self, files):
+ """Get Drop files and place paths in control
+ @todo: should also check entry points in addition to filetype
+ @param files: list of file paths
+ @postcondition: all non egg files are filtered only placing
+ the eggs in the list.
+ """
+ # Filter out any files that are not eggs
+ good = [ fname for fname in files if fname.split(u'.')[-1] == u'egg' ]
+ for item in good:
+ if self._install.FindString(item) == wx.NOT_FOUND:
+ self._install.Append(item)
+
+ if self._install.GetCount():
+ self._instb.Enable()
+
+ def OnKeyUp(self, evt):
+ """Key Event handler. Removes the selected item from
+ the list control when the delete or backspace kis is pressed.
+ @param evt: wx.KeyEvent(wx.EVT_KEY_UP)
+
+ """
+ if evt.GetKeyCode() in [wx.WXK_DELETE, wx.WXK_BACK]:
+ item = self._install.GetSelection()
+ if item != wx.NOT_FOUND:
+ self._install.Delete(item)
+
+ if not self._install.GetCount():
+ self._instb.Disable()
+
+ evt.Skip()
+
+#-----------------------------------------------------------------------------#
+
+class PBPluginItem(eclib.PanelBoxItemBase):
+ """PanelBox Item to display configuration information about a plugin."""
+ def __init__(self, parent, mod, pdata, bmp=None, enabled=False):
+ """Create the PanelBoxItem
+ @param parent: L{PanelBox}
+ @param mod: module
+ @param pdata: PluginData
+ @keyword bmp: Plugin Icon
+ @keyword enabled: Plugin is currently enabled (bool)
+
+ """
+ super(PBPluginItem, self).__init__(parent)
+
+ # Attributes
+ self._module = mod
+ self._pdata = pdata
+ self._bmp = bmp
+ self._title = wx.StaticText(self, label=self._pdata.GetName())
+ self._version = wx.StaticText(self, label=self._pdata.GetVersion())
+ self._desc = wx.StaticText(self, label=self._pdata.GetDescription())
+ self._auth = wx.StaticText(self, label=_("Author: %s") % self._pdata.GetAuthor())
+ self._enabled = wx.CheckBox(self, label=_("Enable"))
+ self._enabled.SetValue(enabled)
+ bmp = wx.ArtProvider.GetBitmap(str(ed_glob.ID_DELETE), wx.ART_MENU)
+ self._uninstall = eclib.PlateButton(self, label=_("Uninstall"), bmp=bmp,
+ style=eclib.PB_STYLE_NOBG)
+ self._uninstall.Unbind(wx.EVT_ERASE_BACKGROUND)
+ bmp = wx.ArtProvider.GetBitmap(str(ed_glob.ID_PREF), wx.ART_MENU)
+ self._config = eclib.PlateButton(self,
+ label=_("Configure"), bmp=bmp,
+ style=eclib.PB_STYLE_NOBG)
+ self._config.Unbind(wx.EVT_ERASE_BACKGROUND)
+ self._config.Enable(enabled)
+
+ # Setup
+ if not hasattr(mod, 'GetConfigObject'):
+ self._config.Hide()
+
+ ipath = self.GetInstallPath()
+ if ipath:
+ if not os.access(ipath, os.R_OK|os.W_OK):
+ self._uninstall.Show(False)
+ else:
+ util.Log("[pluginmgr][warn] cant find plugin path for %s" % \
+ self._pdata.GetName())
+ self._uninstall.Show(False) # Should not happen
+
+ font = self._title.GetFont()
+ font.SetWeight(wx.FONTWEIGHT_BOLD)
+ self._title.SetFont(font)
+ self._version.SetFont(font)
+
+ if wx.Platform == '__WXMAC__':
+ for ctrl in (self._desc, self._auth, self._enabled,
+ self._config, self._uninstall):
+ ctrl.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
+
+ # Layout
+ self.__DoLayout()
+
+ # Event Handlers
+ self.Bind(wx.EVT_CHECKBOX, self.OnCheck)
+ self.Bind(wx.EVT_BUTTON, self.OnConfigButton, self._config)
+ self.Bind(wx.EVT_BUTTON, self.OnUninstallButton, self._uninstall)
+
+ def __DoLayout(self):
+ """Layout the panel"""
+ vsizer = wx.BoxSizer(wx.VERTICAL)
+ hsizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ # Left side Bitmap and Checkbox
+ hsizer.Add((5, 5), 0)
+ if self._bmp is not None:
+ self._bmp = wx.StaticBitmap(self, bitmap=self._bmp)
+ hsizer.Add(self._bmp, 0, wx.ALIGN_CENTER_VERTICAL)
+
+ # Central area main content
+ csizer = wx.BoxSizer(wx.VERTICAL)
+ tsizer = wx.BoxSizer(wx.HORIZONTAL)
+ tsizer.AddMany([(self._title, 0), ((20, -1), 1, wx.EXPAND),
+ (self._version, 0, wx.ALIGN_RIGHT),
+ ((5, 5), 0)])
+
+ bsizer = wx.BoxSizer(wx.HORIZONTAL)
+ bsizer.AddMany([(self._auth, 0), ((5, -1), 1, wx.EXPAND),
+ (self._enabled, 0, wx.ALIGN_CENTER_VERTICAL),
+ ((5, 5), 0),
+ (self._uninstall, 0, wx.ALIGN_CENTER_VERTICAL),
+ (self._config, 0, wx.ALIGN_CENTER_VERTICAL)])
+ csizer.AddMany([(tsizer, 1, wx.EXPAND), ((3, 3), 0),
+ (self._desc, 0), ((3, 3), 0),
+ (bsizer, 0, wx.EXPAND)])
+
+ # Finish Layout
+ hsizer.AddMany([((5, 5), 0), (csizer, 1, wx.EXPAND), ((5, 5), 0)])
+ vsizer.AddMany([((4, 4), 0), (hsizer, 0, wx.EXPAND), ((4, 4), 0)])
+ self.SetSizer(vsizer)
+ self.SetAutoLayout(True)
+
+ def GetInstallPath(self):
+ """Get the path of the plugin
+ @return: string
+
+ """
+ if self._pdata is not None:
+ dist = self._pdata.GetDist()
+ if dist is not None:
+ return dist.location
+ return u''
+
+ def GetPluginName(self):
+ """Get the name of the plugin
+ @return: string
+
+ """
+ return self._title.GetLabel()
+
+ def GetVersionString(self):
+ """Get the version of the plugin
+ @return: string
+
+ """
+ return self._version.GetLabel()
+
+ def OnConfigButton(self, evt):
+ """Handle when the configuration button is hit."""
+ if self._module is not None:
+ cfg_obj = self._module.GetConfigObject()
+ event = ed_event.NotificationEvent(ed_event.edEVT_NOTIFY,
+ ed_glob.ID_PREF, cfg_obj)
+ wx.PostEvent(self.GetParent(), event)
+
+ def OnUninstallButton(self, evt):
+ """Uninstall the plugin"""
+ msg = _("Are you sure you want to uninstall %s?\nThis cannot be undone.")
+ result = wx.MessageBox(msg % self.GetPluginName(),
+ _("Uninstall Plugin"),
+ wx.OK|wx.CANCEL|wx.ICON_WARNING)
+ if result == wx.OK:
+ self.Enable(False)
+ self._desc.SetLabel(_("This plugin will be uninstalled on next program launch."))
+ self._enabled.SetValue(False)
+ pname = self._title.GetLabel()
+ event = ed_event.NotificationEvent(ed_event.edEVT_NOTIFY, self.GetId(),
+ (pname, False), self)
+ wx.PostEvent(self.GetParent(), event)
+ plist = Profile_Get('UNINSTALL_PLUGINS', default=list())
+ plist.append(self.GetInstallPath())
+ Profile_Set('UNINSTALL_PLUGINS', plist)
+ else:
+ return
+
+ def OnCheck(self, evt):
+ """Notify container of changes to state of plugin"""
+ enabled = self._enabled.GetValue()
+ self._config.Enable(enabled)
+ pname = self._title.GetLabel()
+ event = ed_event.NotificationEvent(ed_event.edEVT_NOTIFY, self.GetId(),
+ (pname, enabled), self)
+ wx.PostEvent(self.GetParent(), event)
+
+ def SetChecked(self, check=True):
+ """Set the checkbox
+ @param check: bool
+
+ """
+ self._enabled.SetValue(check)
+ self._config.Enable(check)
+
+#-----------------------------------------------------------------------------#
+
+class PBDownloadItem(PBPluginItem):
+ """PanelBox Item to display download information about a plugin."""
+ def __init__(self, parent, pdata, bmp=None):
+ """Create the PanelBoxItem
+ @param parent: L{PanelBox}
+ @param pdata: PluginData
+ @keyword bmp: Plugin Icon
+
+ """
+ super(PBDownloadItem, self).__init__(parent, None, pdata, bmp=bmp)
+
+ # Setup
+ self._uninstall.Hide()
+ self._enabled.SetLabel(_("Download"))
+ self.Layout()
+
+#-----------------------------------------------------------------------------#
+
+class PluginErrorItem(eclib.PanelBoxItemBase):
+ """PanelBox Item to display configuration information about a plugin."""
+ def __init__(self, parent, pdata, msg, bmp):
+ """Create the PanelBoxItem
+ @param parent: L{PanelBox}
+ @param pdata: PluginData
+ @param msg: error msg
+ @param bmp: Bitmap
+
+ """
+ super(PluginErrorItem, self).__init__(parent)
+
+ # Attributes
+ self._bmp = bmp
+ self._title = wx.StaticText(self, label=pdata.GetName())
+ self._version = wx.StaticText(self, label=pdata.GetVersion())
+ self._msg = wx.StaticText(self, label=msg)
+ self._auth = wx.StaticText(self, label=_("Author: %s") % pdata.GetAuthor())
+
+ # Setup
+ font = self._title.GetFont()
+ font.SetWeight(wx.FONTWEIGHT_BOLD)
+ self._title.SetFont(font)
+ self._version.SetFont(font)
+
+ if wx.Platform == '__WXMAC__':
+ self._msg.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
+ self._auth.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
+
+ # Layout
+ self.__DoLayout()
+
+ def __DoLayout(self):
+ """Layout the panel"""
+ vsizer = wx.BoxSizer(wx.VERTICAL)
+ hsizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ # Left side Bitmap and Checkbox
+ hsizer.Add((5, 5), 0)
+ if self._bmp is not None:
+ self._bmp = wx.StaticBitmap(self, bitmap=self._bmp)
+ hsizer.Add(self._bmp, 0, wx.ALIGN_CENTER_VERTICAL)
+
+ # Central area main content
+ csizer = wx.BoxSizer(wx.VERTICAL)
+ tsizer = wx.BoxSizer(wx.HORIZONTAL)
+ tsizer.AddMany([(self._title, 0), ((20, -1), 1, wx.EXPAND),
+ (self._version, 0, wx.ALIGN_RIGHT)])
+
+ bsizer = wx.BoxSizer(wx.HORIZONTAL)
+ bsizer.AddMany([(self._auth, 0)])
+ csizer.AddMany([(tsizer, 1, wx.EXPAND), ((3, 3), 0),
+ (self._msg, 0), ((3, 3), 0),
+ (bsizer, 0, wx.EXPAND)])
+
+ # Finish Layout
+ hsizer.AddMany([((5, 5), 0), (csizer, 1, wx.EXPAND), ((5, 5), 0)])
+ vsizer.AddMany([((4, 4), 0), (hsizer, 0, wx.EXPAND), ((4, 4), 0)])
+ self.SetSizer(vsizer)
+ self.SetAutoLayout(True)
+
+#-----------------------------------------------------------------------------#
+
+class PluginData(plugin.PluginData):
+ """Plugin Metadata storage class used to store data
+ about plugins and where to download them from
+ @see: plugin.PluginData
+
+ """
+ def __init__(self, name=u'', descript=u'', \
+ author=u'', ver=u'', url=u''):
+ """Extends PluginData to include information about url to get it from.
+ @keyword name: Plugin name
+ @keyword descript: Plugin short description
+ @keyword author: Plugin Author Name
+ @keyword ver: Plugin Version (Unicode)
+ @keyword url: url to download plugin from
+
+ """
+ super(PluginData, self).__init__(name, descript, author, ver)
+
+ # Attributes
+ self._url = url
+
+ def GetUrl(self):
+ """Returns the URL of the plugin
+ @return: url string of plugins location
+
+ """
+ return self._url
+
+ def SetUrl(self, url):
+ """Sets the url of the plugin.
+ @param url: fully qualified url string
+
+ """
+ if not isinstance(url, basestring):
+ try:
+ url = str(url)
+ except (TypeError, ValueError):
+ url = u''
+ self._url = url
+
+#-----------------------------------------------------------------------------#
+