From d5d665482e118d5c993717c0ec0406b94c57aa1f Mon Sep 17 00:00:00 2001 From: Richard Heck Date: Sat, 12 Jan 2008 04:28:12 +0000 Subject: [PATCH] This commit changes the way individual LyXModule's are represented, both internally and in the .lyx files. The earlier version represented them by their `descriptive name', e.g., "Endnote" or "Theorems (AMS)", these being the same names used in the UI. This was a mistake, as becomes readily apparent when one starts to think about translating these strings. The modules ought to be represented by their filename, without the extension, just as TextClass's are. The changes that accomplish this part are in ModuleList.{h,cpp}, configure.py, and the *.module files themselves. This is a format change, and the lyx2lyx is in those files. By itself, that change would not be major, except for the fact that we do not want the module to be represented in the UI by its filename---e.g., theorems-std---but rather by a descriptive name, such as "Theorems". But that change turns out to be wholly non-trivial. The mechanism for choosing modules was the same as---indeed, was borrowed from---that in GuiCitation: You get a list of modules, and choosing them involves moving strings from one QListView to another. The models underlying these views are just QStringListModels, which means that, when you want to know what modules have been selected, you see what strings are in the "selected" QListView. But these are just the descriptive names, and we can't look up a module by its descriptive name if it's been translated. That, indeed, was the whole point of the change to the new representation. So, we need a more complicated model underlying the QListView, one that will pair an identifying string---the filename minus the extension, in this case---with each item. This turns out not to be terribly difficult, though it took rather a while for me to understand why it's not difficult. There are two parts: (i) GuiSelectionManger gets re-written to use any QAbstractListModel, not just a QStringListModel. This actually seems to improve the code, independently. (ii) We then subclass QAbstractListModel to get the associated ID string, using the Qt::UserRole slot associated with each item to store its ID. This would be almost completely trivial if QAbstractListItem::itemData() included the QVariant associated with this role, but it doesn't, so there are some additional hoops through which to jump. The new model, a GuiIdListModel, is defined in the files by that name. The changes in GuiSelectionManger.{h,cpp} make it more abstract; the changes in GuiDocument.{h,cpp} adapt it to the new framework. I've also updated the module documenation to accord with this change. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22501 a592a061-630c-0410-9148-cb99ea01b6c8 --- development/FORMAT | 5 +- development/scons/scons_manifest.py | 2 + lib/configure.py | 4 +- lib/doc/Customization.lyx | 36 ++- lib/layouts/theorems-ams-extended.module | 2 +- lib/layouts/theorems-ams.module | 2 +- lib/layouts/theorems-chap.module | 2 +- lib/layouts/theorems-sec.module | 2 +- lib/layouts/theorems-starred.module | 2 +- lib/layouts/theorems-std.module | 2 +- lib/lyx2lyx/LyX.py | 26 +- lib/lyx2lyx/lyx_1_6.py | 38 ++- src/Buffer.cpp | 2 +- src/ModuleList.cpp | 21 +- src/ModuleList.h | 14 +- src/frontends/qt4/GuiDocument.cpp | 319 +++++++++++++--------- src/frontends/qt4/GuiDocument.h | 57 ++-- src/frontends/qt4/GuiSelectionManager.cpp | 88 ++++-- src/frontends/qt4/GuiSelectionManager.h | 26 +- src/frontends/qt4/Makefile.am | 2 + 20 files changed, 440 insertions(+), 212 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index 2de19fae70..fcf3fce47a 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -1,11 +1,14 @@ LyX file-format changes ----------------------- +2008-01-12 Richard Heck + * Format incremented to 313: change in how modules are represented + 2008-01-11 Jürgen Spitzmüller * Format incremented to 312: support for sidewaysalgorithm (rotfloat) and wide sideways{figure,table}. -2008-01-10 Richard Heck +2008-01-10 Richard Heck * Format incremented to 311: dummy format to drive the AMS conversion 2007-12-28 Bernhard Reiter diff --git a/development/scons/scons_manifest.py b/development/scons/scons_manifest.py index 28627d7242..71b727a4c2 100644 --- a/development/scons/scons_manifest.py +++ b/development/scons/scons_manifest.py @@ -730,6 +730,7 @@ src_frontends_qt4_header_files = Split(''' GuiGraphics.h GuiGraphicsUi.h GuiHyperlink.h + GuiIdListModel.h GuiImage.h GuiInclude.h GuiIndex.h @@ -818,6 +819,7 @@ src_frontends_qt4_files = Split(''' GuiFontMetrics.cpp GuiGraphics.cpp GuiHyperlink.cpp + GuiIdListModel.cpp GuiImage.cpp GuiInclude.cpp GuiIndex.cpp diff --git a/lib/configure.py b/lib/configure.py index e08032af81..1d3dd6185a 100644 --- a/lib/configure.py +++ b/lib/configure.py @@ -754,6 +754,8 @@ def processModuleFile(file, bool_docbook, bool_linuxdoc): modname = desc = pkgs = req = excl = "" readingDescription = False descLines = [] + filename = file.split(os.sep)[-1] + filename = filename[:-7] for line in open(file).readlines(): if readingDescription: @@ -776,8 +778,6 @@ def processModuleFile(file, bool_docbook, bool_linuxdoc): else: tmp = [s.strip() for s in pkgs.split(",")] pkgs = ",".join(tmp) - - filename = file.split(os.sep)[-1] continue res = r.search(line) if res != None: diff --git a/lib/doc/Customization.lyx b/lib/doc/Customization.lyx index cfa77fd0db..a58b98d180 100644 --- a/lib/doc/Customization.lyx +++ b/lib/doc/Customization.lyx @@ -1,5 +1,5 @@ #LyX 1.6.0svn created this file. For more info see http://www.lyx.org/ -\lyxformat 310 +\lyxformat 313 \begin_document \begin_header \textclass book @@ -58,7 +58,7 @@ \usepackage{multicol} \end_preamble \begin_modules -Logical Markup +logicalmkup \end_modules \language english \inputencoding default @@ -5907,11 +5907,11 @@ theendnotes in ERT where you \end_layout \begin_layout LyX-Code -#Requires: Some Module | Some Other Module +#Requires: somemodule | othermodule \end_layout \begin_layout LyX-Code -#Excludes: Bad Module +#Excludes: badmodule \end_layout \begin_layout Standard @@ -5936,6 +5936,32 @@ at least one no \emph default excluded module may be used. + Note that modules are identified here by their +\emph on +filenames +\emph default + without the .module extension. + So +\begin_inset Flex CharStyle:Code +status collapsed + +\begin_layout Standard +somemodule +\end_layout + +\end_inset + + is really +\begin_inset Flex CharStyle:Code +status collapsed + +\begin_layout Standard +somemodule.module +\end_layout + +\end_inset + +. \end_layout \begin_layout Standard @@ -5947,7 +5973,7 @@ After creating a new module, you will need to reconfigure and then restart Document\SpecialChar \menuseparator Settings \family default -, make some change (or even just highlight something), and then hit +, highlight something, and then hit \begin_inset Quotes eld \end_inset diff --git a/lib/layouts/theorems-ams-extended.module b/lib/layouts/theorems-ams-extended.module index c63a9a4479..f43b301e18 100644 --- a/lib/layouts/theorems-ams-extended.module +++ b/lib/layouts/theorems-ams-extended.module @@ -5,7 +5,7 @@ #Condition, Note, Notation, Summary, Acknowledgement, Conclusion, #Fact, Assumption, and Case, in both starred and non-starred forms. #DescriptionEnd -#Requires: Theorems (AMS) +#Requires: theorems-ams # Original Author : David L. Johnson # Probably broken by Jean-Marc Lasgouttes diff --git a/lib/layouts/theorems-ams.module b/lib/layouts/theorems-ams.module index 7b06b445b9..5947da46a7 100644 --- a/lib/layouts/theorems-ams.module +++ b/lib/layouts/theorems-ams.module @@ -5,7 +5,7 @@ #the theorems are numbered consecutively throughout the document. This can be #changed by loading one of the Theorems (Ordered By ...) modules. #DescriptionEnd -#Excludes: Theorems | Theorems (Starred) +#Excludes: theorems-std | theorems-starred # Original Author : David L. Johnson # Probably broken by Jean-Marc Lasgouttes diff --git a/lib/layouts/theorems-chap.module b/lib/layouts/theorems-chap.module index aae92e23f4..5a747ea6f6 100644 --- a/lib/layouts/theorems-chap.module +++ b/lib/layouts/theorems-chap.module @@ -2,7 +2,7 @@ #DescriptionBegin #Numbers theorems and the like by chapter. #DescriptionEnd -#Requires: Theorems | Theorems (AMS) +#Requires: theorems-std | theorems-ams # Author: Richard Heck diff --git a/lib/layouts/theorems-sec.module b/lib/layouts/theorems-sec.module index 704ac589ba..909a72baa8 100644 --- a/lib/layouts/theorems-sec.module +++ b/lib/layouts/theorems-sec.module @@ -2,7 +2,7 @@ #DescriptionBegin #Numbers theorems and the like by section. #DescriptionEnd -#Requires: Theorems | Theorems (AMS) +#Requires: theorems-std | theorems-ams # Author: Richard Heck diff --git a/lib/layouts/theorems-starred.module b/lib/layouts/theorems-starred.module index df751dbea6..0a7b63afa0 100644 --- a/lib/layouts/theorems-starred.module +++ b/lib/layouts/theorems-starred.module @@ -3,7 +3,7 @@ #Defines only unnumbered theorem environments, and the proof environment, using #the extended AMS machinery. ##DescriptionEnd -#Excludes: Theorems (AMS) | Theorems (Starred) | Theorems (Order By Section) | Theorems (Order By Chapter) +#Excludes: theorems-std | theorems-ams # Author: Richard Heck diff --git a/lib/layouts/theorems-std.module b/lib/layouts/theorems-std.module index 1c2d7dd679..0d111920ca 100644 --- a/lib/layouts/theorems-std.module +++ b/lib/layouts/theorems-std.module @@ -4,7 +4,7 @@ #the theorems are numbered consecutively throughout the document. This can be #changed by loading one of the Theorems (Ordered By ...) modules. #DescriptionEnd -#Excludes: Theorems (AMS) | Theorems (Starred) +#Excludes: theorems-ams | theorems-starred # Author: Richard Heck diff --git a/lib/lyx2lyx/LyX.py b/lib/lyx2lyx/LyX.py index 4c9bf8995a..8b3e069a5a 100644 --- a/lib/lyx2lyx/LyX.py +++ b/lib/lyx2lyx/LyX.py @@ -80,7 +80,7 @@ format_relation = [("0_06", [200], minor_versions("0.6" , 4)), ("1_3", [221], minor_versions("1.3" , 7)), ("1_4", range(222,246), minor_versions("1.4" , 5)), ("1_5", range(246,277), minor_versions("1.5" , 2)), - ("1_6", range(277,313), minor_versions("1.6" , 0))] # JSpitzm: rotfloat support + ("1_6", range(277,314), minor_versions("1.6" , 0))] # Richard Heck: conversion of module representations def formats_list(): @@ -412,6 +412,30 @@ class LyX_base: self.header.insert(j, module) + def get_module_list(self): + i = find_token(self.header, "\\begin_modules", 0) + if (i == -1): + return [] + j = find_token(self.header, "\\end_modules", i) + return self.header[i + 1 : j] + + + def set_module_list(self, mlist): + modbegin = find_token(self.header, "\\begin_modules", 0) + if (modbegin == -1): + #No modules yet included + modbegin = find_token(self.header, "\\textclass", 0) + if modbegin == -1: + self.warning("Malformed LyX document: No \\textclass!!") + return + modend = find_token(self.header, "\\end_modules", modbegin) + if modend == -1: + self.warning("Malformed LyX document: No \\end_modules.") + return + newmodlist = ['\\begin_modules'] + mlist + ['\\end_modules'] + self.header[modbegin:modend + 1] = newmodlist + + def set_parameter(self, param, value): " Set the value of the header parameter." i = find_token(self.header, '\\' + param, 0) diff --git a/lib/lyx2lyx/lyx_1_6.py b/lib/lyx2lyx/lyx_1_6.py index 3996b883bc..a3bf1a788a 100644 --- a/lib/lyx2lyx/lyx_1_6.py +++ b/lib/lyx2lyx/lyx_1_6.py @@ -922,6 +922,40 @@ def convert_framed_notes(document): i = i + 1 +def convert_module_names(document): + modulemap = { 'Braille' : 'braille', 'Endnote' : 'endnotes', 'Foot to End' : 'foottoend',\ + 'Hanging' : 'hanging', 'Linguistics' : 'linguistics', 'Logical Markup' : 'logicalmkup', \ + 'Theorems (AMS-Extended)' : 'theorems-ams-extended', 'Theorems (AMS)' : 'theorems-ams', \ + 'Theorems (Order By Chapter)' : 'theorems-chap', 'Theorems (Order By Section)' : 'theorems-sec', \ + 'Theorems (Starred)' : 'theorems-starred', 'Theorems' : 'theorems-std' } + modlist = document.get_module_list() + newmodlist = [] + for mod in modlist: + if modulemap.has_key(mod): + newmodlist.append(modulemap[mod]) + else: + document.warning("Can't find module %s in the module map!" % mod) + newmodlist.append(mod) + document.set_module_list(newmodlist) + + +def revert_module_names(document): + modulemap = { 'braille' : 'Braille', 'endnotes' : 'Endnote', 'foottoend' : 'Foot to End',\ + 'hanging' : 'Hanging', 'linguistics' : 'Linguistics', 'logicalmkup' : 'Logical Markup', \ + 'theorems-ams-extended' : 'Theorems (AMS-Extended)', 'theorems-ams' : 'Theorems (AMS)', \ + 'theorems-chap' : 'Theorems (Order By Chapter)', 'theorems-sec' : 'Theorems (Order By Section)', \ + 'theorems-starred' : 'Theorems (Starred)', 'theorems-std' : 'Theorems'} + modlist = document.get_module_list() + newmodlist = [] + for mod in modlist: + if modulemap.has_key(mod): + newmodlist.append(modulemap[mod]) + else: + document.warning("Can't find module %s in the module map!" % mod) + newmodlist.append(mod) + document.set_module_list(newmodlist) + + def revert_framed_notes(document): "Revert framed boxes to notes. " i = 0 @@ -1213,9 +1247,11 @@ convert = [[277, [fix_wrong_tables]], [310, []], [311, [convert_ams_classes]], [312, []], + [313, [convert_module_names]] ] -revert = [[311, [revert_rotfloat, revert_widesideways]], +revert = [[312, [revert_module_names]], + [311, [revert_rotfloat, revert_widesideways]], [310, []], [309, [revert_btprintall]], [308, [revert_nocite]], diff --git a/src/Buffer.cpp b/src/Buffer.cpp index d33fa3f31a..7cef49baef 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -118,7 +118,7 @@ namespace os = support::os; namespace { -int const LYX_FORMAT = 312; // JSpitzm: rotfloat support +int const LYX_FORMAT = 313; // Richard Heck: conversion of module representations } // namespace anon diff --git a/src/ModuleList.cpp b/src/ModuleList.cpp index 0b88a8e8f2..3b7e8134cb 100644 --- a/src/ModuleList.cpp +++ b/src/ModuleList.cpp @@ -34,13 +34,15 @@ namespace lyx { ModuleList moduleList; -LyXModule::LyXModule(string const & n, string const & f, +LyXModule::LyXModule(string const & n, string const & i, string const & d, vector const & p, vector const & r, vector const & e): - name(n), filename(f), description(d), + name(n), id(i), description(d), packageList(p), requiredModules(r), excludedModules(e), checked(false) -{} +{ + filename = id + ".module"; +} bool LyXModule::isAvailable() { @@ -204,7 +206,7 @@ LyXModuleList::iterator ModuleList::end() } -LyXModule * ModuleList::operator[](string const & str) +LyXModule * ModuleList::getModuleByName(string const & str) { LyXModuleList::iterator it = modlist_.begin(); for (; it != modlist_.end(); ++it) @@ -215,4 +217,15 @@ LyXModule * ModuleList::operator[](string const & str) return 0; } +LyXModule * ModuleList::operator[](string const & str) +{ + LyXModuleList::iterator it = modlist_.begin(); + for (; it != modlist_.end(); ++it) + if (it->getID() == str) { + LyXModule & mod = *it; + return &mod; + } + return 0; +} + } // namespace lyx diff --git a/src/ModuleList.h b/src/ModuleList.h index 2281a5d74c..804cce1dea 100644 --- a/src/ModuleList.h +++ b/src/ModuleList.h @@ -28,7 +28,7 @@ namespace lyx { class LyXModule { public: /// - LyXModule(std::string const & n, std::string const & f, + LyXModule(std::string const & n, std::string const & i, std::string const & d, std::vector const & p, std::vector const & r, std::vector const & e); @@ -37,6 +37,8 @@ public: /// std::string const & getName() const { return name; } /// + std::string const & getID() const { return id; } + /// std::string const & getFilename() const { return filename; } /// std::string const & getDescription() const { return description; } @@ -53,7 +55,10 @@ public: private: /// what appears in the ui std::string name; - /// the filename, without any path + /// the module's unique identifier + /// at present, this is the filename, without the extension + std::string id; + /// the filename std::string filename; /// a short description for use in the ui std::string description; @@ -93,8 +98,11 @@ public: bool empty() const { return modlist_.empty(); } /// Returns a pointer to the LyXModule with name str. /// Returns a null pointer if no such module is found. + LyXModule * getModuleByName(std::string const & str); + /// Returns a pointer to the LyXModule with filename str. + /// Returns a null pointer if no such module is found. LyXModule * operator[](std::string const & str); -private: + private: /// noncopyable ModuleList(ModuleList const &); /// diff --git a/src/frontends/qt4/GuiDocument.cpp b/src/frontends/qt4/GuiDocument.cpp index 872cb4499f..5736964641 100644 --- a/src/frontends/qt4/GuiDocument.cpp +++ b/src/frontends/qt4/GuiDocument.cpp @@ -189,16 +189,29 @@ ModuleSelMan::ModuleSelMan( QPushButton * delPB, QPushButton * upPB, QPushButton * downPB, - QStringListModel * availableModel, - QStringListModel * selectedModel) : + GuiIdListModel * availableModel, + GuiIdListModel * selectedModel) : GuiSelectionManager(availableLV, selectedLV, addPB, delPB, upPB, downPB, availableModel, selectedModel) {} +namespace { +QModelIndex getSelectedIndex(QListView * lv) +{ + QModelIndex retval = QModelIndex(); + QModelIndexList selIdx = + lv->selectionModel()->selectedIndexes(); + if (!selIdx.empty()) + retval = selIdx.first(); + return retval; +} +} + + void ModuleSelMan::updateAddPB() { - int const arows = availableModel->stringList().size(); + int const arows = availableModel->rowCount(); QModelIndexList const availSels = availableLV->selectionModel()->selectedIndexes(); if (arows == 0 || availSels.isEmpty() || isSelected(availSels.first())) { @@ -207,7 +220,7 @@ void ModuleSelMan::updateAddPB() } QModelIndex const & idx = availableLV->selectionModel()->currentIndex(); - string const modName = fromqstr(idx.data().toString()); + string const modName = getAvailableModel()->getIDString(idx.row()); vector reqs = getRequiredList(modName); vector excl = getExcludedList(modName); @@ -216,7 +229,13 @@ void ModuleSelMan::updateAddPB() return; } - QStringList const & qsl = selectedModel->stringList(); + int const srows = selectedModel->rowCount(); + vector selModList; + for (int i = 0; i < srows; ++i) + selModList.push_back(getSelectedModel()->getIDString(i)); + + vector::const_iterator selModStart = selModList.begin(); + vector::const_iterator selModEnd = selModList.end(); //Check whether some required module is available if (!reqs.empty()) { @@ -224,7 +243,7 @@ void ModuleSelMan::updateAddPB() vector::const_iterator it = reqs.begin(); vector::const_iterator end = reqs.end(); for (; it != end; ++it) { - if (qsl.contains(toqstr(*it))) { + if (find(selModStart, selModEnd, *it) != selModEnd) { foundOne = true; break; } @@ -240,7 +259,7 @@ void ModuleSelMan::updateAddPB() vector::const_iterator it = excl.begin(); vector::const_iterator end = excl.end(); for (; it != end; ++it) { - if (qsl.contains(toqstr(*it))) { + if (find(selModStart, selModEnd, *it) != selModEnd) { addPB->setEnabled(false); return; } @@ -250,9 +269,10 @@ void ModuleSelMan::updateAddPB() addPB->setEnabled(true); } + void ModuleSelMan::updateDownPB() { - int const srows = selectedModel->stringList().size(); + int const srows = selectedModel->rowCount(); if (srows == 0) { downPB->setEnabled(false); return; @@ -265,15 +285,14 @@ void ModuleSelMan::updateDownPB() return; } //determine whether immediately succeding element requires this one - QString const curModName = - selectedLV->selectionModel()->currentIndex().data().toString(); - QStringList const & qsl = selectedModel->stringList(); - int const curIdx = qsl.indexOf(curModName); - if (curIdx < 0 || curIdx == srows - 1) { //this shouldn't happen... + QModelIndex const & curIdx = selectedLV->selectionModel()->currentIndex(); + int curRow = curIdx.row(); + if (curRow < 0 || curRow >= srows - 1) { //this shouldn't happen... downPB->setEnabled(false); return; } - string nextModName = fromqstr(qsl[curIdx + 1]); + string const curModName = getSelectedModel()->getIDString(curRow); + string const nextModName = getSelectedModel()->getIDString(curRow + 1); vector reqs = getRequiredList(nextModName); @@ -287,12 +306,12 @@ void ModuleSelMan::updateDownPB() //if this one is required, there is also an earlier one that is required. //enable it if this module isn't required downPB->setEnabled( - find(reqs.begin(), reqs.end(), fromqstr(curModName)) == reqs.end()); + find(reqs.begin(), reqs.end(), curModName) == reqs.end()); } void ModuleSelMan::updateUpPB() { - int const srows = selectedModel->stringList().size(); + int const srows = selectedModel->rowCount(); if (srows == 0) { upPB->setEnabled(false); return; @@ -304,10 +323,16 @@ void ModuleSelMan::updateUpPB() upPB->setEnabled(false); return; } + //determine whether immediately preceding element is required by this one - QString const curModName = - selectedLV->selectionModel()->currentIndex().data().toString(); - vector reqs = getRequiredList(fromqstr(curModName)); + QModelIndex const & curIdx = selectedLV->selectionModel()->currentIndex(); + int curRow = curIdx.row(); + if (curRow <= -1 || curRow > srows - 1) { //sanity check + downPB->setEnabled(false); + return; + } + string const curModName = getSelectedModel()->getIDString(curRow); + vector reqs = getRequiredList(curModName); //if this one doesn't require anything.... if (reqs.empty()) { @@ -315,13 +340,7 @@ void ModuleSelMan::updateUpPB() return; } - QStringList const & qsl = selectedModel->stringList(); - int const curIdx = qsl.indexOf(curModName); - if (curIdx <= 0) { //this shouldn't happen... - upPB->setEnabled(false); - return; - } - string preModName = fromqstr(qsl[curIdx - 1]); + string preModName = getSelectedModel()->getIDString(curRow - 1); //NOTE This is less flexible than it might be. You could check whether, even //if this one is required, there is also an earlier one that is required. @@ -331,7 +350,7 @@ void ModuleSelMan::updateUpPB() void ModuleSelMan::updateDelPB() { - int const srows = selectedModel->stringList().size(); + int const srows = selectedModel->rowCount(); if (srows == 0) { deletePB->setEnabled(false); return; @@ -346,59 +365,49 @@ void ModuleSelMan::updateDelPB() //determine whether some LATER module requires this one //NOTE Things are arranged so that this is the only way there //can be a problem. At least, we hope so. - QString const curModName = - selectedLV->selectionModel()->currentIndex().data().toString(); - QStringList const & qsl = selectedModel->stringList(); + QModelIndex const & curIdx = + selectedLV->selectionModel()->currentIndex(); + int const curRow = curIdx.row(); + if (curRow < 0 || curRow >= srows) { //this shouldn't happen + deletePB->setEnabled(false); + return; + } + + QString const curModName = curIdx.data().toString(); //We're looking here for a reason NOT to enable the button. If we //find one, we disable it and return. If we don't, we'll end up at //the end of the function, and then we enable it. - QStringList::const_iterator it = qsl.begin(); - QStringList::const_iterator end = qsl.end(); - bool found = false; - for (; it != end; ++it) { - //skip over the ones preceding this one - if (!found) { - if (*it == curModName) { - found = true; - } - continue; - } - - string const mod = fromqstr(*it); - vector reqs = getRequiredList(mod); + for (int i = curRow + 1; i < srows; ++i) { + string const thisMod = getSelectedModel()->getIDString(i); + vector reqs = getRequiredList(thisMod); //does this one require us? if (find(reqs.begin(), reqs.end(), fromqstr(curModName)) == reqs.end()) //no... continue; - //OK, so there is a module that requires us - //is there an EARLIER module that satisfies the require? + //OK, so this module requires us + //is there an EARLIER module that also satisfies the require? //NOTE We demand that it be earlier to keep the list of modules //consistent with the rule that a module must be proceeded by a //required module. There would be more flexible ways to proceed, //but that would be a lot more complicated, and the logic here is //already complicated. (That's why I've left the debugging code.) - //lyxerr << "Testing " << mod << std::endl; - QStringList::const_iterator it2 = qsl.begin(); - QStringList::const_iterator end2 = qsl.end(); - for (; it2 != end2; ++it2) { - //lyxerr << "In loop: Testing " << fromqstr(*it2) << std::endl; - if (*it2 == curModName) { //EARLIER!! - //no other module was found before this one, so... - //lyxerr << "Reached the end of the loop." << std::endl; - deletePB->setEnabled(false); - return; - } + //lyxerr << "Testing " << thisMod << std::endl; + bool foundOne = false; + for (int j = 0; j < curRow; ++j) { + string const mod = getSelectedModel()->getIDString(j); + //lyxerr << "In loop: Testing " << mod << std::endl; //do we satisfy the require? - if (find(reqs.begin(), reqs.end(), fromqstr(*it2)) != reqs.end()) { - //lyxerr << fromqstr(*it2) << " does the trick." << std::endl; + if (find(reqs.begin(), reqs.end(), mod) != reqs.end()) { + //lyxerr << mod << " does the trick." << std::endl; + foundOne = true; break; } } - //did we reach the end of the list? - if (it2 == end2) { - //lyxerr << "Reached end of list." << std::endl; + //did we find a module to satisfy the require? + if (!foundOne) { + //lyxerr << "No matching module found." << std::endl; deletePB->setEnabled(false); return; } @@ -1221,54 +1230,75 @@ namespace { t = subst(t, _("and"), s); return bformat(t, retval, from_ascii(v[vSize - 2]), from_ascii(v[vSize - 1])); } + + vector idsToNames(vector const & idList) + { + vector retval; + vector::const_iterator it = idList.begin(); + vector::const_iterator end = idList.end(); + for (; it != end; ++it) { + LyXModule const * const mod = moduleList[*it]; + if (!mod) + retval.push_back(*it + " (Unavailable)"); + else + retval.push_back(mod->getName()); + } + return retval; + } } void GuiDocument::updateModuleInfo() { selectionManager->update(); + //Module description - QListView const * const lv = selectionManager->selectedFocused() ? - latexModule->selectedLV : - latexModule->availableLV; - if (lv->selectionModel()->selectedIndexes().isEmpty()) + bool const focusOnSelected = selectionManager->selectedFocused(); + QListView const * const lv = + focusOnSelected ? latexModule->selectedLV : latexModule->availableLV; + if (lv->selectionModel()->selectedIndexes().isEmpty()) { latexModule->infoML->document()->clear(); - else { - QModelIndex const & idx = lv->selectionModel()->currentIndex(); - string const modName = fromqstr(idx.data().toString()); - docstring desc = getModuleDescription(modName); - - vector pkgList = getPackageList(modName); - docstring pkgdesc = formatStrVec(pkgList, _("and")); - if (!pkgdesc.empty()) { - if (!desc.empty()) - desc += "\n"; - desc += bformat(_("Package(s) required: %1$s."), pkgdesc); - } - - pkgList = getRequiredList(modName); - pkgdesc = formatStrVec(pkgList, _("or")); - if (!pkgdesc.empty()) { - if (!desc.empty()) - desc += "\n"; - desc += bformat(_("Module required: %1$s."), pkgdesc); - } - - pkgList = getExcludedList(modName); - pkgdesc = formatStrVec(pkgList, _( "and")); - if (!pkgdesc.empty()) { - if (!desc.empty()) - desc += "\n"; - desc += bformat(_("Modules excluded: %1$s."), pkgdesc); - } - - if (!isModuleAvailable(modName)) { - if (!desc.empty()) - desc += "\n"; - desc += _("WARNING: Some packages are unavailable!"); - } - latexModule->infoML->document()->setPlainText(toqstr(desc)); + return; } + QModelIndex const & idx = lv->selectionModel()->currentIndex(); + GuiIdListModel const & idModel = + focusOnSelected ? selected_model_ : available_model_; + string const modName = idModel.getIDString(idx.row()); + docstring desc = getModuleDescription(modName); + + vector pkgList = getPackageList(modName); + docstring pkgdesc = formatStrVec(pkgList, _("and")); + if (!pkgdesc.empty()) { + if (!desc.empty()) + desc += "\n"; + desc += bformat(_("Package(s) required: %1$s."), pkgdesc); + } + + pkgList = getRequiredList(modName); + if (!pkgList.empty()) { + vector const reqDescs = idsToNames(pkgList); + pkgdesc = formatStrVec(reqDescs, _("or")); + if (!desc.empty()) + desc += "\n"; + desc += bformat(_("Module required: %1$s."), pkgdesc); + } + + pkgList = getExcludedList(modName); + if (!pkgList.empty()) { + vector const reqDescs = idsToNames(pkgList); + pkgdesc = formatStrVec(reqDescs, _( "and")); + if (!desc.empty()) + desc += "\n"; + desc += bformat(_("Modules excluded: %1$s."), pkgdesc); + } + + if (!isModuleAvailable(modName)) { + if (!desc.empty()) + desc += "\n"; + desc += _("WARNING: Some packages are unavailable!"); + } + + latexModule->infoML->document()->setPlainText(toqstr(desc)); } @@ -1408,9 +1438,10 @@ void GuiDocument::apply(BufferParams & params) // Modules params.clearLayoutModules(); - QStringList const selMods = selectedModel()->stringList(); - for (int i = 0; i != selMods.size(); ++i) - params.addLayoutModule(lyx::fromqstr(selMods[i])); + int const srows = selected_model_.rowCount(); + vector selModList; + for (int i = 0; i < srows; ++i) + params.addLayoutModule(selected_model_.getIDString(i)); if (mathsModule->amsautoCB->isChecked()) { params.use_amsmath = BufferParams::package_auto; @@ -1922,27 +1953,40 @@ void GuiDocument::saveDocDefault() } +void GuiDocument::updateAvailableModules() +{ + available_model_.clear(); + vector const modInfoList = getModuleInfo(); + int const mSize = modInfoList.size(); + for (int i = 0; i < mSize; ++i) { + modInfoStruct const & modInfo = modInfoList[i]; + available_model_.insertRow(i, modInfo.name, modInfo.id); + } +} + + +void GuiDocument::updateSelectedModules() +{ + //and selected ones, too + selected_model_.clear(); + vector const selModList = getSelectedModules(); + int const sSize = selModList.size(); + for (int i = 0; i < sSize; ++i) { + modInfoStruct const & modInfo = selModList[i]; + selected_model_.insertRow(i, modInfo.name, modInfo.id); + } +} + + void GuiDocument::updateContents() { - //update list of available modules - QStringList strlist; - vector const modNames = getModuleNames(); - vector::const_iterator it = modNames.begin(); - for (; it != modNames.end(); ++it) - strlist.push_back(toqstr(*it)); - available_model_.setStringList(strlist); - //and selected ones, too - QStringList strlist2; - vector const & selMods = getSelectedModules(); - it = selMods.begin(); - for (; it != selMods.end(); ++it) - strlist2.push_back(toqstr(*it)); + updateAvailableModules(); + updateSelectedModules(); + //FIXME It'd be nice to make sure here that the selected //modules are consistent: That required modules are actually //selected, and that we don't have conflicts. If so, we could //at least pop up a warning. - selected_model_.setStringList(strlist2); - updateParams(bp_); } @@ -1973,7 +2017,7 @@ char const * GuiDocument::fontfamilies_gui[5] = { bool GuiDocument::initialiseParams(string const &) { bp_ = buffer().params(); - loadModuleNames(); + loadModuleInfo(); return true; } @@ -1990,15 +2034,29 @@ BufferId GuiDocument::id() const } -vector const & GuiDocument::getModuleNames() +vector const & GuiDocument::getModuleInfo() { return moduleNames_; } -vector const & GuiDocument::getSelectedModules() +vector const GuiDocument::getSelectedModules() { - return params().getModules(); + vector const & mods = params().getModules(); + vector::const_iterator it = mods.begin(); + vector::const_iterator end = mods.end(); + vector mInfo; + for (; it != end; ++it) { + modInfoStruct m; + m.id = *it; + LyXModule * mod = moduleList[*it]; + if (mod) + m.name = mod->getName(); + else + m.name = *it + " (Not Found)"; + mInfo.push_back(m); + } + return mInfo; } @@ -2118,14 +2176,17 @@ bool GuiDocument::providesScale(string const & font) const } -void GuiDocument::loadModuleNames () +void GuiDocument::loadModuleInfo() { moduleNames_.clear(); - LyXModuleList::const_iterator it = moduleList.begin(); - for (; it != moduleList.end(); ++it) - moduleNames_.push_back(it->getName()); - if (!moduleNames_.empty()) - sort(moduleNames_.begin(), moduleNames_.end()); + LyXModuleList::const_iterator it = moduleList.begin(); + LyXModuleList::const_iterator end = moduleList.end(); + for (; it != end; ++it) { + modInfoStruct m; + m.id = it->getID(); + m.name = it->getName(); + moduleNames_.push_back(m); + } } diff --git a/src/frontends/qt4/GuiDocument.h b/src/frontends/qt4/GuiDocument.h index ac9dda04b0..fc136f2f2c 100644 --- a/src/frontends/qt4/GuiDocument.h +++ b/src/frontends/qt4/GuiDocument.h @@ -13,10 +13,13 @@ #ifndef GUIDOCUMENT_H #define GUIDOCUMENT_H -#include "GuiDialog.h" -#include "BulletsModule.h" -#include "GuiSelectionManager.h" +#include + #include "BufferParams.h" +#include "BulletsModule.h" +#include "GuiDialog.h" +#include "GuiIdListModel.h" +#include "GuiSelectionManager.h" #include "support/types.h" @@ -52,13 +55,6 @@ class PreambleModule; /// typedef void const * BufferId; -#include -#include -#include - -#include -#include - template class UiWidget : public QWidget, public UI { @@ -77,8 +73,8 @@ public: QPushButton * delPB, QPushButton * upPB, QPushButton * downPB, - QStringListModel * availableModel, - QStringListModel * selectedModel); + GuiIdListModel * availableModel, + GuiIdListModel * selectedModel); private: /// virtual void updateAddPB(); @@ -88,6 +84,16 @@ private: virtual void updateDownPB(); /// virtual void updateDelPB(); + /// returns availableModel as a GuiIdListModel + GuiIdListModel * getAvailableModel() + { + return dynamic_cast(availableModel); + }; + /// returns selectedModel as a GuiIdListModel + GuiIdListModel * getSelectedModel() + { + return dynamic_cast(selectedModel); + }; }; @@ -158,22 +164,26 @@ private: std::vector lang_; /// Available modules - QStringListModel * availableModel() { return &available_model_; } + GuiIdListModel * availableModel() { return &available_model_; } /// Selected modules - QStringListModel * selectedModel() { return &selected_model_; } + GuiIdListModel * selectedModel() { return &selected_model_; } private: /// Apply changes void applyView(); /// update void updateContents(); + /// + void updateAvailableModules(); + /// + void updateSelectedModules(); /// save as default template void saveDocDefault(); /// reset to default params void useClassDefaults(); /// available modules - QStringListModel available_model_; + GuiIdListModel available_model_; /// selected modules - QStringListModel selected_model_; + GuiIdListModel selected_model_; protected: /// return false if validate_listings_params returns error @@ -201,10 +211,15 @@ protected: BufferParams const & params() const { return bp_; } /// BufferId id() const; + /// + struct modInfoStruct { + std::string name; + std::string id; + }; /// List of available modules - std::vector const & getModuleNames(); + std::vector const & getModuleInfo(); /// Modules in use in current buffer - std::vector const & getSelectedModules(); + std::vector const getSelectedModules(); /// void setLanguage() const; /// @@ -219,11 +234,11 @@ protected: bool providesScale(std::string const & font) const; private: /// - void loadModuleNames(); + void loadModuleInfo(); /// BufferParams bp_; /// List of names of available modules - std::vector moduleNames_; + std::vector moduleNames_; }; @@ -256,4 +271,4 @@ private: } // namespace frontend } // namespace lyx -#endif // QDOCUMENT_H +#endif // GUIDOCUMENT_H diff --git a/src/frontends/qt4/GuiSelectionManager.cpp b/src/frontends/qt4/GuiSelectionManager.cpp index eea9d40edb..74e3cf9de4 100644 --- a/src/frontends/qt4/GuiSelectionManager.cpp +++ b/src/frontends/qt4/GuiSelectionManager.cpp @@ -15,10 +15,14 @@ #include #include "GuiSelectionManager.h" +#include "support/debug.h" + +using std::vector; namespace lyx { namespace frontend { + GuiSelectionManager::GuiSelectionManager( QListView * avail, QListView * sel, @@ -26,8 +30,8 @@ GuiSelectionManager::GuiSelectionManager( QPushButton * del, QPushButton * up, QPushButton * down, - QStringListModel * amod, - QStringListModel * smod) + QAbstractListModel * amod, + QAbstractListModel * smod) { availableLV = avail; selectedLV = sel; @@ -78,7 +82,7 @@ void GuiSelectionManager::update() void GuiSelectionManager::updateAddPB() { - int const arows = availableModel->stringList().size(); + int const arows = availableModel->rowCount(); QModelIndexList const availSels = availableLV->selectionModel()->selectedIndexes(); addPB->setEnabled(arows > 0 && @@ -89,7 +93,7 @@ void GuiSelectionManager::updateAddPB() void GuiSelectionManager::updateDelPB() { - int const srows = selectedModel->stringList().size(); + int const srows = selectedModel->rowCount(); if (srows == 0) { deletePB->setEnabled(false); return; @@ -103,7 +107,7 @@ void GuiSelectionManager::updateDelPB() void GuiSelectionManager::updateUpPB() { - int const srows = selectedModel->stringList().size(); + int const srows = selectedModel->rowCount(); if (srows == 0) { upPB->setEnabled(false); return; @@ -117,7 +121,7 @@ void GuiSelectionManager::updateUpPB() void GuiSelectionManager::updateDownPB() { - int const srows = selectedModel->stringList().size(); + int const srows = selectedModel->rowCount(); if (srows == 0) { downPB->setEnabled(false); return; @@ -128,10 +132,17 @@ void GuiSelectionManager::updateDownPB() downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1); } + bool GuiSelectionManager::isSelected(const QModelIndex & idx) { - QString const str = idx.data().toString(); - return selectedModel->stringList().contains(str); + if (selectedModel->rowCount() == 0) + return false; + QVariant const & str = availableModel->data(idx, Qt::DisplayRole); + QModelIndexList qmil = + selectedModel->match(selectedModel->index(0), + Qt::DisplayRole, str, + Qt::MatchExactly | Qt::MatchWrap); + return !qmil.empty(); } @@ -155,7 +166,8 @@ void GuiSelectionManager::selectedChanged(const QModelIndex & idx, const QModelI } -static QModelIndex getSelectedIndex(QListView * lv) +namespace { +QModelIndex getSelectedIndex(QListView * lv) { QModelIndex retval = QModelIndex(); QModelIndexList selIdx = @@ -164,6 +176,18 @@ static QModelIndex getSelectedIndex(QListView * lv) retval = selIdx.first(); return retval; } +} + + +bool GuiSelectionManager::insertRowToSelected(int i, + QMap const & itemData) +{ + if (i <= -1 || i > selectedModel->rowCount()) + return false; + if (!selectedModel->insertRow(i)) + return false; + return selectedModel->setItemData(selectedModel->index(i), itemData); +} void GuiSelectionManager::addPB_clicked() @@ -171,15 +195,17 @@ void GuiSelectionManager::addPB_clicked() QModelIndex const idxToAdd = getSelectedIndex(availableLV); if (!idxToAdd.isValid()) return; - QModelIndex idx = selectedLV->currentIndex(); + QModelIndex const idx = selectedLV->currentIndex(); + int const srows = selectedModel->rowCount(); + + QMap qm = availableModel->itemData(idxToAdd); + insertRowToSelected(srows, qm); - QStringList keys = selectedModel->stringList(); - keys.append(idxToAdd.data().toString()); - selectedModel->setStringList(keys); selectionChanged(); //signal if (idx.isValid()) selectedLV->setCurrentIndex(idx); + updateHook(); } @@ -190,9 +216,7 @@ void GuiSelectionManager::deletePB_clicked() if (!idx.isValid()) return; - QStringList keys = selectedModel->stringList(); - keys.removeAt(idx.row()); - selectedModel->setStringList(keys); + selectedModel->removeRow(idx.row()); selectionChanged(); //signal int nrows = selectedLV->model()->rowCount(); @@ -211,13 +235,18 @@ void GuiSelectionManager::deletePB_clicked() void GuiSelectionManager::upPB_clicked() { QModelIndex idx = selectedLV->currentIndex(); - + int const pos = idx.row(); - QStringList keys = selectedModel->stringList(); - keys.swap(pos, pos - 1); - selectedModel->setStringList(keys); - selectionChanged(); //signal + if (pos <= 0) + return; + QMap qm = selectedModel->itemData(idx); + + selectedModel->removeRow(pos); + insertRowToSelected(pos - 1, qm); + + selectionChanged(); //signal + selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column())); selectedHasFocus_ = true; updateHook(); @@ -227,11 +256,16 @@ void GuiSelectionManager::upPB_clicked() void GuiSelectionManager::downPB_clicked() { QModelIndex idx = selectedLV->currentIndex(); - + int const pos = idx.row(); - QStringList keys = selectedModel->stringList(); - keys.swap(pos, pos + 1); - selectedModel->setStringList(keys); + if (pos >= selectedModel->rowCount() - 1) + return; + + QMap qm = selectedModel->itemData(idx); + + selectedModel->removeRow(pos); + insertRowToSelected(pos + 1, qm); + selectionChanged(); //signal selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column())); @@ -311,9 +345,7 @@ bool GuiSelectionManager::eventFilter(QObject * obj, QEvent * event) if (keyModifiers == Qt::NoModifier && deletePB->isEnabled()) deletePB_clicked(); else if (keyModifiers == Qt::ControlModifier) { - QStringList list = selectedModel->stringList(); - list.clear(); - selectedModel->setStringList(list); + selectedModel->removeRows(0, selectedModel->rowCount()); updateHook(); } else //ignore it otherwise diff --git a/src/frontends/qt4/GuiSelectionManager.h b/src/frontends/qt4/GuiSelectionManager.h index fa176327e7..43ceea7b50 100644 --- a/src/frontends/qt4/GuiSelectionManager.h +++ b/src/frontends/qt4/GuiSelectionManager.h @@ -17,10 +17,14 @@ #include #include #include -#include +#include #include #include +#include "support/qstring_helpers.h" + +#include + namespace lyx { namespace frontend { @@ -45,8 +49,8 @@ public: QPushButton * delPB, QPushButton * upPB, QPushButton * downPB, - QStringListModel * availableModel, - QStringListModel * selectedModel); + QAbstractListModel * availableModel, + QAbstractListModel * selectedModel); /// Sets the state of the various push buttons, depending upon the /// state of the widgets. (E.g., "delete" is enabled only if the /// selection is non-empty.) @@ -82,6 +86,8 @@ protected: ///been selected (i.e., is also in selectedLV). bool isSelected(const QModelIndex & idx); /// + bool insertRowToSelected(int i, QMap const & itemData); + /// QListView * availableLV; /// QListView * selectedLV; @@ -94,9 +100,9 @@ protected: /// QPushButton * downPB; /// - QStringListModel * availableModel; + QAbstractListModel * availableModel; /// - QStringListModel * selectedModel; + QAbstractListModel * selectedModel; protected Q_SLOTS: /// @@ -104,13 +110,13 @@ protected Q_SLOTS: /// void selectedChanged(const QModelIndex & idx, const QModelIndex &); /// - void addPB_clicked(); + virtual void addPB_clicked(); /// - void deletePB_clicked(); + virtual void deletePB_clicked(); /// - void upPB_clicked(); + virtual void upPB_clicked(); /// - void downPB_clicked(); + virtual void downPB_clicked(); /// void availableLV_clicked(const QModelIndex &); /// @@ -129,7 +135,7 @@ private: virtual void updateDownPB(); /// virtual void updateUpPB(); - + /// bool selectedHasFocus_; }; diff --git a/src/frontends/qt4/Makefile.am b/src/frontends/qt4/Makefile.am index 4aae59b510..aa7a80737c 100644 --- a/src/frontends/qt4/Makefile.am +++ b/src/frontends/qt4/Makefile.am @@ -86,6 +86,7 @@ SOURCEFILES = \ GuiFontMetrics.cpp \ GuiGraphics.cpp \ GuiHyperlink.cpp \ + GuiIdListModel.cpp \ GuiImage.cpp \ GuiInclude.cpp \ GuiIndex.cpp \ @@ -175,6 +176,7 @@ MOCHEADER = \ GuiFontExample.h \ GuiGraphics.h \ GuiHyperlink.h \ + GuiIdListModel.h \ GuiInclude.h \ GuiIndex.h \ GuiKeySymbol.h \