1 /* -*- c++ -*- */
2 /*
3 * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
4 * https://gqrx.dk/
5 *
6 * Copyright 2013 Christian Lindner DL2VCL, Stefano Leucci.
7 *
8 * Gqrx is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3, or (at your option)
11 * any later version.
12 *
13 * Gqrx 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
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Gqrx; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street,
21 * Boston, MA 02110-1301, USA.
22 */
23 #include <Qt>
24 #include <QFile>
25 #include <QStringList>
26 #include <QTextStream>
27 #include <QString>
28 #include <QSet>
29 #include <algorithm>
30 #include <iostream>
31 #include "bookmarks.h"
32
33 const QColor TagInfo::DefaultColor(Qt::lightGray);
34 const QString TagInfo::strUntagged("Untagged");
35 Bookmarks* Bookmarks::m_pThis = 0;
36
Bookmarks()37 Bookmarks::Bookmarks()
38 {
39 TagInfo tag(TagInfo::strUntagged);
40 m_TagList.append(tag);
41 }
42
create()43 void Bookmarks::create()
44 {
45 m_pThis = new Bookmarks;
46 }
47
Get()48 Bookmarks& Bookmarks::Get()
49 {
50 return *m_pThis;
51 }
52
setConfigDir(const QString & cfg_dir)53 void Bookmarks::setConfigDir(const QString& cfg_dir)
54 {
55 m_bookmarksFile = cfg_dir + "/bookmarks.csv";
56 std::cout << "BookmarksFile is " << m_bookmarksFile.toStdString() << std::endl;
57 }
58
add(BookmarkInfo & info)59 void Bookmarks::add(BookmarkInfo &info)
60 {
61 m_BookmarkList.append(info);
62 std::stable_sort(m_BookmarkList.begin(),m_BookmarkList.end());
63 save();
64 emit( BookmarksChanged() );
65 }
66
remove(int index)67 void Bookmarks::remove(int index)
68 {
69 m_BookmarkList.removeAt(index);
70 save();
71 emit BookmarksChanged();
72 }
73
load()74 bool Bookmarks::load()
75 {
76 QFile file(m_bookmarksFile);
77 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
78 {
79 m_BookmarkList.clear();
80 m_TagList.clear();
81
82 // always create the "Untagged" entry.
83 findOrAddTag(TagInfo::strUntagged);
84
85 // Read Tags, until first empty line.
86 while (!file.atEnd())
87 {
88 QString line = QString::fromUtf8(file.readLine().trimmed());
89
90 if(line.isEmpty())
91 break;
92
93 if(line.startsWith("#"))
94 continue;
95
96 QStringList strings = line.split(";");
97 if(strings.count() == 2)
98 {
99 TagInfo &info = findOrAddTag(strings[0]);
100 info.color = QColor(strings[1].trimmed());
101 }
102 else
103 {
104 std::cout << "Bookmarks: Ignoring Line:" << std::endl;
105 std::cout << " " << line.toStdString() << std::endl;
106 }
107 }
108 std::sort(m_TagList.begin(),m_TagList.end());
109
110 // Read Bookmarks, after first empty line.
111 while (!file.atEnd())
112 {
113 QString line = QString::fromUtf8(file.readLine().trimmed());
114 if(line.isEmpty() || line.startsWith("#"))
115 continue;
116
117 QStringList strings = line.split(";");
118 if(strings.count() == 5)
119 {
120 BookmarkInfo info;
121 info.frequency = strings[0].toLongLong();
122 info.name = strings[1].trimmed();
123 info.modulation = strings[2].trimmed();
124 info.bandwidth = strings[3].toInt();
125 // Multiple Tags may be separated by comma.
126 QString strTags = strings[4];
127 QStringList TagList = strTags.split(",");
128 for(int iTag=0; iTag<TagList.size(); ++iTag)
129 {
130 info.tags.append(&findOrAddTag(TagList[iTag].trimmed()));
131 }
132
133 m_BookmarkList.append(info);
134 }
135 else
136 {
137 std::cout << "Bookmarks: Ignoring Line:" << std::endl;
138 std::cout << " " << line.toStdString() << std::endl;
139 }
140 }
141 file.close();
142 std::stable_sort(m_BookmarkList.begin(),m_BookmarkList.end());
143
144 emit BookmarksChanged();
145 return true;
146 }
147 return false;
148 }
149
150 //FIXME: Commas in names
save()151 bool Bookmarks::save()
152 {
153 QFile file(m_bookmarksFile);
154 if(file.open(QFile::WriteOnly | QFile::Truncate | QIODevice::Text))
155 {
156 QTextStream stream(&file);
157
158 stream << QString("# Tag name").leftJustified(20) + "; " +
159 QString(" color") << endl;
160
161 QSet<TagInfo*> usedTags;
162 for (int iBookmark = 0; iBookmark < m_BookmarkList.size(); iBookmark++)
163 {
164 BookmarkInfo& info = m_BookmarkList[iBookmark];
165 for(int iTag = 0; iTag < info.tags.size(); ++iTag)
166 {
167 TagInfo& tag = *info.tags[iTag];
168 usedTags.insert(&tag);
169 }
170 }
171
172 for (QSet<TagInfo*>::iterator i = usedTags.begin(); i != usedTags.end(); i++)
173 {
174 TagInfo& info = **i;
175 stream << info.name.leftJustified(20) + "; " + info.color.name() << endl;
176 }
177
178 stream << endl;
179
180 stream << QString("# Frequency").leftJustified(12) + "; " +
181 QString("Name").leftJustified(25)+ "; " +
182 QString("Modulation").leftJustified(20) + "; " +
183 QString("Bandwidth").rightJustified(10) + "; " +
184 QString("Tags") << endl;
185
186 for (int i = 0; i < m_BookmarkList.size(); i++)
187 {
188 BookmarkInfo& info = m_BookmarkList[i];
189 QString line = QString::number(info.frequency).rightJustified(12) +
190 "; " + info.name.leftJustified(25) + "; " +
191 info.modulation.leftJustified(20)+ "; " +
192 QString::number(info.bandwidth).rightJustified(10) + "; ";
193 for(int iTag = 0; iTag<info.tags.size(); ++iTag)
194 {
195 TagInfo& tag = *info.tags[iTag];
196 if(iTag!=0)
197 {
198 line.append(",");
199 }
200 line.append(tag.name);
201 }
202
203 stream << line << endl;
204 }
205
206 file.close();
207 return true;
208 }
209 return false;
210 }
211
getBookmarksInRange(qint64 low,qint64 high)212 QList<BookmarkInfo> Bookmarks::getBookmarksInRange(qint64 low, qint64 high)
213 {
214 BookmarkInfo info;
215 info.frequency=low;
216 QList<BookmarkInfo>::const_iterator lb = std::lower_bound(m_BookmarkList.begin(), m_BookmarkList.end(), info);
217 info.frequency=high;
218 QList<BookmarkInfo>::const_iterator ub = std::upper_bound(m_BookmarkList.begin(), m_BookmarkList.end(), info);
219
220 QList<BookmarkInfo> found;
221
222 while (lb != ub)
223 {
224 const BookmarkInfo& info = *lb;
225 //if(info.IsActive())
226 {
227 found.append(info);
228 }
229 lb++;
230 }
231
232 return found;
233
234 }
235
findOrAddTag(QString tagName)236 TagInfo &Bookmarks::findOrAddTag(QString tagName)
237 {
238 tagName = tagName.trimmed();
239
240 if (tagName.isEmpty())
241 tagName=TagInfo::strUntagged;
242
243 int idx = getTagIndex(tagName);
244
245 if (idx != -1)
246 return m_TagList[idx];
247
248 TagInfo info;
249 info.name=tagName;
250 m_TagList.append(info);
251 emit TagListChanged();
252 return m_TagList.last();
253 }
254
removeTag(QString tagName)255 bool Bookmarks::removeTag(QString tagName)
256 {
257 tagName = tagName.trimmed();
258
259 // Do not delete "Untagged" tag.
260 if(tagName.compare(TagInfo::strUntagged, tagName)==0)
261 return false;
262
263 int idx = getTagIndex(tagName);
264 if (idx == -1)
265 return false;
266
267 // Delete Tag from all Bookmarks that use it.
268 TagInfo* pTagToDelete = &m_TagList[idx];
269 for(int i=0; i<m_BookmarkList.size(); ++i)
270 {
271 BookmarkInfo& bmi = m_BookmarkList[i];
272 for(int t=0; t<bmi.tags.size(); ++t)
273 {
274 TagInfo* pTag = bmi.tags[t];
275 if(pTag == pTagToDelete)
276 {
277 if(bmi.tags.size()>1) bmi.tags.removeAt(t);
278 else bmi.tags[0] = &findOrAddTag(TagInfo::strUntagged);
279 }
280 }
281 }
282
283 // Delete Tag.
284 m_TagList.removeAt(idx);
285
286 emit BookmarksChanged();
287 emit TagListChanged();
288
289 return true;
290 }
291
setTagChecked(QString tagName,bool bChecked)292 bool Bookmarks::setTagChecked(QString tagName, bool bChecked)
293 {
294 int idx = getTagIndex(tagName);
295 if (idx == -1) return false;
296 m_TagList[idx].active = bChecked;
297 emit BookmarksChanged();
298 emit TagListChanged();
299 return true;
300 }
301
getTagIndex(QString tagName)302 int Bookmarks::getTagIndex(QString tagName)
303 {
304 tagName = tagName.trimmed();
305 for (int i = 0; i < m_TagList.size(); i++)
306 {
307 if (m_TagList[i].name == tagName)
308 return i;
309 }
310
311 return -1;
312 }
313
GetColor() const314 const QColor BookmarkInfo::GetColor() const
315 {
316 for(int iTag=0; iTag<tags.size(); ++iTag)
317 {
318 TagInfo& tag = *tags[iTag];
319 if(tag.active)
320 {
321 return tag.color;
322 }
323 }
324 return TagInfo::DefaultColor;
325 }
326
IsActive() const327 bool BookmarkInfo::IsActive() const
328 {
329 bool bActive = false;
330 for(int iTag=0; iTag<tags.size(); ++iTag)
331 {
332 TagInfo& tag = *tags[iTag];
333 if(tag.active)
334 {
335 bActive = true;
336 break;
337 }
338 }
339 return bActive;
340 }
341