1 /************************************************************************************************
2   Copyright (C) 2004-2007 by Holger Danielsson (holger.danielsson@versanet.de)
3                 2009-2010 by Michel Ludwig (michel.ludwig@kdemail.net)
4  ************************************************************************************************/
5 
6 
7 /***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #include "widgets/codecompletionconfigwidget.h"
17 
18 #include <QCheckBox>
19 #include <QDir>
20 #include <QFileInfo>
21 #include <QGridLayout>
22 #include <QGroupBox>
23 #include <QLabel>
24 #include <QLayout>
25 #include <QSpinBox>
26 #include <QStringList>
27 #include <QTabWidget>
28 #include <QTreeWidget>
29 #include <QVBoxLayout>
30 
31 #include <KConfig>
32 #include <QDialog>
33 #include <KDirWatch>
34 #include <KLocalizedString>
35 #include <KMessageBox>
36 #include <QPushButton>
37 #include <KConfigGroup>
38 
39 #include "dialogs/listselector.h"
40 #include "codecompletion.h"
41 #include "errorhandler.h"
42 #include "kileconfig.h"
43 #include "kiledebug.h"
44 #include "kiletool_enums.h"
45 
CodeCompletionConfigWidget(KConfig * config,KileErrorHandler * errorHandler,QWidget * parent,const char * name)46 CodeCompletionConfigWidget::CodeCompletionConfigWidget(KConfig *config, KileErrorHandler *errorHandler, QWidget *parent, const char *name)
47     : QWidget(parent), m_config(config), m_errorHandler(errorHandler), m_configChanged(false)
48 {
49     setObjectName(name);
50     setupUi(this);
51 
52     // add three pages: Tex/Latex, Dictionary, Abbreviation
53     addPage(m_tabWidget, TexPage, i18n("TeX/LaTeX"), "tex");
54     addPage(m_tabWidget, DictionaryPage, i18n("Dictionary"), "dictionary");
55     addPage(m_tabWidget, AbbreviationPage, i18n("Abbreviation"), "abbreviation");
56 
57     cb_setcursor->setWhatsThis(i18n("Try to place the cursor."));
58     cb_setbullets->setWhatsThis(i18n("Insert bullets where the user must input data."));
59     cb_closeenv->setWhatsThis(i18n("Also close an environment when an opening command is inserted."));
60     cb_autocomplete->setWhatsThis(i18n("Directional or popup-based completion of the TeX/LaTeX commands that are contained in the selected completion files."));
61     sp_latexthreshold->setWhatsThis(i18n("Automatically show a completion list of TeX/LaTeX commands when the word has this length."));
62 
63     cb_showabbrevview->setWhatsThis(i18n("Show abbreviations of the selected completion files in the sidebar"));
64     cb_autocompleteabbrev->setWhatsThis(i18n("Directional or popup-based completion of abbreviations that are contained in the selected completion files."));
65     cb_showcwlview->setWhatsThis(i18n("Show LaTeX commands of the selected completion files in the sidebar"));
66 
67     connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(showPage(int)));
68     connect(m_addFileButton, SIGNAL(clicked()), this, SLOT(addClicked()));
69     connect(m_removeFileButton, SIGNAL(clicked()), this, SLOT(removeClicked()));
70 
71     // find resource directories for cwl files
72     QPair<QString, QString> p = KileCodeCompletion::Manager::getCwlBaseDirs();
73     m_localCwlDir = p.first;
74     m_globalCwlDir = p.second;
75 
76     // Watch for changes in the directories
77     m_dirWatcher = new KDirWatch(this);
78     if (m_dirWatcher) {
79         m_dirWatcher->addDir(m_localCwlDir, KDirWatch::WatchSubDirs | KDirWatch::WatchFiles);
80         m_dirWatcher->addDir(m_globalCwlDir, KDirWatch::WatchSubDirs | KDirWatch::WatchFiles);
81         connect(m_dirWatcher, SIGNAL(created(QString)), this, SLOT(updateCompletionFilesTab(QString)));
82         connect(m_dirWatcher, SIGNAL(deleted(QString)), this, SLOT(updateCompletionFilesTab(QString)));
83     }
84 }
85 
~CodeCompletionConfigWidget()86 CodeCompletionConfigWidget::~CodeCompletionConfigWidget()
87 {
88 }
89 
addPage(QTabWidget * tab,CompletionPage page,const QString & title,const QString & dirname)90 void CodeCompletionConfigWidget::addPage(QTabWidget *tab, CompletionPage page, const QString &title, const QString &dirname)
91 {
92     m_page[page] = new QWidget(tab);
93 
94     m_listview[page] = new QTreeWidget(m_page[page]);
95     m_listview[page]->setHeaderLabels(QStringList() << i18n("Completion Files")
96                                       << i18n("Local File"));
97     m_listview[page]->setAllColumnsShowFocus(true);
98     m_listview[page]->setRootIsDecorated(false);
99     m_listview[page]->setSelectionMode(QAbstractItemView::ExtendedSelection);
100 
101     QGridLayout *grid = new QGridLayout();
102     grid->setMargin(0);
103 //TODO PORT QT5 	grid->setSpacing(QDialog::spacingHint());
104     m_page[page]->setLayout(grid);
105     grid->addWidget(m_listview[page], 0, 0);
106 
107     // add Tab
108     tab->addTab(m_page[page], title);
109 
110     // remember directory name
111     m_dirname << dirname;
112 
113     connect(m_listview[page], SIGNAL(itemSelectionChanged()),
114             this, SLOT(slotSelectionChanged()));
115 }
116 
117 //////////////////// read/write configuration ////////////////////
118 
readConfig()119 void CodeCompletionConfigWidget::readConfig()
120 {
121     // read selected and deselected filenames with wordlists
122     m_wordlist[TexPage] = KileConfig::completeTex();
123     m_wordlist[DictionaryPage]  = KileConfig::completeDict();
124     m_wordlist[AbbreviationPage]  = KileConfig::completeAbbrev();
125 
126     // set checkbox status
127     cb_setcursor->setChecked(KileConfig::completeCursor());
128     cb_setbullets->setChecked(KileConfig::completeBullets());
129     cb_closeenv->setChecked(KileConfig::completeCloseEnv());
130     cb_showabbrevview->setChecked(KileConfig::completeShowAbbrev());
131     cb_showcwlview->setChecked(KileConfig::showCwlCommands());
132 
133     cb_autocomplete->setChecked(KileConfig::completeAuto());
134     cb_autocompleteabbrev->setChecked(KileConfig::completeAutoAbbrev());
135 
136     sp_latexthreshold->setValue(KileConfig::completeAutoThreshold());
137 
138     // insert filenames into listview
139     for (uint i = TexPage; i < NumPages; ++i) {
140         setListviewEntries(CompletionPage(i));
141     }
142 }
143 
writeConfig()144 void CodeCompletionConfigWidget::writeConfig()
145 {
146     // get listview entries
147     for (uint i = TexPage; i < NumPages; ++i) {
148         m_configChanged |= getListviewEntries(CompletionPage(i));
149     }
150 
151     // Konfigurationslisten abspeichern
152     KileConfig::setCompleteTex(m_wordlist[TexPage]);
153     KileConfig::setCompleteDict(m_wordlist[DictionaryPage]);
154     KileConfig::setCompleteAbbrev(m_wordlist[AbbreviationPage]);
155 
156     // save checkbox status
157     KileConfig::setCompleteCursor(cb_setcursor->isChecked());
158     KileConfig::setCompleteBullets(cb_setbullets->isChecked());
159     KileConfig::setCompleteCloseEnv(cb_closeenv->isChecked());
160     KileConfig::setCompleteShowAbbrev(cb_showabbrevview->isChecked());
161     KileConfig::setShowCwlCommands(cb_showcwlview->isChecked());
162 
163     // read autocompletion settings
164     bool autoModeLatex = cb_autocomplete->isChecked();
165     bool autoModeAbbrev = cb_autocompleteabbrev->isChecked();
166 
167     // save settings for Kile autocompletion modes
168     KileConfig::setCompleteAuto(autoModeLatex);
169     KileConfig::setCompleteAutoAbbrev(autoModeAbbrev);
170     KileConfig::setCompleteAutoThreshold(sp_latexthreshold->value());
171 
172     // save changed wordlists?
173     KileConfig::setCompleteChangedLists(m_configChanged);
174 }
175 
176 //////////////////// listview ////////////////////
177 
178 // ListView fr den Konfigurationsdialog einstellen
179 
setListviewEntries(CompletionPage page)180 void CodeCompletionConfigWidget::setListviewEntries(CompletionPage page)
181 {
182     QString listname = m_dirname[page];
183     QString localdir = m_localCwlDir + listname + '/';
184     QString globaldir = m_globalCwlDir + listname + '/';
185 
186     // Daten aus der Konfigurationsliste in das ListView-Widget eintragen
187     m_listview[page]->setUpdatesEnabled(false);
188     m_listview[page]->clear();
189     QStringList::ConstIterator it;
190     for (it = m_wordlist[page].constBegin(); it != m_wordlist[page].constEnd(); ++it) {
191         QString basename = (*it).right((*it).length() - 2);
192         bool localExists = QFileInfo(localdir + basename + ".cwl").exists();
193 
194         QTreeWidgetItem *item = new QTreeWidgetItem(m_listview[page], QStringList(basename));
195         item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
196         if (localExists) {
197             item->setCheckState(0, (*it).at(0) == '1' ? Qt::Checked : Qt::Unchecked);
198             item->setText(1, i18n("yes"));
199         }
200         else {
201             if (QFileInfo(globaldir + basename + ".cwl").exists()) {
202                 item->setCheckState(0, (*it).at(0) == '1' ? Qt::Checked : Qt::Unchecked);
203                 item->setText(1, i18n("no"));
204             }
205             else {
206                 item->setCheckState(0, Qt::Unchecked);
207                 item->setText(1, i18n("File not found"));
208             }
209         }
210     }
211 
212     updateColumnWidth(m_listview[page]);
213     m_listview[page]->setUpdatesEnabled(true);
214 }
215 
updateColumnWidth(QTreeWidget * listview)216 void CodeCompletionConfigWidget::updateColumnWidth(QTreeWidget *listview)
217 {
218     listview->resizeColumnToContents(0);
219     listview->resizeColumnToContents(1);
220     listview->setColumnWidth(0, listview->columnWidth(0) + 60);
221 }
222 
getListviewEntries(CompletionPage page)223 bool CodeCompletionConfigWidget::getListviewEntries(CompletionPage page)
224 {
225     KILE_DEBUG_MAIN << "===bool CodeCompletionConfigWidget::getListviewEntries(CompletionPage" << page << ")";
226 
227     bool changed = false;
228 
229     // count number of entries
230     int n = m_listview[page]->topLevelItemCount();
231 
232     // there are changes if this number has changed
233     if(n != m_wordlist[page].count()) {
234         changed = true;
235     }
236 
237     // clear all stringlist with files, if there are no entries
238     if (n == 0) {
239         m_wordlist[page].clear();
240         return changed;
241     }
242 
243     // now check all entries if they have changed
244     QStringList newfiles;
245     int index = 0;
246     QTreeWidgetItemIterator it(m_listview[page]);
247     while (*it) {
248         QString s = ((*it)->checkState(0) == Qt::Checked) ? "1-" : "0-";
249         s += (*it)->text(0);
250         newfiles.append(s);
251 
252         // check for a change
253         if (index >= m_wordlist[page].count() || m_wordlist[page][index] != s) {
254             changed = true;
255         }
256 
257         // go on
258         ++it;
259         index++;
260     }
261 
262     // only update if there are changes
263     if (changed) {
264         m_wordlist[page] = newfiles;
265     }
266 
267     return changed;
268 }
269 
getListviewEntry(QTreeWidget * listview,const QString & filename)270 QTreeWidgetItem* CodeCompletionConfigWidget::getListviewEntry(QTreeWidget *listview, const QString &filename)
271 {
272     QList<QTreeWidgetItem*> items = listview->findItems(filename, Qt::MatchExactly);
273     if (items.empty()) {
274         return Q_NULLPTR;
275     }
276     else {
277         if (items.count() > 1) {
278             m_errorHandler->printMessage(KileTool::Info, i18n("Wordlist '%1' contains duplicate entries.", filename), i18n("Completion"));
279         }
280         return items.first();
281     }
282 }
283 
284 //////////////////// tabpages parameter ////////////////////
285 
getListview(QWidget * page)286 QTreeWidget *CodeCompletionConfigWidget::getListview(QWidget *page)
287 {
288     for (uint i = TexPage; i < NumPages; ++i) {
289         if (page == m_page[i]) {
290             return m_listview[i];
291         }
292     }
293     return 0;
294 }
295 
getListname(QWidget * page)296 QString CodeCompletionConfigWidget::getListname(QWidget *page)
297 {
298     for (uint i = TexPage; i < NumPages; ++i) {
299         if(page == m_page[i]) {
300             return m_dirname[i];
301         }
302     }
303     return QString();
304 }
305 
306 //////////////////// shwo tabpages ////////////////////
307 
showPage(QWidget * page)308 void CodeCompletionConfigWidget::showPage(QWidget *page)
309 {
310     QTreeWidget *listview = getListview(page);
311     if(listview) {
312         m_removeFileButton->setEnabled(listview->selectedItems().count() > 0);
313     }
314 }
315 
showPage(int index)316 void CodeCompletionConfigWidget::showPage(int index)
317 {
318     showPage(m_tabWidget->widget(index));
319 }
320 
321 //////////////////// add/remove new wordlists ////////////////////
322 
addClicked()323 void CodeCompletionConfigWidget::addClicked()
324 {
325     // determine current subdirectory for current tab page
326     QString listname = getListname(m_tabWidget->currentWidget());
327     QString localPath = m_localCwlDir + listname, globalPath = m_globalCwlDir + listname;
328 
329     // dialog to add cwl files
330     ManageCompletionFilesDialog dlg(i18n("Completion Files"), localPath, globalPath, this);
331 
332     if (dlg.exec()) {
333         QSet<QString> filenames = dlg.selected();
334         if (!filenames.isEmpty()) {
335             QTreeWidget *listview = getListview(m_tabWidget->currentWidget());     // get current page
336             for (QSet<QString>::ConstIterator it = filenames.constBegin(); it != filenames.constEnd(); ++it) {
337                 QString filename = *it;
338                 // Reload map of files.
339                 QMap<QString, QString> filemap = KileCodeCompletion::Manager::getAllCwlFiles(localPath, globalPath);
340 
341                 // Could we accept the wordlist?
342                 QFileInfo fi(filemap[filename]);
343                 if (!filename.isEmpty() && fi.exists() && fi.isReadable()) {
344                     QString basename = filename.left(filename.length() - 4);
345 
346                     // Check if this entry already exists.
347                     QTreeWidgetItem* entry = Q_NULLPTR;
348                     if ((entry = getListviewEntry(listview, basename)) == Q_NULLPTR) {
349                         // A new entry has to be created
350                         entry = new QTreeWidgetItem(listview, QStringList(basename));
351                     }
352 
353                     entry->setFlags(entry->flags() | Qt::ItemIsUserCheckable);
354                     entry->setCheckState(0, Qt::Checked);
355                     entry->setSelected(true);
356                     if (filemap[filename].left(m_localCwlDir.length()) == m_localCwlDir) {
357                         entry->setText(1, i18n("yes"));
358                     }
359                     else {
360                         entry->setText(1, i18n("no"));
361                     }
362                 }
363             }
364             updateColumnWidth(listview);
365         }
366     }
367 }
368 
369 // delete a selected entry
370 
removeClicked()371 void CodeCompletionConfigWidget::removeClicked()
372 {
373     QWidget *page = m_tabWidget->currentWidget();
374     QTreeWidget *list = getListview(page);                              // determine page
375 
376     foreach(QTreeWidgetItem *item, list->selectedItems()) {
377         delete item;
378     }
379 
380     showPage(page);
381 }
382 
slotSelectionChanged()383 void CodeCompletionConfigWidget::slotSelectionChanged()
384 {
385     QTreeWidget *listview = getListview(m_tabWidget->currentWidget());     // get current page
386     m_removeFileButton->setEnabled(listview->selectedItems().count() > 0);
387 }
388 
updateCompletionFilesTab(const QString & path)389 void CodeCompletionConfigWidget::updateCompletionFilesTab(const QString& path)
390 {
391     int localLength = (path.startsWith(m_localCwlDir) ? m_localCwlDir.length() : m_globalCwlDir.length());
392     // 'm_globalCwlDir' and 'm_localCwlDir' are guaranteed to end in '/' (see 'KileCodeCompletion::Manager::getCwlBaseDirs()')
393     QString dirname = path.mid(localLength, path.indexOf('/', localLength) - localLength);
394 
395     int dirnameIdx = m_dirname.indexOf(dirname);
396     if (dirnameIdx >= 0) {
397         m_configChanged |= getListviewEntries(CompletionPage(dirnameIdx));
398         setListviewEntries(CompletionPage(dirnameIdx));
399     }
400 }
401 
402 
403