1 | #!/usr/bin/env python3 |
---|
2 | |
---|
3 | |
---|
4 | import gi |
---|
5 | gi.require_version('Gtk', '3.0') |
---|
6 | from gi.repository import Gtk, Pango, GdkPixbuf, Gdk, Gio, GObject,GLib |
---|
7 | |
---|
8 | import copy |
---|
9 | import gettext |
---|
10 | import Core |
---|
11 | |
---|
12 | #import Dialog |
---|
13 | import dialog |
---|
14 | import time |
---|
15 | import threading |
---|
16 | import os |
---|
17 | from os.path import splitext |
---|
18 | import subprocess |
---|
19 | import tempfile |
---|
20 | |
---|
21 | import airmanager.airmanager as installer |
---|
22 | |
---|
23 | import gettext |
---|
24 | gettext.textdomain('air_manager') |
---|
25 | _ = gettext.gettext |
---|
26 | |
---|
27 | |
---|
28 | |
---|
29 | RSRC="/usr/share/air-manager/rsrc/" |
---|
30 | |
---|
31 | CSS_FILE=RSRC + "air-manager.css" |
---|
32 | DROP_FILE=RSRC+"drop_file.png" |
---|
33 | DROP_CORRECT=RSRC+"drop_file_correct.png" |
---|
34 | DROP_INCORRECT=RSRC+"drop_file_incorrect.png" |
---|
35 | DRAG_ACTION = Gdk.DragAction.COPY |
---|
36 | GTK_SPACING=6 |
---|
37 | |
---|
38 | class InstallBox(Gtk.VBox): |
---|
39 | |
---|
40 | def __init__(self): |
---|
41 | Gtk.VBox.__init__(self) |
---|
42 | self.DROP_CORRECT=RSRC+"drop_file_correct.png" |
---|
43 | self.pb=GdkPixbuf.Pixbuf() |
---|
44 | self.air_file='' |
---|
45 | |
---|
46 | self.core=Core.Core.get_core() |
---|
47 | self.commonFunc=CommonFunc() |
---|
48 | |
---|
49 | builder=Gtk.Builder() |
---|
50 | builder.set_translation_domain('air_manager') |
---|
51 | ui_path=RSRC + "air-manager.ui" |
---|
52 | builder.add_from_file(ui_path) |
---|
53 | |
---|
54 | self.lbl_air=builder.get_object("install_label") |
---|
55 | self.lbl_air.props.halign=Gtk.Align.START |
---|
56 | #Declared here because DropArea needs this references |
---|
57 | self.img_icon=Gtk.Image() |
---|
58 | self.pb=self.img_icon.get_pixbuf() |
---|
59 | self.lbl_drop=Gtk.Label() |
---|
60 | |
---|
61 | self.main_box=builder.get_object("install_data_box") |
---|
62 | self.drop=builder.get_object("btn_drop") |
---|
63 | self.drop.set_name("BTN_MENU") |
---|
64 | self.drop.connect('clicked',self._filechooser) |
---|
65 | self.drop_label=builder.get_object("drop_label") |
---|
66 | |
---|
67 | self.install_button=builder.get_object("install_button") |
---|
68 | self.install_label=builder.get_object("install_label") |
---|
69 | |
---|
70 | self.check_window=builder.get_object("check_window") |
---|
71 | self.check_label=builder.get_object("check_label") |
---|
72 | self.check_pbar=builder.get_object("check_pbar") |
---|
73 | |
---|
74 | drop_param=[self.install_label,self.install_button] |
---|
75 | self.drop_area=DropArea(drop_param) |
---|
76 | self.drop_area.set_info_widgets(self.lbl_drop,self.img_icon) |
---|
77 | drop_box=Gtk.Box(spacing=12) |
---|
78 | msg=_("<b>Drag</b> an air file\nor <b>click</b> to choose") |
---|
79 | self.lbl_drop.set_markup(msg) |
---|
80 | drop_box.add(self.lbl_drop) |
---|
81 | drop_box.add(self.drop_area) |
---|
82 | drop_box.props.halign=Gtk.Align.CENTER |
---|
83 | self.drop.add(drop_box) |
---|
84 | self.drop.set_margin_top(12) |
---|
85 | |
---|
86 | self.img_icon.set_from_file(RSRC+"air-installer_icon.png") |
---|
87 | self.pb=self.img_icon.get_pixbuf() |
---|
88 | |
---|
89 | lbl_text=_("Click to <b>select an icon</b> for the app or use the app's default icon") |
---|
90 | lbl_icon=Gtk.Label() |
---|
91 | lbl_icon.props.halign=Gtk.Align.END |
---|
92 | lbl_icon.props.hexpand=False |
---|
93 | lbl_icon.set_markup(lbl_text) |
---|
94 | lbl_icon.set_name('label') |
---|
95 | lbl_icon.set_max_width_chars(20) |
---|
96 | lbl_icon.set_width_chars(20) |
---|
97 | lbl_icon.set_line_wrap(True) |
---|
98 | |
---|
99 | self.box_icon=Gtk.Box(spacing=6) |
---|
100 | self.box_icon.add(lbl_icon) |
---|
101 | self.box_icon.add(self.img_icon) |
---|
102 | |
---|
103 | self.btn_icon=builder.get_object('btn_icon') |
---|
104 | self.btn_icon.set_name("BTN_MENU") |
---|
105 | self.box_icon.props.halign=Gtk.Align.CENTER |
---|
106 | self.btn_icon.add(self.box_icon) |
---|
107 | self.btn_icon.set_margin_top(12) |
---|
108 | |
---|
109 | |
---|
110 | self.pack_start(self.main_box,True,True,0) |
---|
111 | |
---|
112 | self.add_text_targets() |
---|
113 | self.connect_signals() |
---|
114 | self.set_css_info() |
---|
115 | self.install_button.set_sensitive(False) |
---|
116 | self.btn_icon.connect("clicked",self._set_app_icon) |
---|
117 | GObject.threads_init() |
---|
118 | #def __init__ |
---|
119 | |
---|
120 | def _debug(self,msg): |
---|
121 | print("InstallBox: %s"%msg) |
---|
122 | #def _debug |
---|
123 | |
---|
124 | def _filechooser(self,*args): |
---|
125 | dw=Gtk.FileChooserDialog(_("Select air package"),None,Gtk.FileChooserAction.OPEN,(Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN,Gtk.ResponseType.OK)) |
---|
126 | dw.set_action(Gtk.FileChooserAction.OPEN) |
---|
127 | file_filter=Gtk.FileFilter() |
---|
128 | file_filter.add_pattern('*.air') |
---|
129 | file_filter.set_name("Air files") |
---|
130 | dw.add_filter(file_filter) |
---|
131 | air_chooser=dw.run() |
---|
132 | if air_chooser==Gtk.ResponseType.OK: |
---|
133 | self.install_label.set_text('') |
---|
134 | self.drop_area.air_file=dw.get_filename() |
---|
135 | self.drop_area.set_from_file(DROP_CORRECT) |
---|
136 | self.install_button.set_sensitive(True) |
---|
137 | self.lbl_drop.set_markup(_("<b>Selected app:</b>\n%s")%os.path.basename(self.drop_area.air_file)) |
---|
138 | air_info=installer.AirManager().get_air_info(self.drop_area.air_file) |
---|
139 | self.img_icon.set_from_pixbuf(air_info['pb']) |
---|
140 | self.pb=air_info['pb'] |
---|
141 | dw.destroy() |
---|
142 | #def _filechooser |
---|
143 | |
---|
144 | def add_text_targets(self): |
---|
145 | self.drop_area.drag_dest_set_target_list(None) |
---|
146 | self.drop_area.drag_dest_add_text_targets() |
---|
147 | #def add_text_targets |
---|
148 | |
---|
149 | def _set_app_icon(self,widget): |
---|
150 | |
---|
151 | def _update_preview(*arg): |
---|
152 | if dw.get_preview_filename(): |
---|
153 | if os.path.isfile(dw.get_preview_filename()): |
---|
154 | pb=GdkPixbuf.Pixbuf.new_from_file_at_scale(dw.get_preview_filename(),64,-1,True) |
---|
155 | img_preview.set_from_pixbuf(pb) |
---|
156 | img_preview.show() |
---|
157 | else: |
---|
158 | img_preview.hide() |
---|
159 | |
---|
160 | dw=Gtk.FileChooserDialog(_("Select icon"),None,Gtk.FileChooserAction.OPEN,(Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN,Gtk.ResponseType.OK)) |
---|
161 | dw.set_action(Gtk.FileChooserAction.OPEN) |
---|
162 | img_preview=Gtk.Image() |
---|
163 | img_preview.set_margin_right(GTK_SPACING) |
---|
164 | file_filter=Gtk.FileFilter() |
---|
165 | file_filter.add_pixbuf_formats() |
---|
166 | file_filter.set_name("images") |
---|
167 | dw.add_filter(file_filter) |
---|
168 | dw.set_preview_widget(img_preview) |
---|
169 | img_preview.show() |
---|
170 | dw.set_use_preview_label(False) |
---|
171 | dw.set_preview_widget_active(True) |
---|
172 | dw.connect("update-preview",_update_preview) |
---|
173 | new_icon=dw.run() |
---|
174 | if new_icon==Gtk.ResponseType.OK: |
---|
175 | self.pb=GdkPixbuf.Pixbuf.new_from_file_at_scale(dw.get_filename(),64,-1,True) |
---|
176 | self.img_icon.set_from_pixbuf(self.pb) |
---|
177 | dw.destroy() |
---|
178 | #def _set_app_icon |
---|
179 | |
---|
180 | def connect_signals(self): |
---|
181 | self.install_button.connect("clicked",self.accept_install_clicked) |
---|
182 | #def connect_signals |
---|
183 | |
---|
184 | def set_css_info(self): |
---|
185 | self.style_provider=Gtk.CssProvider() |
---|
186 | |
---|
187 | f=Gio.File.new_for_path(CSS_FILE) |
---|
188 | self.style_provider.load_from_file(f) |
---|
189 | |
---|
190 | Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),self.style_provider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) |
---|
191 | self.check_label.set_name("MSG_LABEL") |
---|
192 | #def set_css_info |
---|
193 | |
---|
194 | def accept_install_clicked(self,widget): |
---|
195 | self.install_label.set_text("") |
---|
196 | tmp_icon=tempfile.mkstemp()[1] |
---|
197 | self._debug(tmp_icon) |
---|
198 | #Copy the icon to temp folder |
---|
199 | self.pb=self.img_icon.get_pixbuf() |
---|
200 | self.pb.savev(tmp_icon,'png',[""],[""]) |
---|
201 | air_file=self.drop_area.air_file |
---|
202 | self.install_t=threading.Thread(target=self.install,args=[air_file,tmp_icon]) |
---|
203 | self.install_t.daemon=True |
---|
204 | self.install_t.start() |
---|
205 | self.check_window.show_all() |
---|
206 | GLib.timeout_add(100,self.pulsate_install) |
---|
207 | #def accept_install_clicked |
---|
208 | |
---|
209 | def pulsate_install(self): |
---|
210 | if self.install_t.is_alive(): |
---|
211 | self.check_pbar.pulse() |
---|
212 | return True |
---|
213 | |
---|
214 | else: |
---|
215 | self.check_window.hide() |
---|
216 | if self.install_err: |
---|
217 | self.install_label.set_name("MSG_LABEL") |
---|
218 | msg=self.commonFunc.get_msg(self.install_err) |
---|
219 | else: |
---|
220 | self.install_label.set_name("MSG_ERROR_LABEL") |
---|
221 | msg=self.commonFunc.get_msg(0) |
---|
222 | |
---|
223 | self.install_label.set_text(msg) |
---|
224 | |
---|
225 | return False |
---|
226 | #def pulsate_install |
---|
227 | |
---|
228 | def install(self,air_file,icon): |
---|
229 | self.install_err=True |
---|
230 | subprocess.check_call(['/usr/bin/xhost','+']) |
---|
231 | try: |
---|
232 | ins=subprocess.check_call(['pkexec','/usr/bin/air-helper-installer.py','install',air_file,icon]) |
---|
233 | self.install_err=False |
---|
234 | except Exception as e: |
---|
235 | self._debug(e) |
---|
236 | subprocess.check_output(["xdg-mime","install","/usr/share/mime/packages/x-air-installer.xml"]) |
---|
237 | subprocess.check_output(["xdg-mime","default","/usr/share/applications/air-installer.desktop","/usr/share/mime/packages/x-air-installer.xml"],input=b"") |
---|
238 | subprocess.check_call(['/usr/bin/xhost','-']) |
---|
239 | #def install |
---|
240 | |
---|
241 | #class InstallBox |
---|
242 | |
---|
243 | |
---|
244 | class DropArea(Gtk.Image): |
---|
245 | |
---|
246 | def __init__(self,drop_param): |
---|
247 | self.dbg=True |
---|
248 | self.drop=False |
---|
249 | self.commonFunc=CommonFunc() |
---|
250 | self.install_label=drop_param[0] |
---|
251 | self.install_button=drop_param[1] |
---|
252 | Gtk.Image.__init__(self) |
---|
253 | Gtk.Image.set_from_file(self,DROP_FILE) |
---|
254 | self.drag_dest_set(Gtk.DestDefaults.ALL, [], DRAG_ACTION) |
---|
255 | self.air_file='' |
---|
256 | self.info_label='' |
---|
257 | |
---|
258 | self.connect("drag-data-received", self.on_drag_data_received) |
---|
259 | |
---|
260 | self.text="" |
---|
261 | #def __init__ |
---|
262 | |
---|
263 | def _debug(self,msg): |
---|
264 | if self.dbg: |
---|
265 | print("DropArea: %s"%msg) |
---|
266 | #def _debug |
---|
267 | |
---|
268 | def set_info_widgets(self,gtkLabel,gtkImg): |
---|
269 | self.info_label=gtkLabel |
---|
270 | self.img_icon=gtkImg |
---|
271 | |
---|
272 | def on_drag_data_received(self, widget, drag_context, x,y, data,info, time): |
---|
273 | self.drop=True |
---|
274 | text = data.get_text() |
---|
275 | text=text.strip().split("//") |
---|
276 | |
---|
277 | text[1]=text[1].replace('%20',' ') |
---|
278 | self._debug("Checking %s"%text[1]) |
---|
279 | check=self.commonFunc.check_extension(text[1]) |
---|
280 | |
---|
281 | if check["status"]: |
---|
282 | self.install_label.set_text('') |
---|
283 | Gtk.Image.set_from_file(self,DROP_CORRECT) |
---|
284 | self.install_button.set_sensitive(True) |
---|
285 | self.air_file=text[1] |
---|
286 | if self.info_label: |
---|
287 | self.info_label.set_markup(_("<b>Selected app:</b>\n%s")%os.path.basename(self.air_file)) |
---|
288 | air_info=installer.AirManager().get_air_info(self.air_file) |
---|
289 | self.img_icon.set_from_pixbuf(air_info['pb']) |
---|
290 | self.pb=air_info['pb'] |
---|
291 | self._debug("File %s"%self.air_file) |
---|
292 | else: |
---|
293 | Gtk.Image.set_from_file(self,DROP_INCORRECT) |
---|
294 | self.install_button.set_sensitive(False) |
---|
295 | self.air_file='' |
---|
296 | |
---|
297 | param={'status':check['status'],'code':check['code'],'file_name':text[1],'install_label':self.install_label,'install_button':self.install_button} |
---|
298 | self.commonFunc.manage_outputinfo(**param) |
---|
299 | #def on_drag_data_received |
---|
300 | |
---|
301 | #class DropArea |
---|
302 | |
---|
303 | |
---|
304 | class CommonFunc(): |
---|
305 | |
---|
306 | def _debug(self,msg): |
---|
307 | print("%s"%msg) |
---|
308 | |
---|
309 | def check_extension(self,file_name): |
---|
310 | result={} |
---|
311 | result["status"]="" |
---|
312 | result["code"]="" |
---|
313 | |
---|
314 | if file_name ==None: |
---|
315 | result["status"]=False |
---|
316 | result["code"]=0 |
---|
317 | else: |
---|
318 | try: |
---|
319 | if file_name.endswith('.air'): |
---|
320 | result["status"]=True |
---|
321 | else: |
---|
322 | result["status"]=False |
---|
323 | result["code"]=1 |
---|
324 | except: |
---|
325 | result["status"]=False |
---|
326 | result["code"]=2 |
---|
327 | print ("Unable to detect extension" ) |
---|
328 | self._debug(result) |
---|
329 | return result |
---|
330 | #def check_extension |
---|
331 | |
---|
332 | def manage_outputinfo(self,**kwargs): |
---|
333 | if kwargs: |
---|
334 | path=os.path.dirname(kwargs['file_name']) |
---|
335 | #def manage_ouputinfo |
---|
336 | |
---|
337 | def get_msg(self,code): |
---|
338 | msg_text='' |
---|
339 | if code==0: |
---|
340 | msg_text=_("Installation successful") |
---|
341 | |
---|
342 | elif code==1: |
---|
343 | msg_text=_("Error: File with incorret extension .air is required") |
---|
344 | |
---|
345 | elif code==2: |
---|
346 | msg_text=_("Error: Unable to detect file extension") |
---|
347 | |
---|
348 | elif code==3: |
---|
349 | msg_text=_("Error: Unknown") |
---|
350 | |
---|
351 | elif code==4: |
---|
352 | msg_text=_("Error: Unknown") |
---|
353 | elif code==10: |
---|
354 | msg_text=_("Error: Unknown") |
---|
355 | |
---|
356 | elif code==11: |
---|
357 | msg_text=_("Error: Unknown") |
---|
358 | |
---|
359 | elif code==12: |
---|
360 | msg_text=_("Error: Unknown") |
---|
361 | |
---|
362 | elif code==13: |
---|
363 | msg_text=_("Error: Unknown") |
---|
364 | |
---|
365 | elif code==14: |
---|
366 | msg_text=_("Error: Unknown") |
---|
367 | |
---|
368 | elif code==15: |
---|
369 | msg_text=_("Error: Unknown") |
---|
370 | |
---|
371 | elif code==16: |
---|
372 | msg_text=_("Error: Unknown") |
---|
373 | return msg_text |
---|
374 | #def get_msg |
---|