1
2
3
4
5
6
7
8
9
10
11
12
13
14 import screenlets
15 import gtk
16 import dbus
17 import os
18 import sys
19 import stat
20 import gettext
21 import re
22 import urllib
23 gettext.textdomain('screenlets')
24 gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX + '/share/locale')
25 import gobject
26 from distutils.version import LooseVersion
27 from subprocess import *
28 from HTMLParser import HTMLParser
29 from BeautifulSoup import BeautifulStoneSoup
30 try:
31 import gnomevfs
32 except:
33 pass
35 return gettext.gettext(s)
36
37
38
39
40
41
44 self.reset()
45 self.fed = []
49 return ''.join(self.fed)
50
52 """Simple html to pango stripper."""
53 s = MLStripper()
54 s.feed(html)
55 no_html = s.get_data()
56 decoded = BeautifulStoneSoup(no_html, convertEntities=BeautifulStoneSoup.HTML_ENTITIES)
57 result = decoded.encode("UTF-8")
58 return result.strip(" \n")
59
60
62 """Returns the system autostart directory"""
63 desktop_environment = 'gnome'
64
65 if os.environ.get('KDE_FULL_SESSION') == 'true':
66 desktop_environment = 'kde'
67 elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
68 desktop_environment = 'gnome'
69 else:
70 try:
71 import commands
72 info = commands.getoutput('xprop -root _DT_SAVE_MODE')
73 if ' = "xfce4"' in info:
74 desktop_environment = 'xfce'
75 except (OSError, RuntimeError):
76 pass
77
78
79
80 if desktop_environment == 'kde':
81 return os.environ['HOME'] + '/.kde/Autostart/'
82 elif desktop_environment == 'gnome':
83 return os.environ['HOME'] + '/.config/autostart/'
84 elif desktop_environment == 'xfce':
85 return os.environ['HOME'] + '/.config/autostart/'
86
87 if os.geteuid()==0:
88
89 USER = 0
90 DIR_USER = screenlets.INSTALL_PREFIX + '/share/screenlets'
91 DIR_AUTOSTART = '/etc/xdg/autostart'
92 else:
93
94 USER = 1
95 DIR_USER = os.environ['HOME'] + '/.screenlets'
96 DIR_AUTOSTART = get_autostart_dir()
97
98
99
101 """checks if the one starting the screenlet is the screenlets manager"""
102 if str(sys.argv[0]).find('screenlets-manager') != -1:
103 return True
104 else:
105 return False
106
108 """Check whether 'str' contains ALL of the chars in 'set'"""
109 for c in set:
110 if c not in str: return 0;
111 return 1;
113 """Check whether 'str' contains ANY of the chars in 'set'"""
114 return 1 in [c in str for c in set]
115
117 """Create a .desktop-file for the screenlet with the given name in
118 $HOME/.config/autostart."""
119 if not os.path.isdir(DIR_AUTOSTART):
120
121 if screenlets.show_question(None,
122 _("There is no existing autostart directory for your user account yet. Do you want me to automatically create it for you?"),
123 _('Error')):
124 print "Auto-create autostart dir ..."
125 os.system('mkdir %s' % DIR_AUTOSTART)
126 if not os.path.isdir(DIR_AUTOSTART):
127 screenlets.show_error(None, _("Automatic creation failed. Please manually create the directory:\n%s") % DIR_AUTOSTART, _('Error'))
128 return False
129 else:
130 screenlets.show_message(None, _("Please manually create the directory:\n%s") % DIR_AUTOSTART)
131 return False
132 if name.endswith('Screenlet'):
133 name = name[:-9]
134 starter = '%s%sScreenlet.desktop' % (DIR_AUTOSTART, name)
135
136 for f in os.listdir(DIR_AUTOSTART):
137 a = f.find(name + 'Screenlet')
138 if a != -1:
139 print str(f) + ' duplicate entry'
140 os.system('rm %s%s' % (chr(34)+DIR_AUTOSTART,f+chr(34)))
141 print 'Removed duplicate entry'
142 if not os.path.isfile(starter) and not os.path.exists(os.environ['HOME'] + '/.config/autostart/CalendarScreenlet'):
143 path = find_first_screenlet_path(name)
144 if path:
145 print "Create autostarter for: %s/%sScreenlet.py" % (path, name)
146 code = ['[Desktop Entry]']
147 code.append('Name=%sScreenlet' % name)
148 code.append('Encoding=UTF-8')
149 code.append('Version=1.0')
150 code.append('Type=Application')
151 code.append('Exec= python -u %s/%sScreenlet.py' % (path, name))
152 code.append('X-GNOME-Autostart-enabled=true')
153
154 f = open(starter, 'w')
155 if f:
156 for l in code:
157 f.write(l + '\n')
158 f.close()
159 return True
160 print 'Failed to create autostarter for %s.' % name
161 return False
162 else:
163 print "Starter already exists."
164 return True
165
167 """Delete the autostart for the given screenlet."""
168 if name.endswith('Screenlet'):
169 name = name[:-9]
170 print 'Delete autostarter for %s.' % name
171 os.system('rm %s%sScreenlet.desktop' % (DIR_AUTOSTART, name))
172 for f in os.listdir(DIR_AUTOSTART):
173 a = f.find(name + 'Screenlet')
174 if a != -1:
175 print str(f) + ' duplicate entry'
176 os.system('rm %s%s' % (chr(34)+DIR_AUTOSTART,f+chr(34)))
177 print 'Removed duplicate entry'
178
180 """Returns screenlet name on form 'foobar-screenlet' by main screenlet class file path."""
181 return path.lower().replace(".py", "").split("/")[path.count("/")].replace("screenlet", "-screenlet")
182
184 """Returns screenlet name on form 'foobar-screenlet' by screenlet class name."""
185 return name.lower().replace("screenlet", "-screenlet")
186
188 """Returns screenlet name on form 'foobar-screenlet' by shortened screenlet class name."""
189 return name.lower() + "-screenlet"
190
192 """Detect if Screenlets default PPA is enabled on system."""
193 import commands
194 result = commands.getstatusoutput("ls /etc/apt/sources.list.d/screenlets*ppa*.list | xargs grep '^deb.*'")[0]
195 return result == 0
196
198 """Returns translator by screenlet class path from __file__."""
199 mo_domain = get_screenlet_linux_name_by_class_path(path)
200
201 t = gettext.translation(mo_domain, screenlets.INSTALL_PREFIX + '/share/locale', fallback = True)
202
203 if not isinstance(t, gettext.GNUTranslations):
204 cut_path_here = path.rfind('/')
205 if cut_path_here > 0:
206 screenlet_dir = path[0:cut_path_here]
207 else:
208 screenlet_dir = os.getcwd()
209 mo_dir = screenlet_dir + "/mo"
210 t = gettext.translation(mo_domain, mo_dir, fallback = True)
211 return t.lgettext
212
214 """Internal function: Returns true if the given string contains one of the
215 Screenlets paths."""
216
217 for path in screenlets.SCREENLETS_PATH:
218 if string.find(path) > -1:
219 return True
220 return False
221
223 """Create the userdir for the screenlets."""
224 if not os.path.isdir(os.environ['HOME'] + '/.screenlets'):
225 try:
226 os.mkdir(os.environ['HOME'] + '/.screenlets')
227 except:
228 print 'coulnt create user dir'
229
230
232 """Scan the Screenlets paths for the occurence of screenlet "name" with the
233 highest version and return the full path to it. This function is used to get
234 the theme/data directories for a Screenlet and run the Screenlet."""
235 available_versions_paths = []
236
237 for dir in screenlets.SCREENLETS_PATH:
238 try:
239 for name in os.listdir(dir):
240 name_py = name + 'Screenlet.py'
241 path = dir + '/' + name
242 if not stat.S_ISDIR(os.stat(path).st_mode):
243 continue
244
245 if os.access(path + '/' + name_py, os.F_OK):
246 if name == screenlet_name:
247 available_versions_paths.append(path)
248 else:
249
250
251 pass
252 except OSError:
253 pass
254 if len(available_versions_paths) == 1:
255 return available_versions_paths[0]
256 elif len(available_versions_paths) > 1:
257 path_and_version = []
258 for version_path in available_versions_paths:
259 path_and_version.append({'version': get_screenlet_metadata_by_path(version_path)['version'], 'path': version_path})
260
261 sorted_versions = sorted(path_and_version, key=lambda x: LooseVersion(x["version"]), reverse=True)
262 return sorted_versions[0]['path']
263
264
265 return None
266
268 img = gtk.gdk.pixbuf_new_from_file_at_size(\
269 screenlets.INSTALL_PREFIX + '/share/screenlets-manager/noimage.svg',width,height)
270
271 for path in screenlets.SCREENLETS_PATH:
272 for ext in ['svg', 'png']:
273 img_path = "%s/%s/icon.%s" % (path, screenlet_name, ext)
274 if os.path.isfile(img_path):
275 try:
276 img = gtk.gdk.pixbuf_new_from_file_at_size(img_path,width,height)
277 except Exception, ex:
278 pass
279 return img
280
282 x = len(first)
283 begin = data.find(first) +x
284 end = data.find(last, begin)
285 return data[begin:end]
286
344
353
366
368 """Scan the Screenlets paths for all existing screenlets and return their
369 names (without trailing "Screenlet") as a list of strings."""
370 sls = []
371
372 refresh_available_screenlet_paths()
373
374 for dir in screenlets.SCREENLETS_PATH:
375 try:
376 for name in os.listdir(dir):
377 path = dir + '/' + name
378
379 if not stat.S_ISDIR(os.stat(path).st_mode):
380 continue
381
382 if os.access(path + '/' + name + 'Screenlet.py', os.F_OK):
383 if not sls.count(name):
384 sls.append(name)
385 else:
386 pass
387 except OSError:
388 pass
389 sls.sort()
390 return sls
391
392 import session
394 """Returns a list with names of running screenlets or None if no
395 Screenlet is currently running. Function returns False if an error
396 happened!"""
397 running = []
398 tempfile = screenlets.TMP_DIR + '/' + screenlets.TMP_FILE
399 if not os.path.isfile(tempfile):
400 return None
401 f = open(tempfile, 'r')
402 if f:
403 running = f.readlines()
404 f.close()
405 for i in xrange(len(running)):
406 running[i] = running[i][:-1]
407
408 p = os.popen("ps aux | awk '/Screenlet.py/{ print $11, $12, $13, $14, $15, $16 }'")
409 lst = []
410 regex = re.compile('/([A-Za-z0-9]+)Screenlet.py ')
411 for line in p.readlines():
412 if not line.endswith('awk /Screenlet.py/{\n') and line != 'sh -c\n' \
413 and _contains_path(line):
414 slname = regex.findall(line)
415 if slname and type(slname) == list and len(slname) > 0:
416 lst.append(slname[0]+'Screenlet')
417 p.close()
418 for a in lst:
419 if a not in running:
420 running.append(a)
421 return running
422
423
424
426 """Returns a list with names of running screenlets. The list can be empty if
427 no Screenlet is currently running."""
428 p = os.popen("ps aux | awk '/Screenlet.py/{ print $11, $12, $13, $14, $15, $16 }'")
429 lst = []
430 regex = re.compile('/([A-Za-z0-9]+)Screenlet.py ')
431 for line in p.readlines():
432 if not line.endswith('awk /Screenlet.py/{\n') and line != 'sh -c\n' \
433 and _contains_path(line):
434 slname = regex.findall(line)
435 if slname and type(slname) == list and len(slname) > 0:
436 lst.append(slname[0]+'Screenlet')
437 p.close()
438 return lst
439
440
441
442
444 """Returns the PID of the given screenlet (if running) or None."""
445 p = os.popen("ps aux | awk '/[" + name[0] + "]" + name[1:] + \
446 "Screenlet.py/{ print $2, $11, $12, $13, $14, $15, $16 }'")
447 line = p.readlines()
448 p.close()
449
450 if len(line) and _contains_path(line[0]):
451 return int(line[0].split(' ')[0])
452 return None
453
455 """http://www.freedesktop.org/wiki/Software/xdg-user-dirs"""
456
457 user_dirs_dirs = os.path.expanduser("~/.config/user-dirs.dirs")
458 if os.path.exists(user_dirs_dirs):
459 f = open(user_dirs_dirs, "r")
460 for line in f.readlines():
461 if line.startswith(key):
462 return os.path.expandvars(line[len(key)+2:-2])
463 return default
464
466 """Check if the daemon is already running and return its interface."""
467 bus = dbus.SessionBus()
468 if bus:
469 try:
470 proxy_obj = bus.get_object(screenlets.DAEMON_BUS, screenlets.DAEMON_PATH)
471 if proxy_obj:
472 return dbus.Interface(proxy_obj, screenlets.DAEMON_IFACE)
473
474 except Exception, ex:
475 print "Error in ScreenletsManager.connect_daemon: %s" % ex
476 return None
477
479 """Returns desktop dir"""
480 desktop_dir = get_user_dir("XDG_DESKTOP_DIR", os.path.expanduser("~/Desktop"))
481 desktop_dir = urllib.unquote(desktop_dir)
482 return desktop_dir
483
485 """Returns filenames of window droped files"""
486 filename = ''
487 filenames = []
488
489 try:
490 txt = unicode.encode(sel_data.get_text(), 'utf-8')
491 except:
492 txt = sel_data.get_text()
493 txta = urllib.unquote(txt)
494 txta = str(txta).split('\n')
495
496 for txt in txta:
497 if txt and txt != '':
498
499 if txt.startswith('file://'):
500 filename = txt[7:]
501 else:
502 print 'Invalid string: %s.' % txt
503 else:
504
505 uris = sel_data.get_uris()
506 if uris and len(uris)>0:
507
508 filename = uris[0][7:]
509 if filename != '':
510 filenames.append(chr(34) +filename + chr(34))
511
512 return filenames
513
515 """Returns mount points in media"""
516 mountlist = os.popen('mount -l').read()
517 prog = re.compile("^/dev/.*?\son\s/media/(.*?) .*?(\[(.*?)\])?$", re.MULTILINE)
518 return prog.findall(mountlist)
519
521 """Returns gtk bookmarks """
522 _bookmarks_path = os.path.expanduser("~/.gtk-bookmarks")
523 _places = []
524 try:
525 for line in file(_bookmarks_path):
526 line = line.strip()
527
528 if " " in line:
529 uri, name = line.split(" ", 1)
530
531 else:
532 uri = line
533
534 path = urllib.splittype(uri)[1]
535 name = urllib.unquote(os.path.split(path)[1])
536
537 try:
538 if os.path.exists(uri):
539 continue
540
541 except TypeError:
542 continue
543
544 _places.append((uri, name))
545 return _places
546 except IOError, err:
547 print "Error loading GTK bookmarks:", err
548
555
567
574
576 """Reads fstab file"""
577 fstab = []
578 f = open(filename, 'r')
579 for line in f:
580 if (not line.isspace() and not line.startswith('#') and not line.lower().startswith('none')) :
581 fstabline = line.split()
582 if fstabline[1] != 'none' and fstabline[1] != '/proc': fstab.append(fstabline[1])
583
584 fstab.sort()
585 return fstab
586
588 """Reads a file"""
589 f = open(filename, 'r')
590 t = f.read()
591 f.close()
592 return t
593
594
596 """Strips HTML tags of a string"""
597 return re.sub(r"<.*?>|</.*?>","",string)
598
599
600
602 """Adds Screenlets-daemon to autostart if not already"""
603 if not os.path.isdir(DIR_AUTOSTART):
604
605 if screenlets.show_question(None, _("There is no existing autostart directory for your user account yet. Do you want me to automatically create it for you?"), _('Error')):
606 print "Auto-create autostart dir ..."
607 os.system('mkdir %s' % DIR_AUTOSTART)
608 if not os.path.isdir(DIR_AUTOSTART):
609 screenlets.show_error(None, _("Automatic creation failed. Please manually create the directory:\n%s") % DIR_AUTOSTART, _('Error'))
610 return False
611 else:
612 screenlets.show_message(None, _("Please manually create the directory:\n%s") % DIR_AUTOSTART)
613 return False
614 starter = '%sScreenlets Daemon.desktop' % (DIR_AUTOSTART)
615
616 if not os.path.isfile(starter) and os.path.isfile('%sscreenlets-daemon.desktop' % (DIR_AUTOSTART)) == False:
617 print "Create autostarter for: Screenlets Daemon"
618 code = ['[Desktop Entry]']
619 code.append('Encoding=UTF-8')
620 code.append('Version=1.0')
621 code.append('Name=Screenlets Daemon')
622 code.append('Type=Application')
623 code.append('Exec=%s/share/screenlets-manager/screenlets-daemon.py' % (screenlets.INSTALL_PREFIX))
624 code.append('X-GNOME-Autostart-enabled=true')
625 f = open(starter, 'w')
626 if f:
627 for l in code:
628 f.write(l + '\n')
629 f.close()
630 return True
631 print 'Failed to create autostarter for %s.' % name
632 return False
633 else:
634 print "Starter already exists."
635 return True
636
642
643
644
646 """Opens anything"""
647 os.system('xdg-open ' + name + ' &')
648
649
650
651
652
654 """A container with info about a screenlet."""
655
656 - def __init__ (self, name, lname, info, author, version, icon):
657 self.name = name
658 self.lname = lname
659 self.info = info.replace("\n", '').replace('\t', ' ')
660 self.author = author
661 self.version = version
662 self.icon = icon
663 self.active = False
664 self.system = not os.path.isfile('%s/%s/%sScreenlet.py' % (DIR_USER, name, name))
665 self.autostart = os.path.isfile(DIR_AUTOSTART + '/' + name + 'Screenlet.desktop')
666
667
668
670 '''
671 A simple wrapper around Gnome VFS file monitors. Emits created, deleted,
672 and changed events. Incoming events are queued, with the latest event
673 cancelling prior undelivered events.
674 '''
675
676
677 __gsignals__ = {
678 "event" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
679 (gobject.TYPE_STRING, gobject.TYPE_INT)),
680 "created" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
681 "deleted" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
682 "changed" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,))
683 }
684
686 gobject.GObject.__init__(self)
687
688 if os.path.isabs(path):
689 self.path = "file://" + path
690 else:
691 self.path = path
692 try:
693 self.type = gnomevfs.get_file_info(path).type
694 except gnomevfs.Error:
695 self.type = gnomevfs.MONITOR_FILE
696
697 self.monitor = None
698 self.pending_timeouts = {}
699
701 if not self.monitor:
702 if self.type == gnomevfs.FILE_TYPE_DIRECTORY:
703 monitor_type = gnomevfs.MONITOR_DIRECTORY
704 else:
705 monitor_type = gnomevfs.MONITOR_FILE
706 self.monitor = gnomevfs.monitor_add(self.path, monitor_type, self._queue_event)
707
709 try:
710 gobject.source_remove(self.pending_timeouts[info_uri])
711 del self.pending_timeouts[info_uri]
712 except KeyError:
713 pass
714
716 self._clear_timeout(info_uri)
717 self.pending_timeouts[info_uri] = \
718 gobject.timeout_add(250, self._timeout_cb, monitor_uri, info_uri, event)
719
722
724 gnomevfs.monitor_cancel(self.monitor)
725 self.monitor = None
726
728 if event in (gnomevfs.MONITOR_EVENT_METADATA_CHANGED,
729 gnomevfs.MONITOR_EVENT_CHANGED):
730 self.emit("changed", info_uri)
731 elif event == gnomevfs.MONITOR_EVENT_CREATED:
732 self.emit("created", info_uri)
733 elif event == gnomevfs.MONITOR_EVENT_DELETED:
734 self.emit("deleted", info_uri)
735 self.emit("event", info_uri, event)
736
737 self._clear_timeout(info_uri)
738 return False
739
740
742 """A simple config/ini-reader class. This is only used for reading the
743 theme.conf files yet, thus it only uses string-values.
744 TODO: add writing-functions and let backend use this, too"""
745
747 self.options = []
748 self.sections = {}
749
751 """Return all options (alternatively only from the given section)."""
752 if section != '':
753 return self.sections[section]
754 else:
755 return self.options
756
758 """Get a variable from the config (optional: only get vars from the
759 specified section)."""
760 if section != '':
761 l = self.sections[section]
762 else:
763 l = self.options
764 for o in l:
765 if o[0] == name:
766 return o[1]
767 return None
768
770 """Returns true if the given section exists."""
771 return self.sections.has_key(name)
772
773 - def load (self, filename):
774 """Load a config/ini-file and save vars in internal list."""
775 f=None
776 try:
777 f = open (filename, "r")
778 except:
779 print "File %s not found" % str(filename)
780 if f:
781 section_name = ''
782 for line in f.readlines():
783
784 line = line.lstrip().lstrip('\t')
785
786
787 if len(line) < 4 or line[0] in ("#", "\n", ";"):
788 pass
789 else:
790
791 tmp = line.split('=', 1)
792
793 if len(tmp) < 2 and len(line) > 5 and line[0] == '[':
794 section_name = line[:-1][1:-1]
795 self.sections[section_name] = []
796
797 else:
798
799 var = tmp[0].rstrip().rstrip('\t')
800 val = tmp[1][:-1].lstrip()
801
802
803 if var != '' and val != '':
804 o = [var, val]
805 self.options.append(o)
806 if section_name != '':
807 try:
808 self.sections[section_name].append(o)
809 except:
810 print "Section %s not found!" % section_name
811 f.close()
812 return True
813 else:
814 return False
815
816
817
819 """A simple and conveniet wrapper for the notification-service. Allows
820 screenlets to easily pop up notes with their own icon (if any)."""
821
823 self.bus = dbus.SessionBus()
824 self.notifications = dbus.Interface(\
825 self.bus.get_object('org.freedesktop.Notifications',
826 '/org/freedesktop/Notifications'), 'org.freedesktop.Notifications')
827 self.screenlet = screenlet
828
829 - def notify (self, message, title='', icon='', timeout=-1, screenlet=None):
830 """Send a notification to org.freedesktop.Notifications. The message
831 should contain the text you want to display, title may define a title
832 (summary) for the message, icon can be the full path to an icon,
833 timeout can be set to the desired displaying time in milliseconds."""
834 if self.bus and self.notifications:
835 if not screenlet:
836 screenlet = self.screenlet
837 if screenlet:
838 p = find_first_screenlet_path(screenlet.__class__.__name__[:-9])
839 if p:
840 icon = p + '/icon.svg'
841 title = screenlet.__name__
842 self.notifications.Notify('Screenlets', 0, icon, title, message,
843 [], {}, timeout)
844 return True
845 else:
846 print "Notify: No DBus running or notifications-daemon unavailable."
847 return False
848
849
850 if __name__ == '__main__':
851
852
853 print get_screenlet_metadata('Clock')
854
855
856 print "Find first occurence of a Screenlet:"
857 print find_first_screenlet_path('Clock')
858 print find_first_screenlet_path('Orloj')
859 print find_first_screenlet_path('Weather')
860 print find_first_screenlet_path('Foo')
861
862
863 print "\nList all installed Screenlets:"
864 avail = list_available_screenlets()
865 avail.sort()
866 print avail
867
868
869 print "\nTest INI-reader:"
870 ini = IniReader()
871 if not ini.load('/usr/share/screenlets/CPUMeter/themes/default/theme.conf'):
872 print "Error while loading ini-file"
873 else:
874
875 if ini.has_section('Theme'):
876
877 print ini.get_option('name', section='Theme')
878 print ini.get_option('info', section='Theme')
879
880 if ini.has_section('Options'):
881 for o in ini.list_options(section='Options'):
882 print o[0]
883
884
885 print "\nNotify-test:"
886 n = Notifier()
887 n.notify('Hi there! This is sent through screenlets.utils.Notifier.notify',
888 title='Test')
889 n.notify('A second note ..', title='Another note', timeout=2000)
890 n.notify('A second note ..', title='Another note', icon='/usr/share/screenlets/Notes/icon.svg')
891
892
893 print "\nRunning screenlets: "
894 print list_running_screenlets2()
895 print "\n"
896 print get_screenlet_process('Clock')
897 print get_screenlet_process('Ruler')
898 print get_screenlet_process('Webtest')
899