1/* This file is part of the Kate project.
2 * Based on the snippet plugin from KDevelop 4.
3 *
4 * Copyright (C) 2007 Robert Gruber <rgruber@users.sourceforge.net>
5 * Copyright (C) 2010 Milian Wolff <mail@milianw.de>
6 * Copyright (C) 2012 Christoph Cullmann <cullmann@kde.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "snippetview.h"
25
26#include <QContextMenuEvent>
27#include <KMenu>
28#include <KMessageBox>
29#include <KAction>
30#include <KDebug>
31
32#include "snippet.h"
33#include "katesnippetglobal.h"
34#include "snippetrepository.h"
35#include "snippetstore.h"
36#include "editrepository.h"
37#include "editsnippet.h"
38#include "snippetfilterproxymodel.h"
39
40#include <KGlobalSettings>
41
42#include <KNS3/DownloadDialog>
43#include <knewstuff3/uploaddialog.h>
44
45SnippetView::SnippetView(KateSnippetGlobal* plugin, QWidget* parent)
46 : QWidget(parent), Ui::SnippetViewBase(), m_plugin(plugin)
47{
48 Ui::SnippetViewBase::setupUi(this);
49
50 setWindowTitle(i18n("Snippets"));
51 setWindowIcon (KIcon("document-new"));
52
53 connect(filterText, SIGNAL(clearButtonClicked()),
54 this, SLOT(slotFilterChanged()));
55 connect(filterText, SIGNAL(textChanged(QString)),
56 this, SLOT(slotFilterChanged()));
57
58 snippetTree->setContextMenuPolicy( Qt::CustomContextMenu );
59 snippetTree->viewport()->installEventFilter( this );
60 connect(snippetTree, SIGNAL(customContextMenuRequested(QPoint)),
61 this, SLOT(contextMenu(QPoint)));
62
63 m_proxy = new SnippetFilterProxyModel(this);
64
65 m_proxy->setSourceModel( SnippetStore::self() );
66
67 snippetTree->setModel( m_proxy );
68// snippetTree->setModel( SnippetStore::instance() );
69
70 snippetTree->header()->hide();
71
72 m_addRepoAction = new KAction(KIcon("folder-new"), i18n("Add Repository"), this);
73 connect(m_addRepoAction, SIGNAL(triggered()), this, SLOT(slotAddRepo()));
74 addAction(m_addRepoAction);
75 m_editRepoAction = new KAction(KIcon("folder-txt"), i18n("Edit Repository"), this);
76 connect(m_editRepoAction, SIGNAL(triggered()), this, SLOT(slotEditRepo()));
77 addAction(m_editRepoAction);
78 m_removeRepoAction = new KAction(KIcon("edit-delete"), i18n("Remove Repository"), this);
79 connect(m_removeRepoAction, SIGNAL(triggered()), this, SLOT(slotRemoveRepo()));
80 addAction(m_removeRepoAction);
81
82 m_putNewStuffAction = new KAction(KIcon("get-hot-new-stuff"), i18n("Publish Repository"), this);
83 connect(m_putNewStuffAction, SIGNAL(triggered()), this, SLOT(slotSnippetToGHNS()));
84 addAction(m_putNewStuffAction);
85
86 QAction* separator = new QAction(this);
87 separator->setSeparator(true);
88 addAction(separator);
89
90 m_addSnippetAction = new KAction(KIcon("document-new"), i18n("Add Snippet"), this);
91 connect(m_addSnippetAction, SIGNAL(triggered()), this, SLOT(slotAddSnippet()));
92 addAction(m_addSnippetAction);
93 m_editSnippetAction = new KAction(KIcon("document-edit"), i18n("Edit Snippet"), this);
94 connect(m_editSnippetAction, SIGNAL(triggered()), this, SLOT(slotEditSnippet()));
95 addAction(m_editSnippetAction);
96 m_removeSnippetAction = new KAction(KIcon("document-close"), i18n("Remove Snippet"), this);
97 connect(m_removeSnippetAction, SIGNAL(triggered()), this, SLOT(slotRemoveSnippet()));
98 addAction(m_removeSnippetAction);
99
100 addAction(separator);
101
102 m_getNewStuffAction = new KAction(KIcon("get-hot-new-stuff"), i18n("Get New Snippets"), this);
103 connect(m_getNewStuffAction, SIGNAL(triggered()), this, SLOT(slotGHNS()));
104 addAction(m_getNewStuffAction);
105
106 connect(snippetTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(validateActions()));
107 validateActions();
108}
109
110SnippetView::~SnippetView()
111{
112}
113
114void SnippetView::validateActions()
115{
116
117 QStandardItem* item = currentItem();
118
119 Snippet* selectedSnippet = dynamic_cast<Snippet*>( item );
120 SnippetRepository* selectedRepo = dynamic_cast<SnippetRepository*>( item );
121
122 m_addRepoAction->setEnabled(true);
123 m_editRepoAction->setEnabled(selectedRepo);
124 m_removeRepoAction->setEnabled(selectedRepo);
125 m_putNewStuffAction->setEnabled(selectedRepo);
126
127 m_addSnippetAction->setEnabled(selectedRepo || selectedSnippet);
128 m_editSnippetAction->setEnabled(selectedSnippet);
129 m_removeSnippetAction->setEnabled(selectedSnippet);
130}
131
132QStandardItem* SnippetView::currentItem()
133{
134 ///TODO: support multiple selected items
135 QModelIndex index = snippetTree->currentIndex();
136 index = m_proxy->mapToSource(index);
137 return SnippetStore::self()->itemFromIndex( index );
138}
139
140void SnippetView::slotSnippetClicked (const QModelIndex & index)
141{
142 QStandardItem* item = SnippetStore::self()->itemFromIndex( m_proxy->mapToSource(index) );
143 if (!item)
144 return;
145
146 Snippet* snippet = dynamic_cast<Snippet*>( item );
147 if (!snippet)
148 return;
149
150 m_plugin->insertSnippet( snippet );
151}
152
153void SnippetView::contextMenu (const QPoint& pos)
154{
155 QModelIndex index = snippetTree->indexAt( pos );
156 index = m_proxy->mapToSource(index);
157 QStandardItem* item = SnippetStore::self()->itemFromIndex( index );
158 if (!item) {
159 // User clicked into an empty place of the tree
160 KMenu menu(this);
161 menu.addTitle(i18n("Snippets"));
162
163 menu.addAction(m_addRepoAction);
164 menu.addAction(m_getNewStuffAction);
165
166 menu.exec(snippetTree->mapToGlobal(pos));
167 } else if (Snippet* snippet = dynamic_cast<Snippet*>( item )) {
168 KMenu menu(this);
169 menu.addTitle(i18n("Snippet: %1", snippet->text()));
170
171 menu.addAction(m_editSnippetAction);
172 menu.addAction(m_removeSnippetAction);
173
174 menu.exec(snippetTree->mapToGlobal(pos));
175 } else if (SnippetRepository* repo = dynamic_cast<SnippetRepository*>( item )) {
176 KMenu menu(this);
177 menu.addTitle(i18n("Repository: %1", repo->text()));
178
179 menu.addAction(m_editRepoAction);
180 menu.addAction(m_removeRepoAction);
181 menu.addAction(m_putNewStuffAction);
182 menu.addSeparator();
183
184 menu.addAction(m_addSnippetAction);
185
186 menu.exec(snippetTree->mapToGlobal(pos));
187 }
188}
189
190void SnippetView::slotEditSnippet()
191{
192 QStandardItem* item = currentItem();
193 if (!item)
194 return;
195
196 Snippet* snippet = dynamic_cast<Snippet*>( item );
197 if (!snippet)
198 return;
199
200 SnippetRepository* repo = dynamic_cast<SnippetRepository*>( item->parent() );
201 if (!repo)
202 return;
203
204 EditSnippet dlg(repo, snippet, this);
205 dlg.exec();
206}
207
208void SnippetView::slotAddSnippet()
209{
210 QStandardItem* item = currentItem();
211 if (!item)
212 return;
213
214 SnippetRepository* repo = dynamic_cast<SnippetRepository*>( item );
215 if (!repo) {
216 repo = dynamic_cast<SnippetRepository*>( item->parent() );
217 if ( !repo )
218 return;
219 }
220
221 EditSnippet dlg(repo, 0, this);
222 dlg.exec();
223}
224
225void SnippetView::slotRemoveSnippet()
226{
227 QStandardItem* item = currentItem();
228 if (!item)
229 return;
230
231 SnippetRepository* repo = dynamic_cast<SnippetRepository*>( item->parent() );
232 if (!repo)
233 return;
234
235 int ans = KMessageBox::warningContinueCancel(
236 QApplication::activeWindow(),
237 i18n("Do you really want to delete the snippet \"%1\"?", item->text())
238 );
239 if ( ans == KMessageBox::Continue ) {
240 item->parent()->removeRow(item->row());
241 repo->save();
242 }
243}
244
245void SnippetView::slotAddRepo()
246{
247 EditRepository dlg(0, this);
248 dlg.exec();
249}
250
251void SnippetView::slotEditRepo()
252{
253 QStandardItem* item = currentItem();
254 if (!item)
255 return;
256
257 SnippetRepository* repo = dynamic_cast<SnippetRepository*>( item );
258 if (!repo)
259 return;
260
261 EditRepository dlg(repo, this);
262 dlg.exec();
263}
264
265void SnippetView::slotRemoveRepo()
266{
267 QStandardItem* item = currentItem();
268 if (!item)
269 return;
270
271 SnippetRepository* repo = dynamic_cast<SnippetRepository*>( item );
272 if (!repo)
273 return;
274
275 int ans = KMessageBox::warningContinueCancel(
276 QApplication::activeWindow(),
277 i18n("Do you really want to delete the repository \"%1\" with all its snippets?", repo->text())
278 );
279 if ( ans == KMessageBox::Continue ) {
280 repo->remove();
281 }
282}
283
284void SnippetView::slotFilterChanged()
285{
286 m_proxy->changeFilter( filterText->text() );
287}
288
289void SnippetView::slotGHNS()
290{
291 KNS3::DownloadDialog dialog("ktexteditor_codesnippets_core.knsrc", this);
292 dialog.exec();
293 foreach ( const KNS3::Entry& entry, dialog.changedEntries() ) {
294 foreach ( const QString& path, entry.uninstalledFiles() ) {
295 if ( path.endsWith(".xml") ) {
296 if ( SnippetRepository* repo = SnippetStore::self()->repositoryForFile(path) ) {
297 repo->remove();
298 }
299 }
300 }
301 foreach ( const QString& path, entry.installedFiles() ) {
302 if ( path.endsWith(".xml") ) {
303 SnippetStore::self()->appendRow(new SnippetRepository(path));
304 }
305 }
306 }
307}
308
309void SnippetView::slotSnippetToGHNS()
310{
311 QStandardItem* item = currentItem();
312 if ( !item)
313 return;
314
315 SnippetRepository* repo = dynamic_cast<SnippetRepository*>( item );
316 if ( !repo )
317 return;
318
319 KNS3::UploadDialog dialog("ktexteditor_codesnippets_core.knsrc", this);
320 dialog.setUploadFile(KUrl::fromPath(repo->file()));
321 dialog.setUploadName(repo->text());
322 dialog.exec();
323}
324
325bool SnippetView::eventFilter(QObject* obj, QEvent* e)
326{
327 // no, listening to activated() is not enough since that would also trigger the edit mode which we _dont_ want here
328 // users may still rename stuff via select + F2 though
329 if (obj == snippetTree->viewport()) {
330 const bool singleClick = KGlobalSettings::singleClick();
331 if ( (!singleClick && e->type() == QEvent::MouseButtonDblClick) || (singleClick && e->type() == QEvent::MouseButtonRelease) ) {
332 QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(e);
333 Q_ASSERT(mouseEvent);
334 QModelIndex clickedIndex = snippetTree->indexAt(mouseEvent->pos());
335 if (clickedIndex.isValid() && clickedIndex.parent().isValid()) {
336 slotSnippetClicked(clickedIndex);
337 e->accept();
338 return true;
339 }
340 }
341 }
342 return QObject::eventFilter(obj, e);
343}
344
345
346#include "snippetview.moc"
347