# -.- coding: utf-8 -.- import pygtk import gtk import gnomeapplet import gmenu import ConfigParser import os import threading import time import gobject import syslog import dbus import dbus.service from dbus.mainloop.glib import DBusGMainLoop import gettext gettext.textdomain("lliurex-do") _=gettext.gettext iconsize_16=gtk.icon_size_from_name("16x16") if iconsize_16==gtk.ICON_SIZE_INVALID: print "registering 16x16 icon size" iconsize_16=gtk.icon_size_register("16x16", 16, 16) iconsize_24=gtk.icon_size_from_name("24x24") if iconsize_24==gtk.ICON_SIZE_INVALID: print "registering 24x24 icon size" iconsize_24=gtk.icon_size_register("24x24", 24, 24) iconsize_32=gtk.icon_size_from_name("32x32") if iconsize_32==gtk.ICON_SIZE_INVALID: print "registering 32x32 icon size" iconsize_32=gtk.icon_size_register("32x32", 32, 32) iconsize_48=gtk.icon_size_from_name("48x48") if iconsize_48==gtk.ICON_SIZE_INVALID: print "registering 48x48 icon size" iconsize_48=gtk.icon_size_register("48x48", 48, 48) iconsize_64=gtk.icon_size_from_name("64x64") if iconsize_64==gtk.ICON_SIZE_INVALID: print "registering 64x64 icon size" iconsize_64=gtk.icon_size_register("64x64", 64, 64) iconsize_128=gtk.icon_size_from_name("128x128") if iconsize_128==gtk.ICON_SIZE_INVALID: print "registering 128x128 icon size" iconsize_128 = gtk.icon_size_register("128x128", 128, 128) gtk.gdk.threads_init() class AboutWindow: def __init__(self,do_obj): self.log=do_obj.log self.win = gtk.Window() self.win.set_position(gtk.WIN_POS_CENTER) self.win.set_title(_("About")) self.win.set_deletable(False) self.win.set_property("allow-grow",False) self.win.set_size_request(300,200) vbox = gtk.VBox(homogeneous=True) self.win.add(vbox) img=gtk.image_new_from_stock("gtk-about",gtk.ICON_SIZE_DIALOG) vbox.pack_start(img) label = gtk.Label("Lliurex-Do 3.0\nCopyright: Enrique M.G. 2012\nLliureX Team") vbox.pack_start(label) hbox=gtk.HBox(homogeneous=True) btn=gtk.Button(stock="gtk-close") btn.connect("clicked",self.on_btnclose_clicked) hbox.pack_start(btn,False,False) vbox.pack_start(hbox,False,False) self.win.show_all() def on_btnclose_clicked(self,data): self.win.destroy() class ConfWindow: categories={"Office":"#8989d7d7ffff","Development":"#b1b1f0f06565","Multimedia":"#ffffb4b44e4e","Graphics":"#ffffaaaaffff","System":"#ababd7d73838"} def __init__(self,do_obj): self.log=do_obj.log self.do_obj=do_obj self.win = gtk.Window() self.win.resize(700,500) self.win.set_position(gtk.WIN_POS_CENTER) self.win.set_title(_("Settings")) hbox=gtk.HBox() vbox = gtk.VBox(homogeneous=False) vbox.padding=3 vbox.add(hbox) self.win.add(vbox) self.tbuttons=[] self.tree=gtk.TreeView() self.tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,[('text/plain', 0, 0)],gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE) self.tree.enable_model_drag_dest([('text/plain', 0, 0)],gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE) self.tree.connect("drag-data-received",self.on_drag_data_received_left) self.tree.connect("drag-data-get", self.on_drag_data_get_left) # id - icon - checked - name - color - desktop_id self.model=gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING,gobject.TYPE_STRING,gtk.gdk.Color,gobject.TYPE_STRING) self.tree.set_model(self.model) colname = gtk.TreeViewColumn(_("Available applications")) colicon = gtk.TreeViewColumn() self.tree.append_column(colicon) self.tree.append_column(colname) cell = gtk.CellRendererText() colname.pack_start(cell,True) colname.add_attribute(cell,"text",2) colname.add_attribute(cell,"cell-background-gdk",3) cell = gtk.CellRendererPixbuf() cell.set_property("stock-size",iconsize_64) colicon.pack_start(cell,True) colicon.add_attribute(cell,"icon-name",1) colicon.add_attribute(cell,"cell-background-gdk",3) scr=gtk.ScrolledWindow() scr.add(self.tree) hbox.pack_start(scr) #self.win.add(self.tree) self.tree_apps=gtk.TreeView() self.tree_apps.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,[('text/plain', 0, 0)],gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE) self.tree_apps.enable_model_drag_dest([('text/plain', 0, 0)],gtk.gdk.ACTION_DEFAULT) self.tree_apps.connect("drag-data-received",self.on_drag_data_received_right) self.tree_apps.connect("drag-data-get",self.on_drag_data_get_right) scr=gtk.ScrolledWindow() scr.add(self.tree_apps) hbox.pack_end(scr) self.model_apps=gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING,gobject.TYPE_STRING,gtk.gdk.Color,gobject.TYPE_STRING) self.tree_apps.set_model(self.model_apps) colname = gtk.TreeViewColumn(_("Custom applications")) colicon = gtk.TreeViewColumn() self.tree_apps.append_column(colicon) self.tree_apps.append_column(colname) cell = gtk.CellRendererText() colname.pack_start(cell,True) colname.add_attribute(cell,"text",2) cell = gtk.CellRendererPixbuf() cell.set_property("stock-size",iconsize_64) colicon.pack_start(cell,True) colicon.add_attribute(cell,"icon-name",1) tree_apps=gmenu.lookup_tree("applications.menu") dir_apps = tree_apps.get_root_directory() self.current_category="" for entry in dir_apps.contents: if(entry.get_type()==gmenu.TYPE_ENTRY): print "* ",entry.get_name() if(entry.get_type()==gmenu.TYPE_DIRECTORY): print "exploring [",entry.get_name(),"]" id=entry.get_menu_id() isFound=id in do_obj.applications self.current_category=id #itr=self.model.append(None,[entry.get_name(),entry.get_icon(),isFound,id]) self.aux_dir_explore(entry,None) for app in do_obj.applications: q = self.model.get_iter_first() while not q==None: if self.model.get_value(q,4)==app: self.model_apps.append([self.model.get_value(q,0),self.model.get_value(q,1),self.model.get_value(q,2),self.model.get_value(q,3),self.model.get_value(q,4)]) break q=self.model.iter_next(q) hbox2 = gtk.HBox() hbox2.set_homogeneous(False) hbox2.padding=3 btncancel=gtk.Button(stock=gtk.STOCK_CANCEL) btncancel.connect("button_press_event",self.on_btncancel) hbox2.pack_end(btncancel,False,False) btnok=gtk.Button(stock=gtk.STOCK_OK) btnok.connect("button_press_event",self.on_btnok) hbox2.pack_end(btnok,False,False) vbox.pack_end(hbox2,False,False) self.win.show_all() def aux_dir_explore(self,entry,itr): for d in entry.contents: if(d.get_type()==gmenu.TYPE_ENTRY): #print "> ", d.get_name(), ">",self.current_category if self.current_category in ConfWindow.categories: backcolor=ConfWindow.categories[self.current_category] else: backcolor="#ffffffffffff" print d.get_desktop_file_id() icon_id=d.get_icon() if not icon_id==None: if not gtk.icon_theme_get_default().has_icon(icon_id): print "Icon not found:",icon_id icon_id="empty" else: icon_id="empty" self.model.append([d.get_name(),icon_id,d.get_name(),gtk.gdk.Color(backcolor),d.get_desktop_file_id()]) #if d.get_desktop_file_id() in self.do_obj.applications: #self.model_apps.append([d.get_name(),d.get_icon(),d.get_name(),gtk.gdk.Color(backcolor),d.get_desktop_file_id()]) if d.get_type()==gmenu.TYPE_DIRECTORY: print "exploring >[",entry.get_name(),"]" id=d.get_menu_id() isFound=id in self.do_obj.applications self.current_category=id #self.model.append(None,[d.get_name(),d.get_icon(),isFound,id]) self.aux_dir_explore(d,None) def on_drag_data_get_left(self, treeview, context, selection, target_id,etime): treeselection = treeview.get_selection() model, iter = treeselection.get_selected() data = model.get_value(iter, 0) selection.set(selection.target, 8, model.get_string_from_iter(iter)) def on_drag_data_get_right(self, treeview, context, selection, target_id,etime): treeselection = treeview.get_selection() model, iter = treeselection.get_selected() data = model.get_value(iter, 0) selection.set(selection.target, 8, model.get_string_from_iter(iter)) def on_drag_data_received_left(self,treeview, context, x, y, selection,info, etime): if context.get_source_widget()==self.tree_apps: iter=self.model_apps.get_iter_from_string( selection.data ) self.model_apps.remove(iter) return True def on_drag_data_received_right(self,treeview, context, x, y, selection,info, etime): #reordering elements if context.get_source_widget()==self.tree_apps: iter_source=self.model_apps.get_iter_from_string( selection.data ) dest_data=treeview.get_path_at_pos(x,y) if not dest_data==None: path,tvc,cx,cy=dest_data iter_dest=self.model_apps.get_iter(path) self.model_apps.move_before(iter_source,iter_dest) #this triggers when inserting at last position else: q = self.model_apps.get_iter_first() while not q==None: last=q q=self.model_apps.iter_next(q) self.model_apps.move_before(iter_source,q) #adding new element if context.get_source_widget()==self.tree: iter=self.model.get_iter_from_string( selection.data ) self.log("drop!!!->"+str(self.model.get_value(iter,0))) id=self.model.get_value(iter,4) q = self.model_apps.get_iter_first() while not q==None: if id==self.model_apps.get_value(q,4): self.log("desktop already added") return q=self.model_apps.iter_next(q) dest_data=treeview.get_path_at_pos(x,y) if dest_data==None: self.model_apps.append([self.model.get_value(iter,0),self.model.get_value(iter,1),self.model.get_value(iter,2),self.model.get_value(iter,3),self.model.get_value(iter,4)]) else: path,tvc,cx,cy=dest_data dest_iter=self.model_apps.get_iter(path) self.model_apps.insert_before(dest_iter,[self.model.get_value(iter,0),self.model.get_value(iter,1),self.model.get_value(iter,2),self.model.get_value(iter,3),self.model.get_value(iter,4)]) def on_toggle(self,widget,path): self.log("toggled! %s"%(path)) iter=self.model.get_iter(path) value=self.model.get_value(iter,2) self.model.set_value(iter,2,not value) def on_btnok(self,widget,data): current_categories=[] iter = self.model_apps.get_iter_first() while not iter==None: value = self.model_apps.get_value(iter,4) current_categories.append(value) iter = self.model_apps.iter_next(iter) self.do_obj.save_config(current_categories) self.win.destroy() def on_btncancel(self,widget,data): self.win.destroy() class TouchWindow: SCROLL_LEFT=180 SCROLL_RIGHT=-180 def __init__(self,do_obj,desktops): self.log=do_obj.log self.scroller_thread=None self.log("TouchWindow init") self.log("Screen res:%dx%d"%(gtk.gdk.screen_width(),gtk.gdk.screen_height())) self.scroll_petitions=[] self.win = gtk.Window() self.win.resize(200,200) self.win.set_position(gtk.WIN_POS_CENTER) self.win.set_decorated(False) self.win.connect("key-press-event",self.on_key_press) hbox = gtk.HBox() self.win.add(hbox) #previous button img = gtk.Image() img.set_from_icon_name("go-previous",iconsize_64) btn=gtk.Button() btn.connect("button_press_event",self.on_button_backward_click) vbx=gtk.VBox() vbx.pack_start(img) btn.add(vbx) hbox.pack_start(btn) self.scrbox=gtk.ScrolledWindow() hbox_internal = gtk.HBox() hbox.pack_end(self.scrbox) self.scrbox.add_with_viewport(hbox_internal) resx = int((gtk.gdk.screen_width()-200) / 180) #minimum size if resx<2: resx=2 self.log("Size:%d"%(resx)) self.resx=resx self.total_resx=len(desktops)/2 self.scrbox.set_size_request(resx*180,400) self.scrbox.set_policy(gtk.POLICY_NEVER,gtk.POLICY_NEVER) vbox_internal=None for n in range(len(desktops)): name,icon_name,exec_name=desktops[n] self.log("generating button %s" % (name)) img=gtk.Image() #this may detect whenever icon_name is, unfortunately, an absolute path if icon_name==None: img.set_from_icon_name("image-missing",iconsize_128) else: if "/" in icon_name: if os.path.exists(icon_name): img.set_from_file(icon_name) else: img.set_from_icon_name("image-missing",iconsize_128) else: if os.path.exists("/usr/share/pixmaps/"+icon_name) and os.path.isfile("/usr/share/pixmaps/"+icon_name): img.set_from_file("/usr/share/pixmaps/"+icon_name) else: img.set_from_icon_name(icon_name,iconsize_128) btn=gtk.Button() btn.connect("button_press_event",self.on_button_exec,desktops[n]) btn.set_size_request(180,200) vbx=gtk.VBox() vbx.pack_start(img) vbx.pack_end(gtk.Label(name)) btn.add(vbx) if vbox_internal==None: vbox_internal=gtk.VBox() vbox_internal.pack_start(btn) hbox_internal.pack_start(vbox_internal) else: vbox_internal.pack_start(btn) vbox_internal=None #next button img = gtk.Image() img.set_from_icon_name("go-next",iconsize_64) btn=gtk.Button() btn.connect("button_press_event",self.on_button_forward_click) vbx=gtk.VBox() vbx.pack_start(img) btn.add(vbx) hbox.pack_end(btn) hbox.reorder_child(btn,gtk.PACK_END) def on_button_exec(self,widget,data,extra): name,icon,exe=extra tmp=exe.replace("%f","") tmp=tmp.replace("%F","") tmp=tmp.replace("%u","") tmp=tmp.replace("%U","") tmp=tmp.replace("%i","") tmp=tmp.replace("%c","") tmp=tmp.replace("%k","") tmp=tmp.strip() self.log("Executing:%s" % (tmp)) #ToDo improve exe mechanism os.system(tmp+" &") self.win.hide() def scroll_callback(self,pos): print "scroll thread enter" last=-1 x=0 while True: speed=6 time.sleep(0.005) gtk.gdk.threads_enter() value=self.scrbox.get_hscrollbar().get_value() gtk.gdk.threads_leave() if x==pos: print "scroll thread leaves" break if value==last: print "find a limit, scroll thread leaves" break gtk.gdk.threads_enter() dist=(abs(x-pos)) if dist>540: speed=12 if dist<50: speed=2 if x>=pos: speed=-speed self.scrbox.get_hscrollbar().set_value(value+speed) x=x+speed gtk.gdk.threads_leave() last=value def on_button_forward_click(self,widget,data): self.perform_scroll(self.SCROLL_LEFT) def on_button_backward_click(self,widget,data): self.perform_scroll(self.SCROLL_RIGHT) def on_key_press(self,widget,data): if self.is_showing(): if gtk.gdk.keyval_name(data.keyval)=="Right": self.perform_scroll(self.SCROLL_LEFT) if gtk.gdk.keyval_name(data.keyval)=="Left": self.perform_scroll(self.SCROLL_RIGHT) if gtk.gdk.keyval_name(data.keyval)=="Page_Down": self.perform_scroll(self.SCROLL_RIGHT*self.resx) if gtk.gdk.keyval_name(data.keyval)=="Page_Up": self.perform_scroll(self.SCROLL_LEFT*self.resx) if gtk.gdk.keyval_name(data.keyval)=="Home": self.perform_scroll(self.SCROLL_RIGHT*self.total_resx) if gtk.gdk.keyval_name(data.keyval)=="End": self.perform_scroll(self.SCROLL_LEFT*self.total_resx) self.log("keypress:%s"%(gtk.gdk.keyval_name(data.keyval))) else: self.log("ignoring key press event") return False def perform_scroll(self,direction): if not self.scroller_thread==None: if not self.scroller_thread.is_alive(): self.scroller_thread=threading.Thread(target=self.scroll_callback,args=(direction,)) self.scroller_thread.start() else: self.log("scroll thread already running") else: self.scroller_thread=threading.Thread(target=self.scroll_callback,args=(direction,)) self.scroller_thread.start() def show(self): self.log("show") self.win.set_position(gtk.WIN_POS_CENTER_ALWAYS) self.win.show_all() def hide(self): self.log("hide") self.win.hide() def is_showing(self): return self.win.get_property("visible") def destroy(self): self.win.destroy() class Do(dbus.service.Object): def __init__(self): self.log("init") self.applications=[] DBusGMainLoop(set_as_default=True) bus_name = dbus.service.BusName("net.lliurex.do", bus=dbus.SessionBus()) dbus.service.Object.__init__(self,bus_name, "/net/lliurex/do") @dbus.service.method("net.lliurex.do") def show(self): if self.touchwindow.is_showing(): self.touchwindow.hide() else: self.touchwindow.show() def log(self,txt): print "[Lliurex-Do]:%s" % (txt) #syslog.syslog("[Lliurex-Do]:%s" % (txt)) def create_menu(self): propxml=""" """ propxml=propxml.replace("_About","_"+_("About")) propxml=propxml.replace("_Settings","_"+_("Settings")) verbs = [(("About"), self.show_about_dialog),(("Settings"), self.show_settings)] self.applet.setup_menu(propxml, verbs, None) def show_about_dialog(self,*arguments, **keywords): abt = AboutWindow(self) def show_settings(self,*arguments, **keywords): conf = ConfWindow(self) def check_config(self): self.log("checking set up files") if not os.path.exists(os.path.expanduser("~/.lliurex-do")): self.log("creating setup dir") os.mkdir(os.path.expanduser("~/.lliurex-do")) def importV2ToV3(self,categories,contents,found): for entry in contents: if(entry.get_type()==gmenu.TYPE_ENTRY): if(found): print "IMPORT: ",entry.get_desktop_file_id() self.applications.append(entry.get_desktop_file_id()) if(entry.get_type()==gmenu.TYPE_DIRECTORY): print "* ",entry.get_menu_id() if(found): self.importV2ToV3(categories,entry.contents,found) else: for ca in categories: if ca==entry.get_menu_id(): print "IMPORT DIR:",ca self.importV2ToV3(categories,entry.contents,True) def load_config(self): self.log("loading set up") setup_path=os.path.expanduser("~/.lliurex-do/setup.cfg") self.config=ConfigParser.ConfigParser() if not os.path.exists(setup_path): self.log("setup file not found, using defaults") else: self.config.read(setup_path) if(self.config.has_option("setup","applications")): tmp=self.config.get("setup","applications") self.applications=tmp.split(",") else: self.applications=[] self.log("No applications option found, trying to guess config file version") if(self.config.has_option("setup","categories")): self.log("Guessed lliurex-do V2 setup file") self.log("Trying to import settings...") tmp = self.config.get("setup","categories") categories=tmp.split(",") self.log("Importing categories:"+str(categories)) tree_apps=gmenu.lookup_tree("applications.menu") dir_apps = tree_apps.get_root_directory() self.importV2ToV3(categories,dir_apps.contents,False) else: self.log("failed to guess config file type") def save_config(self,data_list): print "*** Saving config ***" setup_path=os.path.expanduser("~/.lliurex-do/setup.cfg") f = open(setup_path,"wb") tmp="" for data in data_list: tmp=tmp+","+data tmp=tmp.lstrip(",") if(not self.config.has_section("setup")): self.config.add_section("setup") self.config.set("setup","applications",tmp) self.config.write(f) f.close() #this shpuld be moved away tmp=self.config.get("setup","applications") self.applications=tmp.split(",") self.rebuild() self.touchwindow.destroy() self.touchwindow=TouchWindow(self,self.desktops) def on_tree_change(self,tree): self.log(tree) def on_applet_click(self,widget,event): self.log("button pressed") if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: self.create_menu() widget.emit_stop_by_name("button_press_event") if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1: if self.touchwindow.is_showing(): self.touchwindow.hide() else: self.touchwindow.show() def aux_build(self,entry,target): if(entry.get_type()==gmenu.TYPE_ENTRY): id=entry.get_desktop_file_id() if id==target: desktop=(entry.get_name(),entry.get_icon(),entry.get_exec()) self.desktops.append(desktop) if(entry.get_type()==gmenu.TYPE_DIRECTORY): for app in entry.contents: self.aux_build(app,target) def rebuild(self): self.desktops=[] dir_apps = self.tree_apps.get_root_directory() for app in self.applications: self.aux_build(dir_apps,app) def change_background(self,applet, type, color, pixmap): self.log('change_background') applet.set_style(None) applet.modify_style(gtk.RcStyle()) applet.set_applet_flags(gnomeapplet.EXPAND_MINOR) if type == gnomeapplet.COLOR_BACKGROUND: applet.modify_bg(gtk.STATE_NORMAL, color) elif type == gnomeapplet.PIXMAP_BACKGROUND: applet.get_style().bg_pixmap[gtk.STATE_NORMAL] = pixmap if applet.get_window()==None: return x,y,w,h,z=applet.get_window().get_geometry() self.log("geometry:%d"%(h)) if h<=32: self.image.set_from_icon_name("lliurex-do",iconsize_16) if h<48 and h>34: self.image.set_from_icon_name("lliurex-do",iconsize_24) if h<55 and h>=48: self.image.set_from_icon_name("lliurex-do",iconsize_32) if h>55 and h<70: self.image.set_from_icon_name("lliurex-do",iconsize_48) if h>70: self.image.set_from_icon_name("lliurex-do",iconsize_64) def size_allocate(self,widget,rect): if widget.window: x,y,w,h,z = widget.window.get_geometry() self.log("New size allocation:%d"%(h)) def factory(self,applet,iid): self.log("factory") self.applet=applet self.check_config() self.load_config() self.tree_apps=gmenu.lookup_tree("applications.menu") self.tree_apps.add_monitor(self.on_tree_change) self.rebuild() #self.windows.append(DoWindow(self,self.desktops)) #self.windows[0].Show() #self.applet = gnomeapplet.Applet() #self.factory(self.applet) button = gtk.Button() button.set_relief(gtk.RELIEF_NONE) button.set_border_width(0) button.connect("button_press_event", self.on_applet_click) #applet.connect("key-press-event",self.on_key_press) applet.add(button) #applet.set_flags(gnomeapplet.SIZE_MEDIUM) applet.connect('change-background',self.change_background) applet.connect('size-allocate',self.size_allocate) self.image = gtk.Image() self.image.set_padding(0,0) self.image.set_from_icon_name("lliurex-do",iconsize_16) button.add(self.image) applet.show_all() self.touchwindow = TouchWindow(self,self.desktops) self.log("ready")