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