author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (unidiff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /core/pim/addressbook/abtable.cpp | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
Diffstat (limited to 'core/pim/addressbook/abtable.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/pim/addressbook/abtable.cpp | 1091 |
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 | |||
39 | static bool contactCompare( const Contact &cnt, const QRegExp &r, int category ); | ||
40 | |||
41 | //### qtmail/addresslist.cpp hardcodes this filename as well | ||
42 | static 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 | |||
57 | AbTableItem::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 | |||
65 | int AbTableItem::alignment() const | ||
66 | { | ||
67 | return AlignLeft|AlignVCenter; | ||
68 | } | ||
69 | |||
70 | QString AbTableItem::key() const | ||
71 | { | ||
72 | return sortKey; | ||
73 | } | ||
74 | |||
75 | // A way to reset the item, without out doing a delete or a new... | ||
76 | void 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 | |||
90 | AbPickItem::AbPickItem( QTable *t ) : | ||
91 | QTableItem(t, WhenCurrent, "?") | ||
92 | { | ||
93 | } | ||
94 | |||
95 | QWidget *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 | |||
111 | void 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 | |||
125 | AbTable::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 | |||
145 | AbTable::~AbTable() | ||
146 | { | ||
147 | } | ||
148 | |||
149 | void 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 | |||
160 | void 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 | |||
177 | void AbTable::resort() | ||
178 | { | ||
179 | if ( sorting() ) { | ||
180 | if ( lastSortCol == -1 ) | ||
181 | lastSortCol = 0; | ||
182 | sortColumn( lastSortCol, asc, TRUE ); | ||
183 | updateVisible(); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | Contact 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 | |||
198 | void 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 | |||
207 | void 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 | |||
225 | void 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 | |||
238 | void 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 | |||
251 | void 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 | |||
269 | void 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 | |||
302 | QString 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 | |||
316 | QString 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 | |||
442 | void 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 | |||
452 | void 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 | |||
471 | bool 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 | |||
527 | void 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 | |||
540 | void 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 | |||
729 | void 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 | |||
748 | void 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 | |||
769 | void 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 | |||
782 | void 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 | |||
799 | void 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 | ||
811 | void 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 | |||
841 | void 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 | |||
892 | static 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 | |||
913 | void 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 | |||
922 | void AbTable::show() | ||
923 | { | ||
924 | fitColumns(); | ||
925 | QTable::show(); | ||
926 | } | ||
927 | |||
928 | void 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 | |||
943 | void AbTable::itemClicked(int,int col) | ||
944 | { | ||
945 | if ( col == 2 ) { | ||
946 | return; | ||
947 | } else { | ||
948 | emit details(); | ||
949 | } | ||
950 | } | ||
951 | |||
952 | QStringList AbTable::choiceNames() const | ||
953 | { | ||
954 | return choicenames; | ||
955 | } | ||
956 | |||
957 | void 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 | |||
977 | QStringList 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 | |||
994 | void AbTable::setShowCategory( const QString &c ) | ||
995 | { | ||
996 | showCat = c; | ||
997 | updateVisible(); | ||
998 | } | ||
999 | |||
1000 | QString AbTable::showCategory() const | ||
1001 | { | ||
1002 | return showCat; | ||
1003 | } | ||
1004 | |||
1005 | |||
1006 | QStringList AbTable::categories() | ||
1007 | { | ||
1008 | mCat.load( categoryFileName() ); | ||
1009 | QStringList categoryList = mCat.labels( "Contacts" ); | ||
1010 | return categoryList; | ||
1011 | } | ||
1012 | |||
1013 | void 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 | |||
1074 | void 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 | |||
1087 | void AbTable::rowHeightChanged( int row ) | ||
1088 | { | ||
1089 | if ( enablePainting ) | ||
1090 | QTable::rowHeightChanged( row ); | ||
1091 | } | ||