summaryrefslogtreecommitdiff
path: root/core/pim/addressbook/abtable.cpp
Unidiff
Diffstat (limited to 'core/pim/addressbook/abtable.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/pim/addressbook/abtable.cpp1091
1 files changed, 1091 insertions, 0 deletions
diff --git a/core/pim/addressbook/abtable.cpp b/core/pim/addressbook/abtable.cpp
new file mode 100644
index 0000000..0911edf
--- a/dev/null
+++ b/core/pim/addressbook/abtable.cpp
@@ -0,0 +1,1091 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Palmtop Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <qpe/categoryselect.h>
22#include <qpe/config.h>
23#include <qpe/stringutil.h>
24#include <qpe/qcopenvelope_qws.h>
25
26#include <qasciidict.h>
27#include <qdatetime.h>
28#include <qfile.h>
29
30#include "abtable.h"
31
32#include <errno.h>
33#include <fcntl.h>
34#include <unistd.h>
35#include <stdlib.h>
36
37#include <ctype.h> //toupper() for key hack
38
39static bool contactCompare( const Contact &cnt, const QRegExp &r, int category );
40
41//### qtmail/addresslist.cpp hardcodes this filename as well
42static QString journalFileName()
43{
44 QString str = getenv("HOME");
45 str +="/.abjournal";
46 return str;
47}
48
49
50
51/*!
52 \class AbTableItem abtable.h
53
54 \brief QTableItem based class for showing a field of an entry
55*/
56
57AbTableItem::AbTableItem( QTable *t, EditType et, const QString &s,
58 const QString &secondSortKey)
59 : QTableItem( t, et, s )
60{
61 // sortKey = s.lower() + QChar( '\0' ) + secondSortKey.lower();
62 sortKey = Qtopia::buildSortKey( s, secondSortKey );
63}
64
65int AbTableItem::alignment() const
66{
67 return AlignLeft|AlignVCenter;
68}
69
70QString AbTableItem::key() const
71{
72 return sortKey;
73}
74
75// A way to reset the item, without out doing a delete or a new...
76void AbTableItem::setItem( const QString &txt, const QString &secondKey )
77{
78 setText( txt );
79 sortKey = Qtopia::buildSortKey( txt, secondKey );
80
81 // sortKey = txt.lower() + QChar( '\0' ) + secondKey.lower();
82}
83
84/*!
85 \class AbPickItem abtable.h
86
87 \brief QTableItem based class for showing slection of an entry
88*/
89
90AbPickItem::AbPickItem( QTable *t ) :
91 QTableItem(t, WhenCurrent, "?")
92{
93}
94
95QWidget *AbPickItem::createEditor() const
96{
97 QComboBox* combo = new QComboBox( table()->viewport() );
98 ( (AbPickItem*)this )->cb = combo;
99 AbTable* t = static_cast<AbTable*>(table());
100 QStringList c = t->choiceNames();
101 int cur = 0;
102 for (QStringList::ConstIterator it = c.begin(); it!=c.end(); ++it) {
103 if ( *it == text() )
104 cur = combo->count();
105 combo->insertItem(*it);
106 }
107 combo->setCurrentItem(cur);
108 return combo;
109}
110
111void AbPickItem::setContentFromEditor( QWidget *w )
112{
113 if ( w->inherits("QComboBox") )
114 setText( ( (QComboBox*)w )->currentText() );
115 else
116 QTableItem::setContentFromEditor( w );
117}
118
119/*!
120 \class AbTable abtable.h
121
122 \brief QTable based class for showing a list of entries
123*/
124
125AbTable::AbTable( const QValueList<int> *order, QWidget *parent, const char *name )
126// #ifdef QT_QTABLE_NOHEADER_CONSTRUCTOR
127// : QTable( 0, 0, parent, name, TRUE ),
128// #else
129 : QTable( parent, name ),
130// #endif
131 lastSortCol( -1 ),
132 asc( TRUE ),
133 intFields( order ),
134 currFindRow( -2 ),
135 mCat( 0 )
136{
137 mCat.load( categoryFileName() );
138 setSelectionMode( NoSelection );
139 init();
140 setSorting( TRUE );
141 connect( this, SIGNAL(clicked(int,int,int,const QPoint &)),
142 this, SLOT(itemClicked(int,int)) );
143}
144
145AbTable::~AbTable()
146{
147}
148
149void AbTable::init()
150{
151 setNumRows( 0 );
152 setNumCols( 2 );
153
154 horizontalHeader()->setLabel( 0, tr( "Full Name" ));
155 horizontalHeader()->setLabel( 1, tr( "Contact" ));
156 setLeftMargin( 0 );
157 verticalHeader()->hide();
158}
159
160void AbTable::columnClicked( int col )
161{
162 if ( !sorting() )
163 return;
164
165 if ( lastSortCol == -1 )
166 lastSortCol = col;
167
168 if ( col == lastSortCol ) {
169 asc = !asc;
170 } else {
171 lastSortCol = col;
172 asc = TRUE;
173 }
174 resort();
175}
176
177void AbTable::resort()
178{
179 if ( sorting() ) {
180 if ( lastSortCol == -1 )
181 lastSortCol = 0;
182 sortColumn( lastSortCol, asc, TRUE );
183 updateVisible();
184 }
185}
186
187Contact AbTable::currentEntry()
188{
189 Contact cnt;
190 AbTableItem *abItem;
191 abItem = static_cast<AbTableItem*>(item( currentRow(), 0 ));
192 if ( abItem ) {
193 cnt = contactList[abItem];
194 }
195 return cnt;
196}
197
198void AbTable::replaceCurrentEntry( const Contact &newContact )
199{
200 int row = currentRow();
201 updateJournal( newContact, Contact::ACTION_REPLACE, row );
202 updateVisible();
203
204 journalFreeReplace( newContact, row );
205}
206
207void AbTable::deleteCurrentEntry()
208{
209 int row = currentRow();
210 AbTableItem *abItem;
211 abItem = static_cast<AbTableItem*>(item( row, 0 ));
212 Contact oldContact;
213 oldContact = contactList[abItem];
214 updateJournal( oldContact, Contact::ACTION_REMOVE, row );
215
216 // a little wasteful, but it ensure's there is only one place
217 // where we delete.
218 journalFreeRemove( row );
219 updateVisible();
220
221 if ( numRows() == 0 )
222 emit empty( TRUE );
223}
224
225void AbTable::clear()
226{
227 contactList.clear();
228 for ( int r = 0; r < numRows(); ++r ) {
229 for ( int c = 0; c < numCols(); ++c ) {
230 if ( cellWidget( r, c ) )
231 clearCellWidget( r, c );
232 clearCell( r, c );
233 }
234 }
235 setNumRows( 0 );
236}
237
238void AbTable::refresh()
239{
240 int rows = numRows();
241 QString value;
242 AbTableItem *abi;
243 for ( int r = 0; r < rows; ++r ) {
244 abi = static_cast<AbTableItem*>( item(r, 0) );
245 value = findContactContact( contactList[abi] );
246 static_cast<AbTableItem*>( item(r, 1) )->setItem( value, abi->text() );
247 }
248 resort();
249}
250
251void AbTable::keyPressEvent( QKeyEvent *e )
252{
253 char key = toupper( e->ascii() );
254
255 if ( key >= 'A' && key <= 'Z' )
256 moveTo( key );
257
258 switch( e->key() ) {
259 case Qt::Key_Space:
260 case Qt::Key_Return:
261 case Qt::Key_Enter:
262 emit details();
263 break;
264 default:
265 QTable::keyPressEvent( e );
266 }
267}
268
269void AbTable::moveTo( char c )
270{
271
272 int rows = numRows();
273 QString value;
274 AbTableItem *abi;
275 int r;
276 if ( asc ) {
277 r = 0;
278 while ( r < rows-1) {
279 abi = static_cast<AbTableItem*>( item(r, 0) );
280 QChar first = abi->key()[0];
281 //### is there a bug in QChar to char comparison???
282 if ( first.row() || first.cell() >= c )
283 break;
284 r++;
285 }
286 } else {
287 //### should probably disable reverse sorting instead
288 r = rows - 1;
289 while ( r > 0 ) {
290 abi = static_cast<AbTableItem*>( item(r, 0) );
291 QChar first = abi->key()[0];
292 //### is there a bug in QChar to char comparison???
293 if ( first.row() || first.cell() >= c )
294 break;
295 r--;
296 }
297 }
298 setCurrentCell( r, currentColumn() );
299}
300
301
302QString AbTable::findContactName( const Contact &entry )
303{
304 // We use the fileAs, then company, defaultEmail
305 QString str;
306 str = entry.fileAs();
307 if ( str.isEmpty() ) {
308 str = entry.company();
309 if ( str.isEmpty() ) {
310 str = entry.defaultEmail();
311 }
312 }
313 return str;
314}
315
316QString AbTable::findContactContact( const Contact &entry )
317{
318 QString value;
319 value = "";
320 for ( QValueList<int>::ConstIterator it = intFields->begin();
321 it != intFields->end(); ++it ) {
322 switch ( *it ) {
323 default:
324 break;
325 case Qtopia::Title:
326 value = entry.title();
327 break;
328 case Qtopia::Suffix:
329 value = entry.suffix();
330 break;
331 case Qtopia::FileAs:
332 value = entry.fileAs();
333 break;
334 case Qtopia::DefaultEmail:
335 value = entry.defaultEmail();
336 case Qtopia::Emails:
337 value = entry.emails();
338 break;
339 case Qtopia::HomeStreet:
340 value = entry.homeStreet();
341 break;
342 case Qtopia::HomeCity:
343 value = entry.homeCity();
344 break;
345 case Qtopia::HomeState:
346 value = entry.homeState();
347 break;
348 case Qtopia::HomeZip:
349 value = entry.homeZip();
350 break;
351 case Qtopia::HomeCountry:
352 value = entry.homeCountry();
353 break;
354 case Qtopia::HomePhone:
355 value = entry.homePhone();
356 break;
357 case Qtopia::HomeFax:
358 value = entry.homeFax();
359 break;
360 case Qtopia::HomeMobile:
361 value = entry.homeMobile();
362 break;
363 case Qtopia::HomeWebPage:
364 value = entry.homeWebpage();
365 break;
366 case Qtopia::Company:
367 value = entry.company();
368 break;
369 case Qtopia::BusinessCity:
370 value = entry.businessCity();
371 break;
372 case Qtopia::BusinessStreet:
373 value = entry.businessStreet();
374 break;
375 case Qtopia::BusinessZip:
376 value = entry.businessZip();
377 break;
378 case Qtopia::BusinessCountry:
379 value = entry.businessCountry();
380 break;
381 case Qtopia::BusinessWebPage:
382 value = entry.businessWebpage();
383 break;
384 case Qtopia::JobTitle:
385 value = entry.jobTitle();
386 break;
387 case Qtopia::Department:
388 value = entry.department();
389 break;
390 case Qtopia::Office:
391 value = entry.office();
392 break;
393 case Qtopia::BusinessPhone:
394 value = entry.businessPhone();
395 break;
396 case Qtopia::BusinessFax:
397 value = entry.businessFax();
398 break;
399 case Qtopia::BusinessMobile:
400 value = entry.businessMobile();
401 break;
402 case Qtopia::BusinessPager:
403 value = entry.businessPager();
404 break;
405 case Qtopia::Profession:
406 value = entry.profession();
407 break;
408 case Qtopia::Assistant:
409 value = entry.assistant();
410 break;
411 case Qtopia::Manager:
412 value = entry.manager();
413 break;
414 case Qtopia::Spouse:
415 value = entry.spouse();
416 break;
417 case Qtopia::Gender:
418 value = entry.gender();
419 break;
420 case Qtopia::Birthday:
421 value = entry.birthday();
422 break;
423 case Qtopia::Anniversary:
424 value = entry.anniversary();
425 break;
426 case Qtopia::Nickname:
427 value = entry.nickname();
428 break;
429 case Qtopia::Children:
430 value = entry.children();
431 break;
432 case Qtopia::Notes:
433 value = entry.notes();
434 break;
435 }
436 if ( !value.isEmpty() )
437 break;
438 }
439 return value;
440}
441
442void AbTable::addEntry( const Contact &newCnt )
443{
444 int row = numRows();
445 setNumRows( row + 1 );
446 updateJournal( newCnt, Contact::ACTION_ADD );
447 insertIntoTable( newCnt, row );
448 setCurrentCell( row, 0 );
449 updateVisible();
450}
451
452void AbTable::updateJournal( const Contact &cnt,
453 Contact::journal_action action, int row )
454{
455 QFile f( journalFileName() );
456 if ( !f.open(IO_WriteOnly|IO_Append) )
457 return;
458 QString buf;
459 QCString str;
460 buf = "<Contact ";
461 cnt.save( buf );
462 buf += " action=\"" + QString::number( (int)action ) + "\" ";
463 if ( action == Contact::ACTION_REMOVE || action == Contact::ACTION_REPLACE)
464 buf += " actionrow=\"" + QString::number(row) + "\" ";
465 buf += "/>\n";
466 QCString cstr = buf.utf8();
467 f.writeBlock( cstr.data(), cstr.length() );
468 QCopEnvelope( "QPE/PIM", "addressbookUpdated()" );
469}
470
471bool AbTable::save( const QString &fn )
472{
473// QTime t;
474// t.start();
475
476 QString strNewFile = fn + ".new";
477 QFile f( strNewFile );
478 if ( !f.open( IO_WriteOnly|IO_Raw ) )
479 return false;
480
481 int total_written;
482 QString out;
483 out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n"
484 " <Groups>\n"
485 " </Groups>\n"
486 " <Contacts>\n";
487 QMapIterator<AbTableItem*, Contact> it;
488 for ( it = contactList.begin(); it != contactList.end(); ++it ) {
489 out += "<Contact ";
490 it.data().save( out );
491 out += "/>\n";
492 QCString cstr = out.utf8();
493 total_written = f.writeBlock( cstr.data(), cstr.length() );
494 if ( total_written != int(cstr.length()) ) {
495 f.close();
496 QFile::remove( strNewFile );
497 return false;
498 }
499 out = "";
500 }
501 out += " </Contacts>\n</AddressBook>\n";
502
503 QCString cstr = out.utf8();
504 total_written = f.writeBlock( cstr.data(), cstr.length() );
505 if ( total_written != int(cstr.length()) ) {
506 f.close();
507 QFile::remove( strNewFile );
508 return false;
509 }
510 f.close();
511
512// qDebug("saving: %d", t.elapsed() );
513
514 // move the file over, I'm just going to use the system call
515 // because, I don't feel like using QDir.
516 if ( ::rename( strNewFile.latin1(), fn.latin1() ) < 0 ) {
517 qWarning( "problem renaming file %s to %s, errno: %d",
518 strNewFile.latin1(), fn.latin1(), errno );
519 // remove the tmp file...
520 QFile::remove( strNewFile );
521 }
522 // remove the journal...
523 QFile::remove( journalFileName() );
524 return true;
525}
526
527void AbTable::load( const QString &fn )
528{
529 setSorting( false );
530 loadFile( fn, false );
531 // merge in the journal
532 if ( QFile::exists( journalFileName() ) ) {
533 loadFile( journalFileName(), true );
534 save( fn );
535 }
536 setSorting( true );
537 resort();
538}
539
540void AbTable::loadFile( const QString &strFile, bool journalFile )
541{
542// QTime t;
543// t.start();
544 QFile f( strFile );
545 if ( !f.open(IO_ReadOnly) )
546 return;
547 QList<Contact> list;
548 list.setAutoDelete( TRUE );
549 QByteArray ba = f.readAll();
550 f.close();
551 char *uc = ba.data();//(QChar *)data.unicode();
552 int len = ba.size();//data.length();
553 bool foundAction = false;
554 Contact::journal_action action;
555 bool foundKey = false;
556 int journalKey = 0;
557
558 const int JOURNALACTION = Qtopia::Notes + 1;
559 const int JOURNALROW = JOURNALACTION + 1;
560
561 // **********************************
562 // CHANGE THE SIZE OF THE DICT IF YOU ADD ANY MORE FIELDS!!!!
563 // **********************************
564 QAsciiDict<int> dict( 47 );
565 dict.setAutoDelete( TRUE );
566 dict.insert( "Uid", new int(Qtopia::AddressUid) );
567 dict.insert( "Title", new int(Qtopia::Title) );
568 dict.insert( "FirstName", new int(Qtopia::FirstName) );
569 dict.insert( "MiddleName", new int(Qtopia::MiddleName) );
570 dict.insert( "LastName", new int(Qtopia::LastName) );
571 dict.insert( "Suffix", new int(Qtopia::Suffix) );
572 dict.insert( "FileAs", new int(Qtopia::FileAs) );
573 dict.insert( "Categories", new int(Qtopia::AddressCategory) );
574 dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) );
575 dict.insert( "Emails", new int(Qtopia::Emails) );
576 dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) );
577 dict.insert( "HomeCity", new int(Qtopia::HomeCity) );
578 dict.insert( "HomeState", new int(Qtopia::HomeState) );
579 dict.insert( "HomeZip", new int(Qtopia::HomeZip) );
580 dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) );
581 dict.insert( "HomePhone", new int(Qtopia::HomePhone) );
582 dict.insert( "HomeFax", new int(Qtopia::HomeFax) );
583 dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) );
584 dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) );
585 dict.insert( "Company", new int(Qtopia::Company) );
586 dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) );
587 dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) );
588 dict.insert( "BusinessState", new int(Qtopia::BusinessState) );
589 dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) );
590 dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) );
591 dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) );
592 dict.insert( "JobTitle", new int(Qtopia::JobTitle) );
593 dict.insert( "Department", new int(Qtopia::Department) );
594 dict.insert( "Office", new int(Qtopia::Office) );
595 dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) );
596 dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) );
597 dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) );
598 dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) );
599 dict.insert( "Profession", new int(Qtopia::Profession) );
600 dict.insert( "Assistant", new int(Qtopia::Assistant) );
601 dict.insert( "Manager", new int(Qtopia::Manager) );
602 dict.insert( "Spouse", new int(Qtopia::Spouse) );
603 dict.insert( "Children", new int(Qtopia::Children) );
604 dict.insert( "Gender", new int(Qtopia::Gender) );
605 dict.insert( "Birthday", new int(Qtopia::Birthday) );
606 dict.insert( "Anniversary", new int(Qtopia::Anniversary) );
607 dict.insert( "Nickname", new int(Qtopia::Nickname) );
608 dict.insert( "Notes", new int(Qtopia::Notes) );
609 dict.insert( "action", new int(JOURNALACTION) );
610 dict.insert( "actionrow", new int(JOURNALROW) );
611
612 int i = 0;
613 int num = 0;
614 char *point;
615 while ( (point = strstr( uc+i, "<Contact " ) ) != NULL ) {
616 i = point - uc;
617 // if we are reading the standard file, we just need to
618 // insert info, so just say we'll do an insert...
619 action = Contact::ACTION_ADD;
620 // new Contact
621 Contact *cnt = new Contact;
622 i += 9;
623 while ( 1 ) {
624 while ( i < len && (uc[i] == ' ' || uc[i] == '\n' || uc[i] == '\r') )
625 i++;
626 if ( i >= len-2 || (uc[i] == '/' && uc[i+1] == '>') )
627 break;
628 // we have another attribute read it.
629 int j = i;
630 while ( j < len && uc[j] != '=' )
631 j++;
632 char *attr = uc+i;
633 uc[j] = '\0';
634 //qDebug("attr=%s", attr.latin1() );
635 i = ++j; // skip =
636 while ( i < len && uc[i] != '"' )
637 i++;
638 j = ++i;
639 bool haveEnt = FALSE;
640 bool haveUtf = FALSE;
641 while ( j < len && uc[j] != '"' ) {
642 if ( uc[j] == '&' )
643 haveEnt = TRUE;
644 if ( ((unsigned char)uc[j]) > 0x7f )
645 haveUtf = TRUE;
646 j++;
647 }
648
649 if ( j == i ) {
650 // empty value
651 i = j + 1;
652 continue;
653 }
654
655 QString value = haveUtf ? QString::fromUtf8( uc+i, j-i )
656 : QString::fromLatin1( uc+i, j-i );
657 if ( haveEnt )
658 value = Qtopia::plainString( value );
659 i = j + 1;
660
661 int *find = dict[ attr ];
662 if ( !find ) {
663 cnt->setCustomField(attr, value);
664 continue;
665 }
666#if 1
667 switch( *find ) {
668 case Qtopia::AddressUid:
669 cnt->setUid( value.toInt() );
670 break;
671 case Qtopia::AddressCategory:
672 cnt->setCategories( Qtopia::Record::idsFromString( value ));
673 break;
674 case JOURNALACTION:
675 action = Contact::journal_action(value.toInt());
676 break;
677 case JOURNALROW:
678 journalKey = value.toInt();
679 break;
680
681 default:
682 cnt->insert( *find, value );
683 break;
684 }
685#endif
686 }
687
688 // sadly we can't delay adding of items from the journal to get
689 // the proper effect, but then, the journal should _never_ be
690 // that huge, and recovering from a crash is not necessarily
691 // a *fast* thing.
692 switch ( action ) {
693 case Contact::ACTION_ADD:
694 if ( journalFile ) {
695 int myrows = numRows();
696 setNumRows( myrows + 1 );
697 insertIntoTable( *cnt, myrows );
698 delete cnt;
699 }
700 else
701 list.append( cnt );
702 break;
703 case Contact::ACTION_REMOVE:
704 // yup, we don't use the entry to remove the object...
705 journalFreeRemove( journalKey );
706 delete cnt;
707 break;
708 case Contact::ACTION_REPLACE:
709 journalFreeReplace( *cnt, journalKey );
710 delete cnt;
711 break;
712 default:
713 break;
714 }
715 num++;
716 foundAction = false;
717 foundKey = false;
718 // if ( num % 100 == 0 ) {
719 // qDebug("loading file, num=%d, t=%d", num, t.elapsed() );
720 // }
721 }
722 if ( list.count() > 0 ) {
723 internalAddEntries( list );
724 }
725// qDebug("done loading %d, t=%d", num, t.elapsed() );
726
727}
728
729void AbTable::realignTable( int row )
730{
731 QTableItem *ti1,
732 *ti2;
733 int totalRows = numRows();
734 for ( int curr = row; curr < totalRows - 1; curr++ ) {
735 // the same info from the todo list still applies, but I
736 // don't think it is _too_ bad.
737 ti1 = item( curr + 1, 0 );
738 ti2 = item( curr + 1, 1 );
739 takeItem( ti1 );
740 takeItem( ti2 );
741 setItem( curr, 0, ti1 );
742 setItem( curr, 1, ti2 );
743 }
744 setNumRows( totalRows - 1 );
745 resort();
746}
747
748void AbTable::insertIntoTable( const Contact &cnt, int row )
749{
750 QString strName,
751 strContact;
752
753 strName = findContactName( cnt );
754 strContact = findContactContact( cnt );
755
756 AbTableItem *ati;
757 ati = new AbTableItem( this, QTableItem::Never, strName, strContact);
758 contactList.insert( ati, cnt );
759 setItem( row, 0, ati );
760 ati = new AbTableItem( this, QTableItem::Never, strContact, strName);
761 setItem( row, 1, ati );
762
763 //### cannot do this; table only has two columns at this point
764 // setItem( row, 2, new AbPickItem( this ) );
765
766 // resort at some point?
767}
768
769void AbTable::internalAddEntries( QList<Contact> &list )
770{
771 setUpdatesEnabled( FALSE );
772 setNumRows( list.count() );
773 int row = 0;
774 Contact *it;
775 for ( it = list.first(); it; it = list.next() )
776 insertIntoTable( *it, row++ );
777 resort();
778 setUpdatesEnabled( TRUE );
779}
780
781
782void AbTable::journalFreeReplace( const Contact &cnt, int row )
783{
784 QString strName,
785 strContact;
786 AbTableItem *ati;
787
788 strName = findContactName( cnt );
789 strContact = findContactContact( cnt );
790 ati = static_cast<AbTableItem*>(item(row, 0));
791 contactList.remove( ati );
792 ati->setItem( strName, strContact );
793 contactList.insert( ati, cnt );
794
795 ati = static_cast<AbTableItem*>(item(row, 1));
796 ati->setItem( strContact, strName );
797}
798
799void AbTable::journalFreeRemove( int row )
800{
801 AbTableItem *ati;
802 ati = static_cast<AbTableItem*>(item(row, 0));
803 if ( !ati )
804 return;
805 contactList.remove( ati );
806 realignTable( row );
807}
808
809#if QT_VERSION <= 230
810#ifndef SINGLE_APP
811void QTable::paintEmptyArea( QPainter *p, int cx, int cy, int cw, int ch )
812{
813 // Region of the rect we should draw
814 QRegion reg( QRect( cx, cy, cw, ch ) );
815 // Subtract the table from it
816 reg = reg.subtract( QRect( QPoint( 0, 0 ), tableSize() ) );
817 // And draw the rectangles (transformed as needed)
818 QArray<QRect> r = reg.rects();
819 for (unsigned int i=0; i<r.count(); i++)
820 p->fillRect( r[i], colorGroup().brush( QColorGroup::Base ) );
821}
822#endif
823#endif
824
825
826// int AbTable::rowHeight( int ) const
827// {
828// return 18;
829// }
830
831// int AbTable::rowPos( int row ) const
832// {
833// return 18*row;
834// }
835
836// int AbTable::rowAt( int pos ) const
837// {
838// return QMIN( pos/18, numRows()-1 );
839// }
840
841void AbTable::slotDoFind( const QString &findString, bool caseSensitive,
842 bool backwards, int category )
843{
844 if ( currFindRow < -1 )
845 currFindRow = currentRow() - 1;
846 clearSelection( TRUE );
847 int rows,
848 row;
849 AbTableItem *ati;
850 QRegExp r( findString );
851 r.setCaseSensitive( caseSensitive );
852 rows = numRows();
853 static bool wrapAround = true;
854
855 if ( !backwards ) {
856 for ( row = currFindRow + 1; row < rows; row++ ) {
857 ati = static_cast<AbTableItem*>( item(row, 0) );
858 if ( contactCompare( contactList[ati], r, category ) )
859 break;
860
861 }
862 } else {
863 for ( row = currFindRow - 1; row > -1; row-- ) {
864 ati = static_cast<AbTableItem*>( item(row, 0) );
865 if ( contactCompare( contactList[ati], r, category ) )
866 break;
867 }
868 }
869 if ( row >= rows || row < 0 ) {
870 if ( row < 0 )
871 currFindRow = rows;
872 else
873 currFindRow = -1;
874
875 if ( wrapAround )
876 emit signalWrapAround();
877 else
878 emit signalNotFound();
879
880 wrapAround = !wrapAround;
881 } else {
882 currFindRow = row;
883 QTableSelection foundSelection;
884 foundSelection.init( currFindRow, 0 );
885 foundSelection.expandTo( currFindRow, numCols() - 1 );
886 addSelection( foundSelection );
887 setCurrentCell( currFindRow, numCols() - 1 );
888 wrapAround = true;
889 }
890}
891
892static bool contactCompare( const Contact &cnt, const QRegExp &r, int category )
893{
894 bool returnMe;
895 QArray<int> cats;
896 cats = cnt.categories();
897
898 returnMe = false;
899 if ( (category == -1 && cats.count() == 0) || category == -2 )
900 returnMe = cnt.match( r );
901 else {
902 int i;
903 for ( i = 0; i < int(cats.count()); i++ ) {
904 if ( cats[i] == category ) {
905 returnMe = cnt.match( r );
906 break;
907 }
908 }
909 }
910 return returnMe;
911}
912
913void AbTable::fitColumns()
914{
915 int contentsWidth = visibleWidth();
916 int n = numCols();
917 int pw = n == 3 ? columnWidth(2) : 0;
918 setColumnWidth( 0, contentsWidth - contentsWidth / 2 );
919 setColumnWidth( 1, contentsWidth / 2 - pw );
920}
921
922void AbTable::show()
923{
924 fitColumns();
925 QTable::show();
926}
927
928void AbTable::setChoiceNames( const QStringList& list)
929{
930 choicenames = list;
931 if ( choicenames.isEmpty() ) {
932 // hide pick column
933 setNumCols( 2 );
934 } else {
935 // show pick column
936 setNumCols( 3 );
937 setColumnWidth( 2, fontMetrics().width(tr( "Pick" ))+8 );
938 horizontalHeader()->setLabel( 2, tr( "Pick" ));
939 }
940 fitColumns();
941}
942
943void AbTable::itemClicked(int,int col)
944{
945 if ( col == 2 ) {
946 return;
947 } else {
948 emit details();
949 }
950}
951
952QStringList AbTable::choiceNames() const
953{
954 return choicenames;
955}
956
957void AbTable::setChoiceSelection(int /*index*/, const QStringList& /*list*/)
958{
959 /* ######
960
961 QString selname = choicenames.at(index);
962 for (each row) {
963 Contact *c = contactForRow(row);
964 if ( list.contains(c->email) ) {
965 list.remove(c->email);
966 setText(row, 2, selname);
967 }
968 }
969 for (remaining list items) {
970 Contact *c = new contact(item);
971 setText(newrow, 2, selname);
972 }
973
974 */
975}
976
977QStringList AbTable::choiceSelection(int /*index*/) const
978{
979 QStringList r;
980 /* ######
981
982 QString selname = choicenames.at(index);
983 for (each row) {
984 Contact *c = contactForRow(row);
985 if ( text(row,2) == selname ) {
986 r.append(c->email);
987 }
988 }
989
990 */
991 return r;
992}
993
994void AbTable::setShowCategory( const QString &c )
995{
996 showCat = c;
997 updateVisible();
998}
999
1000QString AbTable::showCategory() const
1001{
1002 return showCat;
1003}
1004
1005
1006QStringList AbTable::categories()
1007{
1008 mCat.load( categoryFileName() );
1009 QStringList categoryList = mCat.labels( "Contacts" );
1010 return categoryList;
1011}
1012
1013void AbTable::updateVisible()
1014{
1015 int visible,
1016 totalRows,
1017 id,
1018 totalCats,
1019 it,
1020 row;
1021 bool hide;
1022 AbTableItem *ati;
1023 Contact *cnt;
1024 visible = 0;
1025
1026 setPaintingEnabled( FALSE );
1027
1028 totalRows = numRows();
1029 id = mCat.id( "Contacts", showCat );
1030 QArray<int> cats;
1031 for ( row = 0; row < totalRows; row++ ) {
1032 ati = static_cast<AbTableItem*>( item(row, 0) );
1033 cnt = &contactList[ati];
1034 cats = cnt->categories();
1035 hide = false;
1036 if ( !showCat.isEmpty() ) {
1037 if ( showCat == tr( "Unfiled" ) ) {
1038 if ( cats.count() > 0 )
1039 hide = true;
1040 } else {
1041 // do some comparing
1042 if ( !hide ) {
1043 hide = true;
1044 totalCats = int(cats.count());
1045 for ( it = 0; it < totalCats; it++ ) {
1046 if ( cats[it] == id ) {
1047 hide = false;
1048 break;
1049 }
1050 }
1051 }
1052 }
1053 }
1054 if ( hide ) {
1055 if ( currentRow() == row )
1056 setCurrentCell( -1, 0 );
1057 if ( rowHeight(row) > 0 )
1058 hideRow( row );
1059 } else {
1060 if ( rowHeight(row) == 0 ) {
1061 showRow( row );
1062 adjustRow( row );
1063 }
1064 visible++;
1065 }
1066 }
1067 if ( !visible )
1068 setCurrentCell( -1, 0 );
1069
1070 setPaintingEnabled( TRUE );
1071}
1072
1073
1074void AbTable::setPaintingEnabled( bool e )
1075{
1076 if ( e != enablePainting ) {
1077 if ( !enablePainting ) {
1078 enablePainting = true;
1079 rowHeightChanged( 0 );
1080 viewport()->update();
1081 } else {
1082 enablePainting = false;
1083 }
1084 }
1085}
1086
1087void AbTable::rowHeightChanged( int row )
1088{
1089 if ( enablePainting )
1090 QTable::rowHeightChanged( row );
1091}