00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <QDebug>
00025 #include <QString>
00026 #include <QList>
00027 #include <QObject>
00028 #include <QDomDocument>
00029 #include <QFile>
00030 #include <QFileInfo>
00031 #include <QResource>
00032 #include <QDir>
00033 #include <QTranslator>
00034 #include <QLibraryInfo>
00035
00036 #include <klfutil.h>
00037 #include "klfpluginiface.h"
00038 #include "klfconfig.h"
00039 #include "klfmain.h"
00040
00041
00042 KLF_EXPORT QList<KLFTranslationInfo> klf_avail_translations;
00043
00044 KLF_EXPORT QList<QTranslator*> klf_translators;
00045
00046
00047
00048
00049
00050 QList<KLFPluginInfo> klf_plugins;
00051
00052
00053
00054 QList<KLFAddOnInfo> klf_addons;
00055 bool klf_addons_canimport = false;
00056
00057
00058
00059 KLFAddOnInfo::KLFAddOnInfo(QString rccfpath, bool isFresh)
00060 {
00061 d = new Private;
00062 d->ref = 1;
00063 klfDbg( "KLFAddOnInfo: rccfpath="<<rccfpath<<", Private has ref "<< d->ref ) ;
00064
00065 QFileInfo fi(rccfpath);
00066
00067 d->fname = fi.fileName();
00068 d->dir = fi.absolutePath();
00069 d->fpath = fi.absoluteFilePath();
00070
00071 d->islocal = fi.isWritable() || QFileInfo(d->dir).isWritable();
00072
00073 d->isfresh = isFresh;
00074
00075 QByteArray rccinfodata;
00076
00077
00078 QString mountroot;
00079 QString suffix;
00080 QString minrccfpath = rccfpath.section("/", -1, -1, QString::SectionSkipEmpty);
00081 int k = 0;
00082
00083 while (QFileInfo(mountroot = QString(":/klfaddon_rccmount/%1%2").arg(minrccfpath, suffix)).exists()) {
00084 suffix = QString("_%1").arg(++k);
00085 }
00086 d->rccmountroot = mountroot;
00087 klfDbg( "Mounting resource "<<rccfpath<<" to "<<d->rccmountroot ) ;
00088
00089 bool ok = QResource::registerResource(d->fpath, mountroot);
00090 KLF_ASSERT_CONDITION(ok, "Failed to register resource "<<rccfpath, return; ) ;
00091
00092
00093 {
00094 QFile infofile(mountroot+QLatin1String("/rccinfo/info.xml"));
00095 infofile.open(QIODevice::ReadOnly);
00096 rccinfodata = infofile.readAll();
00097 }
00098
00099
00100 QDir i18ndir(mountroot+QLatin1String("/i18n/"));
00101 d->translations = i18ndir.entryList(QStringList() << "*.qm", QDir::Files);
00102
00103
00104
00105 d->title = QObject::tr("(Name Not Provided)", "[KLFAddOnInfo: add-on information XML data is invalid]");
00106 d->description = QObject::tr("(Invalid XML Data Provided By Add-On)",
00107 "[KLFAddOnInfo: add-on information XML data is invalid]");
00108 d->klfminversion = QString();
00109 d->author = QObject::tr("(No Author Provided)",
00110 "[KLFAddOnInfo: add-on information XML data is invalid]");
00111
00112
00113 QDomDocument xmldoc;
00114 xmldoc.setContent(rccinfodata);
00115
00116 QDomElement xmlroot = xmldoc.documentElement();
00117 if (xmlroot.nodeName() != "rccinfo") {
00118 qWarning("Add-on file `%s' has invalid XML information.", qPrintable(rccfpath));
00119 return;
00120 }
00121 QDomNode n;
00122 for (n = xmlroot.firstChild(); ! n.isNull(); n = n.nextSibling()) {
00123 QDomElement e = n.toElement();
00124 if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
00125 continue;
00126 if ( e.nodeName() == "title" ) {
00127 d->title = e.text().trimmed();
00128 }
00129 if ( e.nodeName() == "author" ) {
00130 d->author = e.text().trimmed();
00131 }
00132 if ( e.nodeName() == "description" ) {
00133 d->description = e.text().trimmed();
00134 }
00135 if ( e.nodeName() == "klfminversion" ) {
00136 d->klfminversion = e.text().trimmed();
00137 }
00138 }
00139
00140 initPlugins();
00141 }
00142
00143 KLF_EXPORT QDebug& operator<<(QDebug& str, const KLFAddOnInfo::PluginSysInfo& i)
00144 {
00145 return str << "KLFAddOnInfo::PluginSysInfo(qtminver="<<i.qtminversion<<"; klfminver="<<i.klfminversion
00146 << "; os="<<i.os<<"; arch="<<i.arch<<")";
00147 }
00148
00149
00150 bool KLFAddOnInfo::PluginSysInfo::isCompatibleWithCurrentSystem() const
00151 {
00152 return
00153 (klfminversion.isEmpty()
00154 || klfVersionCompare(klfminversion, KLF_VERSION_STRING) <= 0) &&
00155 (qtminversion.isEmpty()
00156 || klfVersionCompare(qtminversion, qVersion()) <= 0) &&
00157 os == KLFSysInfo::osString() &&
00158 arch == KLFSysInfo::arch() ;
00159 }
00160
00161
00162 void KLFAddOnInfo::initPlugins()
00163 {
00164
00165 QDir plugdir(d->rccmountroot+QLatin1String("/plugins/"));
00166 PluginSysInfo defpinfo;
00167 defpinfo.qtminversion = "";
00168 defpinfo.klfminversion = "";
00169 defpinfo.os = KLFSysInfo::osString();
00170 defpinfo.arch = KLFSysInfo::arch();
00171 d->plugins = QMap<QString,PluginSysInfo>();
00172 d->pluginList = QStringList();
00173
00174
00175 QStringList unorderedplugins = plugdir.entryList(KLF_DLL_EXT_LIST, QDir::Files);
00176 int k;
00177 for (k = 0; k < unorderedplugins.size(); ++k) {
00178 d->pluginList << unorderedplugins[k];
00179 d->plugins[unorderedplugins[k]] = defpinfo;
00180 }
00181
00182 if (!QFile::exists(plugdir.absoluteFilePath("plugindirinfo.xml"))) {
00183 klfDbg( "KLFAddOnInfo("<<d->fname<<"): No specific plugin directories. plugdirinfo.xml="
00184 <<plugdir.absoluteFilePath("plugindirinfo.xml") ) ;
00185 return;
00186 }
00187
00188
00189 QFile plugdirinfofile(d->rccmountroot+QLatin1String("/plugins/plugindirinfo.xml"));
00190 plugdirinfofile.open(QIODevice::ReadOnly);
00191 QByteArray plugdirinfodata = plugdirinfofile.readAll();
00192
00193
00194 QMap<QString,PluginSysInfo> pdirinfos;
00195
00196
00197 QDomDocument xmldoc;
00198 xmldoc.setContent(plugdirinfodata);
00199
00200 QDomElement xmlroot = xmldoc.documentElement();
00201 if (xmlroot.nodeName() != "klfplugindirs") {
00202 qWarning("KLFAddOnInfo: Add-on plugin dir info file `%s' has invalid XML information.",
00203 qPrintable(d->fpath));
00204 return;
00205 }
00206 QDomNode n;
00207 for (n = xmlroot.firstChild(); ! n.isNull(); n = n.nextSibling()) {
00208 QDomElement e = n.toElement();
00209 if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
00210 continue;
00211 if ( e.nodeName() != "klfplugindir" ) {
00212 qWarning("KLFAddOnInfo(%s): plugindirinfo.xml: skipping unexpected node %s.", qPrintable(d->fpath),
00213 qPrintable(e.nodeName()));
00214 continue;
00215 }
00216
00217 PluginSysInfo psi;
00218 QDomNode nn;
00219 for (nn = e.firstChild(); ! nn.isNull(); nn = nn.nextSibling()) {
00220 QDomElement ee = nn.toElement();
00221 klfDbg( "Node: type="<<nn.nodeType()<<"; name="<<ee.nodeName() ) ;
00222 if ( ee.isNull() || nn.nodeType() != QDomNode::ElementNode )
00223 continue;
00224 if ( ee.nodeName() == "dir" ) {
00225 psi.dir = ee.text().trimmed();
00226 } else if ( ee.nodeName() == "qtminversion" ) {
00227 psi.qtminversion = ee.text().trimmed();
00228 } else if ( ee.nodeName() == "klfminversion" ) {
00229 psi.klfminversion = ee.text().trimmed();
00230 } else if ( ee.nodeName() == "os" ) {
00231 psi.os = ee.text().trimmed();
00232 } else if ( ee.nodeName() == "arch" ) {
00233 psi.arch = ee.text().trimmed();
00234 } else {
00235 qWarning("KLFAddOnInfo(%s): plugindirinfo.xml: skipping unexpected node in <klfplugindirs>: %s.",
00236 qPrintable(d->fpath), qPrintable(ee.nodeName()));
00237 }
00238 }
00239 klfDbg( "\tRead psi="<<psi ) ;
00240
00241 pdirinfos[psi.dir] = psi;
00242 }
00243
00244 QStringList morePluginsList;
00245 for (QMap<QString,PluginSysInfo>::const_iterator it = pdirinfos.begin(); it != pdirinfos.end(); ++it) {
00246 QString dir = it.key();
00247 PluginSysInfo psi = it.value();
00248 if ( ! QFileInfo(d->rccmountroot + "/plugins/" + dir).exists() ) {
00249 qWarning("KLFAddOnInfo(%s): Plugin dir '%s' given in XML info does not exist in resource!",
00250 qPrintable(d->fpath), qPrintable(dir));
00251 continue;
00252 }
00253 QDir plugsubdir(d->rccmountroot + "/plugins/" + dir);
00254 QStringList plugins = plugsubdir.entryList(KLF_DLL_EXT_LIST, QDir::Files);
00255 int j;
00256 for (j = 0; j < plugins.size(); ++j) {
00257 QString p = dir+"/"+plugins[j];
00258 morePluginsList << p;
00259 d->plugins[p] = psi;
00260 }
00261 }
00262
00263 QStringList fullList = morePluginsList;
00264 fullList << d->pluginList;
00265 d->pluginList = fullList;
00266
00267 klfDbg( "Loaded plugins: list="<<d->pluginList<<"; map="<<d->plugins ) ;
00268 }
00269
00270
00271 QStringList KLFAddOnInfo::localPluginList() const
00272 {
00273 QStringList lplugins;
00274 for (int k = 0; k < d->pluginList.size(); ++k) {
00275 if ( d->plugins[d->pluginList[k]].isCompatibleWithCurrentSystem() )
00276 lplugins << QDir::cleanPath(pluginLocalSubDirName(d->pluginList[k])+"/"+QFileInfo(d->pluginList[k]).fileName());
00277 }
00278 return lplugins;
00279 }
00280
00281
00282 KLFAddOnInfo::KLFAddOnInfo(const KLFAddOnInfo& other)
00283 {
00284 d = other.d;
00285 if (d)
00286 d->ref++;
00287 }
00288
00289 KLFAddOnInfo::~KLFAddOnInfo()
00290 {
00291 if (d) {
00292 d->ref--;
00293 if (d->ref <= 0) {
00294
00295 QResource::unregisterResource(d->fpath, d->rccmountroot);
00296 delete d;
00297 }
00298 }
00299 }
00300
00301
00302
00303
00304 KLFI18nFile::KLFI18nFile(QString filepath)
00305 {
00306 QFileInfo fi(filepath);
00307 QString fn = fi.fileName();
00308 QDir d = fi.absoluteDir();
00309
00310 int firstunderscore = fn.indexOf('_');
00311 int endbasename = fn.endsWith(".qm") ? fn.length() - 3 : fn.length() ;
00312 if (firstunderscore == -1)
00313 firstunderscore = endbasename;
00314
00315 fpath = d.absoluteFilePath(fn);
00316 name = fn.mid(0, firstunderscore);
00317 locale = fn.mid(firstunderscore+1, endbasename-(firstunderscore+1));
00318 locale_specificity = (locale.split('_', QString::SkipEmptyParts)).size() ;
00319 }
00320
00321
00322
00323
00324 void klf_add_avail_translation(KLFI18nFile i18nfile)
00325 {
00326 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00327 klfDbg("i18nfile.fpath="<<i18nfile.fpath<<" is translation to "<<i18nfile.locale) ;
00328
00329 QFileInfo fi(i18nfile.fpath);
00330
00331 klfDbg("fi.canonicalPath()="<<fi.canonicalPath()<<", Qt translations location="
00332 <<QFileInfo(QLibraryInfo::location(QLibraryInfo::TranslationsPath)).canonicalFilePath()) ;
00333
00334 if ( fi.canonicalPath() ==
00335 QFileInfo(QLibraryInfo::location(QLibraryInfo::TranslationsPath)).canonicalFilePath()
00336 || i18nfile.name == "qt" ) {
00337
00338
00339 return;
00340 }
00341
00342
00343 bool needsRegistration = true;
00344 bool needsNiceName = true;
00345 int alreadyRegisteredIndex = -1;
00346
00347 int kk;
00348 for (kk = 0; kk < klf_avail_translations.size(); ++kk) {
00349 if (klf_avail_translations[kk].localename == i18nfile.locale) {
00350 needsRegistration = false;
00351 alreadyRegisteredIndex = kk;
00352 needsNiceName = ! klf_avail_translations[kk].hasnicetranslatedname;
00353 klfDbg("translation "<<i18nfile.locale<<" is already registered. needs nice name?="<< needsNiceName) ;
00354 }
00355 }
00356
00357 klfDbg("Needs registration?="<<needsRegistration<<"; needs nice name?="<<needsNiceName) ;
00358 if ( ! needsRegistration && ! needsNiceName ) {
00359
00360 return;
00361 }
00362
00363 klfDbg("will load translation file "<<fi.completeBaseName()<<", abs path="<<fi.absolutePath()) ;
00364
00365
00366 QTranslator translator;
00367 translator.load(fi.completeBaseName(), fi.absolutePath(), "_", "."+fi.suffix());
00368 KLFTranslationInfo ti;
00369 ti.localename = i18nfile.locale;
00370 struct klf_qtTrNoop3 { const char *source; const char *comment; };
00371 klf_qtTrNoop3 lang
00372 = QT_TRANSLATE_NOOP3("QObject", "English (US)",
00373 "[[The Language (possibly with Country) you are translating to, e.g. `Deutsch']]");
00374 ti.translatedname = translator.translate("QObject", lang.source, lang.comment);
00375 ti.hasnicetranslatedname = true;
00376 if (ti.translatedname == "English" || ti.translatedname.isEmpty()) {
00377 QLocale lc(i18nfile.locale);
00378 QString s;
00379 if ( i18nfile.locale.indexOf("_") != -1 ) {
00380
00381 s = QString("%1 (%2)").arg(QLocale::languageToString(lc.language()))
00382 .arg(QLocale::countryToString(lc.country()));
00383 } else {
00384 s = QString("%1").arg(QLocale::languageToString(lc.language()));
00385 }
00386 ti.translatedname = s;
00387 ti.hasnicetranslatedname = false;
00388 }
00389 if (needsRegistration)
00390 klf_avail_translations.append(ti);
00391 else if (needsNiceName && ti.hasnicetranslatedname)
00392 klf_avail_translations[alreadyRegisteredIndex] = ti;
00393 }
00394
00395
00396 KLF_EXPORT void klf_reload_translations(QCoreApplication *app, const QString& currentLocale)
00397 {
00398
00399
00400
00401
00402
00403 int j, k;
00404
00405
00406 for (k = 0; k < klf_translators.size(); ++k) {
00407 app->removeTranslator(klf_translators[k]);
00408 delete klf_translators[k];
00409 }
00410 klf_translators.clear();
00411
00412
00413
00414 QMap<QString, QMap<int, QList<KLFI18nFile> > > i18nFiles;
00415
00416 QSet<QString> names;
00417
00418 QStringList i18ndirlist;
00419
00420 for (k = 0; k < klf_addons.size(); ++k) {
00421 i18ndirlist << klf_addons[k].rccmountroot()+"/i18n";
00422 }
00423 i18ndirlist << ":/i18n"
00424 << klfconfig.homeConfigDirI18n
00425 << klfconfig.globalShareDir+"/i18n"
00426 << QLibraryInfo::location(QLibraryInfo::TranslationsPath);
00427
00428 for (j = 0; j < i18ndirlist.size(); ++j) {
00429
00430 QDir i18ndir(i18ndirlist[j]);
00431 if ( ! i18ndir.exists() )
00432 continue;
00433 QStringList files = i18ndir.entryList(QStringList() << QString::fromLatin1("*.qm"), QDir::Files);
00434 for (k = 0; k < files.size(); ++k) {
00435 KLFI18nFile i18nfile(i18ndir.absoluteFilePath(files[k]));
00436
00437
00438 i18nFiles[i18nfile.name][i18nfile.locale_specificity] << i18nfile;
00439 names << i18nfile.name;
00440 qDebug("Found translation %s", qPrintable(i18nfile.fpath));
00441 klf_add_avail_translation(i18nfile);
00442 }
00443 }
00444
00445
00446 QString lc = currentLocale;
00447 if (lc.isEmpty())
00448 lc = "en_US";
00449 QStringList lcparts = lc.split("_");
00450
00451
00452
00453
00454 QStringList translationsToLoad;
00455
00456
00457 for (QSet<QString>::const_iterator it = names.begin(); it != names.end(); ++it) {
00458 QString name = *it;
00459 QMap< int, QList<KLFI18nFile> > translations = i18nFiles[name];
00460 int specificity = lcparts.size();
00461 while (specificity >= 0) {
00462
00463 QString testlocale = QStringList(lcparts.mid(0, specificity)).join("_");
00464
00465
00466 QList<KLFI18nFile> list = translations[specificity];
00467 for (j = 0; j < list.size(); ++j) {
00468 if (list[j].locale == testlocale) {
00469
00470
00471 translationsToLoad << list[j].fpath;
00472
00473 specificity = -1;
00474 }
00475 }
00476
00477 specificity--;
00478 }
00479 }
00480
00481
00482
00483 for (j = 0; j < translationsToLoad.size(); ++j) {
00484
00485
00486 QTranslator *translator = new QTranslator(app);
00487 QFileInfo fi(translationsToLoad[j]);
00488
00489
00490 bool res = translator->load(fi.completeBaseName(), fi.absolutePath(), "_", "."+fi.suffix());
00491 if ( res ) {
00492 app->installTranslator(translator);
00493 klf_translators << translator;
00494 } else {
00495 qWarning("Failed to load translator %s.", qPrintable(translationsToLoad[j]));
00496 delete translator;
00497 }
00498 }
00499 }
00500
00501
00502
00503
00504 KLF_EXPORT QString klfFindTranslatedDataFile(const QString& baseFileName, const QString& extension)
00505 {
00506 QString loc = klfconfig.UI.locale;
00507 QStringList suffixes;
00508 suffixes << "_"+loc
00509 << "_"+loc.section('_',0,0)
00510 << "";
00511 int k = 0;
00512 QString fn;
00513 while ( k < suffixes.size() &&
00514 ! QFile::exists(fn = QString("%1%2%3").arg(baseFileName, suffixes[k], extension)) ) {
00515 klfDbg( "base="<<baseFileName<<" extn="<<extension<<"; tried fn="<<fn ) ;
00516 ++k;
00517 }
00518 if (k >= suffixes.size()) {
00519 qWarning()<<KLF_FUNC_NAME<<": Can't find good translated file for "<<qPrintable(baseFileName+extension)
00520 <<"! last try was "<<fn;
00521 return QString();
00522 }
00523 return fn;
00524 }
00525
00526
00527
00528
00529 KLF_EXPORT void klfDataStreamWriteHeader(QDataStream& stream, const QString headermagic)
00530 {
00531
00532 stream.device()->setProperty("klfDataStreamAppVersion",
00533 QVariant::fromValue<QString>(KLF_DATA_STREAM_APP_VERSION));
00534
00535
00536 stream.setVersion(QDataStream::Qt_3_3);
00537 stream << headermagic
00538 << (qint16)KLF_DATA_STREAM_APP_VERSION_MAJ
00539 << (qint16)KLF_DATA_STREAM_APP_VERSION_MIN
00540 << (qint16)QDataStream::Qt_4_4;
00541 stream.setVersion(QDataStream::Qt_4_4);
00542
00543
00544 }
00545
00546 KLF_EXPORT bool klfDataStreamReadHeader(QDataStream& stream, const QStringList possibleHeaders,
00547 QString *readHeader, QString *readCompatKLFVersion)
00548 {
00549 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00550
00551 QString s;
00552 stream.setVersion(QDataStream::Qt_3_3);
00553 stream >> s;
00554 if (!possibleHeaders.contains(s) || stream.status() != QDataStream::Ok) {
00555 klfDbg("Read bad header: "<<s) ;
00556 if (readHeader != NULL)
00557 *readHeader = QString();
00558 return false;
00559 }
00560 if (readHeader != NULL)
00561 *readHeader = s;
00562
00563
00564 qint16 vmaj, vmin;
00565 stream >> vmaj >> vmin;
00566 if (stream.status() != QDataStream::Ok) {
00567 if (readCompatKLFVersion)
00568 *readCompatKLFVersion = QString();
00569 return false;
00570 }
00571 klfDbg("read app compat version = "<<vmaj<<"."<<vmin) ;
00572
00573 QString compatKLFVersion = QString("%1.%2").arg(vmaj).arg(vmin);
00574
00575 if (vmaj > klfVersionMaj() || (vmaj == klfVersionMaj() && vmin > klfVersionMin())) {
00576 if (readCompatKLFVersion != NULL)
00577 *readCompatKLFVersion = compatKLFVersion;
00578 return false;
00579 }
00580
00581
00582 if (vmaj <= 2) {
00583 stream.setVersion(QDataStream::Qt_3_3);
00584 } else {
00585 qint16 version;
00586 stream >> version;
00587 stream.setVersion(version);
00588 }
00589
00590
00591
00592 stream.device()->setProperty("klfDataStreamAppVersion", QVariant::fromValue<QString>(compatKLFVersion));
00593
00594
00595 return true;
00596 }