Fixed error if we can't find an icon
[AddApp.git] / AddApp / launcher.py
blobd9bf1a8e09679341e6c40bd6cea6d57c5c3adf6a
1 from zeroinstall.injector import policy, namespaces, model
2 from zeroinstall.injector.iface_cache import iface_cache
3 import os, shutil, sys
4 import rox
5 from rox import saving
6 from xml.dom import minidom
7 import urllib2
8 import tempfile, shutil
10 addapp_uri = "http://rox.sourceforge.net/2005/interfaces/AddApp"
12 class Policy(policy.Policy):
13 pass
15 class NoMain(Exception):
16 pass
18 class AppLauncher(saving.Saveable):
19 def __init__(self, uri):
20 self.uri = uri
21 self.icon = None
22 self._icon_tmp = None
24 self.policy = Policy(uri)
25 self.policy.freshness = 0 # No further updates
26 self.policy.network_use = model.network_offline
27 self.policy.recalculate()
28 iface = iface_cache.get_interface(uri)
29 self.impl = self.policy.implementation[iface]
30 if not self.impl:
31 raise Exception("Failed to select an implementation of %s" % iface)
32 if hasattr(self.impl, 'main'):
33 self.main = self.impl.main
34 else:
35 self.main = iface.main
36 if not self.main:
37 raise NoMain("Interface '%s' cannot be executed directly; it is just a library "
38 "to be used by other programs (or missing 'main' attribute on the "
39 "root <interface> element)." % iface.name)
41 def save_to_file(self, path):
42 os.mkdir(path)
43 assert "'" not in self.uri
44 iface = iface_cache.get_interface(self.uri)
46 apprun = os.fdopen(os.open(os.path.join(path, 'AppRun'), os.O_CREAT | os.O_WRONLY, 0777), 'w')
47 if self.uri == 'http://rox.sourceforge.net/2005/interfaces/AddApp':
48 # Funky special case: AddApp behaves differently when run through AppRun
49 apprun.write("""#!/bin/sh
50 if [ "$*" = "--versions" ]; then
51 exec 0launch -gd '%s'
52 elif [ "$*" = "" ]; then
53 exec 0launch '%s' --prompt
54 elif [ "$*" = "--help" ]; then
55 exec 0launch '%s' --show-help '%s'
56 else
57 exec 0launch '%s' -- "$@"
58 fi""" % (self.uri, self.uri, addapp_uri, self.uri, self.uri))
59 else:
60 if len(iface.get_metadata(namespaces.XMLNS_IFACE, 'needs-terminal')):
61 xterm = addapp_uri + ' --run-in-terminal '
62 else:
63 xterm = ''
64 apprun.write("""#!/bin/sh
65 if [ "$*" = "--versions" ]; then
66 exec 0launch -gd '%(uri)s'
67 elif [ "$*" = "--help" ]; then
68 exec 0launch '%(addapp)s' --show-help '%(uri)s'
69 else
70 exec 0launch %(xterm)s'%(uri)s' "$@"
71 fi""" % {'uri': self.uri, 'addapp': addapp_uri, 'xterm': xterm})
73 apprun.close()
74 if self.icon:
75 shutil.copyfile(self.icon, os.path.join(path, '.DirIcon'))
77 doc = self.get_appinfo(iface)
78 info_file = file(os.path.join(path, 'AppInfo.xml'), 'w')
79 doc.writexml(info_file)
80 info_file.close()
82 if self.is_applet(iface):
83 appletrun = os.fdopen(os.open(os.path.join(path, 'AppletRun'),
84 os.O_CREAT | os.O_WRONLY, 0777), 'w')
85 appletrun.write("""#!/bin/sh
86 exec 0launch --main=AppletRun '%s' "$@"
87 """ % self.uri)
88 appletrun.close()
90 def get_appinfo(self, iface):
91 impl_path = self.get_impl_path(self.impl)
92 if impl_path:
93 appinfo = os.path.join(impl_path, os.path.dirname(self.main), 'AppInfo.xml')
94 else:
95 appinfo = None
97 if appinfo and os.path.exists(appinfo):
98 doc = minidom.parse(appinfo)
99 else:
100 doc = minidom.parseString("<?xml version='1.0'?>\n<AppInfo/>")
101 root = doc.documentElement
102 appmenu = self.get_item(root, 'AppMenu')
103 self.add_item(appmenu, '--versions', {'en': 'Versions...'})
104 self.add_item(appmenu, '--help', {'en': 'Help'})
105 if not root.getElementsByTagName('Summary'):
106 summary = doc.createElement('Summary')
107 summary.appendChild(doc.createTextNode('%s -- %s' % (iface.name, iface.summary)))
108 root.appendChild(summary)
109 for summary in root.getElementsByTagName('Summary'):
110 summary.appendChild(doc.createTextNode('\n(%s)' % iface.uri))
112 for about in root.getElementsByTagName('About'):
113 for version in about.getElementsByTagName('Version'):
114 if not version.childNodes: continue
115 for c in version.childNodes:
116 version.removeChild(c)
117 version.appendChild(doc.createTextNode('(launcher)'))
118 return doc
120 def add_item(self, menu, option, labels):
121 doc = menu.ownerDocument
122 item = doc.createElement('Item')
123 kids = menu.childNodes
124 if kids:
125 menu.insertBefore(item, kids[0])
126 else:
127 menu.appendChild(item)
128 item.setAttribute('option', option)
130 for lang in labels:
131 label = doc.createElement('Label')
132 item.appendChild(label)
133 label.setAttribute('xml:lang', lang)
134 label.appendChild(doc.createTextNode(labels[lang]))
136 def get_item(self, parent, name):
137 items = parent.getElementsByTagName(name)
138 if items:
139 return items[0]
140 item = parent.ownerDocument.createElement(name)
141 parent.appendChild(item)
142 return item
144 def get_impl_path(self, impl):
145 if impl.id.startswith('.') or impl.id.startswith('/'):
146 return impl.id
147 elif impl.id.startswith('package:'):
148 return None
149 else:
150 impl_path = self.policy.get_implementation_path(impl)
151 assert impl_path
152 return impl_path
154 def is_applet(self, iface):
155 impl_path = self.get_impl_path(self.impl)
156 if impl_path:
157 applet_run = os.path.join(impl_path, os.path.dirname(self.main), 'AppletRun')
158 return os.path.exists(applet_run)
159 else:
160 return False
162 def get_icon(self):
163 """Sets self.icon to a filename of an icon for the app (downloading it first), or None."""
165 if hasattr(iface_cache, 'get_icon_path'):
166 iface = iface_cache.get_interface(self.uri)
167 path = iface_cache.get_icon_path(iface)
168 if path:
169 self.icon = path
170 return
172 # Older versions of 0launch...
174 from zeroinstall.injector import reader, model, basedir, namespaces
175 if self.uri.startswith('/'):
176 cached = self.uri
177 else:
178 cached = basedir.load_first_cache(namespaces.config_site, 'interfaces', model.escape(self.uri))
179 if not cached:
180 print >>sys.stderr, "Internal error: interface '%s' still not cached!" % self.uri
181 return
182 from xml.dom import minidom
183 doc = minidom.parse(cached)
184 names = doc.documentElement.getElementsByTagNameNS(namespaces.XMLNS_IFACE, 'icon')
185 for x in names:
186 type = x.getAttribute('type')
187 if str(type) != 'image/png':
188 print "Skipping unknown icon type '%s'" % type
189 continue
190 icon_uri = x.getAttribute('href')
191 print "Downloading icon from", icon_uri
192 try:
193 icon_data = urllib2.urlopen(icon_uri)
194 except:
195 rox.report_exception()
196 return
197 tmp = tempfile.NamedTemporaryFile()
198 shutil.copyfileobj(icon_data, tmp)
199 icon_data.close()
200 tmp.flush()
202 self._icon_tmp = tmp
203 self.icon = tmp.name
205 def delete_icon(self):
206 """Remove the temporary icon file, if any"""
207 self._icon_tmp = None