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

List:       kmail-devel
Subject:    [PATCH] Usability fix for POP filters - keyboard handling
From:       Ladislav Strojil <Ladislav.Strojil () seznam ! cz>
Date:       2003-09-25 22:30:13
[Download RAW message or body]

[" " (multipart/signed)]


Hi,

I have created a patch for KMail POP filters so that the confirmation dialog 
allows keyboard usage.
(see also at http://patches.strojil.cz/kmail_popfilters.patch.html)

Summary:
Added the ability to use keyboard for action selection
Cleaned the code a little bit - removed some dead code 
(there's more to clean - key() method seems useless)
The old code kept track of which column corresponds to which action but it was 
not done properly. IMHO this is not worth the extra work. I have changed the 
code to expect the action columns to be the first three (and added an assert 
that might catch future changes). 
The patch is still not perfect, TAB key does not behave itself. I will have a 
look at that later.

The patch is locally tested, seems to work fine. 

Direct comments please to ladislav.strojil@seznam.cz, I am not currently 
subscribed to this ML.

Cheers,
Láïa (aka Bwian)

-- 
    ~       Ladislav Strojil, MFF UK
  ' v '               
 //   \\              
/(     )\    Powered by Penguin.
  ^ ' ^

[Attachment #5 (application/pgp-signature)]
["kmail_popfilters.patch" (text/x-diff)]

Index: kmpopfiltercnfrmdlg.cpp
===================================================================
RCS file: /home/kde/kdepim/kmail/kmpopfiltercnfrmdlg.cpp,v
retrieving revision 1.16
diff -u -3 -p -r1.16 kmpopfiltercnfrmdlg.cpp
--- kmpopfiltercnfrmdlg.cpp	3 Jun 2002 13:15:55 -0000	1.16
+++ kmpopfiltercnfrmdlg.cpp	25 Sep 2003 22:13:38 -0000
@@ -25,75 +25,77 @@
 
 ////////////////////////////////////////
 ///  view
-KMPopHeadersView::KMPopHeadersView(QWidget *aParent)
+KMPopHeadersView::KMPopHeadersView(QWidget *aParent, KMPopFilterCnfrmDlg *aDialog)
       : KListView(aParent)
 {
-  mColumnOf[Down] = addColumn(QIconSet(QPixmap(mDown)), QString::null, 24);
-  mColumnOf[Later] = addColumn(QIconSet(QPixmap(mLater)), QString::null, 24);
-  mColumnOf[Delete] = addColumn(QIconSet(QPixmap(mDel)), QString::null, 24);
+  mDialog=aDialog;
+  int mDownIndex=addColumn(QIconSet(QPixmap(mDown)), QString::null, 24);
+  assert( mDownIndex == Down ); //This code relies on the fact that radiobuttons are \
the first three columns for easier Column-Action mapping +			        //it does not \
necessarily be true - you could redefine mapToColumn and mapToAction to eg. shift \
those numbers by 1 +  addColumn(QIconSet(QPixmap(mLater)), QString::null, 24);
+  addColumn(QIconSet(QPixmap(mDel)), QString::null, 24);
+  
   /*int subjCol =*/ addColumn(i18n("Subject"), 180);
   /*int sendCol =*/ addColumn(i18n("Sender"), 150);
   int dateCol = addColumn(i18n("Date"), 120);
   int sizeCol = addColumn(i18n("Size"), 80);
 
-  setSelectionMode(QListView::NoSelection);
-  setColumnAlignment(mColumnOf[Down], Qt::AlignHCenter);
-  setColumnAlignment(mColumnOf[Later], Qt::AlignHCenter);
-  setColumnAlignment(mColumnOf[Delete], Qt::AlignHCenter);
+  setAllColumnsShowFocus(true);
+  
+  setColumnAlignment(Down, Qt::AlignHCenter);
+  setColumnAlignment(Later, Qt::AlignHCenter);
+  setColumnAlignment(Delete, Qt::AlignHCenter);
   setColumnAlignment(sizeCol, Qt::AlignRight);
+  
   setSorting(dateCol, false);
   setShowSortIndicator(true);
-  header()->setResizeEnabled(false, mColumnOf[Down]);
-  header()->setResizeEnabled(false, mColumnOf[Later]);
-  header()->setResizeEnabled(false, mColumnOf[Delete]);
-  header()->setClickEnabled(false, mColumnOf[Down]);
-  header()->setClickEnabled(false, mColumnOf[Later]);
-  header()->setClickEnabled(false, mColumnOf[Delete]);
-  header()->setMovingEnabled(false);
+  header()->setResizeEnabled(false, Down);
+  header()->setResizeEnabled(false, Later);
+  header()->setResizeEnabled(false, Delete);
+  header()->setClickEnabled(false, Down);
+  header()->setClickEnabled(false, Later);
+  header()->setClickEnabled(false, Delete);
 
-  mActionAt[mColumnOf[Down]] = Down;
-  mActionAt[mColumnOf[Later]] = Later;
-  mActionAt[mColumnOf[Delete]] = Delete;
+  //we rely on fixed column order, so we forbid this
+  header()->setMovingEnabled(false);
 
   connect(this, SIGNAL(pressed(QListViewItem*, const QPoint&, int)),
         SLOT(slotPressed(QListViewItem*, const QPoint&, int)));
-  connect(this->header(), SIGNAL(indexChange(int , int , int)),
-        SLOT(slotIndexChanged(int , int , int)));
 }
 
 KMPopHeadersView::~KMPopHeadersView()
 {
 }
 
-int KMPopHeadersView::mapToColumn(KMPopFilterAction aAction)
+//Handle keystrokes - Left and Right key select previous/next action correspondingly
+void KMPopHeadersView::keyPressEvent( QKeyEvent *e ) 
 {
-  return (mColumnOf.contains(aAction))? mColumnOf[aAction]: NoAction;
-}
-
-KMPopFilterAction KMPopHeadersView::mapToAction(int aColumn)
-{
-  return (mActionAt.contains(aColumn))? mActionAt[aColumn]: NoAction;
+    if (e->key() == Key_Left) {
+	    KMPopHeadersViewItem *item = dynamic_cast<KMPopHeadersViewItem*>( \
selectedItem() ); +	    if (item&&mDialog) {
+		    if (item->action()) { //here we rely on the fact that the leftmost action is \
equal to 0! +			    item->setAction((KMPopFilterAction)((int)item->action()-1));
+			    mDialog->setAction( selectedItem(), item->action());
+		    }
+	    }
+    } else if (e->key() == Key_Right) {
+	    KMPopHeadersViewItem *item = dynamic_cast<KMPopHeadersViewItem*>( \
selectedItem() ); +	    if (item&&mDialog) {
+		    if (item->action()<NoAction-1) { //here we rely on the fact that right most \
action is one less than NoAction! +			    \
item->setAction((KMPopFilterAction)((int)item->action()+1)); +			    \
mDialog->setAction( selectedItem(), item->action()); +		    }
+	    }
+    } else {
+	    QListView::keyPressEvent( e );
+    }
 }
 
-/** No descriptions */
-void KMPopHeadersView::slotPressed(QListViewItem* aItem, const QPoint&,
-        int aColumn) {
+void KMPopHeadersView::slotPressed(QListViewItem* aItem, const QPoint&, int aColumn) \
{  if ( !aItem ) return;
   KMPopHeadersViewItem *item = dynamic_cast<KMPopHeadersViewItem*>(aItem);
   assert( item );
-  item->check(mapToAction(aColumn));
-}
-
-void KMPopHeadersView::slotIndexChanged(int, int aFromIndex, int aToIndex)
-{
-  if(aFromIndex < aToIndex)
-      --aToIndex;
-
-  mActionAt[aToIndex] = mActionAt[aFromIndex];
-  mActionAt.erase(aFromIndex);
-
-  mColumnOf[mActionAt[aToIndex]] = aToIndex;
-  mColumnOf.erase(mActionAt[aFromIndex]);
+  item->setAction(mapToAction(aColumn));
 }
 
 const char *KMPopHeadersView::mUnchecked[26] = {
@@ -232,38 +234,26 @@ KMPopHeadersViewItem::KMPopHeadersViewIt
       : KListViewItem(aParent)
 {
   mParent = aParent;
+  mAction = NoAction;
 
-  switch(aAction)
-  {
-  case Delete:
-    check(Delete);
-    break;
-  case Down:
-    check(Down);
-    break;
-  case Later:
-  default:
-    check(Later);
-    break;
-  }
+  setPixmap(mParent->mapToColumn(Delete), QPixmap(KMPopHeadersView::mUnchecked));
+  setPixmap(mParent->mapToColumn(Down), QPixmap(KMPopHeadersView::mUnchecked));
+  setPixmap(mParent->mapToColumn(Later), QPixmap(KMPopHeadersView::mUnchecked));
+
+  setAction( aAction );
 }
 
 KMPopHeadersViewItem::~KMPopHeadersViewItem()
 {
 }
 
-void KMPopHeadersViewItem::check(KMPopFilterAction aAction)
+void KMPopHeadersViewItem::setAction(KMPopFilterAction aAction)
 {
-  if(aAction != NoAction && !mChecked[aAction])
+  if(aAction != NoAction && aAction!=mAction)
   {
-    setPixmap(mParent->mapToColumn(Down), QPixmap(KMPopHeadersView::mUnchecked));
-    setPixmap(mParent->mapToColumn(Later), QPixmap(KMPopHeadersView::mUnchecked));
-    setPixmap(mParent->mapToColumn(Delete), QPixmap(KMPopHeadersView::mUnchecked));
+    if ( mAction!=NoAction ) setPixmap(mParent->mapToColumn(mAction), \
                QPixmap(KMPopHeadersView::mUnchecked));
     setPixmap(mParent->mapToColumn(aAction), QPixmap(KMPopHeadersView::mChecked));
-    mChecked[Down] = false;
-    mChecked[Later] = false;
-    mChecked[Delete] = false;
-    mChecked[aAction] = true;
+    mAction=aAction;
   }
 }
 
@@ -293,7 +283,7 @@ KMPopFilterCnfrmDlg::KMPopFilterCnfrmDlg
       : KDialogBase(aParent, aName, TRUE, i18n("KMail POP Filter"), Ok | Help, Ok, \
FALSE)  {
   unsigned int rulesetCount = 0;
-  mHeaders = aHeaders;
+  //mHeaders = aHeaders;
   mShowLaterMsgs = aShowLaterMsgs;
   mLowerBoxVisible = false;
 
@@ -309,7 +299,7 @@ KMPopFilterCnfrmDlg::KMPopFilterCnfrmDlg
 
   QVGroupBox *upperBox = new QVGroupBox(i18n("Messages Exceeding Size"), w);
   upperBox->hide();
-  KMPopHeadersView *lv = new KMPopHeadersView(upperBox);
+  KMPopHeadersView *lv = new KMPopHeadersView(upperBox, this);
   vbl->addWidget(upperBox);
 
   QVGroupBox *lowerBox = new QVGroupBox(i18n("Ruleset Filtered Messages: none"), w);
@@ -318,7 +308,7 @@ KMPopFilterCnfrmDlg::KMPopFilterCnfrmDlg
       i18n("Show messages matched by a filter ruleset"));
   QCheckBox* cb = new QCheckBox(checkBoxText, lowerBox);
   cb->setEnabled(false);
-  mFilteredHeaders = new KMPopHeadersView(lowerBox);
+  mFilteredHeaders = new KMPopHeadersView(lowerBox, this);
   mFilteredHeaders->hide();
   vbl->addWidget(lowerBox);
 
@@ -366,25 +356,8 @@ KMPopFilterCnfrmDlg::KMPopFilterCnfrmDlg
 
     if(lvi)
     {
-      // todo: extrect the whole listitem thing into a method
       mItemMap[lvi] = headers;
-      KMMessage *msg = headers->header();
-      // set the subject
-      QString tmp = msg->subject();
-      if(tmp.isEmpty())
-        tmp = i18n("no subject");
-      lvi->setText(3, tmp);
-      // set the sender
-      tmp = msg->fromStrip();
-      if(tmp.isEmpty())
-        tmp = i18n("unknown");
-      lvi->setText(4, msg->fromStrip());
-      // set the date
-      lvi->setText(5, KMime::DateFormatter::formatDate( KMime::DateFormatter::Fancy, \
                msg->date() ) );
-      // set the size
-      lvi->setText(6, QString("%1").arg(headers->header()->msgLength()));
-      // Date for sorting
-      lvi->setText(7, msg->dateIsoStr());
+      setupLVI(lvi,headers->header());
     }
   }
 
@@ -407,6 +380,30 @@ KMPopFilterCnfrmDlg::~KMPopFilterCnfrmDl
 {
 }
 
+void KMPopFilterCnfrmDlg::setupLVI(KMPopHeadersViewItem *lvi, KMMessage *msg) 
+{
+      // set the subject
+      QString tmp = msg->subject();
+      if(tmp.isEmpty())
+        tmp = i18n("no subject");
+      lvi->setText(3, tmp);
+      // set the sender
+      tmp = msg->fromStrip();
+      if(tmp.isEmpty())
+        tmp = i18n("unknown");
+      lvi->setText(4, msg->fromStrip());
+      // set the date
+      lvi->setText(5, KMime::DateFormatter::formatDate( KMime::DateFormatter::Fancy, \
msg->date() ) ); +      // set the size
+      lvi->setText(6, QString("%1").arg(msg->msgLength()));
+      // Date for sorting
+      lvi->setText(7, msg->dateIsoStr());
+}
+
+void KMPopFilterCnfrmDlg::setAction(QListViewItem *aItem, KMPopFilterAction aAction) \
 +{
+    mItemMap[aItem]->setAction(aAction);
+}
 /**
   This Slot is called whenever a ListView item was pressed.
   It checks for the column the button was pressed in and changes the action if the
@@ -415,17 +412,7 @@ KMPopFilterCnfrmDlg::~KMPopFilterCnfrmDl
 */
 void KMPopFilterCnfrmDlg::slotPressed(QListViewItem *aItem, const QPoint &, int \
aColumn)  {
-  switch(aColumn)
-  {
-  case 0:
-    mItemMap[aItem]->setAction(Down);
-    break;
-  case 1:
-    mItemMap[aItem]->setAction(Later);
-    break;
-  case 2:
-    mItemMap[aItem]->setAction(Delete);
-  }
+  if ( aColumn>=0 && aColumn<NoAction ) \
setAction(aItem,KMPopHeadersView::mapToAction(aColumn));  }
 
 void KMPopFilterCnfrmDlg::slotToggled(bool aOn)
@@ -437,27 +424,10 @@ void KMPopFilterCnfrmDlg::slotToggled(bo
       // show download and delete msgs in the list view too
       for(KMPopHeaders* headers = mDDLList.first(); headers; headers = \
mDDLList.next())  {
-      // todo: extract the whole listitem thing into a method
         KMPopHeadersViewItem *lvi = new KMPopHeadersViewItem(mFilteredHeaders, \
headers->action());  mItemMap[lvi] = headers;
         mDelList.append(lvi);
-        KMMessage *msg = headers->header();
-        // set the subject
-        QString tmp = msg->subject();
-        if(tmp.isEmpty())
-          tmp = i18n("no subject");
-        lvi->setText(3, tmp);
-        // set the sender
-        tmp = msg->fromStrip();
-        if(tmp.isEmpty())
-          tmp = i18n("unknown");
-        lvi->setText(4, msg->fromStrip());
-        // set the date
-        lvi->setText(5, KMime::DateFormatter::formatDate( \
                KMime::DateFormatter::Fancy, msg->date() ) );
-        // set the size
-        lvi->setText(6, QString("%1").arg(headers->header()->msgLength()));
-        // Date for sorting
-        lvi->setText(7, msg->dateIsoStr());
+        setupLVI(lvi,headers->header());
       }
     }
 
Index: kmpopfiltercnfrmdlg.h
===================================================================
RCS file: /home/kde/kdepim/kmail/kmpopfiltercnfrmdlg.h,v
retrieving revision 1.5
diff -u -3 -p -r1.5 kmpopfiltercnfrmdlg.h
--- kmpopfiltercnfrmdlg.h	8 Dec 2001 09:00:29 -0000	1.5
+++ kmpopfiltercnfrmdlg.h	25 Sep 2003 22:13:38 -0000
@@ -29,6 +29,7 @@
 class QWidget;
 class QString;
 
+class KMPopFilterCnfrmDlg;
 /**
   * @author Heiko Hund
   */
@@ -37,22 +38,21 @@ class KMPopHeadersView : public KListVie
   Q_OBJECT
 
 public:
-  KMPopHeadersView(QWidget *aParent = 0);
+  KMPopHeadersView(QWidget *aParent=0, KMPopFilterCnfrmDlg *aDialog=0);
   ~KMPopHeadersView();
-  int mapToColumn(KMPopFilterAction aAction);
-  KMPopFilterAction mapToAction(int aColumn);
+  static const KMPopFilterAction mapToAction(int aColumn) { return \
(KMPopFilterAction)aColumn;}; +  static const int mapToColumn(KMPopFilterAction \
aAction) { return (int)aAction;};  static const char *mUnchecked[26];
   static const char *mChecked[26];
 protected:
   static const char *mLater[25];
   static const char *mDown[20];
   static const char *mDel[19];
-  QMap<KMPopFilterAction, int> mColumnOf;
-  QMap<int, KMPopFilterAction> mActionAt;
+  void keyPressEvent( QKeyEvent *k);
 
 protected slots: // Protected slots
   void slotPressed(QListViewItem* aItem, const QPoint& aPoint, int aColumn);
-  void slotIndexChanged(int aSection, int aFromIndex, int aToIndex);
+  KMPopFilterCnfrmDlg *mDialog;
 };
 
 
@@ -62,31 +62,37 @@ class KMPopHeadersViewItem : public KLis
 public:
   KMPopHeadersViewItem(KMPopHeadersView *aParent, KMPopFilterAction aAction);
   ~KMPopHeadersViewItem();
-  void check(KMPopFilterAction aAction);
+  void setAction(KMPopFilterAction aAction);
+  KMPopFilterAction action() { return mAction; };
   virtual void paintFocus(QPainter *, const QColorGroup & cg, const QRect &r);
   virtual QString key(int col, bool ascending) const;
 protected:
   KMPopHeadersView *mParent;
-  QMap<KMPopFilterAction, bool> mChecked;
+  KMPopFilterAction mAction;
 };
 
 
 class KMPopFilterCnfrmDlg : public KDialogBase
 {
+	friend class KMPopHeadersView;
   Q_OBJECT
 protected:
   KMPopFilterCnfrmDlg() { };
-  QPtrList<KMPopHeaders> *mHeaders;
   QMap<QListViewItem*, KMPopHeaders*> mItemMap;
   QPtrList<KMPopHeadersViewItem> mDelList;
   QPtrList<KMPopHeaders> mDDLList;
   KMPopHeadersView *mFilteredHeaders;
   bool mLowerBoxVisible;
   bool mShowLaterMsgs;
+  void setupLVI(KMPopHeadersViewItem *lvi, KMMessage *msg);
+	
 
 public:
   KMPopFilterCnfrmDlg(QPtrList<KMPopHeaders> *aHeaders, const QString &aAccount, \
bool aShowLaterMsgs = false, QWidget *aParent=0, const char *aName=0);  \
~KMPopFilterCnfrmDlg(); +
+public:
+  void setAction(QListViewItem *aItem, KMPopFilterAction aAction);
 
 protected slots: // Protected slots
   /**
Index: kmpopheaders.h
===================================================================
RCS file: /home/kde/kdepim/kmail/kmpopheaders.h,v
retrieving revision 1.2
diff -u -3 -p -r1.2 kmpopheaders.h
--- kmpopheaders.h	22 May 2002 16:07:06 -0000	1.2
+++ kmpopheaders.h	25 Sep 2003 22:13:38 -0000
@@ -14,7 +14,9 @@
 #include <qstring.h>
 #include "kmmessage.h"
 
-enum KMPopFilterAction {NoAction, Down, Later, Delete} ;
+
+enum KMPopFilterAction {Down=0, Later=1, Delete=2, NoAction=3}; //Keep these \
corresponding to the column numbers in the dialog for easier coding  +								//or \
change mapToAction and mapToColumn in KMPopHeadersView  
 class KMPopHeaders {
 public:



_______________________________________________
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