From 9eada6e94f345d0c1a2d93de5dc56613a93a3ea0 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 5 Jan 2015 00:31:05 -0600 Subject: Begin unifying interface for customizable settings. --- quacker/settings.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'quacker/settings.h') diff --git a/quacker/settings.h b/quacker/settings.h index 785b726..5a05452 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -76,9 +76,10 @@ protected: QComboBox *m_alphabetNameCombo; QComboBox *m_themeNameCombo; QComboBox *m_boardNameCombo; - QPushButton *m_addBoard; + QPushButton *m_editLexicon; + QPushButton *m_editAlphabet; + QPushButton *m_editTheme; QPushButton *m_editBoard; - QPushButton *m_deleteBoard; QString m_dataDir; QString m_themeName; -- cgit v1.2.3 From b8024ae268e49c17c40da105b9e22aaa41fcffeb Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 3 Aug 2015 19:30:11 -0500 Subject: Finish Add/Edit/Remove for boards. Delete functionality is now inside of the edit dialog. Which will make things less messy when other edit dialogs are introduced. --- .gitignore | 1 + quacker/boardsetupdialog.cpp | 21 +++++++++++++++ quacker/boardsetupdialog.h | 2 ++ quacker/settings.cpp | 64 +++++++++++--------------------------------- quacker/settings.h | 1 - quackle.sublime-project | 2 +- 6 files changed, 41 insertions(+), 50 deletions(-) (limited to 'quacker/settings.h') diff --git a/.gitignore b/.gitignore index a345851..43da009 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ ChangeLog dawginput.raw playabilities.raw smaller.raw +DerivedData diff --git a/quacker/boardsetupdialog.cpp b/quacker/boardsetupdialog.cpp index 7198b17..379f79d 100644 --- a/quacker/boardsetupdialog.cpp +++ b/quacker/boardsetupdialog.cpp @@ -58,6 +58,7 @@ BoardSetupDialog::BoardSetupDialog(QWidget *parent) : QDialog(parent) m_saveChanges = new QPushButton(tr("&Save Changes")); m_cancel = new QPushButton(tr("&Cancel")); m_undoAll = new QPushButton(tr("&Undo All Changes")); + m_deleteBoard = new QPushButton(tr("&Delete Board")); QVBoxLayout * superLayout = new QVBoxLayout; Geometry::setupFramedLayout(superLayout); @@ -96,6 +97,8 @@ BoardSetupDialog::BoardSetupDialog(QWidget *parent) : QDialog(parent) leftSideLayout->addWidget(dimensionGroup); leftSideLayout->addWidget(symmetryGroup); leftSideLayout->addWidget(m_undoAll); + if (!m_originalName.isEmpty()) + leftSideLayout->addWidget(m_deleteBoard); leftSideLayout->addStretch(); mainLayout->addLayout(leftSideLayout); @@ -118,6 +121,7 @@ BoardSetupDialog::BoardSetupDialog(QWidget *parent) : QDialog(parent) connect(m_saveChanges, SIGNAL(clicked()), this, SLOT(accept())); connect(m_cancel, SIGNAL(clicked()), this, SLOT(reject())); connect(m_undoAll, SIGNAL(clicked()), this, SLOT(undoAllChanges())); + connect(m_deleteBoard, SIGNAL(clicked()), this, SLOT(deleteBoard())); connect(m_horizontalSymmetry, SIGNAL(stateChanged(int)), this, SLOT(symmetryChanged())); connect(m_verticalSymmetry, SIGNAL(stateChanged(int)), this, SLOT(symmetryChanged())); connect(m_diagonalSymmetry, SIGNAL(stateChanged(int)), this, SLOT(symmetryChanged())); @@ -239,3 +243,20 @@ void BoardSetupDialog::undoAllChanges() QUACKLE_DATAMANAGER->setBoardParameters(Quackle::BoardParameters::Deserialize(boardStream)); parametersChanged(QString()); } + +void BoardSetupDialog::deleteBoard() +{ + QString message = "Do you really want to delete the game board \""; + message += m_originalName; + message += "\"?"; + if (QMessageBox::warning(NULL, QString("Confirm Deletion"), message, + QMessageBox::Yes | QMessageBox::Default, + QMessageBox::No | QMessageBox::Escape) == QMessageBox::Yes) + { + CustomQSettings settings; + settings.beginGroup("quackle/boardparameters"); + settings.remove(m_originalName); + QDialog::reject(); + } +} + diff --git a/quacker/boardsetupdialog.h b/quacker/boardsetupdialog.h index e5432cc..0176d1a 100644 --- a/quacker/boardsetupdialog.h +++ b/quacker/boardsetupdialog.h @@ -48,6 +48,7 @@ protected slots: void parametersChanged(const QString &unused); void symmetryChanged(); void undoAllChanges(); + void deleteBoard(); private: QCheckBox *m_horizontalSymmetry; @@ -62,6 +63,7 @@ private: QPushButton *m_saveChanges; QPushButton *m_cancel; QPushButton *m_undoAll; + QPushButton *m_deleteBoard; Quackle::Game m_game; BoardSetupFrame * m_boardFrame; diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 67804ad..15448f5 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -148,7 +148,7 @@ void Settings::createGUI() themeNameLabel->setBuddy(m_themeNameCombo); m_editTheme = new QPushButton(tr("Edit...")); m_editTheme->setMaximumWidth(60); - connect(m_editTheme, SIGNAL(clicked()), this, SLOT(editTheme)); + connect(m_editTheme, SIGNAL(clicked()), this, SLOT(editTheme())); themeLayout->addWidget(themeNameLabel); themeLayout->addWidget(m_themeNameCombo); @@ -160,41 +160,19 @@ void Settings::createGUI() QStringList boardItems; populateListFromFilenames(boardItems, "boards"); m_boardNameCombo->addItems(boardItems); + m_boardNameCombo->addItem(tr("Add new board...")); QHBoxLayout *boardLayout = new QHBoxLayout; QLabel *boardNameLabel = new QLabel(tr("&Board:")); boardNameLabel->setBuddy(m_boardNameCombo); m_editBoard = new QPushButton(tr("Edit...")); m_editBoard->setMaximumWidth(60); - connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard)); + connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); boardLayout->addWidget(boardNameLabel); boardLayout->addWidget(m_boardNameCombo); boardLayout->addWidget(m_editBoard); - // m_addBoard = new QPushButton(tr("Add Board")); - // connect(m_addBoard, SIGNAL(clicked()), this, SLOT(addBoard())); - - // m_editBoard = new QPushButton(tr("&Edit Board")); - // connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); - - // m_deleteBoard = new QPushButton(tr("&Delete Board")); - // connect(m_deleteBoard, SIGNAL(clicked()), this, SLOT(deleteBoard())); - - // loadBoardNameCombo(); - - // QGroupBox *boardGroup = new QGroupBox("Game Board Definitions"); - // QGridLayout *boardLayout = new QGridLayout(boardGroup); - // QLabel *boardNameLabel = new QLabel(tr("&Board:")); - // boardNameLabel->setBuddy(m_boardNameCombo); - - // boardLayout->addWidget(boardNameLabel, 0, 0); - // boardLayout->addWidget(m_boardNameCombo, 0, 1, 1, -1); - // boardLayout->addWidget(m_addBoard, 1, 0); - // boardLayout->addWidget(m_editBoard, 1, 1); - // boardLayout->addWidget(m_deleteBoard, 1, 2); - - vlayout->addLayout(lexiconLayout); vlayout->addLayout(alphabetLayout); vlayout->addLayout(themeLayout); @@ -357,6 +335,11 @@ void Settings::themeChanged(const QString &themeName) void Settings::boardChanged(const QString &boardName) { + if (m_boardNameCombo->currentIndex() == m_boardNameCombo->count() - 1) + { + addBoard(); + return; + } CustomQSettings settings; settings.setValue("quackle/settings/board-name", boardName); @@ -383,6 +366,7 @@ void Settings::addBoard() (const uchar *)boardParameterStream.str().data(), boardParameterStream.str().size()); settings.setValue(boardName, QVariant(boardParameterBytes)); + m_boardNameCombo->setCurrentIndex(-1); boardChanged(boardName); } else @@ -416,28 +400,8 @@ void Settings::editBoard() boardChanged(newBoardName); } PixmapCacher::self()->invalidate(); -} - -void Settings::deleteBoard() -{ - int oldIndex = m_boardNameCombo->currentIndex(); - QString boardName = m_boardNameCombo->currentText(); - QString message = "Do you really want to delete the game board \""; - message += boardName; - message += "\"?"; - if (QMessageBox::warning(NULL, QString("Confirm Deletion"), message, - QMessageBox::Yes | QMessageBox::Default, - QMessageBox::No | QMessageBox::Escape) == QMessageBox::Yes) - { - CustomQSettings settings; - settings.beginGroup("quackle/boardparameters"); - settings.remove(boardName); - loadBoardNameCombo(); - if (oldIndex != 0) - oldIndex--; - m_boardNameCombo->setCurrentIndex(oldIndex); - boardChanged(m_boardNameCombo->currentText()); - } + loadBoardNameCombo(); + emit refreshViews(); } void Settings::loadBoardNameCombo() @@ -453,10 +417,14 @@ void Settings::loadBoardNameCombo() QStringList boardNames = settings.childKeys(); boardNames.sort(); m_boardNameCombo->addItems(boardNames); + m_boardNameCombo->addItem("Add new board..."); settings.endGroup(); QString currentItem = settings.value("quackle/settings/board-name", QString("")).toString(); - m_boardNameCombo->setCurrentIndex(m_boardNameCombo->findText(currentItem)); + int currentItemIndex = m_boardNameCombo->findText(currentItem); + if (m_boardNameCombo->count() > 0 && currentItemIndex < 0) + currentItemIndex = 0; + m_boardNameCombo->setCurrentIndex(currentItemIndex); } void Settings::populateListFromFilenames(QStringList& list, const QString &path) diff --git a/quacker/settings.h b/quacker/settings.h index 5a05452..bc0e8fa 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -64,7 +64,6 @@ protected slots: void addBoard(); void editBoard(); - void deleteBoard(); void setQuackleToUseLexiconName(const string &lexiconName); void setQuackleToUseAlphabetName(const string &alphabetName); diff --git a/quackle.sublime-project b/quackle.sublime-project index 167db4b..a219eb1 100644 --- a/quackle.sublime-project +++ b/quackle.sublime-project @@ -7,7 +7,7 @@ "playabilities.raw", "smaller.raw", ".gitattributes", "*.Debug", "*.Release", "*.pfx", "*.cer"], "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib", "lexica", - "strategy", "debug", "release", "makeswelexicon", "lisp"] + "strategy", "debug", "release", "makeswelexicon", "lisp", "DerivedData"] } ] } -- cgit v1.2.3 From dc92d571f4f97f6420fdf1a94cc41c1d2808d71b Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sun, 9 Aug 2015 05:13:19 -0500 Subject: Progress on edit lexicon dialog. * Files can now be loaded from user directory as well as app directory. * Edit lexicon dialog has been added, and pretty much all of the GUI elements framed out. Not actually implemented, yet. * Embiggen the board configuration dialog. * Some bits of code refactoring. --- datamanager.cpp | 42 ++++++------ datamanager.h | 19 ++++-- quacker/boardsetupdialog.cpp | 2 +- quacker/lexicondialog.cpp | 134 ++++++++++++++++++++++++++++++++++++++ quacker/lexicondialog.h | 64 +++++++++++++++++++ quacker/settings.cpp | 149 +++++++++++++++++++++++++++++-------------- quacker/settings.h | 9 ++- quackletest.cpp | 2 +- test/testharness.cpp | 2 +- 9 files changed, 343 insertions(+), 80 deletions(-) create mode 100644 quacker/lexicondialog.cpp create mode 100644 quacker/lexicondialog.h (limited to 'quacker/settings.h') diff --git a/datamanager.cpp b/datamanager.cpp index e188668..eb65afd 100644 --- a/datamanager.cpp +++ b/datamanager.cpp @@ -41,7 +41,8 @@ DataManager::DataManager() : m_evaluator(0), m_parameters(0), m_alphabetParameters(0), m_boardParameters(0), m_lexiconParameters(0), m_strategyParameters(0) { m_self = this; - setDataDirectory("."); + setAppDataDirectory("."); + setUserDataDirectory("."); seedRandomNumbers((int)time(NULL)); m_alphabetParameters = new EnglishAlphabetParameters; @@ -122,6 +123,7 @@ void DataManager::cleanupComputerPlayers() bool DataManager::fileExists(const string &filename) { + // fixme: convert to wchar struct stat buf; int i = stat(filename.c_str(), &buf); if (i == 0) @@ -130,36 +132,38 @@ bool DataManager::fileExists(const string &filename) return false; } -string DataManager::findDataFile(const string &subDirectory, const string &lexicon, string file) +string DataManager::findDataFile(const string &subDirectory, const string &lexicon, const string &file) { - string firstTry = makeDataFilename(subDirectory, lexicon, file); - if (fileExists(firstTry)) - return firstTry; - - string secondTry = makeDataFilename(subDirectory, m_backupLexicon, file); - if (fileExists(secondTry)) - return secondTry; + string fname = makeDataFilename(subDirectory, lexicon, file, true); + if (!fileExists(fname)) + fname = makeDataFilename(subDirectory, lexicon, file, false); + if (!fileExists(fname)) + fname = makeDataFilename(subDirectory, m_backupLexicon, file, false); + if (!fileExists(fname)) + fname = string(); - return string(); + return fname; } -string DataManager::findDataFile(const string &subDirectory, string file) +string DataManager::findDataFile(const string &subDirectory, const string &file) { - string firstTry = makeDataFilename(subDirectory, file); - if (fileExists(firstTry)) - return firstTry; + string fname = makeDataFilename(subDirectory, file, true); + if (!fileExists(fname)) + fname = makeDataFilename(subDirectory, file, false); + if (!fileExists(fname)) + fname = string(); - return string(); + return fname; } -string DataManager::makeDataFilename(const string &subDirectory, const string &lexicon, string file) +string DataManager::makeDataFilename(const string &subDirectory, const string &lexicon, const string &file, bool user) { - return m_dataDirectory + "/" + subDirectory + "/" + lexicon + "/" + file; + return (user ? m_userDataDirectory : m_appDataDirectory) + "/" + subDirectory + "/" + lexicon + "/" + file; } -string DataManager::makeDataFilename(const string &subDirectory, string file) +string DataManager::makeDataFilename(const string &subDirectory, const string &file, bool user) { - return m_dataDirectory + "/" + subDirectory + "/" + file; + return (user ? m_userDataDirectory : m_appDataDirectory) + "/" + subDirectory + "/" + file; } void DataManager::seedRandomNumbers(unsigned int seed) diff --git a/datamanager.h b/datamanager.h index 15e793a..196d525 100644 --- a/datamanager.h +++ b/datamanager.h @@ -99,21 +99,24 @@ public: // Find a file at datadir/subdir/lexicon/file. // If this doesn't exist, tries backupLexicon instead of lexicon. // Returns empty string if the file is not found. - string findDataFile(const string &subDirectory, const string &lexicon, string file); + string findDataFile(const string &subDirectory, const string &lexicon, const string &file); // Find a file at datadir/subdir/file. // Returns empty string if the file is not found. - string findDataFile(const string &subDirectory, string file); + string findDataFile(const string &subDirectory, const string &file); // returns similarly-named file - string makeDataFilename(const string &subDirectory, const string &lexicon, string file); - string makeDataFilename(const string &subDirectory, string file); + string makeDataFilename(const string &subDirectory, const string &lexicon, const string &file, bool user); + string makeDataFilename(const string &subDirectory, const string &file, bool user); void setBackupLexicon(string backupLexicon) { m_backupLexicon = backupLexicon; } string backupLexicon() { return m_backupLexicon; } - void setDataDirectory(string directory) { m_dataDirectory = directory; } - string dataDirectory() { return m_dataDirectory; } + void setAppDataDirectory(string directory) { m_appDataDirectory = directory; } + string appDataDirectory() { return m_appDataDirectory; } + + void setUserDataDirectory(string directory) { m_userDataDirectory = directory; } + string userDataDirectory() { return m_userDataDirectory; } void seedRandomNumbers(unsigned int seed); int randomNumber(); @@ -123,7 +126,9 @@ private: bool fileExists(const string &filename); - string m_dataDirectory; + string m_appDataDirectory; + + string m_userDataDirectory; // lexicon that has all data files string m_backupLexicon; diff --git a/quacker/boardsetupdialog.cpp b/quacker/boardsetupdialog.cpp index 379f79d..2bfd5ff 100644 --- a/quacker/boardsetupdialog.cpp +++ b/quacker/boardsetupdialog.cpp @@ -33,7 +33,7 @@ BoardSetupDialog::BoardSetupDialog(QWidget *parent) : QDialog(parent) { - resize(600,450); + resize(700,550); setSizeGripEnabled(true); // construct the board diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp new file mode 100644 index 0000000..f812eed --- /dev/null +++ b/quacker/lexicondialog.cpp @@ -0,0 +1,134 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "lexicondialog.h" +#include "customqsettings.h" +#include "geometry.h" + + +LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDialog(parent) +{ + m_originalName = originalName; + + resize(450,350); + + // construct the UI elements + m_lexiconName = new QLineEdit(); + m_alphabetCombo = new QComboBox(); + + m_addWordsFromFile = new QPushButton(tr("Add words from &file...")); + m_clearAllWords = new QPushButton(tr("Clear &words and start again")); + + + m_lexiconInformation = new QLabel(""); + m_lexiconInformation->setWordWrap(true); + + m_saveChanges = new QPushButton(tr("&Save Changes")); + m_cancel = new QPushButton(tr("&Cancel")); + m_deleteLexicon = new QPushButton(tr("&Delete Lexicon")); + + QLabel * lexiconNameLabel = new QLabel(tr("&Lexicon name:")); + QLabel * alphabetLabel = new QLabel(tr("&Alphabet:")); + lexiconNameLabel->setBuddy(m_lexiconName); + alphabetLabel->setBuddy(m_alphabetCombo); + + QVBoxLayout * layout = new QVBoxLayout; + Geometry::setupFramedLayout(layout); + QHBoxLayout * lexiconRow = new QHBoxLayout; + Geometry::setupInnerLayout(lexiconRow); + QHBoxLayout * addRemoveWordsRow = new QHBoxLayout; + Geometry::setupInnerLayout(addRemoveWordsRow); + QHBoxLayout * buttonRow = new QHBoxLayout; + Geometry::setupInnerLayout(buttonRow); + QGroupBox * lexiconInformationGroup = new QGroupBox(tr("Lexicon information:")); + QVBoxLayout * lexiconInformationLayout = new QVBoxLayout(lexiconInformationGroup); + + // build the layout + lexiconRow->addWidget(lexiconNameLabel); + lexiconRow->addWidget(m_lexiconName); + lexiconRow->addStretch(); + lexiconRow->addWidget(alphabetLabel); + lexiconRow->addWidget(m_alphabetCombo); + + addRemoveWordsRow->addWidget(m_addWordsFromFile); + addRemoveWordsRow->addWidget(m_clearAllWords); + + lexiconInformationLayout->addWidget(m_lexiconInformation); + + buttonRow->addWidget(m_deleteLexicon); + buttonRow->addStretch(); + buttonRow->addWidget(m_cancel); + buttonRow->addWidget(m_saveChanges); + + layout->addLayout(lexiconRow); + layout->addLayout(addRemoveWordsRow); + layout->addWidget(lexiconInformationGroup); + layout->addStretch(); + layout->addLayout(buttonRow); + + setLayout(layout); + m_saveChanges->setDefault(true); + + // hook up signals and slots + // connect(m_lexiconName, SIGNAL(textEdited(const QString &)), this, SLOT(parametersChanged(const QString &))); + connect(m_addWordsFromFile, SIGNAL(clicked()), this, SLOT(addWordsFromFile())); + connect(m_saveChanges, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_cancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(m_deleteLexicon, SIGNAL(clicked()), this, SLOT(deleteLexicon())); + + setWindowTitle(tr("Configure Lexicon - Quackle")); + updateLexiconInformation(); + + // sync game board with control states and draw board +} + +LexiconDialog::~LexiconDialog() +{ + +} + +void LexiconDialog::deleteLexicon() +{ + +} + +void LexiconDialog::addWordsFromFile() +{ + +} + +void LexiconDialog::accept() +{ + QDialog::accept(); +} + +void LexiconDialog::updateLexiconInformation() +{ + QString text; + text.append(tr("File name:")); + text.append(tr("\n\nFile size:")); + text.append(tr("\n\nWord count:")); + text.append(tr("\n\nLexicon hash:")); + + m_lexiconInformation->setText(text); +} diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h new file mode 100644 index 0000000..cdc0a59 --- /dev/null +++ b/quacker/lexicondialog.h @@ -0,0 +1,64 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef QUACKER_LEXICONDIALOG_H +#define QUACKER_LEXICONDIALOG_H + +#include +#include + +#include +#include + +using namespace std; + +class QComboBox; +class QLabel; +class QLineEdit; +class QPushButton; + +class LexiconDialog : public QDialog +{ +Q_OBJECT + +public: + LexiconDialog(QWidget *parent = 0, const QString &originalName = QString()); + ~LexiconDialog(); + virtual void accept(); + + void updateLexiconInformation(); + +protected slots: + void deleteLexicon(); + void addWordsFromFile(); + +private: + QLineEdit *m_lexiconName; + QComboBox *m_alphabetCombo; + QPushButton *m_addWordsFromFile; + QPushButton *m_clearAllWords; + QLabel *m_lexiconInformation; + + QPushButton *m_saveChanges; + QPushButton *m_cancel; + QPushButton *m_deleteLexicon; + + QString m_originalName; +}; + +#endif diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 15448f5..c516b91 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -45,6 +45,7 @@ #include "boardsetupdialog.h" #include "customqsettings.h" #include "graphicalboard.h" +#include "lexicondialog.h" Settings *Settings::m_self = 0; Settings *Settings::self() @@ -80,17 +81,18 @@ Settings::Settings(QWidget *parent) #endif if (QFile::exists("data")) - m_dataDir = "data"; + m_appDataDir = "data"; else if (QFile::exists("../data")) - m_dataDir = "../data"; + m_appDataDir = "../data"; else if (QFile::exists("Quackle.app/Contents/data")) - m_dataDir = "Quackle.app/Contents/data"; + m_appDataDir = "Quackle.app/Contents/data"; else { if (!directory.cd("data") || !directory.cd("../data")) QMessageBox::critical(0, tr("Error Initializing Data Files - Quacker"), tr("

Could not open data directory. Quackle will be useless. Try running the quacker executable with quackle/quacker/ as the current directory.

")); - m_dataDir = directory.absolutePath(); + m_appDataDir = directory.absolutePath(); } + m_userDataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); } void Settings::createGUI() @@ -98,86 +100,69 @@ void Settings::createGUI() if (m_lexiconNameCombo != 0) return; - QVBoxLayout *vlayout = new QVBoxLayout(this); + QGridLayout *layout = new QGridLayout(this); m_lexiconNameCombo = new QComboBox; connect(m_lexiconNameCombo, SIGNAL(activated(const QString &)), this, SLOT(lexiconChanged(const QString &))); - QStringList lexiconItems; - populateListFromFilenames(lexiconItems, "lexica"); - m_lexiconNameCombo->addItems(lexiconItems); + populateComboFromFilenames(m_lexiconNameCombo, "lexica", "lexicon"); - QHBoxLayout *lexiconLayout = new QHBoxLayout; QLabel *lexiconNameLabel = new QLabel(tr("&Lexicon:")); lexiconNameLabel->setBuddy(m_lexiconNameCombo); m_editLexicon = new QPushButton(tr("Edit...")); m_editLexicon->setMaximumWidth(60); connect(m_editLexicon, SIGNAL(clicked()), this, SLOT(editLexicon())); - lexiconLayout->addWidget(lexiconNameLabel); - lexiconLayout->addWidget(m_lexiconNameCombo); - lexiconLayout->addWidget(m_editLexicon); - m_alphabetNameCombo = new QComboBox; connect(m_alphabetNameCombo, SIGNAL(activated(const QString &)), this, SLOT(alphabetChanged(const QString &))); - QStringList alphabetItems; - populateListFromFilenames(alphabetItems, "alphabets"); - m_alphabetNameCombo->addItems(alphabetItems); + populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ""); - QHBoxLayout *alphabetLayout = new QHBoxLayout; QLabel *alphabetNameLabel = new QLabel(tr("&Alphabet:")); alphabetNameLabel->setBuddy(m_alphabetNameCombo); m_editAlphabet = new QPushButton(tr("Edit...")); m_editAlphabet->setMaximumWidth(60); connect(m_editAlphabet, SIGNAL(clicked()), this, SLOT(editAlphabet())); - alphabetLayout->addWidget(alphabetNameLabel); - alphabetLayout->addWidget(m_alphabetNameCombo); - alphabetLayout->addWidget(m_editAlphabet); - m_themeNameCombo = new QComboBox; connect(m_themeNameCombo, SIGNAL(activated(const QString &)), this, SLOT(themeChanged(const QString &))); - QStringList themeItems; - populateListFromFilenames(themeItems, "themes"); - m_themeNameCombo->addItems(themeItems); + populateComboFromFilenames(m_themeNameCombo, "themes", ""); - QHBoxLayout *themeLayout = new QHBoxLayout; QLabel *themeNameLabel = new QLabel(tr("&Theme:")); themeNameLabel->setBuddy(m_themeNameCombo); m_editTheme = new QPushButton(tr("Edit...")); m_editTheme->setMaximumWidth(60); connect(m_editTheme, SIGNAL(clicked()), this, SLOT(editTheme())); - themeLayout->addWidget(themeNameLabel); - themeLayout->addWidget(m_themeNameCombo); - themeLayout->addWidget(m_editTheme); - m_boardNameCombo = new QComboBox; connect(m_boardNameCombo, SIGNAL(activated(const QString &)), this, SLOT(boardChanged(const QString &))); - QStringList boardItems; - populateListFromFilenames(boardItems, "boards"); - m_boardNameCombo->addItems(boardItems); - m_boardNameCombo->addItem(tr("Add new board...")); + populateComboFromFilenames(m_boardNameCombo, "boards", "board"); - QHBoxLayout *boardLayout = new QHBoxLayout; QLabel *boardNameLabel = new QLabel(tr("&Board:")); boardNameLabel->setBuddy(m_boardNameCombo); m_editBoard = new QPushButton(tr("Edit...")); m_editBoard->setMaximumWidth(60); connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); - boardLayout->addWidget(boardNameLabel); - boardLayout->addWidget(m_boardNameCombo); - boardLayout->addWidget(m_editBoard); - - vlayout->addLayout(lexiconLayout); - vlayout->addLayout(alphabetLayout); - vlayout->addLayout(themeLayout); - vlayout->addLayout(boardLayout); - vlayout->addStretch(); + layout->addWidget(lexiconNameLabel, 0, 0, Qt::AlignRight); + layout->addWidget(m_lexiconNameCombo, 0, 1); + layout->addWidget(m_editLexicon, 0, 2); + layout->addWidget(alphabetNameLabel, 1, 0, Qt::AlignRight); + layout->addWidget(m_alphabetNameCombo, 1, 1); + // layout->addWidget(m_editAlphabet, 1, 2); + layout->addWidget(themeNameLabel, 2, 0, Qt::AlignRight); + layout->addWidget(m_themeNameCombo, 2, 1); + // layout->addWidget(m_editTheme, 2, 2); + layout->addWidget(boardNameLabel, 3, 0, Qt::AlignRight); + layout->addWidget(m_boardNameCombo, 3, 1); + layout->addWidget(m_editBoard, 3, 2); + + layout->setColumnMinimumWidth(3, 0); + layout->setColumnStretch(3, 1); + layout->setRowMinimumHeight(4, 0); + layout->setRowStretch(4, 1); load(); } @@ -201,7 +186,8 @@ void Settings::initialize() CustomQSettings settings; QUACKLE_DATAMANAGER->setBackupLexicon("twl06"); - QUACKLE_DATAMANAGER->setDataDirectory(m_dataDir.toStdString()); + QUACKLE_DATAMANAGER->setAppDataDirectory(m_appDataDir.toStdString()); + QUACKLE_DATAMANAGER->setUserDataDirectory(m_userDataDir.toStdString()); QString lexiconName = settings.value("quackle/settings/lexicon-name", QString("twl06")).toString(); @@ -272,11 +258,13 @@ void Settings::setQuackleToUseAlphabetName(const string &alphabetName) void Settings::setQuackleToUseThemeName(const QString &themeName) { m_themeName = themeName; - QString themeFile = m_dataDir + "/themes/" + themeName + ".ini"; + QString themeFile = m_userDataDir + "/themes/" + themeName + ".ini"; + if (!QFile::exists(themeFile)) + themeFile = m_appDataDir + "/themes/" + themeName + ".ini"; if (!QFile::exists(themeFile)) { m_themeName = "traditional"; - themeFile = m_dataDir + "/themes/traditional.ini"; + themeFile = m_appDataDir + "/themes/traditional.ini"; } PixmapCacher::self()->readTheme(themeFile); } @@ -303,6 +291,11 @@ void Settings::setQuackleToUseBoardName(const QString &boardName) void Settings::lexiconChanged(const QString &lexiconName) { + if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1) + { + editLexicon(); + return; + } string lexiconNameString = QuackleIO::Util::qstringToStdString(lexiconName); setQuackleToUseLexiconName(lexiconNameString); @@ -314,6 +307,11 @@ void Settings::lexiconChanged(const QString &lexiconName) void Settings::alphabetChanged(const QString &alphabetName) { + if (m_alphabetNameCombo->currentIndex() == m_alphabetNameCombo->count() - 1) + { + editAlphabet(); + return; + } string alphabetNameString = QuackleIO::Util::qstringToStdString(alphabetName); setQuackleToUseAlphabetName(alphabetNameString); @@ -325,6 +323,11 @@ void Settings::alphabetChanged(const QString &alphabetName) void Settings::themeChanged(const QString &themeName) { + if (m_themeNameCombo->currentIndex() == m_themeNameCombo->count() - 1) + { + editTheme(); + return; + } setQuackleToUseThemeName(themeName); CustomQSettings settings; @@ -427,18 +430,62 @@ void Settings::loadBoardNameCombo() m_boardNameCombo->setCurrentIndex(currentItemIndex); } -void Settings::populateListFromFilenames(QStringList& list, const QString &path) +void Settings::editLexicon() +{ + QString name = m_lexiconNameCombo->currentText(); + if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1) + name = ""; + LexiconDialog dialog(this, name); + if (dialog.exec()) + { + populateComboFromFilenames(m_lexiconNameCombo, "lexica", "lexicon"); + load(); + } +} + +void Settings::editAlphabet() +{ +#if 0 + QString name = m_alphabetNameCombo->currentText(); + if (m_alphabetNameCombo->currentIndex() == m_alphabetNameCombo->count() - 1) + name = ""; + AlphabetDialog dialog(this); + if (dialog.exec()) + { + populateComboFromFilenames(m_alphabetNameCombo, "alphabets", "alphabet"); + load(); + } +#endif // 0 +} + +void Settings::editTheme() +{ +#if 0 + QString name = m_themeNameCombo->currentText(); + if (m_themeNameCombo->currentIndex() == m_themeNameCombo->count() - 1) + name = ""; + ThemeDialog dialog(this); + if (dialog.exec()) + { + populateThemeFromFilenames(m_themeNameCombo, "themes", "theme"); + load(); + } +#endif // 0 +} + +void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label) { QStringList fileList; - QDir dir(m_dataDir); + QDir dir(m_appDataDir); if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); - dir = QDir(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); + dir = QDir(m_userDataDir); if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); QStringListIterator i(fileList); QString fileName; + QStringList list; int periodPos; while (i.hasNext()) @@ -452,4 +499,8 @@ void Settings::populateListFromFilenames(QStringList& list, const QString &path) } } list.removeDuplicates(); + + combo->addItems(list); + if (label.size() > 0) + combo->addItem(QString(tr("Add new ")).append(path).append("...")); } diff --git a/quacker/settings.h b/quacker/settings.h index bc0e8fa..ee1b59a 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -65,6 +65,10 @@ protected slots: void addBoard(); void editBoard(); + void editLexicon(); + void editAlphabet(); + void editTheme(); + void setQuackleToUseLexiconName(const string &lexiconName); void setQuackleToUseAlphabetName(const string &alphabetName); void setQuackleToUseThemeName(const QString &themeName); @@ -79,7 +83,8 @@ protected: QPushButton *m_editAlphabet; QPushButton *m_editTheme; QPushButton *m_editBoard; - QString m_dataDir; + QString m_appDataDir; + QString m_userDataDir; QString m_themeName; private: @@ -87,7 +92,7 @@ private: void loadBoardNameCombo(); // load up an item list based on a list of filenames - void populateListFromFilenames(QStringList& list, const QString &path); + void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label); static Settings *m_self; }; diff --git a/quackletest.cpp b/quackletest.cpp index 5f3066c..e69c2cb 100644 --- a/quackletest.cpp +++ b/quackletest.cpp @@ -45,7 +45,7 @@ int main() { Quackle::DataManager dataManager; - dataManager.setDataDirectory("data"); + dataManager.setAppDataDirectory("data"); dataManager.lexiconParameters()->loadDawg(Quackle::LexiconParameters::findDictionaryFile("twl06.dawg")); dataManager.lexiconParameters()->loadGaddag(Quackle::LexiconParameters::findDictionaryFile("twl06.gaddag")); dataManager.strategyParameters()->initialize("twl06"); diff --git a/test/testharness.cpp b/test/testharness.cpp index 64847d1..683443f 100644 --- a/test/testharness.cpp +++ b/test/testharness.cpp @@ -191,7 +191,7 @@ void TestHarness::startUp() UVcout << "Starting up."; m_dataManager.setBackupLexicon("twl06"); - m_dataManager.setDataDirectory("../data"); + m_dataManager.setAppDataDirectory("../data"); QString alphabetFile = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(QuackleIO::Util::qstringToStdString(m_alphabet) + ".quackle_alphabet")); QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; -- cgit v1.2.3 From f814628b3aabef20db3320a5e58217f758fa2760 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Tue, 18 Aug 2015 10:30:10 -0500 Subject: Populate alphabet popup in lexicon dialog. Moved Settings::populateComboFromFileNames() to be a static method, then invoked it from the lexicon dialog. --- quacker/lexicondialog.cpp | 13 ++++++++----- quacker/settings.cpp | 4 ++-- quacker/settings.h | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'quacker/settings.h') diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index f812eed..9d1998c 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -23,6 +23,7 @@ #include "lexicondialog.h" #include "customqsettings.h" +#include "settings.h" #include "geometry.h" @@ -60,7 +61,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi Geometry::setupInnerLayout(addRemoveWordsRow); QHBoxLayout * buttonRow = new QHBoxLayout; Geometry::setupInnerLayout(buttonRow); - QGroupBox * lexiconInformationGroup = new QGroupBox(tr("Lexicon information:")); + QGroupBox * lexiconInformationGroup = new QGroupBox(tr("Lexicon information")); QVBoxLayout * lexiconInformationLayout = new QVBoxLayout(lexiconInformationGroup); // build the layout @@ -97,6 +98,8 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi connect(m_deleteLexicon, SIGNAL(clicked()), this, SLOT(deleteLexicon())); setWindowTitle(tr("Configure Lexicon - Quackle")); + + Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ""); updateLexiconInformation(); // sync game board with control states and draw board @@ -125,10 +128,10 @@ void LexiconDialog::accept() void LexiconDialog::updateLexiconInformation() { QString text; - text.append(tr("File name:")); - text.append(tr("\n\nFile size:")); - text.append(tr("\n\nWord count:")); - text.append(tr("\n\nLexicon hash:")); + text.append(tr("File name: ")); + text.append(tr("\n\nFile size: ")); + text.append(tr("\n\nWord count: ")); + text.append(tr("\n\nLexicon hash: ")); m_lexiconInformation->setText(text); } diff --git a/quacker/settings.cpp b/quacker/settings.cpp index c516b91..3c42a39 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -476,10 +476,10 @@ void Settings::editTheme() void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label) { QStringList fileList; - QDir dir(m_appDataDir); + QDir dir(self()->m_appDataDir); if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); - dir = QDir(m_userDataDir); + dir = QDir(self()->m_userDataDir); if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); diff --git a/quacker/settings.h b/quacker/settings.h index ee1b59a..cee0562 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -39,6 +39,9 @@ public: static Settings *self(); + // load up an item list based on a list of filenames + static void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label); + signals: void refreshViews(); @@ -91,9 +94,6 @@ private: // populate the popup based on what's in QSettings void loadBoardNameCombo(); - // load up an item list based on a list of filenames - void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label); - static Settings *m_self; }; -- cgit v1.2.3 From 5350a57f1be22b28914fca14225c73dac5b30b24 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 7 Sep 2015 14:19:46 -0500 Subject: Auto-generate gaddags Need to add a user interface, but gaddags are now auto-generated if they can't be found. Some specific improvements here: * FixedLengthString gained a pop_back member. * Add code to allow v1 gaddags and v0 dawgs to work together. * Change memory allocation of dawgs and gaddags to be dynamic (the old limit didn't accommodate the ridiculously large Polish dictionary in the gaddag) * The Settings class now knows a bit about generating gaddags. This will be important for giving UI feedback. * Fixed several places using filenames which should be using string, not UVString. * Dawg/GaddagFactory should have been using UVString, not QString. My misunderstanding. --- fixedstring.h | 8 +++++ lexiconparameters.cpp | 81 +++++++++++++++++++++++++++++------------ lexiconparameters.h | 6 ++-- quacker/settings.cpp | 86 ++++++++++++++++++++++++++++++++++---------- quacker/settings.h | 9 +++-- quackleio/dawgfactory.cpp | 14 ++++---- quackleio/dawgfactory.h | 6 ++-- quackleio/flexiblealphabet.h | 2 -- quackleio/gaddagfactory.cpp | 71 ++++++++++++++++++++---------------- quackleio/gaddagfactory.h | 7 ++-- 10 files changed, 197 insertions(+), 93 deletions(-) (limited to 'quacker/settings.h') diff --git a/fixedstring.h b/fixedstring.h index e8db0bf..a31ecd6 100644 --- a/fixedstring.h +++ b/fixedstring.h @@ -54,6 +54,7 @@ class FixedLengthString size_type size() const { return length(); } void clear() { m_end = m_data; } void push_back(char c); + void pop_back(); const char* constData() const { return m_data; } int compare(const FixedLengthString& s) const; @@ -221,6 +222,13 @@ FixedLengthString::push_back(char c) *this += c; } +inline void +FixedLengthString::pop_back() +{ + assert(size() > 0); + m_end--; +} + inline int FixedLengthString::compare(const FixedLengthString& s) const { diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index f6e646b..6761fc1 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -32,7 +32,6 @@ class Quackle::V0LexiconInterpreter : public LexiconInterpreter virtual void loadDawg(ifstream &file, LexiconParameters &lexparams) { int i = 0; - file.unget(); // version 0 doesn't have a version byte...it's just the node byte which is always set to 0 while (!file.eof()) { file.read((char*)(lexparams.m_dawg) + i, 7); @@ -43,7 +42,6 @@ class Quackle::V0LexiconInterpreter : public LexiconInterpreter virtual void loadGaddag(ifstream &file, LexiconParameters &lexparams) { int i = 0; - file.unget(); while (!file.eof()) { file.read((char*)(lexparams.m_gaddag) + i, 4); @@ -74,6 +72,7 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter { int i = 0; unsigned char bytes[3]; + file.get(); // skip past version byte file.read(lexparams.m_hash, sizeof(lexparams.m_hash)); file.read((char*)bytes, 3); lexparams.m_wordcount = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; @@ -87,14 +86,22 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter virtual void loadGaddag(ifstream &file, LexiconParameters &lexparams) { char hash[16]; + file.get(); // skip past version byte file.read(hash, sizeof(hash)); if (memcmp(hash, lexparams.m_hash, sizeof(hash))) { - lexparams.unloadGaddag(); // don't use a mismatched gaddag - return; + // If we're using a v0 DAWG, then ignore the hash + for (size_t i = 0; i < sizeof(lexparams.m_hash); i++) + { + if (lexparams.m_hash[0] != 0) + { + lexparams.unloadGaddag(); // don't use a mismatched gaddag + return; + } + } } - int i = 0; + size_t i = 0; while (!file.eof()) { file.read((char*)(lexparams.m_gaddag) + i, 4); @@ -160,20 +167,16 @@ void LexiconParameters::loadDawg(const string &filename) } char versionByte = file.get(); - switch(versionByte) + m_interpreter = createInterpreter(versionByte); + if (m_interpreter == NULL) { - case 0: - m_interpreter = new V0LexiconInterpreter(); - break; - case 1: - m_interpreter = new V1LexiconInterpreter(); - break; - default: - UVcout << "couldn't open dawg " << filename.c_str() << endl; - return; + UVcout << "couldn't open file " << filename.c_str() << endl; + return; } - m_dawg = new unsigned char[7000000]; + file.seekg(0, ios_base::end); + m_dawg = new unsigned char[file.tellg()]; + file.seekg(0, ios_base::beg); m_interpreter->loadDawg(file, *this); } @@ -191,19 +194,53 @@ void LexiconParameters::loadGaddag(const string &filename) } char versionByte = file.get(); - if (versionByte != m_interpreter->versionNumber()) + if (versionByte < m_interpreter->versionNumber()) return; - m_gaddag = new unsigned char[40000000]; + file.seekg(0, ios_base::end); + m_gaddag = new unsigned char[file.tellg()]; + file.seekg(0, ios_base::beg); - m_interpreter->loadGaddag(file, *this); + // must create a local interpreter because dawg/gaddag versions might not match + LexiconInterpreter* interpreter = createInterpreter(versionByte); + if (interpreter != NULL) + { + interpreter->loadGaddag(file, *this); + delete interpreter; + } + else + unloadGaddag(); } string LexiconParameters::findDictionaryFile(const string &lexicon) { - return DataManager::self()->findDataFile("lexica", lexicon); + return QUACKLE_DATAMANAGER->findDataFile("lexica", lexicon); +} + +UVString LexiconParameters::hashString(bool shortened) const +{ + const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + string hashStr; + for (size_t i = 0; i < sizeof(m_hash); i++) + { + hashStr.push_back(hex[(m_hash[i] & 0xF0) >> 4]); + hashStr.push_back(hex[m_hash[i] & 0x0F]); + if (shortened && i == 5) + break; + if (i % 2 == 1) + hashStr.push_back('-'); + } + return hashStr; } -QString hashString() const +LexiconInterpreter* LexiconParameters::createInterpreter(char version) const { - return QString(QByteArray(m_hash, sizeof(m_hash)).toHex()); + switch(version) + { + case 0: + return new V0LexiconInterpreter(); + case 1: + return new V1LexiconInterpreter(); + default: + return NULL; + } } diff --git a/lexiconparameters.h b/lexiconparameters.h index b5bc564..3890d8d 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -19,8 +19,6 @@ #ifndef QUACKLE_LEXICONPARAMETERS_H #define QUACKLE_LEXICONPARAMETERS_H -#include -#include "alphabetparameters.h" #include "gaddag.h" namespace Quackle @@ -77,7 +75,7 @@ public: } const GaddagNode *gaddagRoot() const { return (GaddagNode *) &m_gaddag[0]; }; - QString hashString() const; + UVString hashString(bool shortened) const; protected: unsigned char *m_dawg; @@ -86,6 +84,8 @@ protected: LexiconInterpreter *m_interpreter; char m_hash[16]; int m_wordcount; + + LexiconInterpreter* createInterpreter(char version) const; }; } diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 362e916..1febdb5 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -93,6 +93,8 @@ Settings::Settings(QWidget *parent) m_appDataDir = directory.absolutePath(); } m_userDataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + QDir qdir(m_userDataDir); + qdir.mkpath("lexica"); } void Settings::createGUI() @@ -195,49 +197,97 @@ void Settings::initialize() if (lexiconName == "cswfeb07") lexiconName = "cswapr07"; - setQuackleToUseLexiconName(QuackleIO::Util::qstringToStdString(lexiconName)); - setQuackleToUseAlphabetName(QuackleIO::Util::qstringToStdString(settings.value("quackle/settings/alphabet-name", QString("english")).toString())); + setQuackleToUseLexiconName(lexiconName); + setQuackleToUseAlphabetName(settings.value("quackle/settings/alphabet-name", QString("english")).toString()); setQuackleToUseThemeName(settings.value("quackle/settings/theme-name", QString("traditional")).toString()); setQuackleToUseBoardName(settings.value("quackle/settings/board-name", QString("")).toString()); } -void Settings::setQuackleToUseLexiconName(const string &lexiconName) +void Settings::buildGaddag(const string &filename) { - if (QUACKLE_LEXICON_PARAMETERS->lexiconName() != lexiconName) + GaddagFactory factory((UVString())); + Quackle::LetterString word; + + pushIndex(factory, word, 1); + factory.generate(); + factory.writeIndex(filename); +} + +void Settings::pushIndex(GaddagFactory &factory, Quackle::LetterString &word, int index) +{ + unsigned int p; + Quackle::Letter letter; + bool t; + bool lastchild; + bool british; + int playability; + + do + { + QUACKLE_LEXICON_PARAMETERS->dawgAt(index, p, letter, t, lastchild, british, playability); + word.push_back(letter); + if (t) + factory.pushWord(word); + if (p) + pushIndex(factory, word, p); + index++; + word.pop_back(); + } while (!lastchild); +} + + +void Settings::setQuackleToUseLexiconName(const QString &lexiconName) +{ + string lexiconNameStr = lexiconName.toStdString(); + if (QUACKLE_LEXICON_PARAMETERS->lexiconName() != lexiconNameStr) { - QUACKLE_LEXICON_PARAMETERS->setLexiconName(lexiconName); + QUACKLE_LEXICON_PARAMETERS->setLexiconName(lexiconNameStr); - string dawgFile = Quackle::LexiconParameters::findDictionaryFile(lexiconName + ".dawg"); + string dawgFile = Quackle::LexiconParameters::findDictionaryFile(lexiconNameStr + ".dawg"); if (dawgFile.empty()) { - UVcout << "Dawg for lexicon '" << lexiconName << "' does not exist." << endl; + UVcout << "Dawg for lexicon '" << lexiconNameStr << "' does not exist." << endl; QUACKLE_LEXICON_PARAMETERS->unloadDawg(); } else QUACKLE_LEXICON_PARAMETERS->loadDawg(dawgFile); - string gaddagFile = Quackle::LexiconParameters::findDictionaryFile(lexiconName + ".gaddag"); + if (!QUACKLE_LEXICON_PARAMETERS->hasDawg()) + { + QUACKLE_LEXICON_PARAMETERS->unloadGaddag(); + return; + } + + string gaddagFile = Quackle::LexiconParameters::findDictionaryFile(lexiconNameStr + ".gaddag"); if (gaddagFile.empty()) { - UVcout << "Gaddag for lexicon '" << lexiconName << "' does not exist." << endl; + UVcout << "Gaddag for lexicon '" << lexiconNameStr << "' does not exist." << endl; QUACKLE_LEXICON_PARAMETERS->unloadGaddag(); } else QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile); - QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconName); + if (!QUACKLE_LEXICON_PARAMETERS->hasGaddag()) + { + gaddagFile = QUACKLE_DATAMANAGER->makeDataFilename("lexica", lexiconNameStr + ".gaddag", true); + buildGaddag(gaddagFile); + QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile); + } + + QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconNameStr); } } -void Settings::setQuackleToUseAlphabetName(const string &alphabetName) +void Settings::setQuackleToUseAlphabetName(const QString &alphabetName) { - if (QUACKLE_ALPHABET_PARAMETERS->alphabetName() != alphabetName) + string alphabetNameStr = alphabetName.toStdString(); + if (QUACKLE_ALPHABET_PARAMETERS->alphabetName() != alphabetNameStr) { - QString alphabetFile = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(alphabetName + ".quackle_alphabet")); + QString alphabetFileStr = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(alphabetNameStr + ".quackle_alphabet")); QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; - flexure->setAlphabetName(alphabetName); - if (flexure->load(alphabetFile)) + flexure->setAlphabetName(alphabetNameStr); + if (flexure->load(alphabetFileStr)) { if (flexure->length() != QUACKLE_ALPHABET_PARAMETERS->length() && QUACKLE_ALPHABET_PARAMETERS->alphabetName() != "default") { @@ -295,8 +345,7 @@ void Settings::lexiconChanged(const QString &lexiconName) editLexicon(); return; } - string lexiconNameString = QuackleIO::Util::qstringToStdString(lexiconName); - setQuackleToUseLexiconName(lexiconNameString); + setQuackleToUseLexiconName(lexiconName); CustomQSettings settings; settings.setValue("quackle/settings/lexicon-name", lexiconName); @@ -311,8 +360,7 @@ void Settings::alphabetChanged(const QString &alphabetName) editAlphabet(); return; } - string alphabetNameString = QuackleIO::Util::qstringToStdString(alphabetName); - setQuackleToUseAlphabetName(alphabetNameString); + setQuackleToUseAlphabetName(alphabetName); CustomQSettings settings; settings.setValue("quackle/settings/alphabet-name", alphabetName); diff --git a/quacker/settings.h b/quacker/settings.h index cee0562..fab2f3f 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -24,6 +24,8 @@ #include #include +#include "quackleio/gaddagfactory.h" + class QComboBox; class QCheckBox; class QPushButton; @@ -72,8 +74,8 @@ protected slots: void editAlphabet(); void editTheme(); - void setQuackleToUseLexiconName(const string &lexiconName); - void setQuackleToUseAlphabetName(const string &alphabetName); + void setQuackleToUseLexiconName(const QString &lexiconName); + void setQuackleToUseAlphabetName(const QString &alphabetName); void setQuackleToUseThemeName(const QString &themeName); void setQuackleToUseBoardName(const QString &lexiconName); @@ -94,6 +96,9 @@ private: // populate the popup based on what's in QSettings void loadBoardNameCombo(); + void buildGaddag(const string &filename); + void pushIndex(GaddagFactory &factory, Quackle::LetterString &word, int index); + static Settings *m_self; }; diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 74b4346..3a971a3 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -25,10 +25,10 @@ #include "util.h" -DawgFactory::DawgFactory(const QString& alphabetFile) +DawgFactory::DawgFactory(const UVString& alphabetFile) { QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; - flexure->load(alphabetFile); + flexure->load(QuackleIO::Util::uvStringToQString(alphabetFile)); m_alphas = flexure; m_root.insmallerdict = false; @@ -45,12 +45,10 @@ DawgFactory::~DawgFactory() delete m_alphas; } -bool DawgFactory::pushWord(const QString& word, bool inSmaller, int playability) +bool DawgFactory::pushWord(const UVString& word, bool inSmaller, int playability) { - UVString originalString = QuackleIO::Util::qstringToString(word); - UVString leftover; - Quackle::LetterString encodedWord = m_alphas->encode(originalString, &leftover); + Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover); if (leftover.empty()) { if (m_root.pushWord(encodedWord, inSmaller, playability)) @@ -129,9 +127,9 @@ void DawgFactory::generate() m_root.print(m_nodelist); } -void DawgFactory::writeIndex(const QString& filename) +void DawgFactory::writeIndex(const UVString& filename) { - ofstream out(QuackleIO::Util::qstringToStdString(filename).c_str(), ios::out | ios::binary); + ofstream out(filename.c_str(), ios::out | ios::binary); unsigned char bytes[7]; bytes[0] = (m_encodableWords & 0x00FF0000) >> 16; diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 23bb4f5..051e632 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -26,7 +26,7 @@ class DawgFactory { public: - DawgFactory(const QString& alphabetFile); + DawgFactory(const UVString& alphabetFile); ~DawgFactory(); int wordCount() const { return m_root.wordCount(); }; @@ -35,10 +35,10 @@ public: int unencodableWords() const { return m_unencodableWords; }; int duplicateWords() const { return m_duplicateWords; }; - bool pushWord(const QString& word, bool inSmaller, int playability); + bool pushWord(const UVString& word, bool inSmaller, int playability); void hashWord(const Quackle::LetterString &word); void generate(); - void writeIndex(const QString& fname); + void writeIndex(const UVString& filename); const char* hashBytes() { return m_hash.charptr; }; diff --git a/quackleio/flexiblealphabet.h b/quackleio/flexiblealphabet.h index 89bd1f4..d5db68a 100644 --- a/quackleio/flexiblealphabet.h +++ b/quackleio/flexiblealphabet.h @@ -21,8 +21,6 @@ #include "alphabetparameters.h" -class QString; - namespace QuackleIO { diff --git a/quackleio/gaddagfactory.cpp b/quackleio/gaddagfactory.cpp index 7f666cb..53ccf04 100644 --- a/quackleio/gaddagfactory.cpp +++ b/quackleio/gaddagfactory.cpp @@ -24,11 +24,15 @@ #include "gaddagfactory.h" #include "util.h" -GaddagFactory::GaddagFactory(const QString& alphabetFile) +GaddagFactory::GaddagFactory(const UVString &alphabetFile) + : m_encodableWords(0), m_unencodableWords(0), m_alphas(NULL) { - QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; - flexure->load(alphabetFile); - m_alphas = flexure; + if (!alphabetFile.empty()) + { + QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; + flexure->load(QuackleIO::Util::uvStringToQString(alphabetFile)); + m_alphas = flexure; + } // So the separator is sorted to last. m_root.t = false; @@ -44,35 +48,13 @@ GaddagFactory::~GaddagFactory() delete m_alphas; } -bool GaddagFactory::pushWord(const QString& word) +bool GaddagFactory::pushWord(const UVString &word) { - UVString originalString = QuackleIO::Util::qstringToString(word); - UVString leftover; - Quackle::LetterString encodedWord = m_alphas->encode(originalString, &leftover); + Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover); if (leftover.empty()) { - ++m_encodableWords; - hashWord(encodedWord); - // FIXME: This hash will fail if duplicate words are passed in. - // But testing for duplicate words isn't so easy without keeping - // an entirely separate list. - - for (unsigned i = 1; i <= encodedWord.length(); i++) - { - Quackle::LetterString newword; - - for (int j = i - 1; j >= 0; j--) - newword.push_back(encodedWord[j]); - - if (i < encodedWord.length()) - { - newword.push_back(internalSeparatorRepresentation); // "^" - for (unsigned j = i; j < encodedWord.length(); j++) - newword.push_back(encodedWord[j]); - } - m_gaddagizedWords.push_back(newword); - } + pushWord(encodedWord); return true; } @@ -80,6 +62,32 @@ bool GaddagFactory::pushWord(const QString& word) return false; } +bool GaddagFactory::pushWord(const Quackle::LetterString &word) +{ + ++m_encodableWords; + hashWord(word); + // FIXME: This hash will fail if duplicate words are passed in. + // But testing for duplicate words isn't so easy without keeping + // an entirely separate list. + + for (unsigned i = 1; i <= word.length(); i++) + { + Quackle::LetterString newword; + + for (int j = i - 1; j >= 0; j--) + newword.push_back(word[j]); + + if (i < word.length()) + { + newword.push_back(internalSeparatorRepresentation); // "^" + for (unsigned j = i; j < word.length(); j++) + newword.push_back(word[j]); + } + m_gaddagizedWords.push_back(newword); + } + return true; +} + void GaddagFactory::hashWord(const Quackle::LetterString &word) { QCryptographicHash wordhash(QCryptographicHash::Md5); @@ -93,6 +101,7 @@ void GaddagFactory::hashWord(const Quackle::LetterString &word) void GaddagFactory::generate() { + sort(m_gaddagizedWords.begin(), m_gaddagizedWords.end()); Quackle::WordList::const_iterator wordsEnd = m_gaddagizedWords.end(); for (Quackle::WordList::const_iterator wordsIt = m_gaddagizedWords.begin(); wordsIt != wordsEnd; ++wordsIt) m_root.pushWord(*wordsIt); @@ -100,13 +109,13 @@ void GaddagFactory::generate() // m_root.pushWord(words); } -void GaddagFactory::writeIndex(const QString &fname) +void GaddagFactory::writeIndex(const string &fname) { m_nodelist.push_back(&m_root); m_root.print(m_nodelist); - ofstream out(QuackleIO::Util::qstringToStdString(fname).c_str(), ios::out | ios::binary); + ofstream out(fname.c_str(), ios::out | ios::binary); out.put(1); // GADDAG format version 1 out.write(m_hash.charptr, sizeof(m_hash.charptr)); diff --git a/quackleio/gaddagfactory.h b/quackleio/gaddagfactory.h index 03cb546..415baff 100644 --- a/quackleio/gaddagfactory.h +++ b/quackleio/gaddagfactory.h @@ -27,7 +27,7 @@ public: static const Quackle::Letter internalSeparatorRepresentation = QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE; - GaddagFactory(const QString& alphabetFile); + GaddagFactory(const UVString &alphabetFile); ~GaddagFactory(); int wordCount() const { return m_gaddagizedWords.size(); }; @@ -35,11 +35,12 @@ public: int encodableWords() const { return m_encodableWords; }; int unencodableWords() const { return m_unencodableWords; }; - bool pushWord(const QString& word); + bool pushWord(const UVString &word); + bool pushWord(const Quackle::LetterString &word); void hashWord(const Quackle::LetterString &word); void sortWords() { sort(m_gaddagizedWords.begin(), m_gaddagizedWords.end()); }; void generate(); - void writeIndex(const QString& fname); + void writeIndex(const string &fname); const char* hashBytes() { return m_hash.charptr; }; -- cgit v1.2.3 From 69e3dcefb882c743b136df8e5c81b4182b135f6b Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sat, 10 Oct 2015 19:09:36 -0500 Subject: Progress on the lexicon dialog. Now prints better stats. Now loads the dictionary you're editing. Now disables the Delete button at appropriate times. --- datamanager.cpp | 6 ++++++ datamanager.h | 3 +++ lexiconparameters.cpp | 5 +++++ lexiconparameters.h | 1 + quacker/lexicondialog.cpp | 36 +++++++++++++++++++++++++++--------- quacker/lexicondialog.h | 4 ++-- quacker/settings.cpp | 30 ++++++++++++++++++++++++++---- quacker/settings.h | 2 ++ quackleio/dawgfactory.cpp | 37 +++++++++++++++++++++++++++++++++++-- quackleio/dawgfactory.h | 8 ++++++-- 10 files changed, 113 insertions(+), 19 deletions(-) (limited to 'quacker/settings.h') diff --git a/datamanager.cpp b/datamanager.cpp index eb65afd..916610a 100644 --- a/datamanager.cpp +++ b/datamanager.cpp @@ -156,6 +156,12 @@ string DataManager::findDataFile(const string &subDirectory, const string &file) return fname; } +bool DataManager::hasUserDataFile(const string &subDirectory, const string &file) +{ + string fname = makeDataFilename(subDirectory, file, true); + return fileExists(fname); +} + string DataManager::makeDataFilename(const string &subDirectory, const string &lexicon, const string &file, bool user) { return (user ? m_userDataDirectory : m_appDataDirectory) + "/" + subDirectory + "/" + lexicon + "/" + file; diff --git a/datamanager.h b/datamanager.h index 196d525..75bce54 100644 --- a/datamanager.h +++ b/datamanager.h @@ -105,6 +105,9 @@ public: // Returns empty string if the file is not found. string findDataFile(const string &subDirectory, const string &file); + // Returns true if the data file is in user-land. + bool hasUserDataFile(const string &subDirectory, const string &file); + // returns similarly-named file string makeDataFilename(const string &subDirectory, const string &lexicon, const string &file, bool user); string makeDataFilename(const string &subDirectory, const string &file, bool user); diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index 9da3b70..bc10773 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -224,6 +224,11 @@ string LexiconParameters::findDictionaryFile(const string &lexicon) return QUACKLE_DATAMANAGER->findDataFile("lexica", lexicon); } +bool LexiconParameters::hasUserDictionaryFile(const string &lexicon) +{ + return QUACKLE_DATAMANAGER->hasUserDataFile("lexica", lexicon); +} + UVString LexiconParameters::hashString(bool shortened) const { const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; diff --git a/lexiconparameters.h b/lexiconparameters.h index 9f34be6..4eda4f3 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -68,6 +68,7 @@ public: // finds a file in the lexica data directory static string findDictionaryFile(const string &lexicon); + static bool hasUserDictionaryFile(const string &lexicon); // a convenience field; this is unused by libquackle string lexiconName() const { return m_lexiconName; }; diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index a7566c6..e11ae41 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -103,9 +103,21 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ""); alphabetChanged(m_alphabetCombo->currentText()); - updateLexiconInformation(); - // sync game board with control states and draw board + string dawgFileName = originalName.toStdString() + ".dawg"; + QString dawgFullFileName; + if (!originalName.isEmpty()) + dawgFullFileName = QString::fromStdString(Quackle::LexiconParameters::findDictionaryFile(dawgFileName)); + + if (!dawgFullFileName.isEmpty()) + { + m_deleteLexicon->setEnabled(Quackle::LexiconParameters::hasUserDictionaryFile(dawgFileName)); + addWordsFromDawgFile(dawgFullFileName); + } + else + m_deleteLexicon->setEnabled(false); + + updateLexiconInformation(); } LexiconDialog::~LexiconDialog() @@ -134,9 +146,9 @@ void LexiconDialog::addWordsFromFile() for (QList::const_iterator it = files.begin(); it != files.end(); it++) { if (it->endsWith(".dawg", Qt::CaseInsensitive)) - addWordsFromDawgFile(*it, m_alphabetCombo->currentText()); + addWordsFromDawgFile(*it); else - addWordsFromTextFile(*it, m_alphabetCombo->currentText()); + addWordsFromTextFile(*it); } updateLexiconInformation(); } @@ -149,7 +161,7 @@ void LexiconDialog::alphabetChanged(const QString &alphabet) m_alphabetFileName = QString::fromStdString(AlphabetParameters::findAlphabetFile(QuackleIO::Util::qstringToStdString(alphabet))); } -void LexiconDialog::addWordsFromDawgFile(const QString &dawgfile, const QString &alphabetfile) +void LexiconDialog::addWordsFromDawgFile(const QString &dawgfile) { if (!m_wordFactory) m_wordFactory = new DawgFactory(m_alphabetFileName); @@ -185,7 +197,7 @@ void LexiconDialog::addWordsFromDawgRecursive(const LexiconParameters &lexParams } while (!lastchild); } -void LexiconDialog::addWordsFromTextFile(const QString &textFile, const QString &alphabetfile) +void LexiconDialog::addWordsFromTextFile(const QString &textFile) { if (!m_wordFactory) m_wordFactory = new DawgFactory(m_alphabetFileName); @@ -224,14 +236,20 @@ void LexiconDialog::accept() void LexiconDialog::updateLexiconInformation() { int wordCount = m_wordFactory ? m_wordFactory->wordCount() : 0; - QByteArray hash = m_wordFactory ? QByteArray(m_wordFactory->hashBytes(), 16).toHex() : ""; + QByteArray hash = (m_wordFactory && wordCount) ? QByteArray(m_wordFactory->hashBytes(), 16).toHex() : ""; QString text; + QString lengthText; + if (m_wordFactory) + lengthText = QString::fromStdString(m_wordFactory->letterCountString()); + text.append(tr("File name: ")); text.append(tr("\n\nFile size: ")); text.append(tr("\n\nWord count: ")); text.append(QString("%L1").arg(wordCount)); - text.append(tr("\n\nLexicon hash: ")); - text.append(hash); + text.append("\n"); + text.append(lengthText); + text.append(tr("\nLexicon hash: ")); + text.append(hash.left(8)); m_lexiconInformation->setText(text); } diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h index 4df6138..39cd546 100644 --- a/quacker/lexicondialog.h +++ b/quacker/lexicondialog.h @@ -52,9 +52,9 @@ protected slots: void alphabetChanged(const QString &); protected: - void addWordsFromDawgFile(const QString &dawgfile, const QString &alphabetfile); + void addWordsFromDawgFile(const QString &dawgfile); void addWordsFromDawgRecursive(const LexiconParameters &lexParams, Quackle::LetterString &word, int index); - void addWordsFromTextFile(const QString &textFile, const QString &alphabetfile); + void addWordsFromTextFile(const QString &textFile); private: QLineEdit *m_lexiconName; diff --git a/quacker/settings.cpp b/quacker/settings.cpp index ce8583f..3319955 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -172,9 +172,11 @@ void Settings::createGUI() void Settings::load() { m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()))); + m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex(); m_alphabetNameCombo->setCurrentIndex(m_alphabetNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName()))); m_themeNameCombo->setCurrentIndex(m_themeNameCombo->findText(m_themeName)); m_boardNameCombo->setCurrentIndex(m_boardNameCombo->findText(QuackleIO::Util::uvStringToQString(QUACKLE_BOARD_PARAMETERS->name()))); + m_lastGoodBoardValue = m_boardNameCombo->currentIndex(); } void Settings::preInitialize() @@ -343,9 +345,13 @@ void Settings::lexiconChanged(const QString &lexiconName) if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1) { editLexicon(); + if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1 && + m_lexiconNameCombo->currentIndex() != 0) + m_lexiconNameCombo->setCurrentIndex(m_lastGoodLexiconValue); return; } setQuackleToUseLexiconName(lexiconName); + m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex(); CustomQSettings settings; settings.setValue("quackle/settings/lexicon-name", lexiconName); @@ -388,6 +394,9 @@ void Settings::boardChanged(const QString &boardName) if (m_boardNameCombo->currentIndex() == m_boardNameCombo->count() - 1) { addBoard(); + if (m_boardNameCombo->currentIndex() == m_boardNameCombo->count() - 1 && + m_boardNameCombo->currentIndex() != 0) + m_boardNameCombo->setCurrentIndex(m_lastGoodBoardValue); return; } CustomQSettings settings; @@ -530,14 +539,14 @@ void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); - QStringListIterator i(fileList); + QStringList::iterator i; QString fileName; QStringList list; int periodPos; - while (i.hasNext()) + for (i = fileList.begin(); i != fileList.end(); ++i) { - fileName = i.next(); + fileName = *i; periodPos = fileName.indexOf('.'); if (periodPos) { @@ -545,7 +554,20 @@ void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, list << fileName; } } - list.removeDuplicates(); + + for (i = fileList.begin(); i != fileList.end(); ++i) + { + QStringList::iterator j = i; + for (++j; j != fileList.end(); ++j) + { + if (*i == *j) + { + *i = "* " + *i; + list.erase(j); + break; + } + } + } combo->addItems(list); if (label.size() > 0) diff --git a/quacker/settings.h b/quacker/settings.h index fab2f3f..7c5738e 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -100,6 +100,8 @@ private: void pushIndex(GaddagFactory &factory, Quackle::LetterString &word, int index); static Settings *m_self; + int m_lastGoodLexiconValue; + int m_lastGoodBoardValue; }; #endif diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index e7ada85..362dfdc 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -17,6 +17,8 @@ */ +#include +#include #include #include #include @@ -184,6 +186,34 @@ void DawgFactory::writeIndex(const UVString& filename) } } +int DawgFactory::wordCount() const +{ + m_countsByLength.resize(0); + return m_root.wordCount(0, m_countsByLength); +} + +string DawgFactory::letterCountString() const +{ + ostringstream str; + if (m_countsByLength.size() < 16) + m_countsByLength.resize(16, 0); + str << "2s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[2]; + str << "\t6s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[6]; + str << "\t10s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[10]; + str << "\t14s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[14]; + str << "\n3s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[3]; + str << "\t7s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[7]; + str << "\t11s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[11]; + str << "\t15s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[15]; + str << "\n4s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[4]; + str << "\t8s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[8]; + str << "\t12s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[12]; + str << "\n5s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[5]; + str << "\t9s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[9]; + str << "\t13s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[13]; + str << "\n"; + return str.str(); +} void DawgFactory::Node::print(vector< Node* >& nodelist) @@ -287,11 +317,14 @@ bool DawgFactory::Node::equals(const Node &n) const return true; } -int DawgFactory::Node::wordCount() const +int DawgFactory::Node::wordCount(unsigned int depth, vector &countsByLength) const { int wordCount = ((playability == 0) ? 0 : 1); + if (countsByLength.size() < depth + 1) + countsByLength.resize(depth + 1, 0); + countsByLength[depth] += wordCount; for (size_t i = 0; i < children.size(); i++) - wordCount += children[i].wordCount(); + wordCount += children[i].wordCount(depth + 1, countsByLength); return wordCount; } diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 1a1aa7d..8dd6e03 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -19,6 +19,7 @@ #ifndef QUACKLE_DAWGFACTORY_H #define QUACKLE_DAWGFACTORY_H +#include #include #include "flexiblealphabet.h" @@ -29,7 +30,8 @@ public: DawgFactory(const QString &alphabetFile); ~DawgFactory(); - int wordCount() const { return m_root.wordCount(); }; + int wordCount() const; + string letterCountString() const; int nodeCount() const { return m_nodelist.size(); }; int encodableWords() const { return m_encodableWords; }; int unencodableWords() const { return m_unencodableWords; }; @@ -50,7 +52,7 @@ private: void print(vector< Node* >& m_nodelist); int letterSum() const; - int wordCount() const; + int wordCount(unsigned int depth, vector &countsByLength) const; bool equals(const Node &n) const; Quackle::Letter c; @@ -65,6 +67,7 @@ private: mutable bool sumexplored; mutable int sum; + mutable vector counts; bool deleted; Node* cloneof; @@ -75,6 +78,7 @@ private: int m_unencodableWords; int m_duplicateWords; vector< Node* > m_nodelist; + mutable vector m_countsByLength; Quackle::AlphabetParameters *m_alphas; Node m_root; union { -- cgit v1.2.3 From ef4273ba47a2da9cea0aed59235e2d0a86bb8d7e Mon Sep 17 00:00:00 2001 From: John Fultz Date: Tue, 13 Oct 2015 12:00:13 -0500 Subject: Saving custom dictionaries now really works. * Fix a number of remaining bugs in the lexicon dialog. * Fix an error reading the v1 DAWG. * Improve the word counting mechanism. * Make sure the lexicn dialog properly selects and loads its dictionary after it's done. * Implement deleting of user dictionaries. * Clean up dictionary info text in lexicon dialog. * Disable gaddag generation...still have to fix that up to happen at sensible times and with user notification. --- lexiconparameters.cpp | 7 ++--- lexiconparameters.h | 2 +- quacker/lexicondialog.cpp | 72 +++++++++++++++++++++++++++++------------------ quacker/lexicondialog.h | 7 ++++- quacker/settings.cpp | 68 +++++++++++++++++++++++++++++--------------- quacker/settings.h | 2 +- quackleio/dawgfactory.cpp | 60 +++++++++++++-------------------------- quackleio/dawgfactory.h | 17 +++++------ 8 files changed, 128 insertions(+), 107 deletions(-) (limited to 'quacker/settings.h') diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index bc10773..74de78f 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -82,8 +82,6 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter file >> lexparams.m_utf8Alphabet[i]; file.get(); // separator space } - file.get(); // whitespace separator - lexparams.m_wordcount = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; while (!file.eof()) { file.read((char*)(lexparams.m_dawg) + i, 7); @@ -123,18 +121,18 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter p = (dawg[index] << 16) + (dawg[index + 1] << 8) + (dawg[index + 2]); letter = dawg[index + 3]; - t = (p != 0); lastchild = ((letter & 64) != 0); british = !(letter & 128); letter = (letter & 63) + QUACKLE_FIRST_LETTER; playability = (dawg[index + 4] << 16) + (dawg[index + 5] << 8) + (dawg[index + 6]); + t = (playability != 0); } virtual int versionNumber() const { return 1; } }; LexiconParameters::LexiconParameters() - : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL), m_wordcount(0) + : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL), m_wordCount(0) { memset(m_hash, 0, sizeof(m_hash)); } @@ -155,6 +153,7 @@ void LexiconParameters::unloadDawg() delete[] m_dawg; m_dawg = NULL; delete m_interpreter; + m_interpreter = NULL; } void LexiconParameters::unloadGaddag() diff --git a/lexiconparameters.h b/lexiconparameters.h index 4eda4f3..f29a589 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -88,7 +88,7 @@ protected: string m_lexiconName; LexiconInterpreter *m_interpreter; char m_hash[16]; - int m_wordcount; + int m_wordCount; vector m_utf8Alphabet; LexiconInterpreter* createInterpreter(char version) const; diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index f92efb1..6630e1f 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -40,7 +40,7 @@ public: }; LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDialog(parent), - m_wordFactory(NULL) + m_deleted(false), m_wordFactory(NULL) { m_originalName = originalName; @@ -57,6 +57,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi m_lexiconInformation = new QLabel(""); m_lexiconInformation->setWordWrap(true); + m_lexiconInformation->setTextInteractionFlags(Qt::TextBrowserInteraction); m_saveChanges = new QPushButton(tr("&Save Changes")); m_cancel = new QPushButton(tr("&Cancel")); @@ -107,6 +108,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi // hook up signals and slots connect(m_lexiconName, SIGNAL(textEdited(const QString &)), this, SLOT(parametersChanged(const QString &))); connect(m_addWordsFromFile, SIGNAL(clicked()), this, SLOT(addWordsFromFile())); + connect(m_clearAllWords, SIGNAL(clicked()), this, SLOT(loadOriginalDictionary())); connect(m_saveChanges, SIGNAL(clicked()), this, SLOT(accept())); connect(m_cancel, SIGNAL(clicked()), this, SLOT(reject())); connect(m_deleteLexicon, SIGNAL(clicked()), this, SLOT(deleteLexicon())); @@ -114,26 +116,13 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi setWindowTitle(tr("Configure Lexicon - Quackle")); - Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ""); + Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ".quackle_alphabet", ""); alphabetChanged(m_alphabetCombo->currentText()); - string dawgFileName = originalName.toStdString() + ".dawg"; - QString dawgFullFileName; - if (!originalName.isEmpty()) - dawgFullFileName = QString::fromStdString(Quackle::LexiconParameters::findDictionaryFile(dawgFileName)); - m_lexiconName->setValidator(m_fileNameValidator); m_lexiconName->setText(m_originalName); - if (!dawgFullFileName.isEmpty()) - { - m_deleteLexicon->setEnabled(Quackle::LexiconParameters::hasUserDictionaryFile(dawgFileName)); - addWordsFromDawgFile(dawgFullFileName); - } - else - m_deleteLexicon->setEnabled(false); - - updateLexiconInformation(true); + loadOriginalDictionary(); } LexiconDialog::~LexiconDialog() @@ -144,9 +133,11 @@ LexiconDialog::~LexiconDialog() void LexiconDialog::deleteLexicon() { - delete m_wordFactory; - m_wordFactory = NULL; - updateLexiconInformation(); + string lexiconNameStr = m_originalName.toStdString(); + string filename = QUACKLE_DATAMANAGER->makeDataFilename("lexica", lexiconNameStr + ".dawg", true); + QFile(QString::fromStdString(filename)).remove(); + m_deleted = true; + QDialog::accept(); } void LexiconDialog::addWordsFromFile() @@ -245,8 +236,40 @@ void LexiconDialog::addWordsFromTextFile(const QString &textFile) } } +void LexiconDialog::loadOriginalDictionary() +{ + delete m_wordFactory; + m_wordFactory = NULL; + string dawgFileName = m_originalName.toStdString() + ".dawg"; + QString dawgFullFileName; + if (!m_originalName.isEmpty()) + dawgFullFileName = QString::fromStdString(Quackle::LexiconParameters::findDictionaryFile(dawgFileName)); + + if (!dawgFullFileName.isEmpty()) + { + m_deleteLexicon->setEnabled(Quackle::LexiconParameters::hasUserDictionaryFile(dawgFileName)); + m_lexiconInformation->setText(tr("Loading dictionary...")); + show(); + qApp->processEvents(); + addWordsFromDawgFile(dawgFullFileName); + } + else + m_deleteLexicon->setEnabled(false); + + updateLexiconInformation(true); +} + void LexiconDialog::accept() { + string lexiconNameStr = m_lexiconName->text().toStdString(); + string filename = QUACKLE_DATAMANAGER->makeDataFilename("lexica", lexiconNameStr + ".dawg", true); + m_lexiconInformation->setText(tr("Compressing and writing dictionary file...\nThis may take a few minutes.")); + qApp->processEvents(); + m_wordFactory->generate(); + m_lexiconInformation->setText(tr("Writing dictionary file...")); + qApp->processEvents(); + m_wordFactory->writeIndex(filename); + m_finalLexiconName = m_lexiconName->text(); QDialog::accept(); } @@ -256,12 +279,6 @@ void LexiconDialog::updateLexiconInformation(bool firstTime) QString text; QString lengthText; - // only recompute word count when the dictionary changes - if (m_wordFactory && hash != m_previousHash) - { - m_wordFactory->computeWordCount(); - m_previousHash = hash; - } int wordCount = m_wordFactory ? m_wordFactory->wordCount() : 0; if (wordCount == 0) { @@ -274,9 +291,7 @@ void LexiconDialog::updateLexiconInformation(bool firstTime) if (firstTime) m_originalHash = hash; - text.append(tr("File name: ")); - text.append(tr("\n\nFile size: ")); - text.append(tr("\n\nWord count: ")); + text.append(tr("Word count: ")); text.append(QString("%L1").arg(wordCount)); text.append("\n"); text.append(lengthText); @@ -286,4 +301,5 @@ void LexiconDialog::updateLexiconInformation(bool firstTime) m_lexiconInformation->setText(text); m_saveChanges->setEnabled(hash != m_originalHash && !m_lexiconName->text().isEmpty()); + m_clearAllWords->setEnabled(hash != m_originalHash); } diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h index fa80ec1..1f1605b 100644 --- a/quacker/lexicondialog.h +++ b/quacker/lexicondialog.h @@ -45,6 +45,9 @@ public: ~LexiconDialog(); virtual void accept(); + bool itemWasDeleted() { return m_deleted; }; + const QString &lexiconName() { return m_finalLexiconName; }; + void updateLexiconInformation(bool firstTime = false); protected slots: @@ -52,6 +55,7 @@ protected slots: void deleteLexicon(); void addWordsFromFile(); void alphabetChanged(const QString &); + void loadOriginalDictionary(); protected: void addWordsFromDawgFile(const QString &dawgfile); @@ -73,7 +77,8 @@ private: QString m_originalName; QString m_alphabetFileName; QByteArray m_originalHash; - QByteArray m_previousHash; + QString m_finalLexiconName; + bool m_deleted; DawgFactory *m_wordFactory; }; diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 3319955..6435605 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -107,7 +107,7 @@ void Settings::createGUI() m_lexiconNameCombo = new QComboBox; connect(m_lexiconNameCombo, SIGNAL(activated(const QString &)), this, SLOT(lexiconChanged(const QString &))); - populateComboFromFilenames(m_lexiconNameCombo, "lexica", "lexicon"); + populateComboFromFilenames(m_lexiconNameCombo, "lexica", ".dawg", "lexicon"); QLabel *lexiconNameLabel = new QLabel(tr("&Lexicon:")); lexiconNameLabel->setBuddy(m_lexiconNameCombo); @@ -118,7 +118,7 @@ void Settings::createGUI() m_alphabetNameCombo = new QComboBox; connect(m_alphabetNameCombo, SIGNAL(activated(const QString &)), this, SLOT(alphabetChanged(const QString &))); - populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ""); + populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ".quackle_alphabet", ""); QLabel *alphabetNameLabel = new QLabel(tr("&Alphabet:")); alphabetNameLabel->setBuddy(m_alphabetNameCombo); @@ -129,7 +129,7 @@ void Settings::createGUI() m_themeNameCombo = new QComboBox; connect(m_themeNameCombo, SIGNAL(activated(const QString &)), this, SLOT(themeChanged(const QString &))); - populateComboFromFilenames(m_themeNameCombo, "themes", ""); + populateComboFromFilenames(m_themeNameCombo, "themes", ".ini", ""); QLabel *themeNameLabel = new QLabel(tr("&Theme:")); themeNameLabel->setBuddy(m_themeNameCombo); @@ -140,7 +140,7 @@ void Settings::createGUI() m_boardNameCombo = new QComboBox; connect(m_boardNameCombo, SIGNAL(activated(const QString &)), this, SLOT(boardChanged(const QString &))); - populateComboFromFilenames(m_boardNameCombo, "boards", "board"); + populateComboFromFilenames(m_boardNameCombo, "boards", "", "board"); QLabel *boardNameLabel = new QLabel(tr("&Board:")); boardNameLabel->setBuddy(m_boardNameCombo); @@ -172,6 +172,8 @@ void Settings::createGUI() void Settings::load() { m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()))); + if (m_lexiconNameCombo->currentIndex() == -1) + m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()) + "*")); m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex(); m_alphabetNameCombo->setCurrentIndex(m_alphabetNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName()))); m_themeNameCombo->setCurrentIndex(m_themeNameCombo->findText(m_themeName)); @@ -269,12 +271,12 @@ void Settings::setQuackleToUseLexiconName(const QString &lexiconName) else QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile); - if (!QUACKLE_LEXICON_PARAMETERS->hasGaddag()) - { - gaddagFile = QUACKLE_DATAMANAGER->makeDataFilename("lexica", lexiconNameStr + ".gaddag", true); - buildGaddag(gaddagFile); - QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile); - } + // if (!QUACKLE_LEXICON_PARAMETERS->hasGaddag()) + // { + // gaddagFile = QUACKLE_DATAMANAGER->makeDataFilename("lexica", lexiconNameStr + ".gaddag", true); + // buildGaddag(gaddagFile); + // QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile); + // } QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconNameStr); } @@ -342,6 +344,9 @@ void Settings::setQuackleToUseBoardName(const QString &boardName) void Settings::lexiconChanged(const QString &lexiconName) { + QString lexicon = lexiconName; + if (lexicon.endsWith("*")) + lexicon.truncate(lexicon.size() - 1); if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1) { editLexicon(); @@ -350,11 +355,11 @@ void Settings::lexiconChanged(const QString &lexiconName) m_lexiconNameCombo->setCurrentIndex(m_lastGoodLexiconValue); return; } - setQuackleToUseLexiconName(lexiconName); + setQuackleToUseLexiconName(lexicon); m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex(); CustomQSettings settings; - settings.setValue("quackle/settings/lexicon-name", lexiconName); + settings.setValue("quackle/settings/lexicon-name", lexicon); emit refreshViews(); } @@ -465,9 +470,6 @@ void Settings::editBoard() void Settings::loadBoardNameCombo() { - if (m_lexiconNameCombo == 0) - return; - while (m_boardNameCombo->count() > 0) m_boardNameCombo->removeItem(0); @@ -489,12 +491,30 @@ void Settings::loadBoardNameCombo() void Settings::editLexicon() { QString name = m_lexiconNameCombo->currentText(); + if (name.endsWith("*")) + name.truncate(name.size() - 1); if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1) name = ""; LexiconDialog dialog(this, name); if (dialog.exec()) { - populateComboFromFilenames(m_lexiconNameCombo, "lexica", "lexicon"); + populateComboFromFilenames(m_lexiconNameCombo, "lexica", ".dawg", "lexicon"); + qApp->processEvents(); + if (dialog.itemWasDeleted()) + { + m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(name)); + QUACKLE_LEXICON_PARAMETERS->setLexiconName(""); // force lexicon to reload + QUACKLE_LEXICON_PARAMETERS->unloadAll(); + if (m_lexiconNameCombo->currentIndex() != -1) + setQuackleToUseLexiconName(name); + } + else if (!dialog.lexiconName().isEmpty()) + { + QUACKLE_LEXICON_PARAMETERS->setLexiconName(""); // force lexicon to reload + QUACKLE_LEXICON_PARAMETERS->unloadAll(); + setQuackleToUseLexiconName(dialog.lexiconName()); + m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(name + "*")); + } load(); } } @@ -508,7 +528,7 @@ void Settings::editAlphabet() AlphabetDialog dialog(this); if (dialog.exec()) { - populateComboFromFilenames(m_alphabetNameCombo, "alphabets", "alphabet"); + populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ".quackle_alphabet", "alphabet"); load(); } #endif // 0 @@ -529,8 +549,11 @@ void Settings::editTheme() #endif // 0 } -void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label) +void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &extension, const QString &label) { + while (combo->count() > 0) + combo->removeItem(0); + QStringList fileList; QDir dir(self()->m_appDataDir); if (dir.cd(path)) @@ -547,6 +570,8 @@ void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, for (i = fileList.begin(); i != fileList.end(); ++i) { fileName = *i; + if (!fileName.endsWith(extension)) + continue; periodPos = fileName.indexOf('.'); if (periodPos) { @@ -555,14 +580,13 @@ void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, } } - for (i = fileList.begin(); i != fileList.end(); ++i) + for (i = list.begin(); i != list.end(); ++i) { - QStringList::iterator j = i; - for (++j; j != fileList.end(); ++j) + for (QStringList::iterator j = i + 1; j != list.end(); ++j) { if (*i == *j) { - *i = "* " + *i; + *i = *i + "*"; list.erase(j); break; } diff --git a/quacker/settings.h b/quacker/settings.h index 7c5738e..babea3c 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -42,7 +42,7 @@ public: static Settings *self(); // load up an item list based on a list of filenames - static void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label); + static void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &extension, const QString &label); signals: void refreshViews(); diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 869ef8e..4dd4ec3 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -28,6 +28,8 @@ DawgFactory::DawgFactory(const QString &alphabetFile) + : m_encodableWords(0), m_unencodableWords(0), m_duplicateWords(0), + m_countsByLength(Quackle::FixedLengthString::maxSize, 0) { QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; flexure->load(alphabetFile); @@ -40,7 +42,6 @@ DawgFactory::DawgFactory(const QString &alphabetFile) m_root.lastchild = true; m_hash.int32ptr[0] = m_hash.int32ptr[1] = m_hash.int32ptr[2] = m_hash.int32ptr[3] = 0; - m_encodableWords = m_unencodableWords = m_duplicateWords = m_wordCount = 0; } DawgFactory::~DawgFactory() @@ -48,7 +49,7 @@ DawgFactory::~DawgFactory() delete m_alphas; } -bool DawgFactory::pushWord(const UVString& word, bool inSmaller, int playability) +bool DawgFactory::pushWord(const UVString &word, bool inSmaller, int playability) { UVString leftover; Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover); @@ -59,11 +60,12 @@ bool DawgFactory::pushWord(const UVString& word, bool inSmaller, int playability return false; } -bool DawgFactory::pushWord(const Quackle::LetterString& word, bool inSmaller, int playability) +bool DawgFactory::pushWord(const Quackle::LetterString &word, bool inSmaller, int playability) { if (m_root.pushWord(word, inSmaller, playability)) { ++m_encodableWords; + ++m_countsByLength[word.length()]; hashWord(word); return true; } @@ -133,7 +135,7 @@ void DawgFactory::generate() m_root.print(m_nodelist); } -void DawgFactory::writeIndex(const UVString& filename) +void DawgFactory::writeIndex(const string &filename) { ofstream out(filename.c_str(), ios::out | ios::binary); unsigned char bytes[7]; @@ -187,37 +189,26 @@ void DawgFactory::writeIndex(const UVString& filename) } } -void DawgFactory::computeWordCount() const -{ - m_countsByLength.resize(0); - m_wordCount = m_root.wordCount(0, m_countsByLength); -} - string DawgFactory::letterCountString() const { ostringstream str; - if (m_countsByLength.size() < 16) - m_countsByLength.resize(16, 0); - str << "2s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[2]; - str << "\t6s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[6]; - str << "\t10s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[10]; - str << "\t14s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[14]; - str << "\n3s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[3]; - str << "\t7s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[7]; - str << "\t11s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[11]; - str << "\t15s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[15]; - str << "\n4s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[4]; - str << "\t8s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[8]; - str << "\t12s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[12]; - str << "\n5s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[5]; - str << "\t9s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[9]; - str << "\t13s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[13]; - str << "\n"; + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + const int letterCount = j * 4 + i + 2; + if (j != 0) + str << "\t"; + if (m_countsByLength[letterCount] > 0) + str << letterCount << "s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[letterCount]; + } + str << "\n"; + } return str.str(); } -void DawgFactory::Node::print(vector< Node* >& nodelist) +void DawgFactory::Node::print(vector< Node* > &nodelist) { written = true; @@ -255,7 +246,7 @@ void DawgFactory::Node::print(vector< Node* >& nodelist) // returns true if the word was actually added...false if it's a duplicate. -bool DawgFactory::Node::pushWord(const Quackle::LetterString& word, bool inSmaller, int pb) +bool DawgFactory::Node::pushWord(const Quackle::LetterString &word, bool inSmaller, int pb) { bool added; if (word.length() == 0) { @@ -318,17 +309,6 @@ bool DawgFactory::Node::equals(const Node &n) const return true; } -int DawgFactory::Node::wordCount(unsigned int depth, vector &countsByLength) const -{ - int wordCount = ((playability == 0) ? 0 : 1); - if (countsByLength.size() < depth + 1) - countsByLength.resize(depth + 1, 0); - countsByLength[depth] += wordCount; - for (size_t i = 0; i < children.size(); i++) - wordCount += children[i].wordCount(depth + 1, countsByLength); - return wordCount; -} - int DawgFactory::Node::letterSum() const { if (sumexplored) diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 5872dc3..efcc455 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -30,19 +30,18 @@ public: DawgFactory(const QString &alphabetFile); ~DawgFactory(); - void computeWordCount() const; - int wordCount() const { return m_wordCount; }; + int wordCount() const { return m_encodableWords; }; string letterCountString() const; int nodeCount() const { return m_nodelist.size(); }; int encodableWords() const { return m_encodableWords; }; int unencodableWords() const { return m_unencodableWords; }; int duplicateWords() const { return m_duplicateWords; }; - bool pushWord(const UVString& word, bool inSmaller, int playability); - bool pushWord(const Quackle::LetterString& word, bool inSmaller, int playability); + bool pushWord(const UVString &word, bool inSmaller, int playability); + bool pushWord(const Quackle::LetterString &word, bool inSmaller, int playability); void hashWord(const Quackle::LetterString &word); void generate(); - void writeIndex(const UVString& filename); + void writeIndex(const string &filename); const char* hashBytes() { return m_hash.charptr; }; @@ -50,10 +49,9 @@ private: class Node { public: bool pushWord(const Quackle::LetterString& word, bool inSmaller, int pb); - void print(vector< Node* >& m_nodelist); + void print(vector< Node* > &m_nodelist); int letterSum() const; - int wordCount(unsigned int depth, vector &countsByLength) const; bool equals(const Node &n) const; Quackle::Letter c; @@ -67,7 +65,7 @@ private: bool lastchild; mutable bool sumexplored; - mutable int sum; + mutable unsigned int sum; mutable vector counts; bool deleted; @@ -79,8 +77,7 @@ private: int m_unencodableWords; int m_duplicateWords; vector< Node* > m_nodelist; - mutable int m_wordCount; - mutable vector m_countsByLength; + vector m_countsByLength; Quackle::AlphabetParameters *m_alphas; Node m_root; union { -- cgit v1.2.3 From fd6daafffbcce3bfc385d7508a1ca50174840912 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Wed, 14 Oct 2015 01:29:30 -0500 Subject: Convert dictionaries, add csw15. * All dictionaries are now in v1 DAWG format. * Three corrupt dictionaries were removed. * Implement a way to tag dictionaries for copyright purposes. * CSW15 is now a thing. --- data/lexica/copyrights.txt | 2 ++ data/lexica/csw12.dawg | Bin 1600543 -> 1600616 bytes data/lexica/csw15.dawg | Bin 0 -> 1459769 bytes data/lexica/cswapr07.dawg | Bin 2641009 -> 2641082 bytes data/lexica/greek.dawg | Bin 1248807 -> 1248900 bytes data/lexica/korean.dawg | Bin 1014944 -> 1015145 bytes data/lexica/norwegian.dawg | Bin 1701427 -> 1701518 bytes data/lexica/ods5.dawg | Bin 899171 -> 899244 bytes data/lexica/osps.dawg | Bin 2570477 -> 2570571 bytes data/lexica/russian.dawg | Bin 240528 -> 0 bytes data/lexica/sowpods.dawg | Bin 1473486 -> 1473559 bytes data/lexica/tuvan.dawg | Bin 15492 -> 0 bytes data/lexica/twl06.dawg | Bin 1816584 -> 1816657 bytes data/lexica/twl06_wild.dawg | Bin 6215056 -> 0 bytes data/lexica/twl98.dawg | Bin 938070 -> 938143 bytes lexiconparameters.cpp | 27 +++++++++++++++++++++------ lexiconparameters.h | 4 ++-- quacker/lexicondialog.cpp | 3 ++- quacker/quacker.cpp | 32 +++++++++++++++++++++++--------- quacker/settings.cpp | 12 +++++++++--- quacker/settings.h | 2 ++ quackle.sublime-project | 4 ++-- 22 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 data/lexica/copyrights.txt create mode 100644 data/lexica/csw15.dawg delete mode 100644 data/lexica/russian.dawg delete mode 100644 data/lexica/tuvan.dawg delete mode 100644 data/lexica/twl06_wild.dawg (limited to 'quacker/settings.h') diff --git a/data/lexica/copyrights.txt b/data/lexica/copyrights.txt new file mode 100644 index 0000000..1e3e553 --- /dev/null +++ b/data/lexica/copyrights.txt @@ -0,0 +1,2 @@ +eea8dfe5:Collins Scrabble™ Words 2012, ©HarperCollins Publishers Ltd 2015 +48dea2c8:Collins Scrabble™ Words 2015, ©HarperCollins Publishers Ltd 2015 diff --git a/data/lexica/csw12.dawg b/data/lexica/csw12.dawg index f98ef8b..822bd95 100644 Binary files a/data/lexica/csw12.dawg and b/data/lexica/csw12.dawg differ diff --git a/data/lexica/csw15.dawg b/data/lexica/csw15.dawg new file mode 100644 index 0000000..da4defa Binary files /dev/null and b/data/lexica/csw15.dawg differ diff --git a/data/lexica/cswapr07.dawg b/data/lexica/cswapr07.dawg index a2863ac..6e6dca7 100644 Binary files a/data/lexica/cswapr07.dawg and b/data/lexica/cswapr07.dawg differ diff --git a/data/lexica/greek.dawg b/data/lexica/greek.dawg index 90835e5..b8666ec 100644 Binary files a/data/lexica/greek.dawg and b/data/lexica/greek.dawg differ diff --git a/data/lexica/korean.dawg b/data/lexica/korean.dawg index 6aa5d3e..c55769e 100644 Binary files a/data/lexica/korean.dawg and b/data/lexica/korean.dawg differ diff --git a/data/lexica/norwegian.dawg b/data/lexica/norwegian.dawg index 0acdb33..2db2269 100644 Binary files a/data/lexica/norwegian.dawg and b/data/lexica/norwegian.dawg differ diff --git a/data/lexica/ods5.dawg b/data/lexica/ods5.dawg index eabc68a..8d440d9 100644 Binary files a/data/lexica/ods5.dawg and b/data/lexica/ods5.dawg differ diff --git a/data/lexica/osps.dawg b/data/lexica/osps.dawg index 6c02ecd..6d81d3d 100644 Binary files a/data/lexica/osps.dawg and b/data/lexica/osps.dawg differ diff --git a/data/lexica/russian.dawg b/data/lexica/russian.dawg deleted file mode 100644 index 3b01c8e..0000000 Binary files a/data/lexica/russian.dawg and /dev/null differ diff --git a/data/lexica/sowpods.dawg b/data/lexica/sowpods.dawg index b5d89df..e052222 100644 Binary files a/data/lexica/sowpods.dawg and b/data/lexica/sowpods.dawg differ diff --git a/data/lexica/tuvan.dawg b/data/lexica/tuvan.dawg deleted file mode 100644 index f5f31b3..0000000 Binary files a/data/lexica/tuvan.dawg and /dev/null differ diff --git a/data/lexica/twl06.dawg b/data/lexica/twl06.dawg index 9388487..e0afed7 100644 Binary files a/data/lexica/twl06.dawg and b/data/lexica/twl06.dawg differ diff --git a/data/lexica/twl06_wild.dawg b/data/lexica/twl06_wild.dawg deleted file mode 100644 index 34e78a0..0000000 Binary files a/data/lexica/twl06_wild.dawg and /dev/null differ diff --git a/data/lexica/twl98.dawg b/data/lexica/twl98.dawg index a835b7c..e6f451e 100644 Binary files a/data/lexica/twl98.dawg and b/data/lexica/twl98.dawg differ diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index 74de78f..f04a941 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -132,7 +132,7 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter }; LexiconParameters::LexiconParameters() - : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL), m_wordCount(0) + : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL) { memset(m_hash, 0, sizeof(m_hash)); } @@ -228,22 +228,37 @@ bool LexiconParameters::hasUserDictionaryFile(const string &lexicon) return QUACKLE_DATAMANAGER->hasUserDataFile("lexica", lexicon); } -UVString LexiconParameters::hashString(bool shortened) const +string LexiconParameters::hashString(bool shortened) const { - const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; string hashStr; for (size_t i = 0; i < sizeof(m_hash); i++) { hashStr.push_back(hex[(m_hash[i] & 0xF0) >> 4]); hashStr.push_back(hex[m_hash[i] & 0x0F]); - if (shortened && i == 5) + if (shortened && i == 3) break; - if (i % 2 == 1) - hashStr.push_back('-'); } return hashStr; } +string LexiconParameters::copyrightString() const +{ + string copyrightsFilename = QUACKLE_DATAMANAGER->makeDataFilename("lexica", "copyrights.txt", false); + fstream copyrightsFile(copyrightsFilename, ios_base::in); + while (copyrightsFile.good() && !copyrightsFile.eof()) + { + string line; + getline(copyrightsFile, line); + if (line.size() < 9 || line.find_first_of(':') != 8) + continue; + if (hashString(true).compare(line.substr(0,8)) != 0) + continue; + return line.substr(9, line.size()); + } + return string(); +} + LexiconInterpreter* LexiconParameters::createInterpreter(char version) const { switch(version) diff --git a/lexiconparameters.h b/lexiconparameters.h index f29a589..d19f695 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -80,7 +80,8 @@ public: } const GaddagNode *gaddagRoot() const { return (GaddagNode *) &m_gaddag[0]; }; - UVString hashString(bool shortened) const; + string hashString(bool shortened) const; + string copyrightString() const; protected: unsigned char *m_dawg; @@ -88,7 +89,6 @@ protected: string m_lexiconName; LexiconInterpreter *m_interpreter; char m_hash[16]; - int m_wordCount; vector m_utf8Alphabet; LexiconInterpreter* createInterpreter(char version) const; diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index 6630e1f..91eb676 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -117,6 +117,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi setWindowTitle(tr("Configure Lexicon - Quackle")); Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ".quackle_alphabet", ""); + m_alphabetCombo->setCurrentIndex(m_alphabetCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName()))); alphabetChanged(m_alphabetCombo->currentText()); m_lexiconName->setValidator(m_fileNameValidator); @@ -300,6 +301,6 @@ void LexiconDialog::updateLexiconInformation(bool firstTime) m_lexiconInformation->setText(text); - m_saveChanges->setEnabled(hash != m_originalHash && !m_lexiconName->text().isEmpty()); + m_saveChanges->setEnabled(true/*hash != m_originalHash && !m_lexiconName->text().isEmpty()*/); m_clearAllWords->setEnabled(hash != m_originalHash); } diff --git a/quacker/quacker.cpp b/quacker/quacker.cpp index a1f7064..2f407fa 100644 --- a/quacker/quacker.cpp +++ b/quacker/quacker.cpp @@ -155,13 +155,10 @@ void TopLevel::finishInitialization() void TopLevel::introduceToUser() { CustomQSettings settings; - QString lexiconName = settings.value("quackle/settings/lexicon-name", QString("twl06")).toString(); - if (lexiconName == "csw12") { - statusMessage(tr("The WESPA wordlist (CSW12) is copyright Harper Collins 2011.")); - } else { - statusMessage(tr("Enjoy your quackling. Choose \"New game...\" from the Game menu to begin.")); - } - + QString statusText = QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->copyrightString().c_str()); + if (statusText.isEmpty()) + statusText = tr("Enjoy your quackling. Choose \"New game...\" from the Game menu to begin."); + statusMessage(statusText); parseCommandLineOptions(); if (!CustomQSettings().contains("quackle/hasBeenRun")) @@ -2120,7 +2117,7 @@ void TopLevel::firstTimeRun() void TopLevel::about() { - QMessageBox::about(this, tr("About Quackle 1.0"), dialogText(tr( + QString aboutText = tr( "

Quackle 1.0 is a crossword game playing, analysis, and study tool. Visit the Quackle homepage at http://quackle.org for more information.

" "

Quackle was written by Jason Katz-Brown, John O'Laughlin, John Fultz, Matt Liberty, and Anand Buddhdev. We thank the anonymous donor who made this software free.

" "

Copyright 2005-2015 by

" @@ -2130,7 +2127,24 @@ void TopLevel::about() "" "

Quackle is free, open-source software licensed under the terms of the GNU General Public License Version 3. See

" "

http://quackle.org/LICENSE

" -))); +"

Dictionary copyrights

    " +); + + FILE* file = fopen(QUACKLE_DATAMANAGER->makeDataFilename("lexica", "copyrights.txt", false).c_str(), "r"); + if (file) + { + QTextStream strm(file); + while (!strm.atEnd()) + { + QString line = strm.readLine(); + int pos = line.indexOf(':'); + if (pos != -1 && pos + 1 < line.size()) + aboutText += "
  • " + line.mid(pos + 1) + "
  • "; + } + fclose(file); + aboutText += "
"; + } + QMessageBox::about(this, tr("About Quackle 1.0"), dialogText(aboutText)); } void TopLevel::hints() diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 6435605..e22a29e 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -148,6 +148,8 @@ void Settings::createGUI() m_editBoard->setMaximumWidth(60); connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); + m_copyrightLabel = new QLabel(); + layout->addWidget(lexiconNameLabel, 0, 0, Qt::AlignRight); layout->addWidget(m_lexiconNameCombo, 0, 1); layout->addWidget(m_editLexicon, 0, 2); @@ -160,25 +162,28 @@ void Settings::createGUI() layout->addWidget(boardNameLabel, 3, 0, Qt::AlignRight); layout->addWidget(m_boardNameCombo, 3, 1); layout->addWidget(m_editBoard, 3, 2); + layout->addWidget(m_copyrightLabel, 4, 0, 1, -1, Qt::AlignTop); layout->setColumnMinimumWidth(3, 0); layout->setColumnStretch(3, 1); layout->setRowMinimumHeight(4, 0); layout->setRowStretch(4, 1); + load(); } void Settings::load() { - m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()))); + m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->lexiconName().c_str()))); if (m_lexiconNameCombo->currentIndex() == -1) - m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()) + "*")); + m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->lexiconName().c_str()) + "*")); m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex(); - m_alphabetNameCombo->setCurrentIndex(m_alphabetNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName()))); + m_alphabetNameCombo->setCurrentIndex(m_alphabetNameCombo->findText(QString::fromUtf8(QUACKLE_ALPHABET_PARAMETERS->alphabetName().c_str()))); m_themeNameCombo->setCurrentIndex(m_themeNameCombo->findText(m_themeName)); m_boardNameCombo->setCurrentIndex(m_boardNameCombo->findText(QuackleIO::Util::uvStringToQString(QUACKLE_BOARD_PARAMETERS->name()))); m_lastGoodBoardValue = m_boardNameCombo->currentIndex(); + m_copyrightLabel->setText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->copyrightString().c_str())); } void Settings::preInitialize() @@ -279,6 +284,7 @@ void Settings::setQuackleToUseLexiconName(const QString &lexiconName) // } QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconNameStr); + m_copyrightLabel->setText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->copyrightString().c_str())); } } diff --git a/quacker/settings.h b/quacker/settings.h index babea3c..ef3449e 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -29,6 +29,7 @@ class QComboBox; class QCheckBox; class QPushButton; +class QLabel; using namespace std; @@ -88,6 +89,7 @@ protected: QPushButton *m_editAlphabet; QPushButton *m_editTheme; QPushButton *m_editBoard; + QLabel *m_copyrightLabel; QString m_appDataDir; QString m_userDataDir; QString m_themeName; diff --git a/quackle.sublime-project b/quackle.sublime-project index 2ca8db5..c7f47cd 100644 --- a/quackle.sublime-project +++ b/quackle.sublime-project @@ -6,8 +6,8 @@ "file_exclude_patterns" : ["*.tgz", "*.sublime-workspace", ".tags*", "dawginput.raw", "playabilities.raw", "smaller.raw", ".gitattributes", "*.Debug", "*.Release", "*.pfx", "*.cer", - "makegaddag", "makeminidawg", "gaddagize", "Makefile"], - "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib", "lexica", + "makegaddag", "makeminidawg", "gaddagize", "Makefile", "*.dawg", "*.gaddag"], + "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib", "strategy", "debug", "release", "makeswelexicon", "lisp", "DerivedData"] } ] -- cgit v1.2.3