3
by David Callé
* New API |
1 |
#! /usr/bin/python3
|
1
by David Callé
* Init VirtualBox scope |
2 |
# -*- coding: utf-8 -*-
|
3 |
||
3
by David Callé
* New API |
4 |
# Copyright (C) 2013 David Callé <davidc@framli.eu>
|
5 |
# Copyright (C) 2012 Christopher Wayne <cwayne@ubuntu.com>
|
|
1
by David Callé
* Init VirtualBox scope |
6 |
# This program is free software: you can redistribute it and/or modify it
|
7 |
# under the terms of the GNU General Public License version 3, as published
|
|
8 |
# by the Free Software Foundation.
|
|
9 |
#
|
|
10 |
# This program is distributed in the hope that it will be useful, but
|
|
11 |
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
|
12 |
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
|
13 |
# PURPOSE. See the GNU General Public License for more details.
|
|
14 |
#
|
|
15 |
# You should have received a copy of the GNU General Public License along
|
|
16 |
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17 |
||
18 |
from gi.repository import Unity, UnityExtras |
|
3
by David Callé
* New API |
19 |
from gi.repository import Gio, GLib |
1
by David Callé
* Init VirtualBox scope |
20 |
import subprocess |
21 |
import re |
|
22 |
import shlex |
|
23 |
import gettext |
|
21
by David Callé
Initial VirtualBox previews |
24 |
import datetime |
1
by David Callé
* Init VirtualBox scope |
25 |
|
3
by David Callé
* New API |
26 |
APP_NAME = 'unity-scope-virtualbox' |
27 |
LOCAL_PATH = '/usr/share/locale/' |
|
1
by David Callé
* Init VirtualBox scope |
28 |
gettext.bindtextdomain(APP_NAME, LOCAL_PATH) |
29 |
gettext.textdomain(APP_NAME) |
|
30 |
_ = gettext.gettext |
|
31 |
||
3
by David Callé
* New API |
32 |
GROUP_NAME = 'com.canonical.Unity.Scope.Boxes.Virtualbox' |
33 |
UNIQUE_PATH = '/com/canonical/unity/scope/boxes/virtualbox' |
|
34 |
SEARCH_URI = '' |
|
35 |
SEARCH_HINT = _('Search Virtualbox') |
|
36 |
NO_RESULTS_HINT = _('Sorry, there is no VirtualBox machine that matches your search.') |
|
37 |
PROVIDER_CREDITS = _('Powered by VirtualBox') |
|
38 |
SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/' |
|
39 |
PROVIDER_ICON = SVG_DIR+'service-virtualbox.svg' |
|
40 |
DEFAULT_RESULT_ICON = 'virtualbox' |
|
41 |
DEFAULT_RESULT_MIMETYPE = 'application/x-virtualbox-vbox' |
|
42 |
DEFAULT_RESULT_TYPE = Unity.ResultType.DEFAULT |
|
43 |
EXTRA_DATA = None |
|
44 |
||
15.1.1
by David Callé
Update category id to match master |
45 |
c1 = {'id' :'boxes', |
3
by David Callé
* New API |
46 |
'name' :_('Virtual Machines'), |
47 |
'icon' :SVG_DIR+'group-boxes.svg', |
|
48 |
'renderer':Unity.CategoryRenderer.VERTICAL_TILE} |
|
49 |
CATEGORIES = [c1] |
|
50 |
||
51 |
FILTERS = [] |
|
52 |
||
53 |
EXTRA_METADATA = [] |
|
54 |
||
55 |
def search(search, filters): |
|
56 |
'''
|
|
57 |
Any search method returning results as a list of tuples.
|
|
58 |
Available tuple fields:
|
|
59 |
uri (string)
|
|
60 |
icon (string)
|
|
61 |
title (string)
|
|
62 |
comment (string)
|
|
63 |
dnd_uri (string)
|
|
64 |
mimetype (string)
|
|
65 |
category (int)
|
|
66 |
result_type (Unity ResultType)
|
|
67 |
extras metadata fields (variant)
|
|
68 |
'''
|
|
69 |
results = [] |
|
70 |
try: |
|
71 |
if not EXTRA_DATA: |
|
72 |
vboxlist = subprocess.check_output(['vboxmanage', 'list', 'vms']) |
|
73 |
else: |
|
74 |
vboxlist = EXTRA_DATA |
|
75 |
for vbox in vboxlist.splitlines(): |
|
21
by David Callé
Initial VirtualBox previews |
76 |
vbox_name = re.sub('\{.*\}', '', vbox.decode('utf-8')) |
77 |
uuid = re.match(r'.*\{(.*)\}', vbox.decode('utf-8')) |
|
78 |
if uuid: |
|
79 |
uuid = uuid.group(1) |
|
80 |
else: |
|
81 |
uuid = shlex.split(vbox_name)[0] |
|
82 |
if search.lower() in vbox_name.lower(): |
|
83 |
results.append({'uri':uuid, |
|
84 |
'title':shlex.split(vbox_name)[0]}) |
|
3
by David Callé
* New API |
85 |
except Exception as error: |
86 |
print (error) |
|
21
by David Callé
Initial VirtualBox previews |
87 |
print (results) |
3
by David Callé
* New API |
88 |
return results |
89 |
||
90 |
||
91 |
# Classes below this point establish communication
|
|
92 |
# with Unity, you probably shouldn't modify them.
|
|
93 |
||
94 |
||
95 |
class MySearch (Unity.ScopeSearchBase): |
|
96 |
def __init__(self, search_context): |
|
97 |
super (MySearch, self).__init__() |
|
98 |
self.set_search_context (search_context) |
|
99 |
||
100 |
def do_run (self): |
|
101 |
'''
|
|
102 |
Adds results to the model
|
|
103 |
'''
|
|
1
by David Callé
* Init VirtualBox scope |
104 |
try: |
3
by David Callé
* New API |
105 |
result_set = self.search_context.result_set |
106 |
for i in search(self.search_context.search_query, |
|
107 |
self.search_context.filter_state): |
|
108 |
if not 'uri' in i or not i['uri'] or i['uri'] == '': |
|
109 |
continue
|
|
110 |
if not 'icon' in i or not i['icon'] or i['icon'] == '': |
|
111 |
i['icon'] = DEFAULT_RESULT_ICON |
|
112 |
if not 'mimetype' in i or not i['mimetype'] or i['mimetype'] == '': |
|
113 |
i['mimetype'] = DEFAULT_RESULT_MIMETYPE |
|
114 |
if not 'result_type' in i or not i['result_type'] or i['result_type'] == '': |
|
115 |
i['result_type'] = DEFAULT_RESULT_TYPE |
|
116 |
if not 'category' in i or not i['category'] or i['category'] == '': |
|
117 |
i['category'] = 0 |
|
118 |
if not 'title' in i or not i['title']: |
|
119 |
i['title'] = '' |
|
120 |
if not 'comment' in i or not i['comment']: |
|
121 |
i['comment'] = '' |
|
122 |
if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '': |
|
123 |
i['dnd_uri'] = i['uri'] |
|
21
by David Callé
Initial VirtualBox previews |
124 |
i['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS) |
125 |
result_set.add_result(**i) |
|
1
by David Callé
* Init VirtualBox scope |
126 |
except Exception as error: |
3
by David Callé
* New API |
127 |
print (error) |
128 |
||
21
by David Callé
Initial VirtualBox previews |
129 |
class Preview (Unity.ResultPreviewer): |
130 |
||
131 |
def do_run(self): |
|
132 |
preview = Unity.GenericPreview.new(self.result.title, self.result.comment, None) |
|
133 |
preview.props.image_source_uri = self.result.icon_hint |
|
134 |
vboxinfo = subprocess.check_output(['vboxmanage', 'showvminfo', self.result.uri]) |
|
135 |
for line in vboxinfo.splitlines(): |
|
136 |
line = line.decode('utf-8') |
|
137 |
if line.startswith('Guest OS:'): |
|
138 |
os = re.match(r'.*:\s+([^\s].*)',line) |
|
139 |
if os: |
|
140 |
os_text = os.group(1) |
|
22
by David Callé
OS -> Operating System |
141 |
preview.add_info(Unity.InfoHint.new("os", _("Operating System"), None, os_text)) |
21
by David Callé
Initial VirtualBox previews |
142 |
if line.startswith('Memory size:'): |
143 |
memsize = re.match(r'.*:\s+([^\s].*)',line) |
|
144 |
if memsize: |
|
145 |
memsize_text = memsize.group(1) |
|
146 |
preview.add_info(Unity.InfoHint.new("memsize", _("Base Memory"), None, memsize_text)) |
|
147 |
if line.startswith('VRAM size:'): |
|
148 |
vramsize = re.match(r'.*:\s+([^\s].*)',line) |
|
149 |
if vramsize: |
|
150 |
vramsize_text = vramsize.group(1) |
|
151 |
preview.add_info(Unity.InfoHint.new("vramsize", _("Video Memory"), None, vramsize_text)) |
|
152 |
if line.startswith('Number of CPUs:'): |
|
153 |
numcpu = re.match(r'.*:\s+([^\s].*)',line) |
|
154 |
if numcpu: |
|
155 |
numcpu_text = numcpu.group(1) |
|
156 |
preview.add_info(Unity.InfoHint.new("numcpu", _("Number of CPUs"), None, numcpu_text)) |
|
157 |
if line.startswith('State:'): |
|
158 |
state = re.match(r'.*:\s+([^\s].*)\(since\s(.*)\)',line) |
|
159 |
if state: |
|
160 |
state_text = state.group(1) |
|
161 |
date = state.group(2) |
|
162 |
||
163 |
translated_date = datetime.datetime.strptime(date[:-3], '%Y-%m-%dT%H:%M:%S.%f') |
|
164 |
preview.props.subtitle = state_text.title() + "(%s)" % translated_date.strftime('%c') |
|
165 |
icon = Gio.FileIcon.new (Gio.file_new_for_path(PROVIDER_ICON)) |
|
166 |
view_action = Unity.PreviewAction.new("launch", _("Launch"), icon) |
|
167 |
preview.add_action(view_action) |
|
168 |
return preview |
|
169 |
||
3
by David Callé
* New API |
170 |
class Scope (Unity.AbstractScope): |
171 |
def __init__(self): |
|
172 |
Unity.AbstractScope.__init__(self) |
|
173 |
||
174 |
def do_get_search_hint (self): |
|
175 |
return SEARCH_HINT |
|
176 |
||
177 |
def do_get_schema (self): |
|
178 |
'''
|
|
179 |
Adds specific metadata fields
|
|
180 |
'''
|
|
181 |
schema = Unity.Schema.new () |
|
182 |
if EXTRA_METADATA: |
|
183 |
for m in EXTRA_METADATA: |
|
184 |
schema.add_field(m['id'], m['type'], m['field']) |
|
185 |
#FIXME should be REQUIRED for credits
|
|
186 |
schema.add_field('provider_credits', 's', Unity.SchemaFieldType.OPTIONAL) |
|
187 |
return schema |
|
188 |
||
189 |
def do_get_categories (self): |
|
190 |
'''
|
|
191 |
Adds categories
|
|
192 |
'''
|
|
193 |
cs = Unity.CategorySet.new () |
|
194 |
if CATEGORIES: |
|
195 |
for c in CATEGORIES: |
|
196 |
cat = Unity.Category.new (c['id'], c['name'], |
|
197 |
Gio.ThemedIcon.new(c['icon']), |
|
198 |
c['renderer']) |
|
199 |
cs.add (cat) |
|
200 |
return cs |
|
201 |
||
202 |
def do_get_filters (self): |
|
203 |
'''
|
|
204 |
Adds filters
|
|
205 |
'''
|
|
206 |
fs = Unity.FilterSet.new () |
|
207 |
# if FILTERS:
|
|
208 |
#
|
|
209 |
return fs |
|
210 |
||
211 |
def do_get_group_name (self): |
|
212 |
return GROUP_NAME |
|
213 |
||
214 |
def do_get_unique_name (self): |
|
215 |
return UNIQUE_PATH |
|
216 |
||
217 |
def do_create_search_for_query (self, search_context): |
|
218 |
se = MySearch (search_context) |
|
219 |
return se |
|
220 |
||
21
by David Callé
Initial VirtualBox previews |
221 |
def do_create_previewer(self, result, metadata): |
222 |
rp = Preview() |
|
223 |
rp.set_scope_result(result) |
|
224 |
rp.set_search_metadata(metadata) |
|
225 |
return rp |
|
226 |
||
227 |
def do_activate(self, result, metadata, id): |
|
228 |
GLib.spawn_async(["/usr/bin/vboxmanage", "startvm", result.uri]) |
|
229 |
return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri=None) |
|
230 |
||
3
by David Callé
* New API |
231 |
def load_scope(): |
232 |
return Scope() |