[nanny: 7/13] Implement systray as a notify service, fix some information services.



commit 1a295699e53c5a97717461f729d598c9e24c64c3
Author: Guido Tabbernuk <boamaod gmail com>
Date:   Mon Feb 27 03:01:56 2012 +0200

    Implement systray as a notify service, fix some information services.

 client/common/src/DBusClient.py                    |    6 +-
 client/gnome/systray/src/SystrayNanny.py           |  145 +++++++++++++-------
 daemon/data/applists/Makefile.am                   |    6 +-
 daemon/data/applists/browser                       |    7 +
 daemon/data/applists/{browsers.w32 => browser.w32} |    0
 daemon/data/applists/browsers                      |    3 -
 daemon/src/Chrono.py                               |    3 +-
 daemon/src/NannyDBus.py                            |    8 +-
 daemon/src/QuarterBack.py                          |  143 ++++++++++----------
 9 files changed, 189 insertions(+), 132 deletions(-)
---
diff --git a/client/common/src/DBusClient.py b/client/common/src/DBusClient.py
index 11ff332..0a305f2 100755
--- a/client/common/src/DBusClient.py
+++ b/client/common/src/DBusClient.py
@@ -44,7 +44,7 @@ class DBusClient(gobject.GObject):
     __metaclass__ = Singleton
 
     __gsignals__ = {'user-notification': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
-                                          (gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_INT))
+                                          (gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_BOOLEAN))
                    }
 
     def __init__(self):
@@ -261,5 +261,5 @@ class DBusClient(gobject.GObject):
     def check_web_access (self, uid, url):
         return self.nanny_wcf.CheckWebAccess (uid, url)
 
-    def __on_user_notification_cb (self, block_status, user_id, app_id, next_change, available_time):
-        self.emit ('user-notification', block_status, user_id, app_id, next_change, available_time)
+    def __on_user_notification_cb (self, block_status, user_id, app_id, next_change, available_time, active):
+        self.emit ('user-notification', block_status, user_id, app_id, next_change, available_time, active)
diff --git a/client/gnome/systray/src/SystrayNanny.py b/client/gnome/systray/src/SystrayNanny.py
old mode 100644
new mode 100755
index 7e9c327..28b973d
--- a/client/gnome/systray/src/SystrayNanny.py
+++ b/client/gnome/systray/src/SystrayNanny.py
@@ -37,6 +37,8 @@ elif os.name == "nt":
 import gobject
 from gettext import ngettext
 
+from datetime import datetime, timedelta
+
 import nanny.client.common
 import nanny.client.gnome.systray
 import gettext
@@ -46,15 +48,16 @@ ngettext = gettext.ngettext
 
 class SystrayNanny(gtk.StatusIcon):
     def __init__(self):
-        #atributes
-        self.times_left = { 0:[-1, False], 1:[-1, False], 2:[-1, False] , 3:[-1, False] }
-        self.times_show = { 0:-1, 1:-1, 2:-1, 3:-1 }
-        self.app_names = { 0: _("Session"),
-                           1: _("Web browser"),
-                           2: _("e-Mail"),
-                           3: _("Instant messanger")
+        #attributes
+        self.times_left = { 0:[-1, False, False], 1:[-1, False, False], 2:[-1, False, False] , 3:[-1, False, False] }
+        self.app_names = { 0: _("your session"),
+                           1: _("web browser"),
+                           2: _("e-mail"),
+                           3: _("instant messanger")
                          }
-        self.look_times = [ 60, 30, 15, 5, 4, 3, 2, 1 ]
+        self.notified = {0: -1, 1: -1, 2: -1, 3: -1 }
+        self.notify_moments_deny = [ 60, 30, 15, 5, 2, 1, 0 ]
+        self.notify_moments_grant = [ 60, 30, 15, 5, 0 ]
         
         #systray
         gtk.StatusIcon.__init__ (self)
@@ -93,58 +96,106 @@ class SystrayNanny(gtk.StatusIcon):
             available_time = ret[k][2]
             self.__handlerUserNotification(self.dbus,  block_status, 
                                            user_id, app_id, 
-                                           next_change, available_time)
+                                           next_change, available_time, active)
         
         return True
 
-    def __handlerUserNotification(self, dbus, block_status, user_id, app_id, next_change, available_time):
+    def __handlerUserNotification(self, dbus, block_status, user_id, app_id, next_change, available_time, active):
         if os.name == "posix" :
             uid= str(os.getuid())
         elif os.name == "nt":
             uid = self.uid
 
         if uid==user_id:
-            self.times_left[app_id] = [next_change, block_status]
+            print user_id, app_id, next_change, block_status, available_time, active
+
+            # a bit messy right now
+            # this should probably be a special function in QuarterBack
+            # is_blocked now deals only with scheduled blocks
+            minutes_till_midnight = int((datetime.now().replace(day=datetime.now().day+1, minute=0, hour=0, second=0, microsecond=0) - datetime.now()).seconds/60)
+            if (datetime.now() + timedelta(minutes=available_time)).day != datetime.now().day:
+                available_time = minutes_till_midnight + self.dbus.get_max_use_time(userid, appid)
+            elif available_time == 0:
+                if (next_change == -1 or (next_change > minutes_till_midnight and block_status == True)):
+                    next_change = minutes_till_midnight
+                    block_status = True
+            
+            if next_change != -1 or available_time != -1:
+                if next_change != -1 and available_time != -1:
+                    if (available_time == 0 and next_change > 0) or next_change <= available_time:
+                        next_ch_unified = next_change
+                    else:
+                        next_ch_unified = available_time
+                        block_status = False
+                else:
+                    if next_change == -1:
+                        next_ch_unified = available_time
+                        block_status = False
+                    elif available_time == -1:
+                        next_ch_unified = next_change
+            else:
+                next_ch_unified = -1
+
+            self.times_left[app_id] = [next_ch_unified, block_status, active]
 
 
     def __handlerTimer(self):
-        mssg=""
-        mssg_ready=False
+        msg = ""
+        need_to_notify = False
+        
         for app_id in self.times_left:
-            if self.times_left[app_id][0]!=-1:
-                if self.times_show[app_id] == -1:
-                    self.times_show[app_id] = self.times_left[app_id][0] + 60 
-
-                for time in self.look_times:
-                    #first element
-                    if time == self.look_times[0]:
-                        if self.times_left[app_id][0] >= time and self.times_show[app_id]-self.times_left[app_id][0] >= time:
-                            self.times_show[app_id]=self.times_left[app_id][0]
-                            mssg_ready=True
-                    else:
-                        if self.times_left[app_id][0]<= time and self.times_show[app_id]-self.times_left[app_id][0] >= time:
-                            self.times_show[app_id]=self.times_left[app_id][0]
-                            mssg_ready=True
-
-                time = self.__format_time (self.times_left[app_id][0])
-                if len (mssg) > 0:
-                    mssg += "\n"
-                if self.times_left[app_id][1]:
-                    # To translators: In x-minutes the access to <app> will be granted
-                    mssg += _("In %(time)s the access to %(app)s will be granted.") % {'time': time, 'app': self.app_names[app_id]}
+            next_change = self.times_left[app_id][0]
+            block_status = self.times_left[app_id][1]
+            active = self.times_left[app_id][2]
+            if self.times_left[app_id][0] != -1:
+            
+                active_notify = -1
+                if block_status == True:
+                    moments_list = self.notify_moments_grant
                 else:
-                    # To translators: In x-minutes the access to <app> will be denied
-                    mssg += _("In %(time)s the access to %(app)s will be denied.") % {'time': time, 'app': self.app_names[app_id]}
-
-        if mssg_ready:
-            self.__showNotification( mssg )
+                    moments_list = self.notify_moments_deny
+                    
+                for moment in moments_list:
+                    if next_change - 1 <= moment:
+                        active_notify = moment
+                        continue
+                    else:
+                        break
+
+                if ((active and not block_status) or block_status) and next_change > 1:
+                    # running and will be blocked or will be released and ...
+                    # (not running and will be blocked is of no interest)
+
+                    time = self.__format_time (next_change - 1) # -1 is to overcome lags
+                    if len(msg) > 0:
+                        msg += "\n"
+                    if block_status == True:
+                        if next_change <= 1:
+                            msg += _("The access to %(app)s granted.") % {'app': self.app_names[app_id]}
+                        else:
+                            # To translators: In x-minutes the access to <app> will be granted
+                            msg += _("In %(time)s the access to %(app)s will be granted.") % {'time': time, 'app': self.app_names[app_id]}
+                    else:
+                        if next_change <= 1:
+                            msg += _("The access to %(app)s denied.") % {'app': self.app_names[app_id]}
+                        else:
+                            # To translators: In x-minutes the access to <app> will be denied
+                            msg += _("In %(time)s the access to %(app)s will be denied.") % {'time': time, 'app': self.app_names[app_id]}
+
+                    if self.notified[app_id] != active_notify:
+                        self.notified[app_id] = active_notify
+                        need_to_notify = True
+                        
+        if need_to_notify:
+            print "NOTIFY:", msg
+            self.__showNotification(msg)
         
-        if self.last_tooltip != mssg : 
-            self.set_tooltip( mssg )
-            self.last_tooltip = mssg
-            print mssg
+        if self.last_tooltip != msg : 
+            self.set_tooltip(msg)
+            self.last_tooltip = msg
+            print "TOOLTIP:", msg
         
-        if len(mssg) != 0 :
+        if len(msg) != 0 :
             self.set_visible(True)
         else:
             self.set_visible(False)
@@ -167,12 +218,12 @@ class SystrayNanny(gtk.StatusIcon):
 
         return time
 
-    def __showNotification (self, mssg):
+    def __showNotification (self, msg):
         icon_path = os.path.join (nanny.client.gnome.systray.icons_files_dir, "48x48/apps", "nanny.png")
 
         if os.name == "posix":
             pynotify.init ("aa")
-            self.notificacion = pynotify.Notification ("Nanny", mssg, icon_path)
+            self.notificacion = pynotify.Notification ("Nanny", msg, icon_path)
             self.notificacion.show()
         elif os.name == "nt":
-            self.win_notify.new_popup("Nanny", mssg, icon_path)
+            self.win_notify.new_popup("Nanny", msg, icon_path)
diff --git a/daemon/data/applists/Makefile.am b/daemon/data/applists/Makefile.am
index 8e958c3..18de80e 100644
--- a/daemon/data/applists/Makefile.am
+++ b/daemon/data/applists/Makefile.am
@@ -1,12 +1,12 @@
 if NANNY_POSIX_SUPPORT
 appsinfodir = $(sysconfdir)/nanny/applists/
-appsinfo_DATA = browsers email im
+appsinfo_DATA = browser email im
 endif
 
 if NANNY_WIN32_SUPPORT
 appsinfodir = $(sysconfdir)/nanny/applists/
-appsinfo_DATA = browsers.w32 email.w32 im.w32
+appsinfo_DATA = browser.w32 email.w32 im.w32
 endif
 
 
-EXTRA_DIST = browsers email im browsers.w32 email.w32 im.w32
+EXTRA_DIST = browser email im browser.w32 email.w32 im.w32
diff --git a/daemon/data/applists/browser b/daemon/data/applists/browser
new file mode 100644
index 0000000..17ee17c
--- /dev/null
+++ b/daemon/data/applists/browser
@@ -0,0 +1,7 @@
+firefox
+epiphany
+konqueror
+chromium
+chrome
+opera
+midori
diff --git a/daemon/data/applists/browsers.w32 b/daemon/data/applists/browser.w32
similarity index 100%
rename from daemon/data/applists/browsers.w32
rename to daemon/data/applists/browser.w32
diff --git a/daemon/src/Chrono.py b/daemon/src/Chrono.py
index df84841..330539f 100644
--- a/daemon/src/Chrono.py
+++ b/daemon/src/Chrono.py
@@ -62,7 +62,7 @@ class Chrono(gobject.GObject) :
 
         self.quarterback.connect('block-status', self.__update_cb)
 
-    def __update_cb(self, quarterback, block_status, user_id, app_id, next_change, available_time):
+    def __update_cb(self, quarterback, block_status, user_id, app_id, next_change, available_time, active):
         '''Callback that updates the used times of the categories.'''
         if block_status == False:
             app_list = self.__get_application_list(self.categories)
@@ -86,7 +86,6 @@ class Chrono(gobject.GObject) :
                     print "Crash Chrono __update_cb"
             else:
                 category = self.categories[app_id]
-                found = False
                 for proc in proclist:
                     if len(gtop.proc_args(proc)) > 0:
                         process = gtop.proc_args(proc)[0]
diff --git a/daemon/src/NannyDBus.py b/daemon/src/NannyDBus.py
index 0fe0710..e415ce0 100755
--- a/daemon/src/NannyDBus.py
+++ b/daemon/src/NannyDBus.py
@@ -337,12 +337,12 @@ class NannyDBus(dbus.service.Object):
     # --------------------------------------------------------------
 
     @dbus.service.signal("org.gnome.Nanny.Notification",
-                         signature='bsiii')
-    def UserNotification(self, block_status, user_id, app_id, next_change, available_time):
+                         signature='bsiiib')
+    def UserNotification(self, block_status, user_id, app_id, next_change, available_time, active):
         pass
 
-    def __UserNotification_cb(self, quarterback, block_status, user_id, app_id, next_change, available_time):
-        self.UserNotification(block_status, user_id, app_id, next_change, available_time)
+    def __UserNotification_cb(self, quarterback, block_status, user_id, app_id, next_change, available_time, active):
+        self.UserNotification(block_status, user_id, app_id, next_change, available_time, active)
 
 
     # org.gnome.Nanny.WebDatabase
diff --git a/daemon/src/QuarterBack.py b/daemon/src/QuarterBack.py
index a5e2ace..bd12da2 100755
--- a/daemon/src/QuarterBack.py
+++ b/daemon/src/QuarterBack.py
@@ -83,7 +83,7 @@ WEEKDAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]
 class QuarterBack(gobject.GObject) :
     __gsignals__ = {
         'block-status' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
-                          (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,)),
+                          (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,)),
         'update-blocks' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                             (gobject.TYPE_PYOBJECT,)),
         'add-wcf-to-uid' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
@@ -146,6 +146,7 @@ class QuarterBack(gobject.GObject) :
                         self.chrono_times[t][c]["extra_time"] = 0
                         self.chrono_times[t][c]["mercy_count"] = 0
                         self.chrono_times[t][c]["force_close"] = 0
+                        self.chrono_times[t][c]["last_active"] = -1
 
         self.__next_update_info = None
         self.usersmanager = UsersManager()
@@ -161,30 +162,6 @@ class QuarterBack(gobject.GObject) :
         
         gobject.timeout_add(1000, self.__polling_cb)
 
-    def __add_to_archive(self):
-        """Appends usage data of the (previous) day to archive"""
-        
-        output = open(ARCHIVE_DB, 'a')
-        writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC)
-
-        v=[]
-        for t in self.chrono_times:
-            z=[self.chrono_day, t]
-            for c in self.chrono_times[t]:
-                for s in self.chrono_times[t][c]:
-                    z.append(self.chrono_times[t][c][s])
-            v.append(z)
-
-        writer.writerows(v)
-        output.close()
-
-    def __save(self):        
-        output = open(BLOCK_DB, 'wb')
-        p = [self.blocks, self.chore_settings, self.wcf_uid_list,
-             self.chrono_times, self.chrono_day]
-        pickle.dump(p, output)
-        output.close()
-
     def __polling_cb(self):
         self.__check_users_info()
         
@@ -200,25 +177,6 @@ class QuarterBack(gobject.GObject) :
         self.__next_update_info = (time.localtime().tm_min + 1) % 60
         return True
 
-    def __refresh_info(self):
-        for user_id in self.blocks.keys() :
-            for app_id in self.blocks[user_id] :
-                block_status, next_change = self.is_blocked(user_id, app_id)
-                available_time = self.get_available_time(user_id, app_id)
-                self.emit("block-status", block_status, user_id, app_id, next_change, available_time)
-
-    def get_block_status_by_uid(self, user_id):
-        if user_id not in self.blocks.keys():
-            return {}
-        
-        ret = {}
-        for app_id in self.blocks[user_id] :
-            block_status, next_change = self.is_blocked(user_id, app_id)
-            available_time = self.get_available_time(user_id, app_id)
-            ret[app_id] = [block_status, next_change, available_time]
-            
-        return ret
-
     def __check_users_info(self):
         some_users_info_changed = False
         if not self.usersmanager.has_changes() :
@@ -230,13 +188,10 @@ class QuarterBack(gobject.GObject) :
                 self.blocks[user_id] = {0: [], 1: [], 2: [], 3: []}
                 some_users_info_changed = True
             if not self.chrono_times.has_key(user_id) :
-                self.chrono_times[user_id] = {0: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0},
-                                              1: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0},
-                                              2: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0},
-                                              3: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0}}
+                self.chrono_times[user_id] = self.__new_user_chrono_times()
                 some_users_info_changed = True
             if not self.chore_settings.has_key(user_id) :
-                self.chore_settings[user_id] = [True, 5]
+                self.chore_settings[user_id] = [True, 5] # defaults to: can use chores, max 5 unfinished
                 some_users_info_changed = True
 
         # remove deleted users from lists
@@ -282,6 +237,52 @@ class QuarterBack(gobject.GObject) :
             self.emit("update-users-info")
             self.__refresh_info()
 
+    def __refresh_info(self):
+        for user_id in self.blocks.keys() :
+            for app_id in self.blocks[user_id] :
+                block_status, next_change = self.is_blocked(user_id, app_id)
+                available_time = self.get_available_time(user_id, app_id)
+                self.emit("block-status", block_status, user_id, app_id, next_change, available_time, self.chrono_times[user_id][app_id]["last_active"] + 1 >= int(time.time()/60))
+
+    def __get_min_block_status(self, user_id, app_id, min) :
+        for block in self.blocks[user_id][app_id]:
+            l, r = block
+            if l <= min <= r :
+                return True
+        return False
+
+    def __add_to_archive(self):
+        """Appends usage data of the (previous) day to archive"""
+        
+        output = open(ARCHIVE_DB, 'a')
+        writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC)
+
+        v=[]
+        for t in self.chrono_times:
+            z=[self.chrono_day, t]
+            for c in self.chrono_times[t]:
+                for s in self.chrono_times[t][c]:
+                    z.append(self.chrono_times[t][c][s])
+            v.append(z)
+
+        writer.writerows(v)
+        output.close()
+
+
+    def __save(self):        
+        output = open(BLOCK_DB, 'wb')
+        p = [self.blocks, self.chore_settings, self.wcf_uid_list,
+             self.chrono_times, self.chrono_day]
+        pickle.dump(p, output)
+        output.close()
+
+    def __new_user_chrono_times(self):
+        user ={0: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0, "last_active": -1},
+               1: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0, "last_active": -1},
+               2: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0, "last_active": -1},
+               3: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0, "last_active": -1}}
+        return user
+
     def is_allowed_to_use(self, user_id, app_id):
         available_time = self.get_available_time(user_id, app_id)
         is_blocked = self.is_blocked(user_id, app_id)[0]
@@ -316,7 +317,6 @@ class QuarterBack(gobject.GObject) :
         if not self.blocks[user_id].has_key(app_id) :
             return block_status, next_block
 
-        
         if date_time == None:
             t = time.localtime()
             h = t.tm_hour
@@ -331,7 +331,7 @@ class QuarterBack(gobject.GObject) :
         atime = int(w)*24*60 + int(h)*60 + int(m)
 
         block_status = self.__get_min_block_status(user_id, app_id, atime)
-            
+
         week_m_list = range(atime+1, 24*60*7) + range(0, atime+1)
 
         for m in week_m_list :
@@ -352,17 +352,23 @@ class QuarterBack(gobject.GObject) :
                     return block_status, next_block
 
         return block_status, next_block
-                
-                    
-    def __get_min_block_status(self, user_id, app_id, min) :
-        for block in self.blocks[user_id][app_id]:
-            l, r = block
-            if l <= min <= r :
-                return True
-        return False
 
+                
+    def get_block_status_by_uid(self, user_id):
+        if user_id not in self.blocks.keys():
+            return {}
+        
+        ret = {}
+        for app_id in self.blocks[user_id] :
+            block_status, next_change = self.is_blocked(user_id, app_id)
+            available_time = self.get_available_time(user_id, app_id)
+            ret[app_id] = [block_status, next_change, available_time]
+            
+        return ret
 
+                    
     def set_blocks(self, user_id, app_id, data):
+    
         if not self.blocks.has_key(user_id) :
             self.blocks[user_id] = {} 
 
@@ -479,7 +485,7 @@ class QuarterBack(gobject.GObject) :
             self.chrono_times[userid] = new_user
 
         if not self.chrono_times[userid].has_key(appid):
-            new_app = {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0}
+            new_app = {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0, "last_active": -1}
             self.chrono_times[userid][appid] = new_app
 
         self.chrono_times[userid][appid]["max_use"] = mins
@@ -516,11 +522,12 @@ class QuarterBack(gobject.GObject) :
     def subtract_time(self, userid, appid, mins=1):
         if self.chrono_times.has_key(userid):
             if self.chrono_times[userid].has_key(appid):
+                self.chrono_times[userid][appid]["last_active"] = int(time.time()/60)
                 if self.get_available_time(userid, appid) != 0:
-                        self.chrono_times[userid][appid]["used_time"] += mins
-                        print "Substract time (%s, %s) = %s" % (userid, appid, 
-                                                            self.chrono_times[userid][appid]["used_time"])
-                        self.__save()
+                    self.chrono_times[userid][appid]["used_time"] += mins
+                    print "Substract time (%s, %s) = %s" % (userid, appid, 
+                                                        self.chrono_times[userid][appid]["used_time"])
+                    self.__save()
 
     def new_chrono_day(self):
         self.chrono_day = (datetime.today() - datetime.utcfromtimestamp(0)).days
@@ -533,12 +540,8 @@ class QuarterBack(gobject.GObject) :
                 
         self.__save()
 
-    def __new_user_chrono_times(self):
-        user ={0: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0},
-               1: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0},
-               2: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0},
-               3: {"max_use": 0, "used_time": 0, "extra_time": 0, "mercy_count": 0, "force_close": 0}}
-        return user
+
+
 
 gobject.type_register(QuarterBack)
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]