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

List:       kmail-devel
Subject:    address completion
From:       Carsten Pfeiffer <carpdjih () cetus ! zrz ! tu-berlin ! de>
Date:       2001-05-24 1:29:22
[Download RAW message or body]

Hiya,

here's a quick patch that adds {auto,short-auto,popup,shell}-completion 
support to kmail's composer.

Not sure if the From and Reply-To edits should use the same completion-items 
as the others tho (or at all).

It also
- removes some obsolete methods
- moves some methods around (there were KMComposeWin methods in between
  KMLineEdit methods)
- cleans up some things

The bottom of the patch is not very readable -- diff couldn't come up with a 
better one, not even with -d.

And yes, the old Ctrl-T completion is still available, too.

Currently, the entire address is completed/shown, not just the name without 
email-address (a la Outlook). I would need some input if we want that, or not
and some more information about how it works. Does completion in Outlook only 
work with names, or also email-addresses? Does it resolve email-addresses to 
names? I need some use-cases, if we want that.

Cheers,
Carsten Pfeiffer


["completion.patch" (text/x-c)]

? completion.patch
Index: kmcomposewin.cpp
===================================================================
RCS file: /home/kde/kdenetwork/kmail/kmcomposewin.cpp,v
retrieving revision 1.338
diff -u -p -B -w -d -r1.338 kmcomposewin.cpp
--- kmcomposewin.cpp	2001/05/20 19:16:16	1.338
+++ kmcomposewin.cpp	2001/05/24 01:21:44
@@ -29,6 +29,8 @@
 #include <kabapi.h>
 #include <kaction.h>
 #include <kcharsets.h>
+#include <kcompletion.h>
+#include <kcompletionbox.h>
 #include <kcursor.h>
 #include <kstdaction.h>
 #include <kedittoolbar.h>
@@ -151,6 +153,17 @@ KMComposeWin::KMComposeWin(KMMessage *aM
   connect(&mBtnFrom,SIGNAL(clicked()),SLOT(slotAddrBookFrom()));
   connect(&mIdentity,SIGNAL(activated(int)),SLOT(slotIdentityActivated(int)));
 
+  connect(&mEdtTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+          SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
+  connect(&mEdtCc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+          SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
+  connect(&mEdtBcc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+          SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
+  connect(&mEdtReplyTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+          SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
+  connect(&mEdtFrom,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+          SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
+  
   mMainWidget.resize(480,510);
   setCentralWidget(&mMainWidget);
   rethinkFields();
@@ -312,6 +325,14 @@ void KMComposeWin::readConfig(void)
   mAutoPgpSign = config->readNumEntry("pgp-auto-sign", 0);
   mConfirmSend = config->readBoolEntry("confirm-before-send", false);
 
+  int mode = config->readNumEntry("Completion Mode", 
+                                  KGlobalSettings::completionMode() );
+  mEdtFrom.setCompletionMode( (KGlobalSettings::Completion) mode );
+  mEdtReplyTo.setCompletionMode( (KGlobalSettings::Completion) mode );
+  mEdtTo.setCompletionMode( (KGlobalSettings::Completion) mode );
+  mEdtCc.setCompletionMode( (KGlobalSettings::Completion) mode );
+  mEdtBcc.setCompletionMode( (KGlobalSettings::Completion) mode );
+
   readColorConfig();
 
   { // area for config group "General"
@@ -1355,7 +1376,7 @@ void KMComposeWin::addrBookSelInto(KMLin
       return;
     }
   if (dlg.exec()==QDialog::Rejected) return;
-  txt = QString(aLineEdit->text()).stripWhiteSpace();
+  txt = aLineEdit->text().stripWhiteSpace();
   if (!txt.isEmpty())
     {
       if (txt.right(1).at(0)!=',') txt += ", ";
@@ -2252,20 +2273,120 @@ void KMComposeWin::slotIdentityActivated
   mId = identStr;
 }
 
+//-----------------------------------------------------------------------------
+void KMComposeWin::slotSpellcheckConfig()
+{
+  KWin kwin;
+  QTabDialog qtd (this, "tabdialog", true);
+  KSpellConfig mKSpellConfig (&qtd);
+
+  qtd.addTab (&mKSpellConfig, i18n("Spellchecker"));
+  qtd.setCancelButton ();
+
+  kwin.setIcons (qtd.winId(), kapp->icon(), kapp->miniIcon());
+
+  if (qtd.exec())
+    mKSpellConfig.writeGlobalSettings();
+}
+
+//-----------------------------------------------------------------------------
+void KMComposeWin::slotToggleToolBar()
+{
+  if(toolBar("mainToolBar")->isVisible())
+    toolBar("mainToolBar")->hide();
+  else
+    toolBar("mainToolBar")->show();
+}
+
+void KMComposeWin::slotToggleStatusBar()
+{
+  if (statusBar()->isVisible())
+    statusBar()->hide();
+  else
+    statusBar()->show();
+}
+
+void KMComposeWin::slotEditToolbars()
+{
+  KEditToolbar dlg(actionCollection(), "kmcomposerui.rc");
+
+  if (dlg.exec() == true)
+  {
+    createGUI("kmcomposerui.rc");
+    toolbarAction->setChecked(!toolBar()->isHidden());
+  }
+}
+
+void KMComposeWin::slotEditKeys()
+{
+  KKeyDialog::configureKeys(actionCollection(), xmlFile(), true, this);
+}
 
+void KMComposeWin::setReplyFocus( bool hasMessage )
+{
+  mEditor->setFocus();
+  if ( hasMessage )
+    mEditor->setCursorPosition( 1, 0 );
+}
+
+void KMComposeWin::slotCompletionModeChanged( KGlobalSettings::Completion mode)
+{
+    KConfig *config = KGlobal::config();
+    KConfigGroupSaver cs( config, "Composer" );
+    config->writeEntry( "Completion Mode", (int) mode );
+    config->sync(); // maybe not?
+
+    // sync all the lineedits to the same completion mode
+    mEdtFrom.setCompletionMode( mode ); 
+    mEdtReplyTo.setCompletionMode( mode ); 
+    mEdtTo.setCompletionMode( mode ); 
+    mEdtCc.setCompletionMode( mode ); 
+    mEdtBcc.setCompletionMode( mode ); 
+}
+
 //=============================================================================
 //
 //   Class  KMLineEdit
 //
 //=============================================================================
+
+KCompletion * KMLineEdit::s_completion = 0L;
+bool KMLineEdit::s_addressesDirty = false;
+
 KMLineEdit::KMLineEdit(KMComposeWin* composer, QWidget *parent,
 		       const char *name): KMLineEditInherited(parent,name)
 {
   mComposer = composer;
+  if ( !s_completion ) {
+      s_completion = new KCompletion();
+      s_completion->setOrder( KCompletion::Sorted );
+  }
 
   installEventFilter(this);
 
+  if ( useCompletion() ) 
+  {
+      setCompletionObject( s_completion, false ); // we handle it ourself
+      connect( this, SIGNAL( completion(const QString&)), 
+               this, SLOT(slotCompletion() ));
+  
+      // old-style completion via Ctrl-T
   connect (this, SIGNAL(completion()), this, SLOT(slotCompletion()));
+  
+      KCompletionBox *box = completionBox();
+      // ### this disconnect has to be fixed in kdeui
+      disconnect( box, SIGNAL( highlighted( const QString& )),
+                  this, SLOT( setText( const QString& )));
+      connect( box, SIGNAL( highlighted( const QString& )),
+               this, SLOT( slotPopupCompletion( const QString& ) ));
+              
+  
+      // Whenever a new KMLineEdit is created (== a new composer is created), 
+      // we set a dirty flag to reload the addresses upon the first completion.
+      // The address completions are shared between all KMLineEdits.
+      // Is there a signal that tells us about addressbook updates?
+      s_addressesDirty = true;
+  }
 }
 
 
@@ -2277,7 +2398,7 @@ KMLineEdit::~KMLineEdit()
 
 
 //-----------------------------------------------------------------------------
-bool KMLineEdit::eventFilter(QObject*, QEvent* e)
+bool KMLineEdit::eventFilter(QObject *o, QEvent *e)
 {
 #ifdef KeyPress
 #undef KeyPress
@@ -2287,21 +2408,23 @@ bool KMLineEdit::eventFilter(QObject*, Q
   {
     QKeyEvent* k = (QKeyEvent*)e;
 
-    if (k->state()==ControlButton && k->key()==Key_T)
+    if (completionMode() == KGlobalSettings::CompletionNone &&
+        k->state()==ControlButton && k->key()==Key_T)
     {
       emit completion();
       cursorAtEnd();
       return TRUE;
     }
     // ---sven's Return is same Tab and arrow key navigation start ---
-    if (k->key() == Key_Enter || k->key() == Key_Return)
+    if (k->key() == Key_Enter || k->key() == Key_Return && 
+        !completionBox()->isVisible())
     {
       mComposer->focusNextPrevEdit(this,TRUE);
       return TRUE;
     }
     if (k->state()==ControlButton && k->key() == Key_Right)
     {
-      if ((int)strlen(text()) == cursorPosition()) // at End?
+      if ((int)text().length() == cursorPosition()) // at End?
       {
         emit completion();
         cursorAtEnd();
@@ -2321,31 +2444,15 @@ bool KMLineEdit::eventFilter(QObject*, Q
     }
     // ---sven's Return is same Tab and arrow key navigation end ---
 
-  }
-  else if (e->type() == QEvent::MouseButtonPress)
-  {
-    QMouseEvent* me = (QMouseEvent*)e;
-    if (me->button() == RightButton)
-    {
-      QPopupMenu* p = new QPopupMenu;
-      p->insertItem(i18n("Cut"),this,SLOT(cut()));
-      p->insertItem(i18n("Copy"),this,SLOT(copy()));
-      p->insertItem(i18n("Paste"),this,SLOT(paste()));
-      p->insertItem(i18n("Mark all"),this,SLOT(markAll()));
-      setFocus();
-      p->popup(QCursor::pos());
-      return TRUE;
     }
-  }
-  return FALSE;
+  return KMLineEditInherited::eventFilter(o, e);
 }
 
 
 //-----------------------------------------------------------------------------
 void KMLineEdit::cursorAtEnd()
 {
-  QKeyEvent ev( QEvent::KeyPress, Key_End, 0, 0 );
-  QLineEdit::keyPressEvent( &ev );
+    setCursorPosition( text().length() );
 }
 
 
@@ -2355,190 +2462,176 @@ void KMLineEdit::undo()
     keyPressEvent( &k );
 }
 
-//-----------------------------------------------------------------------------
-void KMLineEdit::copy()
+bool KMLineEdit::useCompletion() const
 {
-  QString t = markedText();
-  QApplication::clipboard()->setText(t);
+    // feel free to make this a bit more flexible :)
+    return !name() || qstrncmp(name(), "subjectLine", 11) != 0;
 }
 
 //-----------------------------------------------------------------------------
-void KMLineEdit::cut()
-{
-  if(hasMarkedText())
+void KMLineEdit::slotCompletion()
   {
-    QString t = markedText();
-    QKeyEvent k(QEvent::KeyPress, Key_D, 0, ControlButton);
-    keyPressEvent(&k);
-    QApplication::clipboard()->setText(t);
-  }
-}
+    if ( !useCompletion() )
+        return;
 
-//-----------------------------------------------------------------------------
-void KMLineEdit::paste()
+    QString s(text());
+    QString prevAddr;
+    int n = s.findRev(',');
+    if (n>= 0)
 {
-  QKeyEvent k(QEvent::KeyPress, Key_V, 0, ControlButton);
-  keyPressEvent(&k);
+        prevAddr = s.left(n+1) + ' ';
+        s = s.mid(n+1,255).stripWhiteSpace();
 }
 
-//-----------------------------------------------------------------------------
-void KMLineEdit::markAll()
+    KCompletionBox *box = completionBox();
+
+    if ( s.isEmpty() )
 {
-  selectAll();
+        box->hide();
+        return;
 }
 
-//-----------------------------------------------------------------------------
-void KMLineEdit::slotCompletion()
-{
-  QString t;
-  QString Name(name());
+    KGlobalSettings::Completion  mode = completionMode();
 
-  if (Name == "subjectLine")
-  {
-    //mComposer->focusNextPrevEdit(this,TRUE); //IMHO, it is useless now (sven)
+    if ( s_addressesDirty )
+        loadAddresses();
 
-    return;
-  }
+    QString match;
+    int curPos = cursorPosition();
+    if ( mode != KGlobalSettings::CompletionNone )
+        match = s_completion->makeCompletion( s );
 
-  QPopupMenu pop;
-  int n;
+    // kdDebug() << "** completion for: " << s << " : " << match << endl;
 
-  KMAddrBook adb;
-  adb.readConfig();
+    switch ( mode ) 
+    {
+        case KGlobalSettings::CompletionPopup:
+        {
+            if ( !match.isNull() && match != s )
+            {
+                m_previousAddresses = prevAddr;
+                box->clear();
+                box->insertStringList( s_completion->allMatches( s ));
+                box->popup();
+            }
+            else
+                box->hide();
 
-  if(adb.load() == IO_FatalError)
-    return;
+            break;
+        }
 
-  QString s(text());
-  QString prevAddr;
-  n = s.findRev(',');
-  if (n>=0)
+        case KGlobalSettings::CompletionShell:
   {
-    prevAddr = s.left(n+1) + ' ';
-    s = s.mid(n+1,255).stripWhiteSpace();
+            if ( !match.isNull() && match != s ) 
+            {
+                setText( prevAddr + match );
+                cursorAtEnd();
+            }
+            break;
   }
 
-  n=0;
-  if (!KMAddrBookExternal::useKAB())
-    for (QString a=adb.first(); a; a=adb.next())
+        case KGlobalSettings::CompletionMan: // Short-Auto in fact
+        case KGlobalSettings::CompletionAuto:
       {
-	if (QString(a).find(s,0,false) >= 0)
+            if ( !match.isNull() && match != s ) 
 	  {
-	    pop.insertItem(a);
-	    n++;
+                QString adds = prevAddr + match;
+                validateAndSet( adds, curPos, curPos, adds.length() );
 	  }
+            break;
       }
-  else {
-    QStringList addresses;
-    KabBridge::addresses(&addresses);
+
+        default: // fall through
+        case KGlobalSettings::CompletionNone:
+        {
+            QPopupMenu pop;
+
+            QStringList addresses = s_completion->items();
     QStringList::Iterator it = addresses.begin();
     for (; it != addresses.end(); ++it)
       {
 	if ((*it).find(s,0,false) >= 0)
-	  {
 	    pop.insertItem(*it);
-	    n++;
-	  }
-      }
   }
 
-  if (n > 1)
+            if (pop.count() > 1)
   {
     int id;
     pop.popup(parentWidget()->mapToGlobal(QPoint(x(), y()+height())));
     pop.setActiveItem(pop.idAt(0));
     id = pop.exec();
 
-    if (id!=-1)
-    {
+                if (id > -1)
       setText(prevAddr + pop.text(id));
-      //mComposer->focusNextPrevEdit(this,TRUE);
     }
-  }
-  else if (n==1)
+            else if (pop.count() == 1)
   {
     setText(prevAddr + pop.text(pop.idAt(0)));
-    //mComposer->focusNextPrevEdit(this,TRUE);
   }
 
   setFocus();
   cursorAtEnd();
+            break;
 }
-
-//-----------------------------------------------------------------------------
-void KMLineEdit::dropEvent(QDropEvent *e)
-{
-  QStrList uriList;
-  if(QUriDrag::canDecode(e) && QUriDrag::decode( e, uriList ))
-  {
-    QString ct = text();
-    for (QStrListIterator it(uriList); it; ++it)
-    {
-      if (!ct.isEmpty()) ct.append(", ");
-      KURL u(*it);
-      if (u.protocol() == "mailto") ct.append(QString::fromUtf8(u.path()));
-      else ct.append(QString::fromUtf8(*it));
-    }
-    setText(ct);
   }
-  else QLineEdit::dropEvent(e);
 }
 
-//-----------------------------------------------------------------------------
-void KMComposeWin::slotSpellcheckConfig()
+void KMLineEdit::slotPopupCompletion( const QString& completion )
 {
-
-  KWin kwin;
-  QTabDialog qtd (this, "tabdialog", true);
-  KSpellConfig mKSpellConfig (&qtd);
-
-  qtd.addTab (&mKSpellConfig, i18n("Spellchecker"));
-  qtd.setCancelButton ();
-
-  kwin.setIcons (qtd.winId(), kapp->icon(), kapp->miniIcon());
-
-  if (qtd.exec())
-    mKSpellConfig.writeGlobalSettings();
+    setText( m_previousAddresses + completion );
+    cursorAtEnd();
 }
 
 //-----------------------------------------------------------------------------
-void KMComposeWin::slotToggleToolBar()
+void KMLineEdit::loadAddresses()
 {
-  if(toolBar("mainToolBar")->isVisible())
-    toolBar("mainToolBar")->hide();
-  else
-    toolBar("mainToolBar")->show();
-}
+    s_completion->clear();
+    s_addressesDirty = false;
 
-void KMComposeWin::slotToggleStatusBar()
-{
-  if (statusBar()->isVisible())
-    statusBar()->hide();
-  else
-    statusBar()->show();
-}
+    KMAddrBook adb;
+    adb.readConfig();
 
-void KMComposeWin::slotEditToolbars()
-{
-  KEditToolbar dlg(actionCollection(), "kmcomposerui.rc");
+    if(adb.load() == IO_FatalError)
+        return;
 
-  if (dlg.exec() == true)
+    if (!KMAddrBookExternal::useKAB()) {
+        for (QString a = adb.first(); !a.isEmpty(); a = adb.next())
   {
-    createGUI("kmcomposerui.rc");
-    toolbarAction->setChecked(!toolBar()->isHidden());
+            s_completion->addItem( a );
+            // kdDebug() << "** 1 adding: " << a << endl;
   }
 }
 
-void KMComposeWin::slotEditKeys()
+    else {
+        QStringList addresses;
+        KabBridge::addresses(&addresses);
+        QStringList::Iterator it = addresses.begin();
+        for (; it != addresses.end(); ++it)
 {
-  KKeyDialog::configureKeys(actionCollection(), xmlFile(), true, this);
+            s_completion->addItem( *it );
+            // kdDebug() << "** 2 adding: " << *it << endl;
 }
+    }
+}
 
-void KMComposeWin::setReplyFocus( bool hasMessage )
+
+//-----------------------------------------------------------------------------
+void KMLineEdit::dropEvent(QDropEvent *e)
 {
-  mEditor->setFocus();
-  if ( hasMessage )
-    mEditor->setCursorPosition( 1, 0 );
+  QStrList uriList;
+  if(QUriDrag::canDecode(e) && QUriDrag::decode( e, uriList ))
+  {
+    QString ct = text();
+    for (QStrListIterator it(uriList); it; ++it)
+    {
+      if (!ct.isEmpty()) ct.append(", ");
+      KURL u(*it);
+      if (u.protocol() == "mailto") ct.append(QString::fromUtf8(u.path()));
+      else ct.append(QString::fromUtf8(*it));
+    }
+    setText(ct);
+  }
+  else QLineEdit::dropEvent(e);
 }
 
 
Index: kmcomposewin.h
===================================================================
RCS file: /home/kde/kdenetwork/kmail/kmcomposewin.h,v
retrieving revision 1.113
diff -u -p -B -w -d -r1.113 kmcomposewin.h
--- kmcomposewin.h	2001/05/16 20:16:47	1.113
+++ kmcomposewin.h	2001/05/24 01:21:44
@@ -14,8 +14,9 @@
 #include <qpalette.h>
 #include <qfont.h>
 #include <keditcl.h>
-#include <qlineedit.h>
+#include <klineedit.h>
 #include <kio/job.h>
+#include <kglobalsettings.h>
 
 #include "kmmsgpart.h"
 #include "kmmsgbase.h"
@@ -36,7 +37,7 @@ class QListView;
 class QListViewItem;
 class QPopupMenu;
 class QPushButton;
-class KDNDDropZone;
+class KCompletion;
 class KEdit;
 class KMComposeWin;
 class KMMessage;
@@ -59,8 +60,8 @@ class KMEdit: public KEdit
 {
   Q_OBJECT
 public:
-  KMEdit(QWidget *parent=NULL,KMComposeWin* composer=NULL,
-	 const char *name=NULL);
+  KMEdit(QWidget *parent=0L,KMComposeWin* composer=0L,
+	 const char *name=0L);
   virtual ~KMEdit();
 
   /**
@@ -98,36 +99,43 @@ private:
 
 
 //-----------------------------------------------------------------------------
-#define KMLineEditInherited QLineEdit
-class KMLineEdit : public QLineEdit
+#define KMLineEditInherited KLineEdit
+class KMLineEdit : public KLineEdit
 {
   Q_OBJECT
 
 public:
-  KMLineEdit(KMComposeWin* composer = NULL, QWidget *parent = NULL,
-	     const char *name = NULL);
+  KMLineEdit(KMComposeWin* composer, QWidget *parent = 0L,
+             const char *name = 0L);
   virtual ~KMLineEdit();
 
-  /** Set cursor to end of line. */
-   void cursorAtEnd();
-
 signals:
-  /** Emitted when Ctrl-. (period) is pressed. */
+  /** Emitted when Ctrl-T is pressed. */
   void completion();
 
 public slots:
    void undo();
-   void copy();
-   void cut();
-   void paste();
-   void markAll();
-   void slotCompletion();
+  /** Set cursor to end of line. */
+  void cursorAtEnd();
 
 protected:
   virtual bool eventFilter(QObject*, QEvent*);
   virtual void dropEvent(QDropEvent *e);
   KMComposeWin* mComposer;
-protected:
+
+private slots:
+  void slotCompletion();
+  void slotPopupCompletion( const QString& );
+
+private:
+  void loadAddresses();
+  bool useCompletion() const;
+    
+  QString m_previousAddresses;
+
+  static bool s_addressesDirty;
+  static KCompletion *s_completion;
+
 };
 
 
@@ -140,7 +148,7 @@ class KMComposeWin : public KMTopLevelWi
   friend class KMHeaders;         // needed for the digest forward
 
 public:
-  KMComposeWin(KMMessage* msg=NULL, QString id = "unknown" );
+  KMComposeWin(KMMessage* msg=0L, QString id = "unknown" );
   ~KMComposeWin();
 
   /** Add descriptions to the encodings in the list */
@@ -371,7 +379,6 @@ protected:
 
   KMEdit* mEditor;
   QGridLayout* mGrid;
-  //KDNDDropZone *mDropZone;
   KMMessage *mMsg;
   QListView *mAtmListBox;
   QList<QListViewItem> mAtmItemList;
@@ -410,6 +417,9 @@ protected:
   QString mCharset;
   QString mDefCharset;
   QFont mSavedEditorFont;
+
+private slots:
+  void slotCompletionModeChanged( KGlobalSettings::Completion );
 
 private:
   QColor foreColor,backColor;

_______________________________________________
Kmail Developers mailing list
Kmail@master.kde.org
http://master.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