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 | |
27 | CodeSnippetsManager::CodeSnippetsManager(QObject *parent) : QObject(parent) |
28 | { |
29 | |
30 | } |
31 | |
32 | void CodeSnippetsManager::load() |
33 | { |
34 | loadSnippets(); |
35 | loadNewFileTemplate(); |
36 | } |
37 | |
38 | void CodeSnippetsManager::save() |
39 | { |
40 | saveSnippets(); |
41 | saveNewFileTemplate(); |
42 | } |
43 | |
44 | void 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 | |
106 | void 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 | |
138 | void 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 | |
156 | void 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 | |
170 | const QList<PCodeSnippet> &CodeSnippetsManager::snippets() const |
171 | { |
172 | return mSnippets; |
173 | } |
174 | |
175 | void CodeSnippetsManager::setSnippets(const QList<PCodeSnippet> &newSnippets) |
176 | { |
177 | mSnippets = newSnippets; |
178 | } |
179 | |
180 | const QString &CodeSnippetsManager::newFileTemplate() const |
181 | { |
182 | return mNewFileTemplate; |
183 | } |
184 | |
185 | void CodeSnippetsManager::setNewFileTemplate(const QString &newNewFileTemplate) |
186 | { |
187 | mNewFileTemplate = newNewFileTemplate; |
188 | } |
189 | |
190 | void CodeSnippetsModel::addSnippet(const QString &caption, const QString &prefix, const QString &code, const QString &description, int ) |
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 | |
203 | void 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 | |
211 | void CodeSnippetsModel::clear() |
212 | { |
213 | beginResetModel(); |
214 | mSnippets.clear(); |
215 | endResetModel(); |
216 | } |
217 | |
218 | QModelIndex CodeSnippetsModel::lastSnippetCaption() |
219 | { |
220 | Q_ASSERT(mSnippets.count()>0); |
221 | return createIndex(mSnippets.count()-1,0); |
222 | } |
223 | |
224 | int CodeSnippetsModel::rowCount(const QModelIndex &) const |
225 | { |
226 | return mSnippets.count(); |
227 | } |
228 | |
229 | int CodeSnippetsModel::columnCount(const QModelIndex &) const |
230 | { |
231 | return 4; |
232 | } |
233 | |
234 | QVariant 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 | |
257 | bool 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 | |
283 | QVariant CodeSnippetsModel::(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 | |
300 | Qt::ItemFlags CodeSnippetsModel::flags(const QModelIndex &) const |
301 | { |
302 | return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; |
303 | } |
304 | |
305 | const QList<PCodeSnippet> &CodeSnippetsModel::snippets() const |
306 | { |
307 | return mSnippets; |
308 | } |
309 | |
310 | void CodeSnippetsModel::updateSnippets(const QList<PCodeSnippet> &snippets) |
311 | { |
312 | beginResetModel(); |
313 | mSnippets = snippets; |
314 | endResetModel(); |
315 | } |
316 | |