File indexing completed on 2024-04-28 16:26:30

0001 /************************************************************************************************
0002   Copyright (C) 2004-2007 by Holger Danielsson (holger.danielsson@versanet.de)
0003                 2009-2010 by Michel Ludwig (michel.ludwig@kdemail.net)
0004  ************************************************************************************************/
0005 
0006 
0007 /***************************************************************************
0008  *                                                                         *
0009  *   This program is free software; you can redistribute it and/or modify  *
0010  *   it under the terms of the GNU General Public License as published by  *
0011  *   the Free Software Foundation; either version 2 of the License, or     *
0012  *   (at your option) any later version.                                   *
0013  *                                                                         *
0014  ***************************************************************************/
0015 
0016 #include "widgets/codecompletionconfigwidget.h"
0017 
0018 #include <QCheckBox>
0019 #include <QDir>
0020 #include <QFileInfo>
0021 #include <QGridLayout>
0022 #include <QGroupBox>
0023 #include <QLabel>
0024 #include <QLayout>
0025 #include <QSpinBox>
0026 #include <QStringList>
0027 #include <QTabWidget>
0028 #include <QTreeWidget>
0029 #include <QVBoxLayout>
0030 
0031 #include <KConfig>
0032 #include <QDialog>
0033 #include <KDirWatch>
0034 #include <KLocalizedString>
0035 #include <KMessageBox>
0036 #include <QPushButton>
0037 #include <KConfigGroup>
0038 
0039 #include "dialogs/listselector.h"
0040 #include "codecompletion.h"
0041 #include "errorhandler.h"
0042 #include "kileconfig.h"
0043 #include "kiledebug.h"
0044 #include "kiletool_enums.h"
0045 
0046 CodeCompletionConfigWidget::CodeCompletionConfigWidget(KConfig *config, KileErrorHandler *errorHandler, QWidget *parent, const char *name)
0047     : QWidget(parent), m_config(config), m_errorHandler(errorHandler), m_configChanged(false)
0048 {
0049     setObjectName(name);
0050     setupUi(this);
0051 
0052     // add three pages: Tex/Latex, Dictionary, Abbreviation
0053     addPage(m_tabWidget, TexPage, i18n("TeX/LaTeX"), "tex");
0054     addPage(m_tabWidget, DictionaryPage, i18n("Dictionary"), "dictionary");
0055     addPage(m_tabWidget, AbbreviationPage, i18n("Abbreviation"), "abbreviation");
0056 
0057     cb_setcursor->setWhatsThis(i18n("Try to place the cursor."));
0058     cb_setbullets->setWhatsThis(i18n("Insert bullets where the user must input data."));
0059     cb_closeenv->setWhatsThis(i18n("Also close an environment when an opening command is inserted."));
0060     cb_autocomplete->setWhatsThis(i18n("Directional or popup-based completion of the TeX/LaTeX commands that are contained in the selected completion files."));
0061     sp_latexthreshold->setWhatsThis(i18n("Automatically show a completion list of TeX/LaTeX commands when the word has this length."));
0062 
0063     cb_showabbrevview->setWhatsThis(i18n("Show abbreviations of the selected completion files in the sidebar"));
0064     cb_autocompleteabbrev->setWhatsThis(i18n("Directional or popup-based completion of abbreviations that are contained in the selected completion files."));
0065     cb_showcwlview->setWhatsThis(i18n("Show LaTeX commands of the selected completion files in the sidebar"));
0066 
0067     connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(showPage(int)));
0068     connect(m_addFileButton, SIGNAL(clicked()), this, SLOT(addClicked()));
0069     connect(m_removeFileButton, SIGNAL(clicked()), this, SLOT(removeClicked()));
0070 
0071     // find resource directories for cwl files
0072     QPair<QString, QString> p = KileCodeCompletion::Manager::getCwlBaseDirs();
0073     m_localCwlDir = p.first;
0074     m_globalCwlDir = p.second;
0075 
0076     // Watch for changes in the directories
0077     m_dirWatcher = new KDirWatch(this);
0078     if (m_dirWatcher) {
0079         m_dirWatcher->addDir(m_localCwlDir, KDirWatch::WatchSubDirs | KDirWatch::WatchFiles);
0080         m_dirWatcher->addDir(m_globalCwlDir, KDirWatch::WatchSubDirs | KDirWatch::WatchFiles);
0081         connect(m_dirWatcher, SIGNAL(created(QString)), this, SLOT(updateCompletionFilesTab(QString)));
0082         connect(m_dirWatcher, SIGNAL(deleted(QString)), this, SLOT(updateCompletionFilesTab(QString)));
0083     }
0084 }
0085 
0086 CodeCompletionConfigWidget::~CodeCompletionConfigWidget()
0087 {
0088 }
0089 
0090 void CodeCompletionConfigWidget::addPage(QTabWidget *tab, CompletionPage page, const QString &title, const QString &dirname)
0091 {
0092     m_page[page] = new QWidget(tab);
0093 
0094     m_listview[page] = new QTreeWidget(m_page[page]);
0095     m_listview[page]->setHeaderLabels(QStringList() << i18n("Completion Files")
0096                                       << i18n("Local File"));
0097     m_listview[page]->setAllColumnsShowFocus(true);
0098     m_listview[page]->setRootIsDecorated(false);
0099     m_listview[page]->setSelectionMode(QAbstractItemView::ExtendedSelection);
0100 
0101     QGridLayout *grid = new QGridLayout();
0102     grid->setContentsMargins(0, 0, 0, 0);
0103 //TODO PORT QT5     grid->setSpacing(QDialog::spacingHint());
0104     m_page[page]->setLayout(grid);
0105     grid->addWidget(m_listview[page], 0, 0);
0106 
0107     // add Tab
0108     tab->addTab(m_page[page], title);
0109 
0110     // remember directory name
0111     m_dirname << dirname;
0112 
0113     connect(m_listview[page], SIGNAL(itemSelectionChanged()),
0114             this, SLOT(slotSelectionChanged()));
0115 }
0116 
0117 //////////////////// read/write configuration ////////////////////
0118 
0119 void CodeCompletionConfigWidget::readConfig()
0120 {
0121     // read selected and deselected filenames with wordlists
0122     m_wordlist[TexPage] = KileConfig::completeTex();
0123     m_wordlist[DictionaryPage]  = KileConfig::completeDict();
0124     m_wordlist[AbbreviationPage]  = KileConfig::completeAbbrev();
0125 
0126     // set checkbox status
0127     cb_setcursor->setChecked(KileConfig::completeCursor());
0128     cb_setbullets->setChecked(KileConfig::completeBullets());
0129     cb_closeenv->setChecked(KileConfig::completeCloseEnv());
0130     cb_showabbrevview->setChecked(KileConfig::completeShowAbbrev());
0131     cb_showcwlview->setChecked(KileConfig::showCwlCommands());
0132 
0133     cb_autocomplete->setChecked(KileConfig::completeAuto());
0134     cb_autocompleteabbrev->setChecked(KileConfig::completeAutoAbbrev());
0135 
0136     sp_latexthreshold->setValue(KileConfig::completeAutoThreshold());
0137 
0138     // insert filenames into listview
0139     for (uint i = TexPage; i < NumPages; ++i) {
0140         setListviewEntries(CompletionPage(i));
0141     }
0142 }
0143 
0144 void CodeCompletionConfigWidget::writeConfig()
0145 {
0146     // get listview entries
0147     for (uint i = TexPage; i < NumPages; ++i) {
0148         m_configChanged |= getListviewEntries(CompletionPage(i));
0149     }
0150 
0151     // Konfigurationslisten abspeichern
0152     KileConfig::setCompleteTex(m_wordlist[TexPage]);
0153     KileConfig::setCompleteDict(m_wordlist[DictionaryPage]);
0154     KileConfig::setCompleteAbbrev(m_wordlist[AbbreviationPage]);
0155 
0156     // save checkbox status
0157     KileConfig::setCompleteCursor(cb_setcursor->isChecked());
0158     KileConfig::setCompleteBullets(cb_setbullets->isChecked());
0159     KileConfig::setCompleteCloseEnv(cb_closeenv->isChecked());
0160     KileConfig::setCompleteShowAbbrev(cb_showabbrevview->isChecked());
0161     KileConfig::setShowCwlCommands(cb_showcwlview->isChecked());
0162 
0163     // read autocompletion settings
0164     bool autoModeLatex = cb_autocomplete->isChecked();
0165     bool autoModeAbbrev = cb_autocompleteabbrev->isChecked();
0166 
0167     // save settings for Kile autocompletion modes
0168     KileConfig::setCompleteAuto(autoModeLatex);
0169     KileConfig::setCompleteAutoAbbrev(autoModeAbbrev);
0170     KileConfig::setCompleteAutoThreshold(sp_latexthreshold->value());
0171 
0172     // save changed wordlists?
0173     KileConfig::setCompleteChangedLists(m_configChanged);
0174 }
0175 
0176 //////////////////// listview ////////////////////
0177 
0178 // create ListView for configuration dialog
0179 
0180 void CodeCompletionConfigWidget::setListviewEntries(CompletionPage page)
0181 {
0182     QString listname = m_dirname[page];
0183     QString localdir = m_localCwlDir + listname + '/';
0184     QString globaldir = m_globalCwlDir + listname + '/';
0185 
0186     // add data from config list into ListView widget
0187     m_listview[page]->setUpdatesEnabled(false);
0188     m_listview[page]->clear();
0189     for (const auto& curWord : m_wordlist[page]) {
0190         QString basename = curWord.right(curWord.length() - 2);
0191 
0192         QTreeWidgetItem *item = new QTreeWidgetItem(m_listview[page], QStringList(basename));
0193         item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
0194         if (QFileInfo::exists(localdir + basename + ".cwl")) {
0195             item->setCheckState(0, curWord.at(0) == '1' ? Qt::Checked : Qt::Unchecked);
0196             item->setText(1, i18n("yes"));
0197         }
0198         else if (QFileInfo::exists(globaldir + basename + ".cwl")) {
0199             item->setCheckState(0, curWord.at(0) == '1' ? Qt::Checked : Qt::Unchecked);
0200             item->setText(1, i18n("no"));
0201         }
0202         else {
0203             item->setCheckState(0, Qt::Unchecked);
0204             item->setText(1, i18n("File not found"));
0205         }
0206     }
0207 
0208     updateColumnWidth(m_listview[page]);
0209     m_listview[page]->setUpdatesEnabled(true);
0210 }
0211 
0212 void CodeCompletionConfigWidget::updateColumnWidth(QTreeWidget *listview)
0213 {
0214     listview->resizeColumnToContents(0);
0215     listview->resizeColumnToContents(1);
0216     listview->setColumnWidth(0, listview->columnWidth(0) + 60);
0217 }
0218 
0219 bool CodeCompletionConfigWidget::getListviewEntries(CompletionPage page)
0220 {
0221     KILE_DEBUG_MAIN << "===bool CodeCompletionConfigWidget::getListviewEntries(CompletionPage" << page << ")";
0222 
0223     bool changed = false;
0224 
0225     // count number of entries
0226     int n = m_listview[page]->topLevelItemCount();
0227 
0228     // there are changes if this number has changed
0229     if(n != m_wordlist[page].count()) {
0230         changed = true;
0231     }
0232 
0233     // clear all stringlist with files, if there are no entries
0234     if (n == 0) {
0235         m_wordlist[page].clear();
0236         return changed;
0237     }
0238 
0239     // now check all entries if they have changed
0240     QStringList newfiles;
0241     int index = 0;
0242     QTreeWidgetItemIterator it(m_listview[page]);
0243     while (*it) {
0244         QString s = ((*it)->checkState(0) == Qt::Checked) ? "1-" : "0-";
0245         s += (*it)->text(0);
0246         newfiles.append(s);
0247 
0248         // check for a change
0249         if (index >= m_wordlist[page].count() || m_wordlist[page][index] != s) {
0250             changed = true;
0251         }
0252 
0253         // go on
0254         ++it;
0255         index++;
0256     }
0257 
0258     // only update if there are changes
0259     if (changed) {
0260         m_wordlist[page] = newfiles;
0261     }
0262 
0263     return changed;
0264 }
0265 
0266 QTreeWidgetItem* CodeCompletionConfigWidget::getListviewEntry(QTreeWidget *listview, const QString &filename)
0267 {
0268     QList<QTreeWidgetItem*> items = listview->findItems(filename, Qt::MatchExactly);
0269     if (items.empty()) {
0270         return Q_NULLPTR;
0271     }
0272     else {
0273         if (items.count() > 1) {
0274             m_errorHandler->printMessage(KileTool::Info, i18n("Wordlist '%1' contains duplicate entries.", filename), i18n("Completion"));
0275         }
0276         return items.first();
0277     }
0278 }
0279 
0280 //////////////////// tabpages parameter ////////////////////
0281 
0282 QTreeWidget *CodeCompletionConfigWidget::getListview(QWidget *page)
0283 {
0284     for (uint i = TexPage; i < NumPages; ++i) {
0285         if (page == m_page[i]) {
0286             return m_listview[i];
0287         }
0288     }
0289     return 0;
0290 }
0291 
0292 QString CodeCompletionConfigWidget::getListname(QWidget *page)
0293 {
0294     for (uint i = TexPage; i < NumPages; ++i) {
0295         if(page == m_page[i]) {
0296             return m_dirname[i];
0297         }
0298     }
0299     return QString();
0300 }
0301 
0302 //////////////////// shwo tabpages ////////////////////
0303 
0304 void CodeCompletionConfigWidget::showPage(QWidget *page)
0305 {
0306     QTreeWidget *listview = getListview(page);
0307     if(listview) {
0308         m_removeFileButton->setEnabled(listview->selectedItems().count() > 0);
0309     }
0310 }
0311 
0312 void CodeCompletionConfigWidget::showPage(int index)
0313 {
0314     showPage(m_tabWidget->widget(index));
0315 }
0316 
0317 //////////////////// add/remove new wordlists ////////////////////
0318 
0319 void CodeCompletionConfigWidget::addClicked()
0320 {
0321     // determine current subdirectory for current tab page
0322     QString listname = getListname(m_tabWidget->currentWidget());
0323     QString localPath = m_localCwlDir + listname, globalPath = m_globalCwlDir + listname;
0324 
0325     // dialog to add cwl files
0326     ManageCompletionFilesDialog dlg(i18n("Completion Files"), localPath, globalPath, this);
0327 
0328     if (dlg.exec()) {
0329         QSet<QString> filenames = dlg.selected();
0330         if (!filenames.isEmpty()) {
0331             QTreeWidget *listview = getListview(m_tabWidget->currentWidget());     // get current page
0332             for (QSet<QString>::ConstIterator it = filenames.constBegin(); it != filenames.constEnd(); ++it) {
0333                 QString filename = *it;
0334                 // Reload map of files.
0335                 QMap<QString, QString> filemap = KileCodeCompletion::Manager::getAllCwlFiles(localPath, globalPath);
0336 
0337                 // Could we accept the wordlist?
0338                 QFileInfo fi(filemap[filename]);
0339                 if (!filename.isEmpty() && fi.exists() && fi.isReadable()) {
0340                     QString basename = filename.left(filename.length() - 4);
0341 
0342                     // Check if this entry already exists.
0343                     QTreeWidgetItem* entry = Q_NULLPTR;
0344                     if ((entry = getListviewEntry(listview, basename)) == Q_NULLPTR) {
0345                         // A new entry has to be created
0346                         entry = new QTreeWidgetItem(listview, QStringList(basename));
0347                     }
0348 
0349                     entry->setFlags(entry->flags() | Qt::ItemIsUserCheckable);
0350                     entry->setCheckState(0, Qt::Checked);
0351                     entry->setSelected(true);
0352                     if (filemap[filename].left(m_localCwlDir.length()) == m_localCwlDir) {
0353                         entry->setText(1, i18n("yes"));
0354                     }
0355                     else {
0356                         entry->setText(1, i18n("no"));
0357                     }
0358                 }
0359             }
0360             updateColumnWidth(listview);
0361         }
0362     }
0363 }
0364 
0365 // delete a selected entry
0366 
0367 void CodeCompletionConfigWidget::removeClicked()
0368 {
0369     QWidget *page = m_tabWidget->currentWidget();
0370     QTreeWidget *list = getListview(page);                              // determine page
0371 
0372     foreach(QTreeWidgetItem *item, list->selectedItems()) {
0373         delete item;
0374     }
0375 
0376     showPage(page);
0377 }
0378 
0379 void CodeCompletionConfigWidget::slotSelectionChanged()
0380 {
0381     QTreeWidget *listview = getListview(m_tabWidget->currentWidget());     // get current page
0382     m_removeFileButton->setEnabled(listview->selectedItems().count() > 0);
0383 }
0384 
0385 void CodeCompletionConfigWidget::updateCompletionFilesTab(const QString& path)
0386 {
0387     int localLength = (path.startsWith(m_localCwlDir) ? m_localCwlDir.length() : m_globalCwlDir.length());
0388     // 'm_globalCwlDir' and 'm_localCwlDir' are guaranteed to end in '/' (see 'KileCodeCompletion::Manager::getCwlBaseDirs()')
0389     QString dirname = path.mid(localLength, path.indexOf('/', localLength) - localLength);
0390 
0391     int dirnameIdx = m_dirname.indexOf(dirname);
0392     if (dirnameIdx >= 0) {
0393         m_configChanged |= getListviewEntries(CompletionPage(dirnameIdx));
0394         setListviewEntries(CompletionPage(dirnameIdx));
0395     }
0396 }
0397 
0398