[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kmail-devel
Subject:    Abstracting date handling.
From:       Zack Rusin <zackrat () att ! net>
Date:       2002-03-30 10:39:10
[Download RAW message or body]

Ave,

I abstracted date handling. Let's get right to the point:
a) why?
- peer pressure. Everyone was doing it, I didn't want to be left out, so 
I tried it.
- Marc suggested it,
- message list and the reader weren't unified in that aspect, 
b) how?
- new class - DateFormatter in kmime_util.[cpp|h]
- removes formatDate and fancyDate from kmheaders and all their 
dependencies from other methods/classes,
- unifies the reader and the message list (as far as the dates go)
c) what I don't like about it and you may just as well
- doesn't do your laundry (how disappointing is that?!)
- kmmessage.[cpp|h] abstraction is very weak, it uses helper functions, 
I might switch it over completely to DateFormatter,
- it's not commit ready, it introduces klocale dependency on 
kmime_util.h and mixing kmime with mimelib in some of those files feels 
pretty weird, which is why we probably should wait till the merger with 
the Aegypten branch is done, besides this is not exactly a tiny patch,
d) things that worry me:
- kmfoldermaildir.cpp and kmfoldermbox.cpp look at the patch, I 
commented out those lines, because both those functions have something 
like:
QString dateStr;
(... few lines with no visible connection to dateStr ...)
if ( !dateStr.isEmpty() )
   message->setDate(dateStr);
Unless I'm missing something here (it's 5am so I might be), these
don't make any sense at all. I wanted to contact you before doing
anything with those.

And of course I wanted to ask for some comments, suggestions, fame and
peace on earth. 

Zack

-- 
If it is not on fire, it is a software problem.

["xtestdate.diff" (text/x-diff)]

Index: configuredialog.cpp
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/configuredialog.cpp,v
retrieving revision 1.220
diff -u -3 -p -u -r1.220 configuredialog.cpp
--- configuredialog.cpp	28 Mar 2002 04:49:43 -0000	1.220
+++ configuredialog.cpp	30 Mar 2002 10:31:52 -0000
@@ -2138,9 +2138,9 @@ QString AppearancePage::LayoutTab::helpA
 // hrmpf. Needed b/c I18N_NOOP can't take hints.
 const AppearancePage::LayoutTab::dateDisplayConfigType
 AppearancePage::LayoutTab::dateDisplayConfig[] = {
-  { "ctime", I18N_NOOP("Sta&ndard format (%1)"), CTime },
-  { "localized", I18N_NOOP("Locali&zed format (%1)"), Localized },
-  { "fancyDate", I18N_NOOP("Fanc&y format (%1)"), FancyDate },
+  { "ctime", I18N_NOOP("Sta&ndard format (%1)"), KMime::DateFormatter::CTime },
+  { "localized", I18N_NOOP("Locali&zed format (%1)"), \
KMime::DateFormatter::Localized }, +  { "fancyDate", I18N_NOOP("Fanc&y format (%1)"), \
KMime::DateFormatter::Fancy },  };
 
 AppearancePageLayoutTab::AppearancePageLayoutTab( QWidget * parent, const char * \
name ) @@ -2190,8 +2190,8 @@ AppearancePageLayoutTab::AppearancePageL
   time( &currentTime );
   for ( int i = 0 ; i < numDateDisplayConfig ; i++ )
     mDateDisplay->insert( new QRadioButton( i18n(dateDisplayConfig[i].displayName)
-			         .arg( KMHeaders::formatDate( currentTime,
-				   dateDisplayConfig[i].dateDisplay ) ),
+					    .arg( KMime::FormatDate( currentTime,
+								     dateDisplayConfig[i].dateDisplay ) ),
       mDateDisplay ), i );
 
   vlay->addWidget( mDateDisplay );
Index: configuredialog_p.h
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/configuredialog_p.h,v
retrieving revision 1.15
diff -u -3 -p -u -r1.15 configuredialog_p.h
--- configuredialog_p.h	26 Mar 2002 09:38:15 -0000	1.15
+++ configuredialog_p.h	30 Mar 2002 10:31:54 -0000
@@ -21,6 +21,7 @@
 #include <kdialogbase.h>
 #include <klistview.h>
 #include <klocale.h> // for i18n
+#include <kmime_util.h>
 
 class QPushButton;
 class QLabel;
@@ -581,7 +582,7 @@ protected:
   static const struct dateDisplayConfigType {
     const char *  configName;
     const char *  displayName;
-    KMDateDisplay dateDisplay;
+    KMime::DateFormatter::FormatType dateDisplay;
   } dateDisplayConfig[ numDateDisplayConfig ];
 
 };
Index: kmfoldermaildir.cpp
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/kmfoldermaildir.cpp,v
retrieving revision 1.23
diff -u -3 -p -u -r1.23 kmfoldermaildir.cpp
--- kmfoldermaildir.cpp	8 Mar 2002 21:24:58 -0000	1.23
+++ kmfoldermaildir.cpp	30 Mar 2002 10:31:56 -0000
@@ -514,8 +514,8 @@ void KMFolderMaildir::readFileHeaderInte
 
       KMMsgInfo *mi = new KMMsgInfo(this);
       mi->init(subjStr, fromStr, toStr, 0, status, xmarkStr, replyToIdStr, msgIdStr, \
                file.local8Bit(), f.size());
-      if (!dateStr.isEmpty())
-        mi->setDate(dateStr);
+      //if (!dateStr.isEmpty())
+        //mi->setDate(dateStr);
       mi->setDirty(false);
       mMsgList.append(mi);
 
Index: kmfoldermbox.cpp
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/kmfoldermbox.cpp,v
retrieving revision 1.21
diff -u -3 -p -u -r1.21 kmfoldermbox.cpp
--- kmfoldermbox.cpp	19 Jan 2002 07:48:36 -0000	1.21
+++ kmfoldermbox.cpp	30 Mar 2002 10:31:58 -0000
@@ -504,7 +504,7 @@ int KMFolderMbox::createIndexFromContent
 	  mi = new KMMsgInfo(this);
 	  mi->init(subjStr, fromStr, toStr, 0, KMMsgStatusNew, xmarkStr, replyToIdStr, \
msgIdStr, offs, size);  mi->setStatus("RO","O");
-	  mi->setDate(dateStr);
+	  //mi->setDate(dateStr);
 	  mi->setDirty(FALSE);
 	  mMsgList.append(mi);
 
Index: kmheaders.cpp
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/kmheaders.cpp,v
retrieving revision 1.400
diff -u -3 -p -u -r1.400 kmheaders.cpp
--- kmheaders.cpp	20 Mar 2002 11:58:15 -0000	1.400
+++ kmheaders.cpp	30 Mar 2002 10:32:05 -0000
@@ -197,7 +197,7 @@ public:
 	tmp = tmp.simplifyWhiteSpace();
 
     } else if(col == headers->paintInfo()->dateCol) {
-        tmp = KMHeaders::formatDate(mMsgBase->date(), \
headers->paintInfo()->dateDisplay ); +	tmp = \
headers->date_.dateString(mMsgBase->date());  } else if(col == \
headers->paintInfo()->sizeCol  && headers->paintInfo()->showSize) {
         if (headers->folder()->protocol() == "imap")
@@ -596,12 +596,14 @@ void KMHeaders::readConfig (void)
   { // area for config group "General"
     KConfigGroupSaver saver(config, "General");
     mPaintInfo.showSize = config->readBoolEntry("showMessageSize");
-    mPaintInfo.dateDisplay = FancyDate;
+    date_.setFormat(KMime::DateFormatter::Fancy);
     QString dateDisplay = config->readEntry( "dateDisplay", "fancyDate" );
-    if ( dateDisplay == "ctime" )
-      mPaintInfo.dateDisplay = CTime;
-    else if ( dateDisplay == "localized" )
-      mPaintInfo.dateDisplay = Localized;
+    if ( dateDisplay == "ctime" ) {
+	date_.setFormat(KMime::DateFormatter::CTime);
+    } else if ( dateDisplay == "localized" ) {
+	date_.setFormat(KMime::DateFormatter::Localized);
+    }
+    
   }
 
   readColorConfig();
@@ -2212,80 +2214,16 @@ void KMHeaders::highlightMessage(QListVi
   setFolderInfoStatus();
 }
 
-QString KMHeaders::formatDate( time_t time, KMDateDisplay option)
-{
-    switch ( option )
-    {
-    case FancyDate:
-        return KMHeaders::fancyDate( time );
-    case CTime:
-        return QString::fromLatin1( ctime(  &time ) ).stripWhiteSpace() ;
-    case Localized:
-    {
-        QDateTime tmp;
-        tmp.setTime_t( time );
-	// format date according to locale: short format, show secs
-        return KGlobal::locale()->formatDateTime( tmp, true, true );
-    }
-    }
-    return QString::null;
-}
-
-QDateTime *KMHeaders::now = 0;
-time_t KMHeaders::now_time = 0;
-
-QString KMHeaders::fancyDate( time_t otime )
-{
-    if ( otime <= 0 )
-        return i18n( "unknown" );
 
-    if ( !now ) {
-        time( &now_time );
-        now = new QDateTime();
-        now->setTime_t( now_time );
-    }
 
-    QDateTime old;
-    old.setTime_t( otime );
 
-    // not more than an hour in the future
-    if ( now_time + 60 * 60 >= otime ) {
-        time_t diff = now_time - otime;
 
-        if ( diff < 24 * 60 * 60 ) {
-            if ( old.date().year() == now->date().year() &&
-                 old.date().dayOfYear() == now->date().dayOfYear() )
-                return i18n( "Today %1" ).arg( KGlobal::locale()->
-                                               formatTime( old.time(), true ) );
-        }
-        if ( diff < 2 * 24 * 60 * 60 ) {
-            QDateTime yesterday( now->addDays( -1 ) );
-            if ( old.date().year() == yesterday.date().year() &&
-                 old.date().dayOfYear() == yesterday.date().dayOfYear() )
-                return i18n( "Yesterday %1" ).arg( KGlobal::locale()->
-                                                   formatTime( old.time(), true) );
-        }
-        for ( int i = 3; i < 7; i++ )
-            if ( diff < i * 24 * 60 * 60 ) {
-                QDateTime weekday( now->addDays( -i + 1 ) );
-                if ( old.date().year() == weekday.date().year() &&
-                     old.date().dayOfYear() == weekday.date().dayOfYear() )
-                    return i18n( "1. weekday, 2. time", "%1 %2" ).
-                        arg( KGlobal::locale()->weekDayName( old.date().dayOfWeek() \
                ) ).
-                        arg( KGlobal::locale()->
-                             formatTime( old.time(), true) );
-            }
-    }
+ 
 
-    return KGlobal::locale()->
-        formatDateTime( old );
-}
 
 void KMHeaders::resetCurrentTime()
 {
-    delete now;
-    now = 0;
-    now_time = 0;
+    date_.reset();
     QTimer::singleShot( 1000, this, SLOT( resetCurrentTime() ) );
 }
 
Index: kmheaders.h
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/kmheaders.h,v
retrieving revision 1.94
diff -u -3 -p -u -r1.94 kmheaders.h
--- kmheaders.h	27 Jan 2002 10:24:29 -0000	1.94
+++ kmheaders.h	30 Mar 2002 10:32:07 -0000
@@ -9,6 +9,7 @@
 #include <qmap.h>
 #include <qdragobject.h>
 #include <qdict.h>
+#include <kmime_util.h>
 #include "kmmessage.h"
 
 class KMFolder;
@@ -188,11 +189,6 @@ public:
 
   void highlightMessage(QListViewItem*, bool markitread);
 
-  /** return a string relativ to the current time */
-  static QString fancyDate( time_t otime );
-
-  static QString formatDate( time_t otime, KMDateDisplay date );
-
   QFont dateFont;
 
   bool noRepaint;
@@ -389,9 +385,8 @@ private:
   bool writeSortOrder();
   bool readSortOrder(bool set_selection=FALSE);
 
-  /** cached values for fancyDate */
-  static QDateTime *now;
-  static time_t now_time;
+  /** date handling class */
+  KMime::DateFormatter date_;
 
   /** value of config key Behaviour/LoopOnGotoUnread */
   bool mLoopOnGotoUnread;
Index: kmmessage.cpp
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/kmmessage.cpp,v
retrieving revision 1.277
diff -u -3 -p -u -r1.277 kmmessage.cpp
--- kmmessage.cpp	26 Mar 2002 09:47:58 -0000	1.277
+++ kmmessage.cpp	30 Mar 2002 10:32:12 -0000
@@ -39,6 +39,7 @@
 #include <kglobal.h>
 #include <kwin.h>
 
+#include <kmime_util.h>
 
 #if ALLOW_GUI
 #include <kmessagebox.h>
@@ -297,13 +298,10 @@ QString KMMessage::formatString(const QS
 	   to have a long form of the date used? I don't
 	   like this change to a short XX/XX/YY date format.
 	   At least not for the default. -sanders */
-        {
-          QDateTime datetime;
-          datetime.setTime_t(date());
-          KLocale locale("kmail");
-          locale.setLanguage(sReplyLanguage);
-          result += locale.formatDateTime(datetime, false);
-        }
+	{
+	  KLocale locale("kmail");
+	  result += KMime::LocalizedDate( date(), false, true, &locale, sReplyLanguage );
+	}
         break;
       case 'e':
         result += from();
@@ -1274,7 +1272,13 @@ void KMMessage::setAutomaticFields(bool 
 //-----------------------------------------------------------------------------
 QString KMMessage::dateStr(void) const
 {
-    return headerField("Date").stripWhiteSpace();
+    DwHeaders& header = mMsg->Headers();
+    time_t unixTime;
+    
+    if (!header.HasDate()) return "";
+    unixTime = header.Date().AsUnixTime();
+    
+    return KMime::FancyDate(unixTime);
 }
 
 
@@ -1287,12 +1291,7 @@ QCString KMMessage::dateShortStr(void) c
   if (!header.HasDate()) return "";
   unixTime = header.Date().AsUnixTime();
 
-  QCString result = ctime(&unixTime);
-
-  if (result[result.length()-1]=='\n')
-    result.truncate(result.length()-1);
-
-  return result;
+  return KMime::CTimeDate(unixTime).latin1();
 }
 
 
Index: kmmsgbase.cpp
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/kmmsgbase.cpp,v
retrieving revision 1.107
diff -u -3 -p -u -r1.107 kmmsgbase.cpp
--- kmmsgbase.cpp	2 Mar 2002 11:48:39 -0000	1.107
+++ kmmsgbase.cpp	30 Mar 2002 10:32:14 -0000
@@ -190,7 +190,7 @@ void KMMsgBase::setDate(const QCString& 
 QString KMMsgBase::dateStr(void) const
 {
   time_t d = date();
-  return KMHeaders::fancyDate(d);
+  return KMime::FancyDate(d);
 }
 
 
Index: kmpopfiltercnfrmdlg.cpp
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/kmail/kmpopfiltercnfrmdlg.cpp,v
retrieving revision 1.11
diff -u -3 -p -u -r1.11 kmpopfiltercnfrmdlg.cpp
--- kmpopfiltercnfrmdlg.cpp	8 Mar 2002 21:38:16 -0000	1.11
+++ kmpopfiltercnfrmdlg.cpp	30 Mar 2002 10:32:15 -0000
@@ -379,7 +379,7 @@ KMPopFilterCnfrmDlg::KMPopFilterCnfrmDlg
         tmp = i18n("unknown");
       lvi->setText(4, msg->fromStrip());
       // set the date
-      lvi->setText(5, KMHeaders::fancyDate(msg->date()));
+      lvi->setText(5, KMime::FancyDate(msg->date()));
       // set the size
       lvi->setText(6, QString("%1").arg(headers->header()->msgLength()));
       // Date for sorting
@@ -452,7 +452,7 @@ void KMPopFilterCnfrmDlg::slotToggled(bo
           tmp = i18n("unknown");
         lvi->setText(4, msg->fromStrip());
         // set the date
-        lvi->setText(5, KMHeaders::fancyDate(msg->date()));
+        lvi->setText(5, KMime::FancyDate(msg->date()));
         // set the size
         lvi->setText(6, QString("%1").arg(headers->header()->msgLength()));
         // Date for sorting


["xtestutil.diff" (text/x-diff)]

Index: kmime_util.cpp
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/libkdenetwork/kmime_util.cpp,v
retrieving revision 1.9
diff -u -3 -p -u -r1.9 kmime_util.cpp
--- kmime_util.cpp	27 Mar 2002 19:02:51 -0000	1.9
+++ kmime_util.cpp	30 Mar 2002 10:49:23 -0000
@@ -29,6 +29,8 @@
 #include <time.h> // for time()
 #include <unistd.h> // for getpid()
 
+#include <qdatetime.h>
+
 using namespace KMime;
 
 namespace KMime {
@@ -514,6 +516,170 @@ void addQuotes(QCString &str, bool force
 }
 
 
+DateFormatter::DateFormatter(FormatType fType)
+  : format_(fType), date_(0)
+{
+  locale_ = KGlobal::locale();
+}
+DateFormatter::DateFormatter(const KLocale* locale, 
+			     const QString& localeLanguage,
+			     FormatType fType)
+  : format_(fType), date_(0)
+{
+  locale_ = new KLocale(*locale);
+  locale_->setLanguage(localeLanguage);
+}
+  
+DateFormatter::~DateFormatter()
+{
+  if ( date_ ) 
+    delete date_;
+}
+  
+DateFormatter::FormatType 
+DateFormatter::getFormat() const
+{
+  return format_;
+}
+ 
+void       
+DateFormatter::setFormat(FormatType t)
+{
+  format_ = t;
+}
+
+QString 
+DateFormatter::dateString(time_t otime) const
+{
+  switch (format_) {
+  case Fancy:
+    return fancy(otime);
+    break;
+  case Localized:
+    return localized(otime);
+    break;
+  case CTime:
+    return cTime(otime);
+    break;
+  }
+  return QString::null;
+}
+
+QString 
+DateFormatter::fancy(time_t otime) const 
+{
+  if ( otime <= 0 )
+    return i18n( "unknown" );
+
+  if ( !date_ ) {
+    time( &currentTime_ );
+    date_ = new QDateTime();
+    date_->setTime_t( currentTime_ );
+  }
+
+  QDateTime old;
+  old.setTime_t( otime );
+  
+  // not more than an hour in the future
+  if ( currentTime_ + 60 * 60 >= otime ) {
+    time_t diff = currentTime_ - otime;
+    
+    if ( diff < 24 * 60 * 60 ) {
+      if ( old.date().year() == date_->date().year() &&
+	   old.date().dayOfYear() == date_->date().dayOfYear() )
+	return i18n( "Today %1" ).arg( locale_->
+				       formatTime( old.time(), true ) );
+    }
+    if ( diff < 2 * 24 * 60 * 60 ) {
+      QDateTime yesterday( date_->addDays( -1 ) );
+      if ( old.date().year() == yesterday.date().year() &&
+	   old.date().dayOfYear() == yesterday.date().dayOfYear() )
+	return i18n( "Yesterday %1" ).arg( locale_->
+					   formatTime( old.time(), true) );
+    }
+    for ( int i = 3; i < 7; i++ )
+      if ( diff < i * 24 * 60 * 60 ) {
+	QDateTime weekday( date_->addDays( -i + 1 ) );
+	if ( old.date().year() == weekday.date().year() &&
+	     old.date().dayOfYear() == weekday.date().dayOfYear() )
+	  return i18n( "1. weekday, 2. time", "%1 %2" ).
+	    arg( locale_->weekDayName( old.date().dayOfWeek() ) ).
+	    arg( locale_->formatTime( old.time(), true) );
+      }
+  }
+  
+  return locale_->formatDateTime( old );
+  
+}
+
+QString 
+DateFormatter::localized(time_t otime, bool shortFormat,
+			 bool includeSecs ) const
+{
+  QDateTime tmp;
+  tmp.setTime_t( otime );
+  // format date according to locale
+  return locale_->formatDateTime( tmp, shortFormat, includeSecs );
+}
 
+QString 
+DateFormatter::cTime(time_t otime) const
+{
+  return QString::fromLatin1( ctime(  &otime ) ).stripWhiteSpace() ;
+}
+
+QString 
+DateFormatter::isoDate(time_t otime) const
+{
+  char cstr[64];
+  strftime(cstr, 63, "%Y-%m-%d %H:%M:%S", localtime(&otime));
+  return QString(cstr);
+}
+
+
+void 
+DateFormatter::reset()
+{
+  if ( date_ )
+    delete date_;
+  date_ = 0;
+}
+
+QString 
+FancyDate(time_t time, const KLocale* locale, 
+	  const QString& localeLanguage )
+{
+  DateFormatter f(locale, localeLanguage);
+  return f.fancy(time);
+}
+
+QString 
+LocalizedDate(time_t time , bool shortFormat, bool includeSecs,
+	      const KLocale* locale, const QString& localeLanguage)
+{
+  DateFormatter f(locale, localeLanguage);
+  return f.localized(time, shortFormat, includeSecs);
+}
+
+QString 
+CTimeDate(time_t time)
+{
+  DateFormatter f;
+  return f.cTime(time);
+}
+
+QString 
+FormatDate(time_t time, DateFormatter::FormatType t)
+{
+  DateFormatter f(t);
+  return f.dateString(time);
+}
+
+QString 
+IsoDate(time_t otime) 
+{
+  DateFormatter f;
+  return f.isoDate(otime);
+}
 
 } // namespace KMime
Index: kmime_util.h
===================================================================
RCS file: /home/kdecvs/kde/kdenetwork/libkdenetwork/kmime_util.h,v
retrieving revision 1.4
diff -u -3 -p -u -r1.4 kmime_util.h
--- kmime_util.h	28 Mar 2002 12:48:20 -0000	1.4
+++ kmime_util.h	30 Mar 2002 10:49:23 -0000
@@ -16,11 +16,15 @@
 #ifndef __KMIME_UTIL_H__
 #define __KMIME_UTIL_H__
 
+#include <time.h>
 #include "qstring.h"
 #include "qcstring.h"
 #include "qvaluelist.h"
+#include "kglobal.h"
 
 typedef QValueList<QCString> QCStringList;
+class KLocale;
+class QDateTime;
 
 namespace KMime {
 
@@ -168,8 +172,52 @@ namespace KMime {
   extern void addQuotes(QCString &str, bool forceQuotes);
 
 
-
-
+  class DateFormatter {
+  public:
+    enum FormatType {
+      CTime,
+      Localized,
+      Fancy
+    };
+    
+    DateFormatter(FormatType fType = Fancy);
+    DateFormatter(const KLocale* locale, 
+		  const QString& localeLanguage,
+		  FormatType fType = DateFormatter::Fancy);
+    
+    ~DateFormatter();
+    
+    FormatType getFormat() const;
+    void       setFormat(FormatType t);
+    
+    QString dateString(time_t otime) const;
+    QString fancy(time_t otime) const ;
+    QString localized(time_t otime, bool shortFormat = true,
+		      bool includeSecs = false) const;
+    QString cTime(time_t otime) const;
+    QString isoDate(time_t otime) const;
+    
+    void reset();
+    
+  private:
+    FormatType 	 	 format_;
+    KLocale   		*locale_;
+    mutable time_t     	 currentTime_;
+    mutable QDateTime 	*date_;
+    
+    // disabling copying
+    DateFormatter(const DateFormatter&);
+    DateFormatter operator=(const DateFormatter&);
+  };
+  
+  QString FancyDate(time_t time, const KLocale* locale = KGlobal::locale(), 
+		    const QString& localeLanguage = QString::null );
+  QString LocalizedDate(time_t time, bool shortFormat = true, bool includeSecs = false,
+			const KLocale* locale = KGlobal::locale(), 
+			const QString& localeLanguage = QString::null );
+  QString CTimeDate(time_t time);
+  QString FormatDate(time_t time, DateFormatter::FormatType t);
+  QString IsoDate(time_t t);
 } // namespace KMime
 
 #endif /* __KMIME_UTIL_H__ */

_______________________________________________
KMail Developers mailing list
kmail@mail.kde.org
http://mail.kde.org/mailman/listinfo/kmail

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic