20#include "kexthighscore_internal.h"
31#include <tdeio/netaccess.h>
33#include <tdemessagebox.h>
38#include "kexthighscore.h"
39#include "kexthighscore_gui.h"
40#include "tdeemailsettings.h"
43namespace KExtHighscore
47const char ItemContainer::ANONYMOUS[] =
"_";
48const char ItemContainer::ANONYMOUS_LABEL[] = I18N_NOOP(
"anonymous");
50ItemContainer::ItemContainer()
54ItemContainer::~ItemContainer()
59void ItemContainer::setItem(Item *item)
65TQString ItemContainer::entryName()
const
67 if ( _subGroup.isEmpty() )
return _name;
68 return _name +
"_" + _subGroup;
71TQVariant ItemContainer::read(uint i)
const
75 TQVariant v = _item->defaultValue();
80 return _item->read(i, v);
83TQString ItemContainer::pretty(uint i)
const
86 return _item->pretty(i, read(i));
89void ItemContainer::write(uint i,
const TQVariant &value)
const
91 Q_ASSERT( isStored() );
92 Q_ASSERT( internal->hsConfig().
isLocked() );
94 internal->hsConfig().
writeEntry(i+1, entryName(), value);
97uint ItemContainer::increment(uint i)
const
99 uint v = read(i).toUInt() + 1;
105ItemArray::ItemArray()
106 : _group(
""), _subGroup(
"")
109ItemArray::~ItemArray()
111 for (uint i=0; i<size(); i++)
delete at(i);
114int ItemArray::findIndex(
const TQString &name)
const
116 for (uint i=0; i<size(); i++)
117 if ( at(i)->name()==name )
return i;
121const ItemContainer *ItemArray::item(
const TQString &name)
const
123 int i = findIndex(name);
124 if ( i==-1 ) kdError(11002) << k_funcinfo <<
"no item named \"" << name
129ItemContainer *ItemArray::item(
const TQString &name)
131 int i = findIndex(name);
132 if ( i==-1 ) kdError(11002) << k_funcinfo <<
"no item named \"" << name
137void ItemArray::setItem(
const TQString &name, Item *item)
139 int i = findIndex(name);
140 if ( i==-1 ) kdError(11002) << k_funcinfo <<
"no item named \"" << name
142 bool stored = at(i)->isStored();
143 bool canHaveSubGroup = at(i)->canHaveSubGroup();
144 _setItem(i, name, item, stored, canHaveSubGroup);
147void ItemArray::addItem(
const TQString &name, Item *item,
148 bool stored,
bool canHaveSubGroup)
150 if ( findIndex(name)!=-1 )
151 kdError(11002) <<
"item already exists \"" << name <<
"\"" << endl;
154 at(i) =
new ItemContainer;
155 _setItem(i, name, item, stored, canHaveSubGroup);
158void ItemArray::_setItem(uint i,
const TQString &name, Item *item,
159 bool stored,
bool canHaveSubGroup)
161 at(i)->setItem(item);
162 at(i)->setName(name);
163 at(i)->setGroup(stored ? _group : TQString());
164 at(i)->setSubGroup(canHaveSubGroup ? _subGroup : TQString());
167void ItemArray::setGroup(
const TQString &group)
169 Q_ASSERT( !group.isNull() );
171 for (uint i=0; i<size(); i++)
172 if ( at(i)->isStored() ) at(i)->setGroup(group);
175void ItemArray::setSubGroup(
const TQString &subGroup)
177 Q_ASSERT( !subGroup.isNull() );
178 _subGroup = subGroup;
179 for (uint i=0; i<size(); i++)
180 if ( at(i)->canHaveSubGroup() ) at(i)->setSubGroup(subGroup);
183void ItemArray::read(uint k, Score &data)
const
185 for (uint i=0; i<size(); i++) {
186 if ( !at(i)->isStored() )
continue;
187 data.setData(at(i)->name(), at(i)->read(k));
191void ItemArray::write(uint k,
const Score &data, uint nb)
const
193 for (uint i=0; i<size(); i++) {
194 if ( !at(i)->isStored() )
continue;
195 for (uint j=nb-1; j>k; j--) at(i)->write(j, at(i)->read(j-1));
196 at(i)->write(k, data.data(at(i)->name()));
200void ItemArray::exportToText(TQTextStream &s)
const
202 for (uint k=0; k<nbEntries()+1; k++) {
203 for (uint i=0; i<size(); i++) {
204 const Item *item = at(i)->item();
205 if ( item->isVisible() ) {
206 if ( i!=0 ) s <<
'\t';
207 if ( k==0 ) s << item->label();
208 else s << at(i)->pretty(k-1);
216class ScoreNameItem :
public NameItem
219 ScoreNameItem(
const ScoreInfos &score,
const PlayerInfos &infos)
220 : _score(score), _infos(infos) {}
222 TQString pretty(uint i,
const TQVariant &v)
const {
223 uint
id = _score.item(
"id")->read(i).toUInt();
224 if (
id==0 )
return NameItem::pretty(i, v);
225 return _infos.prettyName(
id-1);
229 const ScoreInfos &_score;
230 const PlayerInfos &_infos;
234ScoreInfos::ScoreInfos(uint maxNbEntries,
const PlayerInfos &infos)
235 : _maxNbEntries(maxNbEntries)
237 addItem(
"id",
new Item((uint)0));
238 addItem(
"rank",
new RankItem,
false);
239 addItem(
"name",
new ScoreNameItem(*
this, infos));
240 addItem(
"score", Manager::createItem(Manager::ScoreDefault));
241 addItem(
"date",
new DateItem);
244uint ScoreInfos::nbEntries()
const
247 for (; i<_maxNbEntries; i++)
248 if ( item(
"score")->read(i)==item(
"score")->item()->defaultValue() )
254const char *HS_ID =
"player id";
255const char *HS_KEY =
"player key";
257PlayerInfos::PlayerInfos()
262 addItem(
"name",
new NameItem);
263 Item *it =
new Item((uint)0, i18n(
"Games Count"),TQt::AlignRight);
264 addItem(
"nb games", it,
true,
true);
265 it = Manager::createItem(Manager::MeanScoreDefault);
266 addItem(
"mean score", it,
true,
true);
267 it = Manager::createItem(Manager::BestScoreDefault);
268 addItem(
"best score", it,
true,
true);
269 addItem(
"date",
new DateItem,
true,
true);
270 it =
new Item(TQString(), i18n(
"Comment"), TQt::AlignLeft);
271 addItem(
"comment", it);
274 addItem(
"nb black marks",
new Item((uint)0),
true,
true);
275 addItem(
"nb lost games",
new Item((uint)0),
true,
true);
276 addItem(
"nb draw games",
new Item((uint)0),
true,
true);
277 addItem(
"current trend",
new Item((
int)0),
true,
true);
278 addItem(
"max lost trend",
new Item((uint)0),
true,
true);
279 addItem(
"max won trend",
new Item((uint)0),
true,
true);
281 struct passwd *pwd = getpwuid(getuid());
282 TQString username = pwd->pw_name;
283#ifdef HIGHSCORE_DIRECTORY
285 for (uint i=0; ;i++) {
286 if ( !internal->hsConfig().
hasEntry(i+1,
"username") ) {
291 if ( internal->hsConfig().
readEntry(i+1,
"username")==username ) {
299 KEMailSettings emailConfig;
300 emailConfig.setProfile(emailConfig.defaultProfileName());
301 TQString name = emailConfig.getSetting(KEMailSettings::RealName);
302 if ( name.isEmpty() || isNameUsed(name) ) name = username;
303 if ( isNameUsed(name) ) name= TQString(ItemContainer::ANONYMOUS);
304#ifdef HIGHSCORE_DIRECTORY
305 internal->hsConfig().
writeEntry(_id+1,
"username", username);
306 item(
"name")->write(_id, name);
310 _oldLocalPlayer = cg.config()->hasKey(HS_ID);
311 _oldLocalId = cg.config()->readUnsignedNumEntry(HS_ID);
312#ifdef HIGHSCORE_DIRECTORY
313 if (_oldLocalPlayer) {
315 TQString prefix = TQString(
"%1_").arg(_oldLocalId+1);
316 TQMap<TQString, TQString> entries =
317 cg.config()->entryMap(
"KHighscore_players");
318 TQMap<TQString, TQString>::const_iterator it;
319 for (it=entries.begin(); it!=entries.end(); ++it) {
320 TQString key = it.key();
321 if ( key.find(prefix)==0 ) {
322 TQString name = key.right(key.length()-prefix.length());
323 if ( name!=
"name" || !isNameUsed(it.data()) )
324 internal->hsConfig().
writeEntry(_id+1, name, it.data());
329 _newPlayer = !_oldLocalPlayer;
330 if (_oldLocalPlayer) _id = _oldLocalId;
333 cg.config()->writeEntry(HS_ID, _id);
334 item(
"name")->write(_id, name);
341void PlayerInfos::createHistoItems(
const TQMemArray<uint> &scores,
bool bound)
343 Q_ASSERT( _histogram.size()==0 );
346 for (uint i=1; i<histoSize(); i++)
347 addItem(histoName(i),
new Item((uint)0),
true,
true);
350bool PlayerInfos::isAnonymous()
const
352 return ( name()==ItemContainer::ANONYMOUS );
355uint PlayerInfos::nbEntries()
const
358 TQStringList list = internal->hsConfig().
readList(
"name", -1);
362TQString PlayerInfos::key()
const
365 return cg.config()->readEntry(HS_KEY, TQString());
368TQString PlayerInfos::histoName(uint i)
const
370 const TQMemArray<uint> &sh = _histogram;
371 Q_ASSERT( i<sh.size() || (_bound || i==sh.size()) );
373 return TQString(
"nb scores greater than %1").arg(sh[sh.size()-1]);
374 return TQString(
"nb scores less than %1").arg(sh[i]);
377uint PlayerInfos::histoSize()
const
379 return _histogram.size() + (_bound ? 0 : 1);
382void PlayerInfos::submitScore(
const Score &score)
const
385 uint nbGames = item(
"nb games")->increment(_id);
386 switch (score.type()) {
388 item(
"nb lost games")->increment(_id);
392 item(
"nb draw games")->increment(_id);
397 if ( score.type()==Won ) {
398 uint nbWonGames = nbGames - item(
"nb lost games")->read(_id).toUInt()
399 - item(
"nb draw games")->read(_id).toUInt()
400 - item(
"nb black marks")->read(_id).toUInt();
401 double mean = (nbWonGames==1 ? 0.0
402 : item(
"mean score")->read(_id).toDouble());
403 mean += (double(score.score()) - mean) / nbWonGames;
404 item(
"mean score")->write(_id, mean);
409 best.setScore( item(
"best score")->read(_id).toUInt() );
411 item(
"best score")->write(_id, score.score());
412 item(
"date")->write(_id, score.data(
"date").toDateTime());
416 int current = item(
"current trend")->read(_id).toInt();
417 switch (score.type()) {
419 if ( current<0 ) current = 0;
421 uint won = item(
"max won trend")->read(_id).toUInt();
422 if ( (uint)current>won ) item(
"max won trend")->write(_id, current);
426 if ( current>0 ) current = 0;
428 uint lost = item(
"max lost trend")->read(_id).toUInt();
429 uint clost = -current;
430 if ( clost>lost ) item(
"max lost trend")->write(_id, clost);
437 item(
"current trend")->write(_id, current);
440 if ( score.type()==Won ) {
441 const TQMemArray<uint> &sh = _histogram;
442 for (uint i=1; i<histoSize(); i++)
443 if ( i==sh.size() || score.score()<sh[i] ) {
444 item(histoName(i))->increment(_id);
450bool PlayerInfos::isNameUsed(
const TQString &newName)
const
452 if ( newName==name() )
return false;
453 for (uint i=0; i<nbEntries(); i++)
454 if ( newName.lower()==item(
"name")->read(i).toString().lower() )
return true;
455 if ( newName==i18n(ItemContainer::ANONYMOUS_LABEL) )
return true;
459void PlayerInfos::modifyName(
const TQString &newName)
const
461 item(
"name")->write(_id, newName);
464void PlayerInfos::modifySettings(
const TQString &newName,
465 const TQString &comment,
466 const TQString &newKey)
const
469 item(
"comment")->write(_id, comment);
471 if ( !newKey.isEmpty() ) cg.config()->writeEntry(HS_KEY, newKey);
474void PlayerInfos::removeKey()
480 TQString str =
"%1 old #%2";
484 sk = str.arg(HS_KEY).arg(i);
485 }
while ( !cg.config()->readEntry(sk, TQString()).isEmpty() );
486 cg.config()->writeEntry(sk, key());
489 cg.config()->deleteEntry(HS_KEY);
493ManagerPrivate::ManagerPrivate(uint nbGameTypes, Manager &m)
494 : manager(m), showStatistics(false), showDrawGames(false),
495 trackLostGames(false), trackDrawGames(false),
496 showMode(Manager::ShowForHigherScore),
497 _first(true), _nbGameTypes(nbGameTypes), _gameType(0)
500void ManagerPrivate::init(uint maxNbEntries)
503 _playerInfos =
new PlayerInfos;
504 _scoreInfos =
new ScoreInfos(maxNbEntries, *_playerInfos);
507ManagerPrivate::~ManagerPrivate()
515const char *DUMMY_STRINGS[] = {
516 I18N_NOOP(
"Undefined error."),
517 I18N_NOOP(
"Missing argument(s)."),
518 I18N_NOOP(
"Invalid argument(s)."),
520 I18N_NOOP(
"Unable to connect to MySQL server."),
521 I18N_NOOP(
"Unable to select database."),
522 I18N_NOOP(
"Error on database query."),
523 I18N_NOOP(
"Error on database insert."),
525 I18N_NOOP(
"Nickname already registered."),
526 I18N_NOOP(
"Nickname not registered."),
527 I18N_NOOP(
"Invalid key."),
528 I18N_NOOP(
"Invalid submit key."),
530 I18N_NOOP(
"Invalid level."),
531 I18N_NOOP(
"Invalid score.")
534const char *UNABLE_TO_CONTACT =
535 I18N_NOOP(
"Unable to contact world-wide highscore server");
537bool ManagerPrivate::getFromQuery(
const TQDomNamedNodeMap &map,
538 const TQString &name, TQString &value,
541 TQDomAttr attr = map.namedItem(name).toAttr();
542 if ( attr.isNull() ) {
543 KMessageBox::sorry(parent,
544 i18n(
"Invalid answer from world-wide "
545 "highscores server (missing item: %1).").arg(name));
548 value = attr.value();
552Score ManagerPrivate::readScore(uint i)
const
555 _scoreInfos->read(i, score);
559int ManagerPrivate::rank(
const Score &score)
const
561 uint nb = _scoreInfos->nbEntries();
564 if ( readScore(i)<score )
break;
565 return (i<_scoreInfos->maxNbEntries() ? (
int)i : -1);
568bool ManagerPrivate::modifySettings(
const TQString &newName,
569 const TQString &comment,
573 bool newPlayer =
false;
575 bool ok = _hsConfig->lockForWriting(widget);
580 ok = ( !_playerInfos->isNameUsed(newName) );
582 _playerInfos->modifySettings(newName, comment, newKey);
583 _hsConfig->writeAndUnlock();
588void ManagerPrivate::convertToGlobal()
593 TQValueVector<Score> scores(_scoreInfos->nbEntries());
594 for (uint i=0; i<scores.count(); i++)
595 scores[i] = readScore(i);
601 for (uint i=0; i<scores.count(); i++)
602 if ( scores[i].data(
"id").toUInt()==_playerInfos->oldLocalId()+1 )
603 submitLocal(scores[i]);
604 _hsConfig->writeAndUnlock();
607void ManagerPrivate::setGameType(uint type)
611 if ( _playerInfos->isNewPlayer() ) {
613 for (uint i=0; i<_nbGameTypes; i++) {
615 manager.convertLegacy(i);
618#ifdef HIGHSCORE_DIRECTORY
619 if ( _playerInfos->isOldLocalPlayer() ) {
621 for (uint i=0; i<_nbGameTypes; i++) {
630 Q_ASSERT( type<_nbGameTypes );
631 _gameType = kMin(type, _nbGameTypes-1);
632 TQString str =
"scores";
633 TQString lab = manager.gameTypeLabel(_gameType, Manager::Standard);
634 if ( !lab.isEmpty() ) {
635 _playerInfos->setSubGroup(lab);
638 _scoreInfos->setGroup(str);
641void ManagerPrivate::checkFirst()
643 if (_first) setGameType(0);
646int ManagerPrivate::submitScore(
const Score &ascore,
647 TQWidget *widget,
bool askIfAnonymous)
651 Score score = ascore;
652 score.setData(
"id", _playerInfos->id() + 1);
653 score.setData(
"date", TQDateTime::currentDateTime());
656 const char *dontAskAgainName =
"highscore_ask_name_dialog";
658 KMessageBox::ButtonCode dummy;
659 if ( score.type()==Won && askIfAnonymous && _playerInfos->isAnonymous()
660 && KMessageBox::shouldBeShownYesNo(dontAskAgainName, dummy) ) {
661 AskNameDialog d(widget);
662 if ( d.exec()==TQDialog::Accepted ) newName = d.name();
663 if ( d.dontAskAgain() )
664 KMessageBox::saveDontShowAgainYesNo(dontAskAgainName,
669 if ( _hsConfig->lockForWriting(widget) ) {
671 if ( !newName.isEmpty() && !_playerInfos->isNameUsed(newName) )
672 _playerInfos->modifyName(newName);
675 _playerInfos->submitScore(score);
676 if ( score.type()==Won ) rank = submitLocal(score);
677 _hsConfig->writeAndUnlock();
683int ManagerPrivate::submitLocal(
const Score &score)
687 uint nb = _scoreInfos->nbEntries();
688 if ( nb<_scoreInfos->maxNbEntries() ) nb++;
689 _scoreInfos->write(r, score, nb);
694void ManagerPrivate::exportHighscores(TQTextStream &s)
696 uint tmp = _gameType;
698 for (uint i=0; i<_nbGameTypes; i++) {
700 if ( _nbGameTypes>1 ) {
701 if ( i!=0 ) s << endl;
702 s <<
"--------------------------------" << endl;
704 << manager.gameTypeLabel(_gameType, Manager::I18N)
708 s <<
"Players list:" << endl;
709 _playerInfos->exportToText(s);
711 s <<
"Highscores list:" << endl;
712 _scoreInfos->exportToText(s);
Class for managing highscore tables.
bool lockForWriting(TQWidget *widget=0)
TQString readEntry(int entry, const TQString &key, const TQString &pDefault=TQString()) const
Reads an entry from the highscore table.
TQStringList readList(const TQString &key, int lastEntry=20) const
Reads a list of entries from the highscore table starting at 1 until lastEntry.
TQVariant readPropertyEntry(int entry, const TQString &key, const TQVariant &pDefault) const
Read a TQVariant entry.
void writeEntry(int entry, const TQString &key, const TQString &value)
bool hasEntry(int entry, const TQString &key) const
void setHighscoreGroup(const TQString &groupname=TQString())
Set the new highscore group.