]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiDocument.cpp
We do not need to update just because the class has been changed.
[lyx.git] / src / frontends / qt4 / GuiDocument.cpp
index 93d1f19efa35aea22bccc437dd407036c136bd4b..872cb4499f8c1607c7a30468ececae06dbb7e988 100644 (file)
@@ -42,6 +42,7 @@
 
 #include "insets/InsetListingsParams.h"
 
+//#include "support/debug.h"
 #include "support/FileName.h"
 #include "support/filetools.h"
 #include "support/lstrings.h"
 #include <algorithm>
 #include <sstream>
 
-using std::distance;
-using std::make_pair;
-using std::pair;
-using std::vector;
-using std::string;
-using std::ostringstream;
-using std::sort;
+using namespace std;
+using namespace lyx::support;
 
 ///
 template<class Pair>
-std::vector<typename Pair::second_type> const
-getSecond(std::vector<Pair> const & pr)
+vector<typename Pair::second_type> const
+getSecond(vector<Pair> const & pr)
 {
-        std::vector<typename Pair::second_type> tmp(pr.size());
-        std::transform(pr.begin(), pr.end(), tmp.begin(),
+        vector<typename Pair::second_type> tmp(pr.size());
+        transform(pr.begin(), pr.end(), tmp.begin(),
                                         boost::bind(&Pair::second, _1));
         return tmp;
 }
@@ -139,10 +135,278 @@ vector<pair<string, lyx::docstring> > pagestyles;
 namespace lyx {
 namespace frontend {
 
-using support::token;
-using support::bformat;
-using support::findToken;
-using support::getVectorFromString;
+namespace {
+       vector<string> getRequiredList(string const & modName) 
+       {
+               LyXModule const * const mod = moduleList[modName];
+               if (!mod)
+                       return vector<string>(); //empty such thing
+               return mod->getRequiredModules();
+       }
+
+
+       vector<string> getExcludedList(string const & modName)
+       {
+               LyXModule const * const mod = moduleList[modName];
+               if (!mod)
+                       return vector<string>(); //empty such thing
+               return mod->getExcludedModules();
+       }
+
+
+       docstring getModuleDescription(string const & modName)
+       {
+               LyXModule const * const mod = moduleList[modName];
+               if (!mod)
+                       return _("Module not found!");
+               return from_ascii(mod->getDescription());
+       }
+
+
+       vector<string> getPackageList(string const & modName)
+       {
+               LyXModule const * const mod = moduleList[modName];
+               if (!mod)
+                       return vector<string>(); //empty such thing
+               return mod->getPackageList();
+       }
+
+
+       bool isModuleAvailable(string const & modName)
+       {
+               LyXModule * mod = moduleList[modName];
+               if (!mod)
+                       return false;
+               return mod->isAvailable();
+       }
+} //anonymous namespace
+
+
+ModuleSelMan::ModuleSelMan(
+       QListView * availableLV, 
+       QListView * selectedLV,
+       QPushButton * addPB, 
+       QPushButton * delPB, 
+       QPushButton * upPB, 
+       QPushButton * downPB,
+       QStringListModel * availableModel,
+       QStringListModel * selectedModel) :
+GuiSelectionManager(availableLV, selectedLV, addPB, delPB,
+                    upPB, downPB, availableModel, selectedModel) 
+{}
+       
+       
+void ModuleSelMan::updateAddPB() 
+{
+       int const arows = availableModel->stringList().size();
+       QModelIndexList const availSels = 
+                       availableLV->selectionModel()->selectedIndexes();
+       if (arows == 0 || availSels.isEmpty()  || isSelected(availSels.first())) {
+               addPB->setEnabled(false);
+               return;
+       }
+       
+       QModelIndex const & idx = availableLV->selectionModel()->currentIndex();
+       string const modName = fromqstr(idx.data().toString());
+       vector<string> reqs = getRequiredList(modName);
+       vector<string> excl = getExcludedList(modName);
+       
+       if (reqs.empty() && excl.empty()) {
+               addPB->setEnabled(true);
+               return;
+       }
+
+       QStringList const & qsl = selectedModel->stringList();
+       
+       //Check whether some required module is available
+       if (!reqs.empty()) {
+               bool foundOne = false;
+               vector<string>::const_iterator it  = reqs.begin();
+               vector<string>::const_iterator end = reqs.end();
+               for (; it != end; ++it) {
+                       if (qsl.contains(toqstr(*it))) {
+                               foundOne = true;
+                               break;
+                       }
+               }
+               if (!foundOne) {
+                       addPB->setEnabled(false);
+                       return;
+               }
+       }
+       
+       //Check whether any excluded module is being used
+       if (!excl.empty()) {
+               vector<string>::const_iterator it  = excl.begin();
+               vector<string>::const_iterator end = excl.end();
+               for (; it != end; ++it) {
+                       if (qsl.contains(toqstr(*it))) {
+                               addPB->setEnabled(false);
+                               return;
+                       }
+               }
+       }
+
+       addPB->setEnabled(true);
+}
+
+void ModuleSelMan::updateDownPB()
+{
+       int const srows = selectedModel->stringList().size();
+       if (srows == 0) {
+               downPB->setEnabled(false);
+               return;
+       }
+       QModelIndexList const selSels = 
+                       selectedLV->selectionModel()->selectedIndexes();
+       //disable if empty or last item is selected
+       if (selSels.empty() || selSels.first().row() == srows - 1) {
+               downPB->setEnabled(false);
+               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...
+               downPB->setEnabled(false);
+               return;
+       }
+       string nextModName = fromqstr(qsl[curIdx + 1]);
+
+       vector<string> reqs = getRequiredList(nextModName);
+
+       //if it doesn't require anything....
+       if (reqs.empty()) {
+               downPB->setEnabled(true);
+               return;
+       }
+
+       //FIXME This should perhaps be more flexible and check whether, even 
+       //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());
+}
+
+void ModuleSelMan::updateUpPB() 
+{
+       int const srows = selectedModel->stringList().size();
+       if (srows == 0) {
+               upPB->setEnabled(false);
+               return;
+       }
+       QModelIndexList const selSels = 
+                       selectedLV->selectionModel()->selectedIndexes();
+       //disable if empty or first item is selected
+       if (selSels.empty() || selSels.first().row() == 0) {
+               upPB->setEnabled(false);
+               return;
+       }
+       //determine whether immediately preceding element is required by this one
+       QString const curModName = 
+               selectedLV->selectionModel()->currentIndex().data().toString();
+       vector<string> reqs = getRequiredList(fromqstr(curModName));
+       
+       //if this one doesn't require anything....
+       if (reqs.empty()) {
+               upPB->setEnabled(true);
+               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]);
+
+       //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.
+       //enable it if the preceding module isn't required
+       upPB->setEnabled(find(reqs.begin(), reqs.end(), preModName) == reqs.end());
+}
+
+void ModuleSelMan::updateDelPB() 
+{
+       int const srows = selectedModel->stringList().size();
+       if (srows == 0) {
+               deletePB->setEnabled(false);
+               return;
+       }
+       QModelIndexList const selSels = 
+                       selectedLV->selectionModel()->selectedIndexes();
+       if (selSels.empty() || selSels.first().row() < 0) {
+               deletePB->setEnabled(false);
+               return;
+       }
+       
+       //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();
+       
+       //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<string> reqs = getRequiredList(mod);
+               //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?
+               //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;
+                       }
+                       //do we satisfy the require? 
+                       if (find(reqs.begin(), reqs.end(), fromqstr(*it2)) != reqs.end()) {
+                               //lyxerr << fromqstr(*it2) << " does the trick." << std::endl;
+                               break;
+                       }
+               }
+               //did we reach the end of the list?
+               if (it2 == end2) {
+                       //lyxerr << "Reached end of list." << std::endl;
+                       deletePB->setEnabled(false);
+                       return;
+               }
+       }
+       //lyxerr << "All's well that ends well." << std::endl;  
+       deletePB->setEnabled(true);
+}
+
 
 /////////////////////////////////////////////////////////////////////
 //
@@ -576,7 +840,7 @@ GuiDocument::GuiDocument(GuiView & lv)
                this, SLOT(classChanged()));
        
        selectionManager = 
-               new GuiSelectionManager(latexModule->availableLV, latexModule->selectedLV, 
+               new ModuleSelMan(latexModule->availableLV, latexModule->selectedLV, 
                        latexModule->addPB, latexModule->deletePB, 
                        latexModule->upPB, latexModule->downPB, 
                        availableModel(), selectedModel());
@@ -923,10 +1187,40 @@ void GuiDocument::updatePagestyle(string const & items, string const & sel)
 void GuiDocument::classChanged()
 {
        textclass_type const tc = latexModule->classCO->currentIndex();
-       bp_.setJustBaseClass(tc);
-       if (lyxrc.auto_reset_options)
+       bp_.setBaseClass(tc);
+       if (lyxrc.auto_reset_options) {
                bp_.useClassDefaults();
-       updateContents();
+               updateContents();
+       }
+}
+
+
+namespace {
+       //This is an insanely complicated attempt to make this sort of thing
+       //work with RTL languages.
+       docstring formatStrVec(vector<string> const & v, docstring const & s) 
+       {
+               //this mess formats the list as "v[0], v[1], ..., [s] v[n]"
+               int const vSize = v.size();
+               if (v.size() == 0)
+                       return docstring();
+               else if (v.size() == 1) 
+                       return from_ascii(v[0]);
+               else if (v.size() == 2) {
+                       docstring retval = _("%1$s and %2$s");
+                       retval = subst(retval, _("and"), s);
+                       return bformat(retval, from_ascii(v[0]), from_ascii(v[1]));
+               }
+               //The idea here is to format all but the last two items...
+               docstring t2 = _("%1$s, %2$s");
+               docstring retval = from_ascii(v[0]);
+               for (int i = 1; i < vSize - 2; ++i)
+                       retval = bformat(t2, retval, from_ascii(v[i])); 
+               //...and then to  plug them, and the last two, into this schema
+               docstring t = _("%1$s, %2$s, and %3$s");
+               t = subst(t, _("and"), s);
+               return bformat(t, retval, from_ascii(v[vSize - 2]), from_ascii(v[vSize - 1]));
+       }
 }
 
 
@@ -936,33 +1230,43 @@ void GuiDocument::updateModuleInfo()
        //Module description
        QListView const * const lv = selectionManager->selectedFocused() ?
                                     latexModule->selectedLV :
-                       latexModule->availableLV;
+                                    latexModule->availableLV;
        if (lv->selectionModel()->selectedIndexes().isEmpty())
                latexModule->infoML->document()->clear();
        else {
-               QModelIndex const idx = lv->selectionModel()->currentIndex();
+               QModelIndex const idx = lv->selectionModel()->currentIndex();
                string const modName = fromqstr(idx.data().toString());
-               string desc = getModuleDescription(modName);
+               docstring desc = getModuleDescription(modName);
+
                vector<string> pkgList = getPackageList(modName);
-               string pkgdesc;
-               //this mess formats the package list as "pkg1, pkg2, and pkg3"
-               int const pkgListSize = pkgList.size();
-               for (int i = 0; i < pkgListSize; ++i) {
-                       if (i == 1) {
-                               if (i == pkgListSize - 1) //last element
-                                       pkgdesc += " and ";
-                               else
-                                       pkgdesc += ", ";
-                       } else if (i > 1) {
-                               if (i == pkgListSize - 1) //last element
-                                       pkgdesc += ", and ";
-                               else
-                                       pkgdesc += ", ";
-                       }
-                       pkgdesc += pkgList[i];
+               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!");
                }
-               if (!pkgdesc.empty())
-                       desc += " Requires " + pkgdesc + ".";
                latexModule->infoML->document()->setPlainText(toqstr(desc));
        }
 }
@@ -972,10 +1276,10 @@ void GuiDocument::updateEmbeddedFileList()
 {
        embeddedFilesModule->filesLW->clear();
        // add current embedded files
-       EmbeddedFiles & files = buffer().embeddedFiles();
-       files.update();
-       EmbeddedFiles::EmbeddedFileList::iterator fit = files.begin();
-       EmbeddedFiles::EmbeddedFileList::iterator fit_end = files.end();
+       EmbeddedFileList & files = buffer().embeddedFiles();
+       files.update(buffer());
+       EmbeddedFileList::iterator fit = files.begin();
+       EmbeddedFileList::iterator fit_end = files.end();
        for (; fit != fit_end; ++fit) {
                QString label = toqstr(fit->relFilename(buffer().filePath()));
                if (fit->refCount() > 1)
@@ -1099,13 +1403,15 @@ void GuiDocument::apply(BufferParams & params)
        params.graphicsDriver =
                tex_graphics[latexModule->psdriverCO->currentIndex()];
        
+       // text layout
+       params.setBaseClass(latexModule->classCO->currentIndex());
+
        // Modules
        params.clearLayoutModules();
        QStringList const selMods = selectedModel()->stringList();
        for (int i = 0; i != selMods.size(); ++i)
                params.addLayoutModule(lyx::fromqstr(selMods[i]));
 
-
        if (mathsModule->amsautoCB->isChecked()) {
                params.use_amsmath = BufferParams::package_auto;
        } else {
@@ -1124,9 +1430,6 @@ void GuiDocument::apply(BufferParams & params)
                        params.use_esint = BufferParams::package_off;
        }
 
-       // text layout
-       params.setJustBaseClass(latexModule->classCO->currentIndex());
-
        if (pageLayoutModule->pagestyleCO->currentIndex() == 0)
                params.pagestyle = "default";
        else {
@@ -1300,10 +1603,10 @@ void GuiDocument::apply(BufferParams & params)
     If not found, return 0.
  */
 template<class A>
-static size_t findPos(std::vector<A> const & vec, A const & val)
+static size_t findPos(vector<A> const & vec, A const & val)
 {
-       typename std::vector<A>::const_iterator it =
-               std::find(vec.begin(), vec.end(), val);
+       typename vector<A>::const_iterator it =
+               find(vec.begin(), vec.end(), val);
        if (it == vec.end())
                return 0;
        return distance(vec.begin(), it);
@@ -1634,6 +1937,10 @@ void GuiDocument::updateContents()
        it = selMods.begin();
        for (; it != selMods.end(); ++it)
                strlist2.push_back(toqstr(*it));
+       //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_);
@@ -1641,7 +1948,7 @@ void GuiDocument::updateContents()
 
 void GuiDocument::useClassDefaults()
 {
-       bp_.setJustBaseClass(latexModule->classCO->currentIndex());
+       bp_.setBaseClass(latexModule->classCO->currentIndex());
        bp_.useClassDefaults();
        updateContents();
 }
@@ -1683,7 +1990,7 @@ BufferId GuiDocument::id() const
 }
 
 
-vector<string> GuiDocument::getModuleNames()
+vector<string> const & GuiDocument::getModuleNames()
 {
        return moduleNames_;
 }
@@ -1695,25 +2002,6 @@ vector<string> const & GuiDocument::getSelectedModules()
 }
 
 
-string GuiDocument::getModuleDescription(string const & modName) const
-{
-       LyXModule const * const mod = moduleList[modName];
-       if (!mod)
-               return string("Module unavailable!");
-       return mod->description;
-}
-
-
-vector<string>
-GuiDocument::getPackageList(string const & modName) const
-{
-       LyXModule const * const mod = moduleList[modName];
-       if (!mod)
-               return vector<string>(); //empty such thing
-       return mod->packageList;
-}
-
-
 TextClass const & GuiDocument::textClass() const
 {
        return textclasslist[bp_.getBaseClass()];
@@ -1738,8 +2026,6 @@ void GuiDocument::dispatchParams()
 
        // Apply the BufferParams. Note that this will set the base class
        // and then update the buffer's layout.
-       //FIXME Could this be done last? Then, I think, we'd get the automatic
-       //update mentioned in the next FIXME...
        dispatch_bufferparams(*this, params(), LFUN_BUFFER_PARAMS_APPLY);
 
        // Generate the colours requested by each new branch.
@@ -1837,7 +2123,7 @@ void GuiDocument::loadModuleNames ()
        moduleNames_.clear();
        LyXModuleList::const_iterator it = moduleList.begin();
        for (; it != moduleList.end(); ++it)
-               moduleNames_.push_back(it->name);
+               moduleNames_.push_back(it->getName());
        if (!moduleNames_.empty())
                sort(moduleNames_.begin(), moduleNames_.end());
 }