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