kpilot

plugin.cc

Go to the documentation of this file.
00001 /* KPilot
00002 **
00003 ** Copyright (C) 2001 by Dan Pilone
00004 ** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00005 **
00006 ** This file defines the base class of all KPilot conduit plugins configuration
00007 ** dialogs. This is necessary so that we have a fixed API to talk to from
00008 ** inside KPilot.
00009 **
00010 ** The factories used by KPilot plugins are also documented here.
00011 */
00012 
00013 /*
00014 ** This program is free software; you can redistribute it and/or modify
00015 ** it under the terms of the GNU Lesser General Public License as published by
00016 ** the Free Software Foundation; either version 2.1 of the License, or
00017 ** (at your option) any later version.
00018 **
00019 ** This program is distributed in the hope that it will be useful,
00020 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00021 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00022 ** GNU Lesser General Public License for more details.
00023 **
00024 ** You should have received a copy of the GNU Lesser General Public License
00025 ** along with this program in a file called COPYING; if not, write to
00026 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00027 ** MA 02110-1301, USA.
00028 */
00029 
00030 /*
00031 ** Bug reports and questions can be sent to kde-pim@kde.org
00032 */
00033 
00034 #include "options.h"
00035 
00036 #include <stdlib.h>
00037 
00038 #include <qdir.h>
00039 #include <qfileinfo.h>
00040 #include <qhbox.h>
00041 #include <qlabel.h>
00042 #include <qlayout.h>
00043 #include <qpushbutton.h>
00044 #include <qregexp.h>
00045 #include <qstringlist.h>
00046 #include <qtabwidget.h>
00047 #include <qtextview.h>
00048 #include <qtimer.h>
00049 
00050 #include <dcopclient.h>
00051 #include <kaboutapplication.h>
00052 #include <kactivelabel.h>
00053 #include <kapplication.h>
00054 #include <kglobal.h>
00055 #include <kiconloader.h>
00056 #include <kinstance.h>
00057 #include <klibloader.h>
00058 #include <kmessagebox.h>
00059 #include <kservice.h>
00060 #include <kservicetype.h>
00061 #include <kstandarddirs.h>
00062 
00063 #include "pilotSerialDatabase.h"
00064 #include "pilotLocalDatabase.h"
00065 
00066 #include "plugin.moc"
00067 
00068 ConduitConfigBase::ConduitConfigBase(QWidget *parent,
00069     const char *name) :
00070     QObject(parent,name),
00071     fModified(false),
00072     fWidget(0L),
00073     fConduitName(i18n("Unnamed"))
00074 {
00075     FUNCTIONSETUP;
00076 }
00077 
00078 ConduitConfigBase::~ConduitConfigBase()
00079 {
00080     FUNCTIONSETUP;
00081 }
00082 
00083 /* slot */ void ConduitConfigBase::modified()
00084 {
00085     fModified=true;
00086     emit changed(true);
00087 }
00088 
00089 /* virtual */ QString ConduitConfigBase::maybeSaveText() const
00090 {
00091     FUNCTIONSETUP;
00092 
00093     return i18n("<qt>The <i>%1</i> conduit's settings have been changed. Do you "
00094         "want to save the changes before continuing?</qt>").arg(this->conduitName());
00095 }
00096 
00097 /* virtual */ bool ConduitConfigBase::maybeSave()
00098 {
00099     FUNCTIONSETUP;
00100 
00101     if (!isModified()) return true;
00102 
00103     int r = KMessageBox::questionYesNoCancel(fWidget,
00104         maybeSaveText(),
00105         i18n("%1 Conduit").arg(this->conduitName()), KStdGuiItem::save(), KStdGuiItem::discard());
00106     if (r == KMessageBox::Cancel) return false;
00107     if (r == KMessageBox::Yes) commit();
00108     return true;
00109 }
00110 
00111 QWidget *ConduitConfigBase::aboutPage(QWidget *parent, KAboutData *ad)
00112 {
00113     FUNCTIONSETUP;
00114 
00115     QWidget *w = new QWidget(parent, "aboutpage");
00116 
00117     QString s;
00118     QLabel *text;
00119     KIconLoader *l = KGlobal::iconLoader();
00120     const KAboutData *p = ad ? ad : KGlobal::instance()->aboutData();
00121 
00122     QGridLayout *grid = new QGridLayout(w, 5, 4, SPACING);
00123 
00124     grid->addColSpacing(0, SPACING);
00125     grid->addColSpacing(4, SPACING);
00126 
00127 
00128     QPixmap applicationIcon =
00129         l->loadIcon(QString::fromLatin1(p->appName()),
00130         KIcon::Desktop,
00131         64, KIcon::DefaultState, 0L,
00132         true);
00133 
00134     if (applicationIcon.isNull())
00135     {
00136         applicationIcon = l->loadIcon(QString::fromLatin1("kpilot"),
00137             KIcon::Desktop);
00138     }
00139 
00140     text = new QLabel(w);
00141     // Experiment with a long non-<qt> string. Use that to find
00142     // sensible widths for the columns.
00143     //
00144     text->setText(i18n("Send questions and comments to kdepim-users@kde.org"));
00145     text->adjustSize();
00146 
00147     int linewidth = text->size().width();
00148     int lineheight = text->size().height();
00149 
00150     // Use the label to display the applciation icon
00151     text->setText(QString::null);
00152     text->setPixmap(applicationIcon);
00153     text->adjustSize();
00154     grid->addWidget(text, 0, 1);
00155 
00156 
00157     KActiveLabel *linktext = new KActiveLabel(w);
00158     grid->addRowSpacing(1,kMax(100,6*lineheight));
00159     grid->addRowSpacing(2,kMax(100,6*lineheight));
00160     grid->addColSpacing(2,SPACING+linewidth/2);
00161     grid->addColSpacing(3,SPACING+linewidth/2);
00162     grid->setRowStretch(1,50);
00163     grid->setRowStretch(2,50);
00164     grid->setColStretch(2,50);
00165     grid->setColStretch(3,50);
00166     linktext->setMinimumSize(linewidth,kMax(260,60+12*lineheight));
00167     linktext->setFixedHeight(kMax(260,60+12*lineheight));
00168     linktext->setVScrollBarMode(QScrollView::Auto/*AlwaysOn*/);
00169     text = new QLabel(w);
00170     grid->addMultiCellWidget(text,0,0,2,3);
00171     grid->addMultiCellWidget(linktext,1,2,1,3);
00172 
00173     // Now set the program and copyright information.
00174     s = CSL1("<qt><h3>");
00175     s += p->programName();
00176     s += ' ';
00177     s += p->version();
00178     s += CSL1("</h3>");
00179     s += p->copyrightStatement() + CSL1("<br></qt>");
00180     text->setText(s);
00181 
00182     linktext->append(p->shortDescription() + CSL1("<br>"));
00183 
00184     if (!p->homepage().isEmpty())
00185     {
00186         s = QString::null;
00187         s += CSL1("<a href=\"%1\">").arg(p->homepage());
00188         s += p->homepage();
00189         s += CSL1("</a><br>");
00190         linktext->append(s);
00191     }
00192 
00193     s = QString::null;
00194     s += i18n("Send questions and comments to <a href=\"mailto:%1\">%2</a>.")
00195         .arg( CSL1("kdepim-users@kde.org") )
00196         .arg( CSL1("kdepim-users@kde.org") );
00197     s += ' ';
00198     s += i18n("Send bug reports to <a href=\"mailto:%1\">%2</a>.")
00199         .arg(p->bugAddress())
00200         .arg(p->bugAddress());
00201     s += ' ';
00202     s += i18n("For trademark information, see the "
00203         "<a href=\"help:/kpilot/trademarks.html\">KPilot User's Guide</a>.");
00204     s += CSL1("<br>");
00205     linktext->append(s);
00206     linktext->append(QString::null);
00207 
00208 
00209 
00210     QValueList<KAboutPerson> pl = p->authors();
00211     QValueList<KAboutPerson>::ConstIterator i;
00212 
00213     s = i18n("<b>Authors:</b> ");
00214 
00215     QString comma = CSL1(", ");
00216 
00217     unsigned int count=1;
00218     for (i=pl.begin(); i!=pl.end(); ++i)
00219     {
00220         s.append(CSL1("%1 (<i>%2</i>)%3")
00221             .arg((*i).name())
00222             .arg((*i).task())
00223             .arg(count<pl.count() ? comma : QString::null)
00224             );
00225         count++;
00226     }
00227     linktext->append(s);
00228 
00229     s = QString::null;
00230     pl = p->credits();
00231     if (pl.count()>0)
00232     {
00233         count=1;
00234         s.append(i18n("<b>Credits:</b> "));
00235         for (i=pl.begin(); i!=pl.end(); ++i)
00236         {
00237             s.append(CSL1("%1 (<i>%2</i>)%3")
00238                 .arg((*i).name())
00239                 .arg((*i).task())
00240                 .arg(count<pl.count() ? comma : QString::null)
00241                 );
00242             count++;
00243         }
00244     }
00245     linktext->append(s);
00246     linktext->ensureVisible(0,0);
00247 
00248     w->adjustSize();
00249 
00250     return w;
00251 }
00252 
00253 /* static */ void ConduitConfigBase::addAboutPage(QTabWidget *tw,
00254     KAboutData *ad)
00255 {
00256     FUNCTIONSETUP;
00257 
00258     Q_ASSERT(tw);
00259 
00260     QWidget *w = aboutPage(tw,ad);
00261     QSize sz = w->size();
00262 
00263     if (sz.width() < tw->size().width())
00264     {
00265         sz.setWidth(tw->size().width());
00266     }
00267     if (sz.height() < tw->size().height())
00268     {
00269         sz.setHeight(tw->size().height());
00270     }
00271 
00272     tw->resize(sz);
00273     tw->addTab(w, i18n("About"));
00274     tw->adjustSize();
00275 }
00276 
00277 
00278 
00279 ConduitAction::ConduitAction(KPilotLink *p,
00280     const char *name,
00281     const QStringList &args) :
00282     SyncAction(p,name),
00283     fDatabase(0L),
00284     fLocalDatabase(0L),
00285     fCtrHH(0L),
00286     fCtrPC(0L), 
00287     fSyncDirection(args),
00288     fConflictResolution(SyncAction::eAskUser),
00289     fFirstSync(false)
00290 {
00291     FUNCTIONSETUP;
00292 
00293     QString cResolution(args.grep(QRegExp(CSL1("--conflictResolution \\d*"))).first());
00294     if (cResolution.isEmpty())
00295     {
00296         fConflictResolution=(SyncAction::ConflictResolution)
00297             cResolution.replace(QRegExp(CSL1("--conflictResolution (\\d*)")), CSL1("\\1")).toInt();
00298     }
00299 
00300     for (QStringList::ConstIterator it = args.begin();
00301         it != args.end();
00302         ++it)
00303     {
00304         DEBUGKPILOT << fname << ": " << *it << endl;
00305     }
00306 
00307     DEBUGKPILOT << fname << ": Direction=" << fSyncDirection.name() << endl;
00308     fCtrHH = new CUDCounter(i18n("Handheld"));
00309     fCtrPC = new CUDCounter(i18n("PC")); 
00310 }
00311 
00312 /* virtual */ ConduitAction::~ConduitAction()
00313 {
00314     FUNCTIONSETUP;
00315     
00316     KPILOT_DELETE(fDatabase);
00317     KPILOT_DELETE(fLocalDatabase);
00318 
00319     KPILOT_DELETE(fCtrHH);
00320     KPILOT_DELETE(fCtrPC);
00321 }
00322 
00323 bool ConduitAction::openDatabases(const QString &name, bool *retrieved)
00324 {
00325     FUNCTIONSETUP;
00326 
00327     DEBUGKPILOT << fname
00328         << ": Trying to open database "
00329         << name << endl;
00330     DEBUGKPILOT << fname
00331         << ": Mode="
00332         << (syncMode().isTest() ? "test " : "")
00333         << (syncMode().isLocal() ? "local " : "")
00334         << endl ;
00335 
00336     KPILOT_DELETE(fLocalDatabase);
00337 
00338     QString localPathName = PilotLocalDatabase::getDBPath() + name;
00339 
00340     // we always want to use the conduits/ directory for our local
00341     // databases. this keeps our backups and data that our conduits use
00342     // for record keeping separate
00343     localPathName.replace(CSL1("DBBackup/"), CSL1("conduits/"));
00344 
00345     DEBUGKPILOT << fname << ": localPathName: [" << localPathName 
00346         << "]" << endl;
00347 
00348     PilotLocalDatabase *localDB = new PilotLocalDatabase( localPathName );
00349 
00350     if (!localDB)
00351     {
00352         WARNINGKPILOT << "Could not initialize object for local copy of database \""
00353             << name
00354             << "\"" << endl;
00355         if (retrieved) *retrieved = false;
00356         return false;
00357     }
00358 
00359     // if there is no backup db yet, fetch it from the palm, open it and set the full sync flag.
00360     if (!localDB->isOpen() )
00361     {
00362         QString dbpath(localDB->dbPathName());
00363         KPILOT_DELETE(localDB);
00364         DEBUGKPILOT << fname
00365             << ": Backup database " << dbpath
00366             << " not found." << endl;
00367         struct DBInfo dbinfo;
00368 
00369 // TODO Extend findDatabase() with extra overload?
00370         if (deviceLink()->findDatabase(Pilot::toPilot( name ), &dbinfo)<0 )
00371         {
00372             WARNINGKPILOT << "Could not get DBInfo for " << name << endl;
00373             if (retrieved) *retrieved = false;
00374             return false;
00375         }
00376 
00377         DEBUGKPILOT << fname
00378                 << ": Found Palm database: " << dbinfo.name <<endl
00379                 << fname << ": type = " << dbinfo.type
00380                 << " creator = " << dbinfo.creator
00381                 << " version = " << dbinfo.version
00382                 << " index = " << dbinfo.index << endl;
00383         dbinfo.flags &= ~dlpDBFlagOpen;
00384 
00385         // make sure the dir for the backup db really exists!
00386         QFileInfo fi(dbpath);
00387         QString path(QFileInfo(dbpath).dir(true).absPath());
00388         if (!path.endsWith(CSL1("/"))) path.append(CSL1("/"));
00389         if (!KStandardDirs::exists(path))
00390         {
00391             DEBUGKPILOT << fname << ": Trying to create path for database: <"
00392                 << path << ">" << endl;
00393             KStandardDirs::makeDir(path);
00394         }
00395         if (!KStandardDirs::exists(path))
00396         {
00397             DEBUGKPILOT << fname << ": Database directory does not exist." << endl;
00398             if (retrieved) *retrieved = false;
00399             return false;
00400         }
00401 
00402         if (!deviceLink()->retrieveDatabase(dbpath, &dbinfo) )
00403         {
00404             WARNINGKPILOT << "Could not retrieve database "
00405                 << name << " from the handheld." << endl;
00406             if (retrieved) *retrieved = false;
00407             return false;
00408         }
00409         localDB = new PilotLocalDatabase( localPathName );
00410         if (!localDB || !localDB->isOpen())
00411         {
00412             WARNINGKPILOT << "local backup of database " << name << " could not be initialized." << endl;
00413             if (retrieved) *retrieved = false;
00414             return false;
00415         }
00416         if (retrieved) *retrieved=true;
00417     }
00418     fLocalDatabase = localDB;
00419 
00420     fDatabase = deviceLink()->database( name );
00421 
00422     if (!fDatabase)
00423     {
00424         WARNINGKPILOT << "Could not open database \""
00425             << name
00426             << "\" on the pilot."
00427             << endl;
00428     }
00429     else
00430     {
00431         fCtrHH->setStartCount(fDatabase->recordCount());
00432     }
00433 
00434     return (fDatabase && fDatabase->isOpen() &&
00435             fLocalDatabase && fLocalDatabase->isOpen() );
00436 }
00437 
00438 
00439 bool ConduitAction::changeSync(SyncMode::Mode m)
00440 {
00441     FUNCTIONSETUP;
00442 
00443     if ( fSyncDirection.isSync() && SyncMode::eFullSync == m)
00444     {
00445         fSyncDirection.setMode(m);
00446         return true;
00447     }
00448     return false;
00449 }
00450 
00451 void ConduitAction::finished()
00452 {
00453     FUNCTIONSETUP;
00454 
00455     if (fDatabase && fCtrHH)
00456         fCtrHH->setEndCount(fDatabase->recordCount());
00457 
00458     if (fCtrHH && fCtrPC) 
00459     {
00460         addSyncLogEntry(fCtrHH->moo() +"\n",false); 
00461         DEBUGKPILOT << fname << ": " << fCtrHH->moo() << endl;
00462         addSyncLogEntry(fCtrPC->moo() +"\n",false);
00463         DEBUGKPILOT << fname << ": " << fCtrPC->moo() << endl;
00464 
00465         // STEP2 of making sure we don't delete our little user's
00466         // precious data...
00467         // sanity checks for handheld...
00468         int hhVolatility = fCtrHH->percentDeleted() + 
00469                  fCtrHH->percentUpdated() +
00470                      fCtrHH->percentCreated();
00471 
00472         int pcVolatility = fCtrPC->percentDeleted() + 
00473                  fCtrPC->percentUpdated() +
00474                      fCtrPC->percentCreated();
00475 
00476         // TODO: allow user to configure this...
00477         // this is a percentage...
00478         int allowedVolatility = 70;
00479 
00480         QString caption = i18n("Large Changes Detected");
00481         // args are already i18n'd
00482         QString query = i18n("The %1 conduit has made a "
00483             "large number of changes to your %2.  Do you want "
00484             "to allow this change?\nDetails:\n\t%3");
00485 
00486         if (hhVolatility > allowedVolatility)
00487         {
00488             query = query.arg(fConduitName)
00489                 .arg(fCtrHH->type()).arg(fCtrHH->moo());
00490 
00491             DEBUGKPILOT << fname << ": Yikes, lots of volatility "
00492                 << "caught.  Check with user: [" << query
00493                 << "]." << endl;
00494 
00495             /*
00496             int rc = questionYesNo(query, caption,
00497                 QString::null, 0 );
00498             if (rc == KMessageBox::Yes)
00499             {
00500                 // TODO: add commit and rollback code.
00501                 // note: this will require some thinking,
00502                 // since we have to undo changes to the
00503                 // pilot databases, changes to the PC
00504                 // resources, changes to the mappings files
00505                 // (record id mapping, etc.)
00506             }
00507             */
00508         }
00509 
00510 
00511     }
00512 
00513 }
00514 
00515 
00516 ConduitProxy::ConduitProxy(KPilotLink *p,
00517     const QString &name,
00518     const SyncAction::SyncMode &m) :
00519     ConduitAction(p,name.latin1(),m.list()),
00520     fDesktopName(name)
00521 {
00522     FUNCTIONSETUP;
00523 }
00524 
00525 /* virtual */ bool ConduitProxy::exec()
00526 {
00527     FUNCTIONSETUP;
00528 
00529     // query that service
00530     KSharedPtr < KService > o = KService::serviceByDesktopName(fDesktopName);
00531     if (!o)
00532     {
00533         WARNINGKPILOT << "Can't find desktop file for conduit "
00534             << fDesktopName
00535             << endl;
00536         addSyncLogEntry(i18n("Could not find conduit %1.").arg(fDesktopName));
00537         return false;
00538     }
00539 
00540 
00541     // load the lib
00542     fLibraryName = o->library();
00543     DEBUGKPILOT << fname
00544         << ": Loading desktop "
00545         << fDesktopName
00546         << " with lib "
00547         << fLibraryName
00548         << endl;
00549 
00550     KLibrary *library = KLibLoader::self()->library(
00551         QFile::encodeName(fLibraryName));
00552     if (!library)
00553     {
00554         WARNINGKPILOT << "Can't load library "
00555             << fLibraryName
00556             << " - "
00557             << KLibLoader::self()->lastErrorMessage()
00558             << endl;
00559         addSyncLogEntry(i18n("Could not load conduit %1.").arg(fDesktopName));
00560         return false;
00561     }
00562 
00563     unsigned long version = PluginUtility::pluginVersion(library);
00564     if ( Pilot::PLUGIN_API != version )
00565     {
00566         WARNINGKPILOT << "Library "
00567             << fLibraryName
00568             << " has version "
00569             << version
00570             << endl;
00571         addSyncLogEntry(i18n("Conduit %1 has wrong version (%2).").arg(fDesktopName).arg(version));
00572         return false;
00573     }
00574 
00575     KLibFactory *factory = library->factory();
00576     if (!factory)
00577     {
00578         WARNINGKPILOT << "Can't find factory in library "
00579             << fLibraryName
00580             << endl;
00581         addSyncLogEntry(i18n("Could not initialize conduit %1.").arg(fDesktopName));
00582         return false;
00583     }
00584 
00585     QStringList l = syncMode().list();
00586 
00587     DEBUGKPILOT << fname << ": Flags: " << syncMode().name() << endl;
00588 
00589     QObject *object = factory->create(fHandle,name(),"SyncAction",l);
00590 
00591     if (!object)
00592     {
00593         WARNINGKPILOT << "Can't create SyncAction." << endl;
00594         addSyncLogEntry(i18n("Could not create conduit %1.").arg(fDesktopName));
00595         return false;
00596     }
00597 
00598     fConduit = dynamic_cast<ConduitAction *>(object);
00599 
00600     if (!fConduit)
00601     {
00602         WARNINGKPILOT << "Can't cast to ConduitAction." << endl;
00603         addSyncLogEntry(i18n("Could not create conduit %1.").arg(fDesktopName));
00604         return false;
00605     }
00606 
00607     addSyncLogEntry(i18n("[Conduit %1]").arg(fDesktopName));
00608 
00609     // Handle the syncDone signal properly & unload the conduit.
00610     QObject::connect(fConduit,SIGNAL(syncDone(SyncAction *)),
00611         this,SLOT(execDone(SyncAction *)));
00612     // Proxy all the log and error messages.
00613     QObject::connect(fConduit,SIGNAL(logMessage(const QString &)),
00614         this,SIGNAL(logMessage(const QString &)));
00615     QObject::connect(fConduit,SIGNAL(logError(const QString &)),
00616         this,SIGNAL(logError(const QString &)));
00617     QObject::connect(fConduit,SIGNAL(logProgress(const QString &,int)),
00618         this,SIGNAL(logProgress(const QString &,int)));
00619 
00620     QTimer::singleShot(0,fConduit,SLOT(execConduit()));
00621     return true;
00622 }
00623 
00624 void ConduitProxy::execDone(SyncAction *p)
00625 {
00626     FUNCTIONSETUP;
00627 
00628     if (p!=fConduit)
00629     {
00630         WARNINGKPILOT << "Unknown conduit @"
00631             << (void *) p
00632             << " finished."
00633             << endl;
00634         emit syncDone(this);
00635         return;
00636     }
00637 
00638     // give our worker a chance to sanity check the results...
00639     fConduit->finished();
00640 
00641     addSyncLogEntry(CSL1("\n"),false); // Put bits of the conduit logs on separate lines
00642 
00643     KPILOT_DELETE(p);
00644 
00645     emit syncDone(this);
00646 }
00647 
00648 
00649 namespace PluginUtility
00650 {
00651 
00652 QString findArgument(const QStringList &a, const QString &arg)
00653 {
00654     FUNCTIONSETUP;
00655 
00656     QString search;
00657 
00658     if (arg.startsWith( CSL1("--") ))
00659     {
00660         search = arg;
00661     }
00662     else
00663     {
00664         search = CSL1("--") + arg;
00665     }
00666     search.append( CSL1("=") );
00667 
00668 
00669     QStringList::ConstIterator end = a.end();
00670     for (QStringList::ConstIterator i = a.begin(); i != end; ++i)
00671     {
00672         if ((*i).startsWith( search ))
00673         {
00674             QString s = (*i).mid(search.length());
00675             return s;
00676         }
00677     }
00678 
00679     return QString::null;
00680 }
00681 
00682 /* static */ bool isRunning(const QCString &n)
00683 {
00684     DCOPClient *dcop = KApplication::kApplication()->dcopClient();
00685     QCStringList apps = dcop->registeredApplications();
00686     return apps.contains(n);
00687 }
00688 
00689 
00690 /* static */ unsigned long pluginVersion(const KLibrary *lib)
00691 {
00692     QString symbol = CSL1("version_");
00693     symbol.append(lib->name());
00694 
00695     if (!lib->hasSymbol(symbol.latin1())) return 0;
00696 
00697     unsigned long *p = (unsigned long *)(lib->symbol(symbol.latin1()));
00698     return *p;
00699 }
00700 
00701 
00702 /* static */ QString pluginVersionString(const KLibrary *lib)
00703 {
00704     QString symbol= CSL1("id_");
00705     symbol.append(lib->name());
00706 
00707     if (!lib->hasSymbol(symbol.latin1())) return QString::null;
00708 
00709     return QString::fromLatin1(*((const char **)(lib->symbol(symbol.latin1()))));
00710 }
00711 
00712 
00713 }
00714 
00715 
00716 CUDCounter::CUDCounter(QString s) :
00717     fC(0),fU(0),fD(0),fStart(0),fEnd(0),fType(s)
00718 {
00719 }
00720 
00721 void CUDCounter::created(unsigned int c)
00722 {
00723     fC += c;
00724 }
00725 
00726 void CUDCounter::updated(unsigned int c)
00727 {
00728     fU += c;
00729 }
00730 
00731 void CUDCounter::deleted(unsigned int c)
00732 {
00733     fD += c;
00734 }
00735 
00736 void CUDCounter::setStartCount(unsigned int t)
00737 {
00738     fStart = t;
00739 }
00740 
00741 void CUDCounter::setEndCount(unsigned int t)
00742 {
00743     fEnd = t;
00744 }
00745 
00746 QString CUDCounter::moo() const
00747 {
00748     QString result = fType + ": " + 
00749         i18n("Start: %1. End: %2. ").arg(fStart).arg(fEnd);
00750 
00751     if (fC > 0) result += i18n("%1 new. ").arg(fC);
00752     if (fU > 0) result += i18n("%1 changed. ").arg(fU);
00753     if (fD > 0) result += i18n("%1 deleted. ").arg(fD);
00754 
00755     if ( (fC+fU+fD) <= 0) result += i18n("No changes made. ");
00756 
00757     return result;
00758 }
00759 
00760