kpilot

addressWidget.cc

Go to the documentation of this file.
00001 /* KPilot
00002 **
00003 ** Copyright (C) 1998-2001 by Dan Pilone
00004 ** Copyright (C) 2003 Reinhold Kainhofer <reinhold@kainhofer.com>
00005 ** Copyright (C) 2004 by Adriaan de Groot
00006 **
00007 ** This file defines the addressWidget, that part of KPilot that
00008 ** displays address records from the Pilot.
00009 */
00010 
00011 /*
00012 ** This program is free software; you can redistribute it and/or modify
00013 ** it under the terms of the GNU General Public License as published by
00014 ** the Free Software Foundation; either version 2 of the License, or
00015 ** (at your option) any later version.
00016 **
00017 ** This program is distributed in the hope that it will be useful,
00018 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00020 ** GNU General Public License for more details.
00021 **
00022 ** You should have received a copy of the GNU General Public License
00023 ** along with this program in a file called COPYING; if not, write to
00024 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00025 ** MA 02110-1301, USA.
00026 */
00027 
00028 /*
00029 ** Bug reports and questions can be sent to kde-pim@kde.org
00030 */
00031 
00032 
00033 #ifndef _KPILOT_OPTIONS_H
00034 #include "options.h"
00035 #endif
00036 
00037 #include <iostream>
00038 #include <cstring>
00039 #include <cstdlib>
00040 
00041 #include <qptrlist.h>
00042 #include <qlistbox.h>
00043 #include <qfile.h>
00044 #include <qpushbutton.h>
00045 #include <qtextstream.h>
00046 #include <qlayout.h>
00047 #include <qlabel.h>
00048 #include <qmultilineedit.h>
00049 #include <qcombobox.h>
00050 #include <qwhatsthis.h>
00051 #include <qtextview.h>
00052 #include <qtextcodec.h>
00053 #include <qregexp.h>
00054 
00055 #include <kapplication.h>
00056 #include <kmessagebox.h>
00057 #include <kdebug.h>
00058 #include <kfiledialog.h>
00059 
00060 #include "kpilotConfig.h"
00061 #include "listItems.h"
00062 #include "addressEditor.h"
00063 #include "pilotLocalDatabase.h"
00064 
00065 #include "addressWidget.moc"
00066 
00067 
00068 AddressWidget::AddressWidget(QWidget * parent,
00069     const QString & path) :
00070     PilotComponent(parent, "component_address", path),
00071     fAddrInfo(0L),
00072     fAddressAppInfo(0L),
00073     fPendingAddresses(0)
00074 {
00075     FUNCTIONSETUP;
00076 
00077     setupWidget();
00078     fAddressList.setAutoDelete(true);
00079 }
00080 
00081 AddressWidget::~AddressWidget()
00082 {
00083     FUNCTIONSETUP;
00084 }
00085 
00086 int AddressWidget::getAllAddresses(PilotDatabase * addressDB)
00087 {
00088     FUNCTIONSETUP;
00089 
00090     int currentRecord = 0;
00091     PilotRecord *pilotRec;
00092     PilotAddress *address;
00093 
00094 
00095 #ifdef DEBUG
00096     DEBUGKPILOT << fname << ": Reading AddressDB..." << endl;
00097 #endif
00098 
00099     while ((pilotRec = addressDB->readRecordByIndex(currentRecord)) != 0L)
00100     {
00101         if (!(pilotRec->isDeleted()) &&
00102             (!(pilotRec->isSecret()) || KPilotSettings::showSecrets()))
00103         {
00104             address = new PilotAddress(pilotRec);
00105             if (address == 0L)
00106             {
00107                 WARNINGKPILOT << "Couldn't allocate record "
00108                     << currentRecord++
00109                     << endl;
00110                 break;
00111             }
00112             fAddressList.append(address);
00113         }
00114         delete pilotRec;
00115 
00116         currentRecord++;
00117     }
00118 
00119 #ifdef DEBUG
00120     DEBUGKPILOT << fname
00121         << ": Total " << currentRecord << " records" << endl;
00122 #endif
00123 
00124     return currentRecord;
00125 }
00126 
00127 void AddressWidget::showComponent()
00128 {
00129     FUNCTIONSETUP;
00130     if ( fPendingAddresses>0 ) return;
00131 
00132 #ifdef DEBUG
00133     DEBUGKPILOT << fname
00134         << ": Reading from directory " << dbPath() << endl;
00135 #endif
00136 
00137     PilotDatabase *addressDB =
00138         new PilotLocalDatabase(dbPath(), CSL1("AddressDB"));
00139 
00140     fAddressList.clear();
00141 
00142     if (addressDB->isOpen())
00143     {
00144         KPILOT_DELETE(fAddressAppInfo);
00145         fAddressAppInfo = new PilotAddressInfo(addressDB);
00146         populateCategories(fCatList, fAddressAppInfo->categoryInfo());
00147         getAllAddresses(addressDB);
00148 
00149     }
00150     else
00151     {
00152         populateCategories(fCatList, 0L);
00153         WARNINGKPILOT << "Could not open local AddressDB" << endl;
00154     }
00155 
00156     KPILOT_DELETE( addressDB );
00157 
00158     updateWidget();
00159 }
00160 
00161 void AddressWidget::hideComponent()
00162 {
00163     FUNCTIONSETUP;
00164     if (fPendingAddresses==0 )
00165     {
00166         fAddressList.clear();
00167         fListBox->clear();
00168 
00169         updateWidget();
00170     }
00171 }
00172 
00173 /* virtual */ bool AddressWidget::preHotSync(QString &s)
00174 {
00175     FUNCTIONSETUP;
00176 
00177     if ( fPendingAddresses )
00178     {
00179 #ifdef DEBUG
00180         DEBUGKPILOT << fname
00181             << ": fPendingAddress="
00182             << fPendingAddresses
00183             << endl;
00184 #endif
00185 
00186 #if KDE_VERSION<220
00187         s = i18n("There are still %1 address editing windows open.")
00188             .arg(QString::number(fPendingAddresses));
00189 #else
00190         s = i18n("There is still an address editing window open.",
00191             "There are still %n address editing windows open.",
00192             fPendingAddresses);
00193 #endif
00194         return false;
00195     }
00196 
00197     return true;
00198 }
00199 
00200 void AddressWidget::postHotSync()
00201 {
00202     FUNCTIONSETUP;
00203 
00204     if ( shown )
00205     {
00206         fAddressList.clear();
00207         showComponent();
00208     }
00209 }
00210 
00211 
00212 void AddressWidget::setupWidget()
00213 {
00214     FUNCTIONSETUP;
00215 
00216     QLabel *label;
00217     QGridLayout *grid = new QGridLayout(this, 6, 4, SPACING);
00218 
00219     fCatList = new QComboBox(this);
00220     grid->addWidget(fCatList, 0, 1);
00221     connect(fCatList, SIGNAL(activated(int)),
00222         this, SLOT(slotSetCategory(int)));
00223     QWhatsThis::add(fCatList,
00224         i18n("<qt>Select the category of addresses to display here.</qt>"));
00225 
00226     label = new QLabel(i18n("Category:"), this);
00227     label->setBuddy(fCatList);
00228     grid->addWidget(label, 0, 0);
00229 
00230     fListBox = new QListBox(this);
00231     grid->addMultiCellWidget(fListBox, 1, 1, 0, 1);
00232     connect(fListBox, SIGNAL(highlighted(int)),
00233         this, SLOT(slotShowAddress(int)));
00234     connect(fListBox, SIGNAL(selected(int)),
00235         this, SLOT(slotEditRecord()));
00236     QWhatsThis::add(fListBox,
00237         i18n("<qt>This list displays all the addresses "
00238             "in the selected category. Click on "
00239             "one to display it to the right.</qt>"));
00240 
00241     label = new QLabel(i18n("Address info:"), this);
00242     grid->addWidget(label, 0, 2);
00243 
00244     // address info text view
00245     fAddrInfo = new QTextView(this);
00246     grid->addMultiCellWidget(fAddrInfo, 1, 4, 2, 2);
00247 
00248     QPushButton *button;
00249     QString wt;
00250 
00251     fEditButton = new QPushButton(i18n("Edit Record..."), this);
00252     grid->addWidget(fEditButton, 2, 0);
00253     connect(fEditButton, SIGNAL(clicked()), this, SLOT(slotEditRecord()));
00254     wt = KPilotSettings::internalEditors() ?
00255         i18n("<qt>You can edit an address when it is selected.</qt>") :
00256         i18n("<qt><i>Editing is disabled by the 'internal editors' setting.</i></qt>");
00257     QWhatsThis::add(fEditButton,wt);
00258 
00259     button = new QPushButton(i18n("New Record..."), this);
00260     grid->addWidget(button, 2, 1);
00261     connect(button, SIGNAL(clicked()), this, SLOT(slotCreateNewRecord()));
00262     wt = KPilotSettings::internalEditors() ?
00263         i18n("<qt>Add a new address to the address book.</qt>") :
00264         i18n("<qt><i>Adding is disabled by the 'internal editors' setting.</i></qt>") ;
00265     QWhatsThis::add(button, wt);
00266     button->setEnabled(KPilotSettings::internalEditors());
00267 
00268 
00269     fDeleteButton = new QPushButton(i18n("Delete Record"), this);
00270     grid->addWidget(fDeleteButton, 3, 0);
00271     connect(fDeleteButton, SIGNAL(clicked()),
00272         this, SLOT(slotDeleteRecord()));
00273     wt = KPilotSettings::internalEditors() ?
00274         i18n("<qt>Delete the selected address from the address book.</qt>") :
00275         i18n("<qt><i>Deleting is disabled by the 'internal editors' setting.</i></qt>") ;
00276 
00277     button = new QPushButton(i18n("Export addresses to file","Export..."), this);
00278     grid->addWidget(button, 3,1);
00279     connect(button, SIGNAL(clicked()), this, SLOT(slotExport()));
00280     QWhatsThis::add(button,
00281         i18n("<qt>Export all addresses in the selected category to CSV format.</qt>") );
00282 
00283     QWhatsThis::add(fDeleteButton,wt);
00284 }
00285 
00286 void AddressWidget::updateWidget()
00287 {
00288     FUNCTIONSETUP;
00289 
00290     if( !fAddressAppInfo )
00291             return;
00292     int addressDisplayMode = KPilotSettings::addressDisplayMode();
00293 
00294     int listIndex = 0;
00295 
00296 #ifdef DEBUG
00297     DEBUGKPILOT << fname
00298         << ": Display Mode=" << addressDisplayMode << endl;
00299 #endif
00300 
00301     int currentCatID = findSelectedCategory(fCatList,
00302         fAddressAppInfo->categoryInfo());
00303 
00304     fListBox->clear();
00305     fAddressList.first();
00306 
00307 #ifdef DEBUG
00308     DEBUGKPILOT << fname << ": Adding records..." << endl;
00309 #endif
00310 
00311     while (fAddressList.current())
00312     {
00313         if ((currentCatID == -1) ||
00314             (fAddressList.current()->category() == currentCatID))
00315         {
00316             QString title = createTitle(fAddressList.current(),
00317                 addressDisplayMode);
00318 
00319             if (!title.isEmpty())
00320             {
00321                 title.remove(QRegExp(CSL1("\n.*")));
00322                 PilotListItem *p = new PilotListItem(title,
00323                     listIndex,
00324                     fAddressList.current());
00325 
00326                 fListBox->insertItem(p);
00327             }
00328         }
00329         listIndex++;
00330         fAddressList.next();
00331     }
00332 
00333     fListBox->sort();
00334 #ifdef DEBUG
00335     DEBUGKPILOT << fname << ": " << listIndex << " records" << endl;
00336 #endif
00337 
00338     slotUpdateButtons();
00339 }
00340 
00341 
00342 
00343 QString AddressWidget::createTitle(PilotAddress * address, int displayMode)
00344 {
00345     // FUNCTIONSETUP;
00346 
00347     QString title;
00348 
00349     switch (displayMode)
00350     {
00351     case 1:
00352         if (!address->getField(entryCompany).isEmpty())
00353         {
00354             title.append(address->getField(entryCompany));
00355         }
00356         if (!address->getField(entryLastname).isEmpty())
00357         {
00358             if (!title.isEmpty())
00359             {
00360                 title.append( CSL1(", "));
00361             }
00362 
00363             title.append(address->getField(entryLastname));
00364         }
00365         break;
00366     case 0:
00367     default:
00368         if (!address->getField(entryLastname).isEmpty())
00369         {
00370             title.append(address->getField(entryLastname));
00371         }
00372 
00373         if (!address->getField(entryFirstname).isEmpty())
00374         {
00375             if (!title.isEmpty())
00376             {
00377                 title.append( CSL1(", "));
00378             }
00379             title.append(address->getField(entryFirstname));
00380         }
00381         break;
00382     }
00383 
00384     if (title.isEmpty())    // One last try
00385     {
00386         if (!fAddressList.current()->getField(entryCompany).isEmpty())
00387         {
00388             title.append(fAddressList.current()->
00389                 getField(entryCompany));
00390         }
00391         if (title.isEmpty())
00392         {
00393             title = i18n("[unknown]");
00394         }
00395     }
00396 
00397     return title;
00398 }
00399 
00400 
00401 /* slot */ void AddressWidget::slotUpdateButtons()
00402 {
00403     FUNCTIONSETUP;
00404 
00405     bool enabled = (fListBox->currentItem() != -1);
00406 
00407     enabled &= KPilotSettings::internalEditors();
00408     fEditButton->setEnabled(enabled);
00409     fDeleteButton->setEnabled(enabled);
00410 }
00411 
00412 void AddressWidget::slotSetCategory(int)
00413 {
00414     FUNCTIONSETUP;
00415 
00416     updateWidget();
00417 }
00418 
00419 void AddressWidget::slotEditRecord()
00420 {
00421     FUNCTIONSETUP;
00422     if ( !shown ) return;
00423 
00424     int item = fListBox->currentItem();
00425 
00426     if (item == -1)
00427         return;
00428 
00429     PilotListItem *p = (PilotListItem *) fListBox->item(item);
00430     PilotAddress *selectedRecord = (PilotAddress *) p->rec();
00431 
00432     if (selectedRecord->id() == 0)
00433     {
00434         KMessageBox::error(0L,
00435             i18n("Cannot edit new records until "
00436                 "HotSynced with Pilot."),
00437             i18n("HotSync Required"));
00438         return;
00439     }
00440 
00441     AddressEditor *editor = new AddressEditor(selectedRecord,
00442         fAddressAppInfo, this);
00443 
00444     connect(editor, SIGNAL(recordChangeComplete(PilotAddress *)),
00445         this, SLOT(slotUpdateRecord(PilotAddress *)));
00446     connect(editor, SIGNAL(cancelClicked()),
00447         this, SLOT(slotEditCancelled()));
00448     editor->show();
00449 
00450     fPendingAddresses++;
00451 }
00452 
00453 void AddressWidget::slotCreateNewRecord()
00454 {
00455     FUNCTIONSETUP;
00456     if ( !shown ) return;
00457 
00458     // Response to bug 18072: Don't even try to
00459     // add records to an empty or unopened database,
00460     // since we don't have the DBInfo stuff to deal with it.
00461     //
00462     //
00463     PilotDatabase *myDB = new PilotLocalDatabase(dbPath(), CSL1("AddressDB"));
00464 
00465     if (!myDB || !myDB->isOpen())
00466     {
00467 #ifdef DEBUG
00468         DEBUGKPILOT << fname
00469             << ": Tried to open "
00470             << dbPath()
00471             << "/AddressDB"
00472             << " and got pointer @"
00473             << (long) myDB
00474             << " with status "
00475             << ( myDB ? myDB->isOpen() : false )
00476             << endl;
00477 #endif
00478 
00479         KMessageBox::sorry(this,
00480             i18n("You cannot add addresses to the address book "
00481                 "until you have done a HotSync at least once "
00482                 "to retrieve the database layout from your Pilot."),
00483             i18n("Cannot Add New Address"));
00484 
00485         if (myDB)
00486             KPILOT_DELETE( myDB );
00487 
00488         return;
00489     }
00490 
00491     AddressEditor *editor = new AddressEditor(0L,
00492         fAddressAppInfo, this);
00493 
00494     connect(editor, SIGNAL(recordChangeComplete(PilotAddress *)),
00495         this, SLOT(slotAddRecord(PilotAddress *)));
00496     connect(editor, SIGNAL(cancelClicked()),
00497         this, SLOT(slotEditCancelled()));
00498     editor->show();
00499 
00500     fPendingAddresses++;
00501 }
00502 
00503 void AddressWidget::slotAddRecord(PilotAddress * address)
00504 {
00505     FUNCTIONSETUP;
00506     if ( !shown && fPendingAddresses==0 ) return;
00507 
00508     int currentCatID = findSelectedCategory(fCatList,
00509         fAddressAppInfo->categoryInfo(), true);
00510 
00511 
00512     address->PilotRecordBase::setCategory(currentCatID);
00513     fAddressList.append(address);
00514     writeAddress(address);
00515     // TODO: Just add the new record to the lists
00516     updateWidget();
00517 
00518     // k holds the item number of the address just added.
00519     //
00520     //
00521     int k = fListBox->count() - 1;
00522 
00523     fListBox->setCurrentItem(k);    // Show the newest one
00524     fListBox->setBottomItem(k);
00525 
00526     fPendingAddresses--;
00527     if ( !shown && fPendingAddresses==0 ) hideComponent();
00528 }
00529 
00530 void AddressWidget::slotUpdateRecord(PilotAddress * address)
00531 {
00532     FUNCTIONSETUP;
00533     if ( !shown && fPendingAddresses==0 ) return;
00534 
00535     writeAddress(address);
00536     int currentRecord = fListBox->currentItem();
00537 
00538     // TODO: Just change the record
00539     updateWidget();
00540     fListBox->setCurrentItem(currentRecord);
00541 
00542     emit(recordChanged(address));
00543 
00544     fPendingAddresses--;
00545     if ( !shown && fPendingAddresses==0 ) hideComponent();
00546 }
00547 
00548 void AddressWidget::slotEditCancelled()
00549 {
00550     FUNCTIONSETUP;
00551 
00552     fPendingAddresses--;
00553     if ( !shown && fPendingAddresses==0 ) hideComponent();
00554 }
00555 
00556 void AddressWidget::slotDeleteRecord()
00557 {
00558     FUNCTIONSETUP;
00559     if ( !shown ) return;
00560 
00561     int item = fListBox->currentItem();
00562 
00563     if (item == -1)
00564         return;
00565 
00566     PilotListItem *p = (PilotListItem *) fListBox->item(item);
00567     PilotAddress *selectedRecord = (PilotAddress *) p->rec();
00568 
00569     if (selectedRecord->id() == 0)
00570     {
00571         KMessageBox::error(this,
00572             i18n("New records cannot be deleted until "
00573                 "HotSynced with pilot."),
00574             i18n("HotSync Required"));
00575         return;
00576     }
00577 
00578     if (KMessageBox::questionYesNo(this,
00579             i18n("Delete currently selected record?"),
00580             i18n("Delete Record?"), KStdGuiItem::del(), KStdGuiItem::cancel()) == KMessageBox::No)
00581         return;
00582 
00583     selectedRecord->setDeleted( true );
00584     writeAddress(selectedRecord);
00585     emit(recordChanged(selectedRecord));
00586     showComponent();
00587 }
00588 
00589 
00590 
00591 void AddressWidget::slotShowAddress(int which)
00592 {
00593     FUNCTIONSETUP;
00594     if (!shown) return;
00595 
00596     PilotListItem *p = (PilotListItem *) fListBox->item(which);
00597     PilotAddress *addr = (PilotAddress *) p->rec();
00598 
00599 #ifdef DEBUG
00600     DEBUGKPILOT << fname
00601         << ": Showing "
00602         << addr->getField(entryLastname)
00603         << " "
00604         << addr->getField(entryFirstname)
00605         << endl;
00606 #endif
00607 
00608     QString text(CSL1("<qt>"));
00609     text += addr->getTextRepresentation(fAddressAppInfo,Qt::RichText);
00610     text += CSL1("</qt>\n");
00611     fAddrInfo->setText(text);
00612 
00613     slotUpdateButtons();
00614 }
00615 
00616 
00617 
00618 void AddressWidget::writeAddress(PilotAddress * which,
00619     PilotDatabase * addressDB)
00620 {
00621     FUNCTIONSETUP;
00622 
00623     // Open a database (myDB) only if needed,
00624     // i.e. only if the passed-in addressDB
00625     // isn't valid.
00626     //
00627     //
00628     PilotDatabase *myDB = addressDB;
00629     bool usemyDB = false;
00630 
00631     if (myDB == 0L || !myDB->isOpen())
00632     {
00633         myDB = new PilotLocalDatabase(dbPath(), CSL1("AddressDB"));
00634         usemyDB = true;
00635     }
00636 
00637     // Still no valid address database...
00638     //
00639     //
00640     if (!myDB->isOpen())
00641     {
00642 #ifdef DEBUG
00643         DEBUGKPILOT << fname << ": Address database is not open" <<
00644             endl;
00645 #endif
00646         return;
00647     }
00648 
00649 
00650     // Do the actual work.
00651     PilotRecord *pilotRec = which->pack();
00652 
00653     myDB->writeRecord(pilotRec);
00654     markDBDirty(CSL1("AddressDB"));
00655     delete pilotRec;
00656 
00657     // Clean up in the case that we allocated our own DB.
00658     //
00659     //
00660     if (usemyDB)
00661     {
00662         KPILOT_DELETE( myDB );
00663     }
00664 }
00665 
00666 #define plu_quiet 1
00667 #include "pilot-addresses.c"
00668 
00669 void AddressWidget::slotExport()
00670 {
00671     FUNCTIONSETUP;
00672     if( !fAddressAppInfo ) return;
00673     int currentCatID = findSelectedCategory(fCatList,
00674         fAddressAppInfo->categoryInfo());
00675 
00676     QString prompt = (currentCatID==-1) ?
00677         i18n("Export All Addresses") :
00678         i18n("Export Address Category %1").arg(fAddressAppInfo->categoryName(currentCatID)) ;
00679 
00680 
00681     QString saveFile = KFileDialog::getSaveFileName(
00682         QString::null,
00683         CSL1("*.csv|Comma Separated Values"),
00684         this,
00685         prompt
00686         );
00687     if (saveFile.isEmpty())
00688     {
00689 #ifdef DEBUG
00690         DEBUGKPILOT << fname << ": No save file selected." << endl;
00691 #endif
00692         return;
00693     }
00694     if (QFile::exists(saveFile) &&
00695         KMessageBox::warningContinueCancel(this,
00696             i18n("The file <i>%1</i> exists. Overwrite?").arg(saveFile),
00697             i18n("Overwrite File?"),
00698             i18n("Overwrite"))!=KMessageBox::Continue)
00699     {
00700 #ifdef DEBUG
00701         DEBUGKPILOT << fname << ": Overwrite file canceled." << endl;
00702 #endif
00703         return;
00704     }
00705 
00706     FILE *f = fopen(QFile::encodeName(saveFile),"w");
00707     if (!f)
00708     {
00709         KMessageBox::sorry(this,
00710             i18n("The file <i>%1</i> could not be opened for writing.").arg(saveFile));
00711         return;
00712     }
00713     fAddressList.first();
00714 
00715 #ifdef DEBUG
00716     DEBUGKPILOT << fname << ": Adding records..." << endl;
00717 #endif
00718 
00719     while (fAddressList.current())
00720     {
00721         const PilotAddress *a = fAddressList.current();
00722         if ((currentCatID == -1) ||
00723             (a->category() == currentCatID))
00724         {
00725             write_record_CSV(f, fAddressAppInfo->info(), a->address(),
00726                 a->attributes(), a->category(), 0);
00727         }
00728         fAddressList.next();
00729     }
00730 
00731     fclose(f);
00732 }
00733