add mp3 and ogg torrent url info to JamendoAlbum
[amarok.git] / src / ktrm.cpp
blobfcd8cd288ae05f56d69da17b274d68891a750dbe
1 /***************************************************************************
2 copyright : (C) 2004 by Scott Wheeler
3 email : wheeler@kde.org
4 ***************************************************************************/
6 /***************************************************************************
7 * This library is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU Lesser General Public License version *
9 * 2.1 as published by the Free Software Foundation. *
10 * *
11 * This library is distributed in the hope that it will be useful, but *
12 * WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Lesser General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with this library; if not, write to the Free Software *
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 *
19 * USA *
20 ***************************************************************************/
21 #define DEBUG_PREFIX "KTRM"
23 #include "ktrm.h"
25 #include "config-amarok.h"
27 #include "debug.h"
28 #include "statusbar.h"
30 #include <KIO/Job>
31 #include <KProtocolManager>
32 #include <KUrl>
34 #include <QDomDocument>
35 #include <QDomElement>
36 #include <QDomNode>
37 #include <QEvent>
38 #include <QFile>
39 #include <QMutex>
40 #include <QObject>
41 #include <QRegExp>
43 //disabling for now
44 //#if HAVE_TUNEPIMP
45 #undef HAVE_TUNEPIMP
46 #define HAVE_TUNEPIMP 0
47 //#endif
49 #if HAVE_TUNEPIMP
51 #if HAVE_TUNEPIMP >= 5
52 #include <tunepimp-0.5/tp_c.h>
53 #else
54 #include <tunepimp/tp_c.h>
55 #endif
57 class KTRMLookup;
59 extern "C"
61 #if HAVE_TUNEPIMP >= 4
62 static void TRMNotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId, TPFileStatus status);
63 #else
64 static void TRMNotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId);
65 #endif
68 /**
69 * This represents the main TunePimp instance and handles incoming requests.
72 class KTRMRequestHandler
74 public:
75 static KTRMRequestHandler *instance()
77 static QMutex mutex;
78 mutex.lock();
79 static KTRMRequestHandler handler;
80 mutex.unlock();
81 return &handler;
84 int startLookup(KTRMLookup *lookup)
86 int id;
87 if(!m_fileMap.contains(lookup->file())) {
88 #if HAVE_TUNEPIMP >= 4
89 id = tp_AddFile(m_pimp, QFile::encodeName(lookup->file()), 0);
90 #else
91 id = tp_AddFile(m_pimp, QFile::encodeName(lookup->file()));
92 #endif
93 m_fileMap.insert(lookup->file(), id);
95 else {
96 id = m_fileMap[lookup->file()];
97 tp_IdentifyAgain(m_pimp, id);
99 m_lookupMap[id] = lookup;
100 return id;
103 void endLookup(KTRMLookup *lookup)
105 tp_ReleaseTrack(m_pimp, tp_GetTrack(m_pimp, lookup->fileId()));
106 tp_Remove(m_pimp, lookup->fileId());
107 m_lookupMapMutex.lock();
108 m_lookupMap.remove(lookup->fileId());
109 m_fileMap.remove( lookup->file() );
110 m_lookupMapMutex.unlock();
113 bool lookupMapContains(int fileId) const
115 m_lookupMapMutex.lock();
116 bool contains = m_lookupMap.contains(fileId);
117 m_lookupMapMutex.unlock();
118 return contains;
121 KTRMLookup *lookup(int fileId) const
123 m_lookupMapMutex.lock();
124 KTRMLookup *l = m_lookupMap[fileId];
125 m_lookupMapMutex.unlock();
126 return l;
129 void removeFromLookupMap(int fileId)
131 m_lookupMapMutex.lock();
132 m_lookupMap.remove(fileId);
133 m_lookupMapMutex.unlock();
136 const tunepimp_t &tunePimp() const
138 return m_pimp;
141 protected:
142 KTRMRequestHandler()
144 m_pimp = tp_New("KTRM", "0.1");
145 //tp_SetDebug(m_pimp, true);
146 #if HAVE_TUNEPIMP < 5
147 tp_SetTRMCollisionThreshold(m_pimp, 100);
148 tp_SetAutoFileLookup(m_pimp,true);
149 #endif
150 tp_SetAutoSaveThreshold(m_pimp, -1);
151 tp_SetMoveFiles(m_pimp, false);
152 tp_SetRenameFiles(m_pimp, false);
153 #if HAVE_TUNEPIMP >= 4
154 tp_SetFileNameEncoding(m_pimp, "UTF-8");
155 #else
156 tp_SetUseUTF8(m_pimp, true);
157 #endif
158 tp_SetNotifyCallback(m_pimp, TRMNotifyCallback, 0);
160 #if HAVE_TUNEPIMP < 5
161 KProtocolManager::reparseConfiguration();
163 if(KProtocolManager::useProxy()) {
164 QString noProxiesFor = KProtocolManager::noProxyFor();
165 QStringList noProxies = QStringList::split(QRegExp("[',''\t'' ']"), noProxiesFor);
166 bool useProxy = true;
168 char server[255];
169 short port;
170 tp_GetServer(m_pimp, server, 255, &port);
171 QString tunepimpHost = QString(server);
172 QString tunepimpHostWithPort = (tunepimpHost + ":%1").arg(port);
174 for(QStringList::ConstIterator it = noProxies.constBegin(); it != noProxies.constEnd(); ++it) {
175 QString normalizedHost = KNetwork::KResolver::normalizeDomain(*it);
176 if(normalizedHost == tunepimpHost ||
177 tunepimpHost.endsWith('.' + normalizedHost)) {
178 useProxy = false;
179 break;
182 if(normalizedHost == tunepimpHostWithPort ||
183 tunepimpHostWithPort.endsWith('.' + normalizedHost)) {
184 useProxy = false;
185 break;
189 if(KProtocolManager::useReverseProxy())
190 useProxy = !useProxy;
192 if(useProxy) {
193 KUrl proxy = KProtocolManager::proxyFor("http");
194 tp_SetProxy(m_pimp, proxy.host().toLatin1(), short(proxy.port()));
197 #else
198 tp_SetMusicDNSClientId(m_pimp, "0c6019606b1d8a54d0985e448f3603ca");
199 #endif
202 ~KTRMRequestHandler()
204 tp_Delete(m_pimp);
207 private:
208 tunepimp_t m_pimp;
209 QMap<int, KTRMLookup *> m_lookupMap;
210 QMap<QString, int> m_fileMap;
211 mutable QMutex m_lookupMapMutex;
216 * A custom event type used for signalling that a TRM lookup is finished.
219 class KTRMEvent : public QEvent
221 public:
222 enum Status {
223 Recognized,
224 Unrecognized,
225 Collision,
226 PuidGenerated,
227 Error
230 KTRMEvent(int fileId, Status status) :
231 QEvent( Type( KTRMEventType ) ),
232 m_fileId(fileId),
233 m_status(status) {}
235 int fileId() const
237 return m_fileId;
240 Status status() const
242 return m_status;
245 static const int KTRMEventType = User + 1984; // random, unique, event id
247 private:
248 int m_fileId;
249 Status m_status;
253 * A helper class to intercept KTRMQueryEvents and call recognized() (from the GUI
254 * thread) for the lookup.
257 class KTRMEventHandler : public QObject
259 public:
260 static void send(int fileId, KTRMEvent::Status status)
262 KApplication::postEvent(instance(), new KTRMEvent(fileId, status));
265 protected:
266 KTRMEventHandler() : QObject() {}
268 static KTRMEventHandler *instance()
270 static QMutex mutex;
271 mutex.lock();
272 static KTRMEventHandler handler;
273 mutex.unlock();
274 return &handler;
277 virtual void customEvent(QEvent *event)
279 if(!event->type() == KTRMEvent::KTRMEventType)
280 return;
282 KTRMEvent *e = static_cast<KTRMEvent *>(event);
284 static QMutex mutex;
285 mutex.lock();
287 if(!KTRMRequestHandler::instance()->lookupMapContains(e->fileId())) {
288 mutex.unlock();
289 return;
292 KTRMLookup *lookup = KTRMRequestHandler::instance()->lookup(e->fileId());
293 #if HAVE_TUNEPIMP >= 4
294 if ( e->status() != KTRMEvent::Unrecognized)
295 #endif
296 KTRMRequestHandler::instance()->removeFromLookupMap(e->fileId());
298 mutex.unlock();
300 switch(e->status()) {
301 case KTRMEvent::Recognized:
302 lookup->recognized();
303 break;
304 case KTRMEvent::Unrecognized:
305 lookup->unrecognized();
306 break;
307 case KTRMEvent::Collision:
308 lookup->collision();
309 break;
310 case KTRMEvent::PuidGenerated:
311 lookup->puidGenerated();
312 break;
313 case KTRMEvent::Error:
314 lookup->error();
315 break;
321 * Callback function for TunePimp lookup events.
323 #if HAVE_TUNEPIMP >= 4
324 static void TRMNotifyCallback(tunepimp_t /*pimp*/, void */*data*/, TPCallbackEnum type, int fileId, TPFileStatus status)
325 #else
326 static void TRMNotifyCallback(tunepimp_t pimp, void */*data*/, TPCallbackEnum type, int fileId)
327 #endif
329 if(type != tpFileChanged)
330 return;
332 #if HAVE_TUNEPIMP < 4
333 track_t track = tp_GetTrack(pimp, fileId);
334 TPFileStatus status = tr_GetStatus(track);
335 #endif
336 //debug() << "Status is: " << status;
338 switch(status) {
339 case eRecognized:
340 KTRMEventHandler::send(fileId, KTRMEvent::Recognized);
341 break;
342 case eUnrecognized:
343 KTRMEventHandler::send(fileId, KTRMEvent::Unrecognized);
344 break;
345 #if HAVE_TUNEPIMP >= 5
346 case ePUIDLookup:
347 case ePUIDCollision:
348 case eFileLookup:
349 KTRMEventHandler::send(fileId, KTRMEvent::PuidGenerated);
350 break;
351 #else
352 case eTRMCollision:
353 #if HAVE_TUNEPIMP >= 4
354 case eUserSelection:
355 #endif
356 KTRMEventHandler::send(fileId, KTRMEvent::Collision);
357 break;
358 #endif
359 case eError:
360 KTRMEventHandler::send(fileId, KTRMEvent::Error);
361 break;
362 default:
363 break;
365 #if HAVE_TUNEPIMP < 4
366 tp_ReleaseTrack(pimp, track);
367 #endif
370 ////////////////////////////////////////////////////////////////////////////////
371 // KTRMResult implementation
372 ////////////////////////////////////////////////////////////////////////////////
374 class KTRMResult::KTRMResultPrivate
376 public:
377 KTRMResultPrivate() : track(0), year(0), relevance(0) {}
378 QString title;
379 QString artist;
380 QString album;
381 int track;
382 int year;
383 double relevance;
385 bool operator== (const KTRMResultPrivate &r) const;
388 bool KTRMResult::KTRMResultPrivate::operator==(const KTRMResultPrivate &r) const
390 return (
391 title == r.title &&
392 artist == r.artist &&
393 album == r.album &&
394 track == r.track &&
395 year == r.year &&
396 relevance == r.relevance
400 ////////////////////////////////////////////////////////////////////////////////
401 // KTRMResult public methods
402 ////////////////////////////////////////////////////////////////////////////////
403 #endif
404 KTRMResult::KTRMResult()
406 #if HAVE_TUNEPIMP
407 d = new KTRMResultPrivate;
408 #endif
411 KTRMResult::KTRMResult(const KTRMResult &result)
413 #if HAVE_TUNEPIMP
414 d = new KTRMResultPrivate(*result.d);
415 #else
416 Q_UNUSED(result);
417 #endif
420 KTRMResult::~KTRMResult()
422 #if HAVE_TUNEPIMP
423 delete d;
424 #endif
427 QString KTRMResult::title() const
429 #if HAVE_TUNEPIMP
430 return d->title;
431 #else
432 return QString();
433 #endif
436 QString KTRMResult::artist() const
438 #if HAVE_TUNEPIMP
439 return d->artist;
440 #else
441 return QString();
442 #endif
445 QString KTRMResult::album() const
447 #if HAVE_TUNEPIMP
448 return d->album;
449 #else
450 return QString();
451 #endif
454 int KTRMResult::track() const
456 #if HAVE_TUNEPIMP
457 return d->track;
458 #else
459 return 0;
460 #endif
463 int KTRMResult::year() const
465 #if HAVE_TUNEPIMP
466 return d->year;
467 #else
468 return 0;
469 #endif
472 bool KTRMResult::operator<(const KTRMResult &r) const
474 #if HAVE_TUNEPIMP
475 return r.d->relevance < d->relevance;
476 #else
477 Q_UNUSED(r);
478 return false;
479 #endif
482 bool KTRMResult::operator>(const KTRMResult &r) const
484 #if HAVE_TUNEPIMP
485 return r.d->relevance > d->relevance;
486 #else
487 Q_UNUSED(r);
488 return true;
489 #endif
492 KTRMResult &KTRMResult::operator= (const KTRMResult &r)
494 #if HAVE_TUNEPIMP
495 d = new KTRMResultPrivate(*r.d);
496 #else
497 Q_UNUSED(r);
498 #endif
499 return *this;
502 bool KTRMResult::operator== (const KTRMResult &r) const
504 #if HAVE_TUNEPIMP
505 return *d == *(r.d);
506 #else
507 Q_UNUSED(r);
508 #endif
509 return false;
513 bool KTRMResult::isEmpty() const
515 #if HAVE_TUNEPIMP
516 return d->title.isEmpty() && d->artist.isEmpty() && d->album.isEmpty() &&
517 d->track == 0 && d->year == 0;
518 #else
519 return true;
520 #endif
522 #if HAVE_TUNEPIMP
523 ////////////////////////////////////////////////////////////////////////////////
524 // KTRMLookup implementation
525 ////////////////////////////////////////////////////////////////////////////////
527 class KTRMLookup::KTRMLookupPrivate
529 public:
530 KTRMLookupPrivate() :
531 fileId(-1) {}
532 QString file;
533 QString errorString;
534 KTRMResultList results;
535 int fileId;
536 bool autoDelete;
538 #endif
539 ////////////////////////////////////////////////////////////////////////////////
540 // KTRMLookup public methods
541 ////////////////////////////////////////////////////////////////////////////////
543 KTRMLookup::KTRMLookup(const QString &file, bool autoDelete)
544 : QObject()
546 #if HAVE_TUNEPIMP
547 d = new KTRMLookupPrivate;
548 d->file = file;
549 d->autoDelete = autoDelete;
550 d->fileId = KTRMRequestHandler::instance()->startLookup(this);
551 #else
552 Q_UNUSED(file);
553 Q_UNUSED(autoDelete);
554 #endif
557 KTRMLookup::~KTRMLookup()
559 #if HAVE_TUNEPIMP
560 KTRMRequestHandler::instance()->endLookup(this);
561 delete d;
562 #endif
565 QString KTRMLookup::file() const
567 #if HAVE_TUNEPIMP
568 return d->file;
569 #else
570 return QString();
571 #endif
574 int KTRMLookup::fileId() const
576 #if HAVE_TUNEPIMP
577 return d->fileId;
578 #else
579 return -1;
580 #endif
583 void KTRMLookup::recognized()
585 #if HAVE_TUNEPIMP
586 debug() << d->file;
588 d->results.clear();
590 metadata_t *metaData = md_New();
591 track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
592 tr_Lock(track);
593 tr_GetServerMetadata(track, metaData);
595 KTRMResult result;
597 result.d->title = QString::fromUtf8(metaData->track);
598 result.d->artist = QString::fromUtf8(metaData->artist);
599 result.d->album = QString::fromUtf8(metaData->album);
600 result.d->track = metaData->trackNum;
601 result.d->year = metaData->releaseYear;
603 d->results.append(result);
605 md_Delete(metaData);
606 tr_Unlock(track);
607 finished();
608 #endif
611 void KTRMLookup::unrecognized()
613 #if HAVE_TUNEPIMP
614 debug() << d->file;
615 #if HAVE_TUNEPIMP >= 4
616 char trm[255];
617 bool finish = false;
618 trm[0] = 0;
619 track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
620 tr_Lock(track);
621 #if HAVE_TUNEPIMP >= 5
622 tr_GetPUID(track, trm, 255);
623 #else
624 tr_GetTRM(track, trm, 255);
625 #endif
626 if ( !trm[0] ) {
627 tr_SetStatus(track, ePending);
628 tp_Wake(KTRMRequestHandler::instance()->tunePimp(), track);
630 else
631 finish = true;
632 tr_Unlock(track);
633 tp_ReleaseTrack(KTRMRequestHandler::instance()->tunePimp(), track);
634 if ( !finish )
635 return;
636 #endif
637 d->results.clear();
638 finished();
640 #endif
643 void KTRMLookup::collision()
645 #if HAVE_TUNEPIMP && HAVE_TUNEPIMP < 5
646 debug() << d->file;
648 track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
650 if(track <= 0) {
651 debug() << "invalid track number";
652 return;
655 tr_Lock(track);
656 int resultCount = tr_GetNumResults(track);
658 QStringList strList = QStringList::split ( '/', d->file );
660 metadata_t *mdata = md_New();
661 strList.append( QString::fromUtf8(mdata->track) );
662 strList.append( QString::fromUtf8(mdata->artist) );
663 strList.append( QString::fromUtf8(mdata->album) );
664 md_Clear(mdata);
666 if(resultCount > 0) {
667 TPResultType type;
668 result_t *results = new result_t[resultCount];
669 tr_GetResults(track, &type, results, &resultCount);
671 switch(type) {
672 case eNone:
673 debug() << "eNone";
674 break;
675 case eArtistList:
676 debug() << "eArtistList";
677 break;
678 case eAlbumList:
679 debug() << "eAlbumList";
680 break;
681 case eTrackList:
683 debug() << "eTrackList";
684 albumtrackresult_t **tracks = reinterpret_cast<albumtrackresult_t **>( results );
685 d->results.clear();
687 for(int i = 0; i < resultCount; i++) {
688 KTRMResult result;
690 result.d->title = QString::fromUtf8(tracks[i]->name);
691 #if HAVE_TUNEPIMP >= 4
692 result.d->artist = QString::fromUtf8(tracks[i]->artist.name);
693 result.d->album = QString::fromUtf8(tracks[i]->album.name);
694 result.d->year = tracks[i]->album.releaseYear;
695 #else
696 result.d->artist = QString::fromUtf8(tracks[i]->artist->name);
697 result.d->album = QString::fromUtf8(tracks[i]->album->name);
698 result.d->year = tracks[i]->album->releaseYear;
699 #endif
700 result.d->track = tracks[i]->trackNum;
701 result.d->relevance =
702 4 * stringSimilarity(strList,result.d->title) +
703 2 * stringSimilarity(strList,result.d->artist) +
704 1 * stringSimilarity(strList,result.d->album);
706 if(!d->results.contains(result)) d->results.append(result);
708 break;
710 case eMatchedTrack:
711 debug() << "eMatchedTrack";
712 break;
715 delete [] results;
718 tr_Unlock(track);
719 tp_ReleaseTrack(KTRMRequestHandler::instance()->tunePimp(), track);
720 qSort(d->results);
722 finished();
723 #endif
726 void KTRMLookup::puidGenerated()
728 #if HAVE_TUNEPIMP >= 5
729 DEBUG_BLOCK
730 debug() << d->file;
731 char puid[255] = {0};
732 track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
733 tr_Lock(track);
735 tr_GetPUID(track, puid, 255);
736 debug() << puid;
737 tr_Unlock(track);
738 tp_ReleaseTrack(KTRMRequestHandler::instance()->tunePimp(), track);
739 d->results.clear();
741 KIO::Job *job = KIO::storedGet( QString( "http://musicbrainz.org/ws/1/track/?type=xml&puid=%1" ).arg( puid ) , KIO::NoReload, KIO::HideProgressInfo );
743 Amarok::StatusBar::instance()->newProgressOperation( job )
744 .setDescription( i18n( "MusicBrainz Lookup" ) );
746 connect( job, SIGNAL( result( KIO::Job* ) ), SLOT( lookupResult( KIO::Job* ) ) );
747 #endif
750 void KTRMLookup::lookupResult( KIO::Job* job )
752 #if HAVE_TUNEPIMP >= 5
753 DEBUG_BLOCK
754 if ( !job->error() == 0 ) {
755 warning() << "[MusicBrainzLookup] KIO error! errno: " << job->error();
756 Amarok::StatusBar::instance()->longMessage( "Couldn't connect to MusicBrainz server." );
757 finished();
758 return;
760 KIO::StoredTransferJob* const storedJob = static_cast<KIO::StoredTransferJob*>( job );
761 QString xml = QString::fromUtf8( storedJob->data().data(), storedJob->data().size() );
763 QDomDocument doc;
764 QDomElement e;
766 if( !doc.setContent( xml ) ) {
767 warning() << "[MusicBrainzLookup] Invalid XML";
768 Amarok::StatusBar::instance()->longMessage( "MusicBrainz returned invalid content." );
769 finished();
770 return;
773 e = doc.namedItem( "metadata" ).toElement().namedItem( "track-list" ).toElement();
775 QStringList strList = QStringList::split ( '/', d->file );
777 QDomNode n = e.namedItem("track");
778 for( ; !n.isNull(); n = n.nextSibling() ) {
779 QDomElement track = n.toElement();
780 KTRMResult result;
782 result.d->title = track.namedItem( "title" ).toElement().text();
783 result.d->artist = track.namedItem( "artist" ).toElement().namedItem( "name" ).toElement().text();
784 QDomNode releaseNode = track.namedItem("release-list").toElement().namedItem("release");
785 for( ; !releaseNode.isNull(); releaseNode = releaseNode.nextSibling() ) {
786 KTRMResult tmpResult( result );
787 QDomElement release = releaseNode.toElement();
789 tmpResult.d->album = release.namedItem( "title" ).toElement().text();
790 QDomNode tracklistN = release.namedItem( "track-list" );
791 if ( !tracklistN.isNull() ) {
792 QDomElement tracklist = tracklistN.toElement();
793 if ( !tracklist.attribute( "offset" ).isEmpty() )
794 tmpResult.d->track = tracklist.attribute( "offset" ).toInt() + 1;
796 //tmpResult.d->year = ???;
797 tmpResult.d->relevance =
798 4 * stringSimilarity(strList,tmpResult.d->title) +
799 2 * stringSimilarity(strList,tmpResult.d->artist) +
800 1 * stringSimilarity(strList,tmpResult.d->album);
801 if( !d->results.contains( tmpResult ) )
802 d->results.append( tmpResult );
806 //qSort(d->results);
808 finished();
809 #else
810 Q_UNUSED( job );
811 #endif
814 void KTRMLookup::error()
816 #if HAVE_TUNEPIMP
817 debug() << d->file;
818 track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
819 char error[1000];
820 tr_GetError( track, error, 1000);
821 debug() << "Error: " << error;
822 d->errorString = error;
823 d->results.clear();
824 finished();
825 #endif
828 KTRMResultList KTRMLookup::results() const
830 #if HAVE_TUNEPIMP
831 return d->results;
832 #else
833 return KTRMResultList();
834 #endif
837 ////////////////////////////////////////////////////////////////////////////////
838 // KTRMLookup protected methods
839 ////////////////////////////////////////////////////////////////////////////////
841 void KTRMLookup::finished()
844 #if HAVE_TUNEPIMP
845 emit sigResult( results(), d->errorString );
847 if(d->autoDelete)
848 deleteLater();
849 #endif
852 ////////////////////////////////////////////////////////////////////////////////
853 // Helper Functions used for sorting MusicBrainz results
854 ////////////////////////////////////////////////////////////////////////////////
855 double stringSimilarity(QStringList &l, QString &s)
857 double max = 0, current = 0;
858 for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
859 if( max < (current = stringSimilarity((*it),s)))
860 max = current;
862 return max;
865 double stringSimilarity(QString s1, QString s2)
867 s1.remove( QRegExp("[\\s\\t\\r\\n]") );
868 s2.remove( QRegExp("[\\s\\t\\r\\n]") );
870 double nCommon = 0;
871 int p1 = 0, p2 = 0, x1 = 0, x2 = 0;
872 int l1 = s1.length(), l2 = s2.length(), l3 = l1 + l2;
873 QChar c1 = 0, c2 = 0;
875 while(p1 < l1 && p2 < l2) {
876 c1 = s1.at(p1); c2 = s2.at(p2);
877 if( c1.toUpper() == c2.toUpper()) {
878 ++nCommon;
879 ++p1; ++p2;
881 else {
882 x1 = s1.indexOf(c2,p1,Qt::CaseInsensitive);
883 x2 = s2.indexOf(c1,p2,Qt::CaseInsensitive);
885 if( (x1 == x2 || -1 == x1) || (-1 != x2 && x1 > x2) )
886 ++p2;
887 else
888 ++p1;
891 return l3 ? (double)(nCommon*2) / (double)(l3) : 1;
898 #include "ktrm.moc"