1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#include "codesnippetsmanager.h"
18#include "settings.h"
19#include "systemconsts.h"
20#include <QMessageBox>
21
22#include <QFile>
23#include <QJsonArray>
24#include <QJsonDocument>
25#include <QJsonObject>
26
27CodeSnippetsManager::CodeSnippetsManager(QObject *parent) : QObject(parent)
28{
29
30}
31
32void CodeSnippetsManager::load()
33{
34 loadSnippets();
35 loadNewFileTemplate();
36}
37
38void CodeSnippetsManager::save()
39{
40 saveSnippets();
41 saveNewFileTemplate();
42}
43
44void CodeSnippetsManager::loadSnippets()
45{
46 //if config file not exists, copy it from data
47 QString filename = includeTrailingPathDelimiter(pSettings->dirs().config()) + DEV_CODESNIPPET_FILE;
48 if (!fileExists(filename)) {
49 QString preFileName = ":/config/codesnippets.json";
50 QFile preFile(preFileName);
51 if (!preFile.open(QFile::ReadOnly)) {
52 QMessageBox::critical(nullptr,
53 tr("Load default code snippets failed"),
54 tr("Can't copy default code snippets '%1' to '%2'.")
55 .arg(preFileName)
56 .arg(filename));
57 return;
58 }
59 QByteArray content=preFile.readAll();
60 QFile file(filename);
61 if (!file.open(QFile::WriteOnly|QFile::Truncate)) {
62 QMessageBox::critical(nullptr,
63 tr("Load default code snippets failed"),
64 tr("Can't copy default code snippets '%1' to '%2'.")
65 .arg(preFileName)
66 .arg(filename));
67 return;
68 }
69 file.write(content);
70 }
71 //read config file
72 QFile file(filename);
73 if (!file.open(QFile::ReadOnly)) {
74 QMessageBox::critical(nullptr,
75 tr("Read code snippets failed"),
76 tr("Can't open code snippet file '%1' for read.")
77 .arg(filename));
78 return;
79 }
80
81 QByteArray json = file.readAll();
82 QJsonParseError error;
83 QJsonDocument doc = QJsonDocument::fromJson(json,&error);
84 if (error.error != QJsonParseError::NoError) {
85 QMessageBox::critical(nullptr,
86 tr("Read code snippets failed"),
87 tr("Read code snippet file '%1' failed:%2")
88 .arg(filename)
89 .arg(error.errorString()));
90 return;
91 }
92 mSnippets.clear();
93 QJsonArray array = doc.array();
94 foreach (const QJsonValue& value,array) {
95 QJsonObject object = value.toObject();
96 PCodeSnippet snippet = std::make_shared<CodeSnippet>();
97 snippet->caption = object["caption"].toString();
98 snippet->prefix = object["prefix"].toString();
99 snippet->code = object["code"].toString();
100 snippet->desc = object["description"].toString();
101 snippet->section = object["section"].toInt();
102 mSnippets.append(snippet);
103 }
104}
105
106void CodeSnippetsManager::saveSnippets()
107{
108 QString filename = includeTrailingPathDelimiter(pSettings->dirs().config()) + DEV_CODESNIPPET_FILE;
109 QFile file(filename);
110 if (!file.open(QFile::WriteOnly | QFile::Truncate)) {
111 QMessageBox::critical(nullptr,
112 tr("Save code snippets failed"),
113 tr("Can't open code snippet file '%1' for write.")
114 .arg(filename));
115 return;
116 }
117 QJsonArray array;
118 foreach (const PCodeSnippet& snippet,mSnippets) {
119 QJsonObject object;
120 object["caption"]=snippet->caption;
121 object["prefix"]=snippet->prefix;
122 object["code"]=snippet->code;
123 object["description"]=snippet->desc;
124 object["section"]=snippet->section;
125 array.append(object);
126 }
127 QJsonDocument doc;
128 doc.setArray(array);
129 if (file.write(doc.toJson())<0) {
130 QMessageBox::critical(nullptr,
131 tr("Save code snippets failed"),
132 tr("Write to code snippet file '%1' failed.")
133 .arg(filename));
134 return;
135 }
136}
137
138void CodeSnippetsManager::loadNewFileTemplate()
139{
140 QString filename = includeTrailingPathDelimiter(pSettings->dirs().config()) + DEV_NEWFILETEMPLATES_FILE;
141 QFile file(filename);
142 if (!file.exists()) {
143 mNewFileTemplate = "";
144 return;
145 }
146 if (!file.open(QFile::ReadOnly)) {
147 QMessageBox::critical(nullptr,
148 tr("Load new file template failed"),
149 tr("Can't open new file template file '%1' for read.")
150 .arg(filename));
151 return;
152 }
153 mNewFileTemplate=QString::fromUtf8(file.readAll());
154}
155
156void CodeSnippetsManager::saveNewFileTemplate()
157{
158 QString filename = includeTrailingPathDelimiter(pSettings->dirs().config()) + DEV_NEWFILETEMPLATES_FILE;
159 QFile file(filename);
160 if (!file.open(QFile::WriteOnly | QFile::Truncate)) {
161 QMessageBox::critical(nullptr,
162 tr("Save new file template failed"),
163 tr("Can't open new file template file '%1' for write.")
164 .arg(filename));
165 return;
166 }
167 file.write(mNewFileTemplate.toUtf8());
168}
169
170const QList<PCodeSnippet> &CodeSnippetsManager::snippets() const
171{
172 return mSnippets;
173}
174
175void CodeSnippetsManager::setSnippets(const QList<PCodeSnippet> &newSnippets)
176{
177 mSnippets = newSnippets;
178}
179
180const QString &CodeSnippetsManager::newFileTemplate() const
181{
182 return mNewFileTemplate;
183}
184
185void CodeSnippetsManager::setNewFileTemplate(const QString &newNewFileTemplate)
186{
187 mNewFileTemplate = newNewFileTemplate;
188}
189
190void CodeSnippetsModel::addSnippet(const QString &caption, const QString &prefix, const QString &code, const QString &description, int menuSection)
191{
192 beginInsertRows(QModelIndex(),mSnippets.count(),mSnippets.count());
193 PCodeSnippet snippet = std::make_shared<CodeSnippet>();
194 snippet->caption = caption;
195 snippet->prefix = prefix;
196 snippet->code = code;
197 snippet->desc = description;
198 snippet->section = menuSection;
199 mSnippets.append(snippet);
200 endInsertRows();
201}
202
203void CodeSnippetsModel::remove(int index)
204{
205 Q_ASSERT(index>=0 && index<mSnippets.count());
206 beginRemoveRows(QModelIndex(),index,index);
207 mSnippets.removeAt(index);
208 endRemoveRows();
209}
210
211void CodeSnippetsModel::clear()
212{
213 beginResetModel();
214 mSnippets.clear();
215 endResetModel();
216}
217
218QModelIndex CodeSnippetsModel::lastSnippetCaption()
219{
220 Q_ASSERT(mSnippets.count()>0);
221 return createIndex(mSnippets.count()-1,0);
222}
223
224int CodeSnippetsModel::rowCount(const QModelIndex &) const
225{
226 return mSnippets.count();
227}
228
229int CodeSnippetsModel::columnCount(const QModelIndex &) const
230{
231 return 4;
232}
233
234QVariant CodeSnippetsModel::data(const QModelIndex &index, int role) const
235{
236 if (!index.isValid()) {
237 return QVariant();
238 }
239 if (role==Qt::DisplayRole
240 || role == Qt::EditRole) {
241 int row = index.row();
242 PCodeSnippet snippet = mSnippets[row];
243 switch(index.column()) {
244 case 0:
245 return snippet->caption;
246 case 1:
247 return snippet->prefix;
248 case 2:
249 return snippet->desc;
250 case 3:
251 return snippet->section;
252 }
253 }
254 return QVariant();
255}
256
257bool CodeSnippetsModel::setData(const QModelIndex &index, const QVariant &value, int role)
258{
259 if (!index.isValid()) {
260 return false;
261 }
262 if (role==Qt::EditRole) {
263 int row = index.row();
264 PCodeSnippet snippet = mSnippets[row];
265 switch(index.column()) {
266 case 0:
267 snippet->caption = value.toString();
268 return true;
269 case 1:
270 snippet->prefix = value.toString();
271 return true;
272 case 2:
273 snippet->desc = value.toString();
274 return true;
275 case 3:
276 snippet->section = value.toInt();
277 return true;
278 }
279 }
280 return false;
281}
282
283QVariant CodeSnippetsModel::headerData(int section, Qt::Orientation orientation, int role) const
284{
285 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
286 switch(section) {
287 case 0:
288 return tr("Caption");
289 case 1:
290 return tr("Completion Prefix");
291 case 2:
292 return tr("Description");
293 case 3:
294 return tr("Menu Section");
295 }
296 }
297 return QVariant();
298}
299
300Qt::ItemFlags CodeSnippetsModel::flags(const QModelIndex &) const
301{
302 return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
303}
304
305const QList<PCodeSnippet> &CodeSnippetsModel::snippets() const
306{
307 return mSnippets;
308}
309
310void CodeSnippetsModel::updateSnippets(const QList<PCodeSnippet> &snippets)
311{
312 beginResetModel();
313 mSnippets = snippets;
314 endResetModel();
315}
316