summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-09-23 22:11:24 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-09-23 22:11:24 +0300
commit27ebfc904b9b3eac3ef5479ed0d0ba166fac1543 (patch)
tree0a7a74bf586701645107c27a04564b41368260db /src
parent48c3553660f4c20fc3bb4fb5df95c058adf9dd87 (diff)
Bookmarks: TOML syntax; field for manual order
The bookmarks file is now saved in a TOML subset and named "bookmarks.ini". This makes it more robust and forwards compatible. Added a field for sort order, since that will be used for manual ordering.
Diffstat (limited to 'src')
-rw-r--r--src/bookmarks.c141
-rw-r--r--src/bookmarks.h3
-rw-r--r--src/ui/sidebarwidget.c45
3 files changed, 156 insertions, 33 deletions
diff --git a/src/bookmarks.c b/src/bookmarks.c
index c27efbfe..94f4be4e 100644
--- a/src/bookmarks.c
+++ b/src/bookmarks.c
@@ -31,13 +31,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
31#include <the_Foundation/path.h> 31#include <the_Foundation/path.h>
32#include <the_Foundation/regexp.h> 32#include <the_Foundation/regexp.h>
33#include <the_Foundation/stringset.h> 33#include <the_Foundation/stringset.h>
34#include <the_Foundation/toml.h>
34 35
35void init_Bookmark(iBookmark *d) { 36void init_Bookmark(iBookmark *d) {
36 init_String(&d->url); 37 init_String(&d->url);
37 init_String(&d->title); 38 init_String(&d->title);
38 init_String(&d->tags); 39 init_String(&d->tags);
39 iZap(d->when); 40 iZap(d->when);
40 d->sourceId = 0; 41 d->parentId = 0;
42 d->order = 0;
41} 43}
42 44
43void deinit_Bookmark(iBookmark *d) { 45void deinit_Bookmark(iBookmark *d) {
@@ -83,7 +85,8 @@ static int cmpTitleAscending_Bookmark_(const iBookmark **a, const iBookmark **b)
83 85
84/*----------------------------------------------------------------------------------------------*/ 86/*----------------------------------------------------------------------------------------------*/
85 87
86static const char *fileName_Bookmarks_ = "bookmarks.txt"; 88static const char *oldFileName_Bookmarks_ = "bookmarks.txt";
89static const char *fileName_Bookmarks_ = "bookmarks.ini"; /* since v1.7 (TOML subset) */
87 90
88struct Impl_Bookmarks { 91struct Impl_Bookmarks {
89 iMutex * mtx; 92 iMutex * mtx;
@@ -123,16 +126,19 @@ void clear_Bookmarks(iBookmarks *d) {
123 unlock_Mutex(d->mtx); 126 unlock_Mutex(d->mtx);
124} 127}
125 128
129static void insertId_Bookmarks_(iBookmarks *d, iBookmark *bookmark, int id) {
130 bookmark->node.key = id;
131 insert_Hash(&d->bookmarks, &bookmark->node);
132}
133
126static void insert_Bookmarks_(iBookmarks *d, iBookmark *bookmark) { 134static void insert_Bookmarks_(iBookmarks *d, iBookmark *bookmark) {
127 lock_Mutex(d->mtx); 135 lock_Mutex(d->mtx);
128 bookmark->node.key = ++d->idEnum; 136 insertId_Bookmarks_(d, bookmark, ++d->idEnum);
129 insert_Hash(&d->bookmarks, &bookmark->node);
130 unlock_Mutex(d->mtx); 137 unlock_Mutex(d->mtx);
131} 138}
132 139
133void load_Bookmarks(iBookmarks *d, const char *dirPath) { 140static void loadOldFormat_Bookmarks(iBookmarks *d, const char *dirPath) {
134 clear_Bookmarks(d); 141 iFile *f = newCStr_File(concatPath_CStr(dirPath, oldFileName_Bookmarks_));
135 iFile *f = newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_));
136 if (open_File(f, readOnly_FileMode | text_FileMode)) { 142 if (open_File(f, readOnly_FileMode | text_FileMode)) {
137 const iRangecc src = range_Block(collect_Block(readAll_File(f))); 143 const iRangecc src = range_Block(collect_Block(readAll_File(f)));
138 iRangecc line = iNullRange; 144 iRangecc line = iNullRange;
@@ -170,6 +176,101 @@ void load_Bookmarks(iBookmarks *d, const char *dirPath) {
170 iRelease(f); 176 iRelease(f);
171} 177}
172 178
179/*----------------------------------------------------------------------------------------------*/
180
181iDeclareType(BookmarkLoader)
182
183struct Impl_BookmarkLoader {
184 iTomlParser *toml;
185 iBookmarks * bookmarks;
186 iBookmark * bm;
187};
188
189static void handleTable_BookmarkLoader_(void *context, const iString *table, iBool isStart) {
190 iBookmarkLoader *d = context;
191 if (isStart) {
192 iAssert(!d->bm);
193 d->bm = new_Bookmark();
194 const int id = toInt_String(table);
195 d->bookmarks->idEnum = iMax(d->bookmarks->idEnum, id);
196 insertId_Bookmarks_(d->bookmarks, d->bm, id);
197 }
198 else {
199 d->bm = NULL;
200 }
201}
202
203static void handleKeyValue_BookmarkLoader_(void *context, const iString *table, const iString *key,
204 const iTomlValue *tv) {
205 iBookmarkLoader *d = context;
206 iBookmark *bm = d->bm;
207 if (bm) {
208 iUnused(table); /* it's the current one */
209 if (!cmp_String(key, "url") && tv->type == string_TomlType) {
210 set_String(&bm->url, tv->value.string);
211 }
212 else if (!cmp_String(key, "title") && tv->type == string_TomlType) {
213 set_String(&bm->title, tv->value.string);
214 }
215 else if (!cmp_String(key, "tags") && tv->type == string_TomlType) {
216 set_String(&bm->tags, tv->value.string);
217 }
218 else if (!cmp_String(key, "icon") && tv->type == int64_TomlType) {
219 bm->icon = (iChar) tv->value.int64;
220 }
221 else if (!cmp_String(key, "created") && tv->type == int64_TomlType) {
222 initSeconds_Time(&bm->when, tv->value.int64);
223 }
224 else if (!cmp_String(key, "parent") && tv->type == int64_TomlType) {
225 bm->parentId = tv->value.int64;
226 }
227 else if (!cmp_String(key, "order") && tv->type == int64_TomlType) {
228 bm->order = tv->value.int64;
229 }
230 }
231}
232
233static void init_BookmarkLoader(iBookmarkLoader *d, iBookmarks *bookmarks) {
234 d->toml = new_TomlParser();
235 setHandlers_TomlParser(d->toml, handleTable_BookmarkLoader_, handleKeyValue_BookmarkLoader_, d);
236 d->bookmarks = bookmarks;
237 d->bm = NULL;
238}
239
240static void deinit_BookmarkLoader(iBookmarkLoader *d) {
241 delete_TomlParser(d->toml);
242}
243
244static void load_BookmarkLoader(iBookmarkLoader *d, iFile *file) {
245 if (!parse_TomlParser(d->toml, collect_String(readString_File(file)))) {
246 fprintf(stderr, "[Bookmarks] syntax error(s) in %s\n", cstr_String(path_File(file)));
247 }
248}
249
250iDefineTypeConstructionArgs(BookmarkLoader, (iBookmarks *b), b)
251
252/*----------------------------------------------------------------------------------------------*/
253
254void load_Bookmarks(iBookmarks *d, const char *dirPath) {
255 clear_Bookmarks(d);
256 /* Load new .ini bookmarks, if present. */
257 iFile *f = iClob(newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_)));
258 if (!open_File(f, readOnly_FileMode | text_FileMode)) {
259 /* As a fallback, try loading the v1.6 bookmarks file. */
260 loadOldFormat_Bookmarks(d, dirPath);
261 /* Set ordering based on titles. */
262 iConstForEach(PtrArray, i, list_Bookmarks(d, cmpTitleAscending_Bookmark_, NULL, NULL)) {
263 iBookmark *bm = i.ptr;
264 bm->order = index_PtrArrayConstIterator(&i) + 1;
265 }
266 return;
267 }
268 iBookmarkLoader loader;
269 init_BookmarkLoader(&loader, d);
270 load_BookmarkLoader(&loader, f);
271 deinit_BookmarkLoader(&loader);
272}
273
173void save_Bookmarks(const iBookmarks *d, const char *dirPath) { 274void save_Bookmarks(const iBookmarks *d, const char *dirPath) {
174 lock_Mutex(d->mtx); 275 lock_Mutex(d->mtx);
175 iRegExp *remotePattern = iClob(new_RegExp("\\bremote\\b", caseSensitive_RegExpOption)); 276 iRegExp *remotePattern = iClob(new_RegExp("\\bremote\\b", caseSensitive_RegExpOption));
@@ -185,12 +286,26 @@ void save_Bookmarks(const iBookmarks *d, const char *dirPath) {
185 continue; 286 continue;
186 } 287 }
187 format_String(str, 288 format_String(str,
188 "%08x %.0lf %s\n%s\n%s\n", 289 "[%d]\n"
290 "url = \"%s\"\n"
291 "title = \"%s\"\n"
292 "tags = \"%s\"\n"
293 "icon = 0x%x\n"
294 "created = %.0f # %s\n",
295 id_Bookmark(bm),
296 cstrCollect_String(quote_String(&bm->url, iFalse)),
297 cstrCollect_String(quote_String(&bm->title, iFalse)),
298 cstrCollect_String(quote_String(&bm->tags, iFalse)),
189 bm->icon, 299 bm->icon,
190 seconds_Time(&bm->when), 300 seconds_Time(&bm->when),
191 cstr_String(&bm->url), 301 cstrCollect_String(format_Time(&bm->when, "%Y-%m-%d")));
192 cstr_String(&bm->title), 302 if (bm->parentId) {
193 cstr_String(&bm->tags)); 303 appendFormat_String(str, "parent = %d\n", bm->parentId);
304 }
305 if (bm->order) {
306 appendFormat_String(str, "order = %d\n", bm->order);
307 }
308 appendCStr_String(str, "\n");
194 writeData_File(f, cstr_String(str), size_String(str)); 309 writeData_File(f, cstr_String(str), size_String(str));
195 } 310 }
196 } 311 }
@@ -223,7 +338,7 @@ iBool remove_Bookmarks(iBookmarks *d, uint32_t id) {
223 if (hasTag_Bookmark(bm, remoteSource_BookmarkTag)) { 338 if (hasTag_Bookmark(bm, remoteSource_BookmarkTag)) {
224 iForEach(Hash, i, &d->bookmarks) { 339 iForEach(Hash, i, &d->bookmarks) {
225 iBookmark *j = (iBookmark *) i.value; 340 iBookmark *j = (iBookmark *) i.value;
226 if (j->sourceId == id_Bookmark(bm)) { 341 if (j->parentId == id_Bookmark(bm)) {
227 remove_HashIterator(&i); 342 remove_HashIterator(&i);
228 delete_Bookmark(j); 343 delete_Bookmark(j);
229 } 344 }
@@ -452,7 +567,7 @@ void requestFinished_Bookmarks(iBookmarks *d, iGmRequest *req) {
452 } 567 }
453 const uint32_t bmId = add_Bookmarks(d, absUrl, titleStr, remoteTag, 0x2913); 568 const uint32_t bmId = add_Bookmarks(d, absUrl, titleStr, remoteTag, 0x2913);
454 iBookmark *bm = get_Bookmarks(d, bmId); 569 iBookmark *bm = get_Bookmarks(d, bmId);
455 bm->sourceId = *(uint32_t *) userData_Object(req); 570 bm->parentId = *(uint32_t *) userData_Object(req);
456 delete_String(titleStr); 571 delete_String(titleStr);
457 } 572 }
458 delete_String(urlStr); 573 delete_String(urlStr);
diff --git a/src/bookmarks.h b/src/bookmarks.h
index 353b4197..dc7eca9a 100644
--- a/src/bookmarks.h
+++ b/src/bookmarks.h
@@ -47,7 +47,8 @@ struct Impl_Bookmark {
47 iString tags; 47 iString tags;
48 iChar icon; 48 iChar icon;
49 iTime when; 49 iTime when;
50 uint32_t sourceId; /* remote */ 50 uint32_t parentId; /* remote source or folder */
51 int order; /* sort order */
51}; 52};
52 53
53iLocalDef uint32_t id_Bookmark (const iBookmark *d) { return d->node.key; } 54iLocalDef uint32_t id_Bookmark (const iBookmark *d) { return d->node.key; }
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index ffedfeeb..3463f1a5 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -116,24 +116,26 @@ static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) {
116 return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; 116 return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0;
117} 117}
118 118
119static int cmpTitle_Bookmark_(const iBookmark **a, const iBookmark **b) { 119static int cmpTree_Bookmark_(const iBookmark **a, const iBookmark **b) {
120 const iBookmark *bm1 = *a, *bm2 = *b; 120 const iBookmark *bm1 = *a, *bm2 = *b;
121 if (bm2->sourceId == id_Bookmark(bm1)) { 121 if (bm2->parentId == id_Bookmark(bm1)) {
122 return -1; 122 return -1;
123 } 123 }
124 if (bm1->sourceId == id_Bookmark(bm2)) { 124 if (bm1->parentId == id_Bookmark(bm2)) {
125 return 1; 125 return 1;
126 } 126 }
127 if (bm1->sourceId == bm2->sourceId) { 127 if (bm1->parentId == bm2->parentId) {
128 return cmpStringCase_String(&bm1->title, &bm2->title); 128 //return cmpStringCase_String(&bm1->title, &bm2->title);
129 return iCmp(bm1->order, bm2->order);
129 } 130 }
130 if (bm1->sourceId) { 131 if (bm1->parentId) {
131 bm1 = get_Bookmarks(bookmarks_App(), bm1->sourceId); 132 bm1 = get_Bookmarks(bookmarks_App(), bm1->parentId);
132 } 133 }
133 if (bm2->sourceId) { 134 if (bm2->parentId) {
134 bm2 = get_Bookmarks(bookmarks_App(), bm2->sourceId); 135 bm2 = get_Bookmarks(bookmarks_App(), bm2->parentId);
135 } 136 }
136 return cmpStringCase_String(&bm1->title, &bm2->title); 137// return cmpStringCase_String(&bm1->title, &bm2->title);
138 return iCmp(bm1->order, bm2->order);
137} 139}
138 140
139static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label, 141static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label,
@@ -331,9 +333,10 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
331 iRegExp *subTag = iClob(new_RegExp("\\b" subscribed_BookmarkTag "\\b", caseSensitive_RegExpOption)); 333 iRegExp *subTag = iClob(new_RegExp("\\b" subscribed_BookmarkTag "\\b", caseSensitive_RegExpOption));
332 iRegExp *remoteSourceTag = iClob(new_RegExp("\\b" remoteSource_BookmarkTag "\\b", caseSensitive_RegExpOption)); 334 iRegExp *remoteSourceTag = iClob(new_RegExp("\\b" remoteSource_BookmarkTag "\\b", caseSensitive_RegExpOption));
333 iRegExp *linkSplitTag = iClob(new_RegExp("\\b" linkSplit_BookmarkTag "\\b", caseSensitive_RegExpOption)); 335 iRegExp *linkSplitTag = iClob(new_RegExp("\\b" linkSplit_BookmarkTag "\\b", caseSensitive_RegExpOption));
334 iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTitle_Bookmark_, NULL, NULL)) { 336 iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTree_Bookmark_, NULL, NULL)) {
335 const iBookmark *bm = i.ptr; 337 const iBookmark *bm = i.ptr;
336 iSidebarItem *item = new_SidebarItem(); 338 iSidebarItem *item = new_SidebarItem();
339 item->listItem.isDraggable = iTrue;
337 item->id = id_Bookmark(bm); 340 item->id = id_Bookmark(bm);
338 item->icon = bm->icon; 341 item->icon = bm->icon;
339 set_String(&item->url, &bm->url); 342 set_String(&item->url, &bm->url);
@@ -1573,12 +1576,14 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
1573 const iSidebarWidget *sidebar = findParentClass_Widget(constAs_Widget(list), 1576 const iSidebarWidget *sidebar = findParentClass_Widget(constAs_Widget(list),
1574 &Class_SidebarWidget); 1577 &Class_SidebarWidget);
1575 const iBool isMenuVisible = isVisible_Widget(sidebar->menu); 1578 const iBool isMenuVisible = isVisible_Widget(sidebar->menu);
1576 const iBool isPressing = isMouseDown_ListWidget(list); 1579 const iBool isDragging = constDragItem_ListWidget(list) == d;
1580 const iBool isPressing = isMouseDown_ListWidget(list) && !isDragging;
1577 const iBool isHover = 1581 const iBool isHover =
1578 (!isMenuVisible && 1582 (!isMenuVisible &&
1579 isHover_Widget(constAs_Widget(list)) && 1583 isHover_Widget(constAs_Widget(list)) &&
1580 constHoverItem_ListWidget(list) == d) || 1584 constHoverItem_ListWidget(list) == d) ||
1581 (isMenuVisible && sidebar->contextItem == d); 1585 (isMenuVisible && sidebar->contextItem == d) ||
1586 isDragging;
1582 const int scrollBarWidth = scrollBarWidth_ListWidget(list); 1587 const int scrollBarWidth = scrollBarWidth_ListWidget(list);
1583#if defined (iPlatformApple) 1588#if defined (iPlatformApple)
1584 const int blankWidth = 0; 1589 const int blankWidth = 0;
@@ -1729,12 +1734,14 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
1729 metaIconWidth 1734 metaIconWidth
1730 - 2 * gap_UI - (blankWidth ? blankWidth - 1.5f * gap_UI : (gap_UI / 2)), 1735 - 2 * gap_UI - (blankWidth ? blankWidth - 1.5f * gap_UI : (gap_UI / 2)),
1731 textPos.y); 1736 textPos.y);
1732 fillRect_Paint(p, 1737 if (!isDragging) {
1733 init_Rect(metaPos.x, 1738 fillRect_Paint(p,
1734 top_Rect(itemRect), 1739 init_Rect(metaPos.x,
1735 right_Rect(itemRect) - metaPos.x, 1740 top_Rect(itemRect),
1736 height_Rect(itemRect)), 1741 right_Rect(itemRect) - metaPos.x,
1737 bg); 1742 height_Rect(itemRect)),
1743 bg);
1744 }
1738 iInt2 mpos = metaPos; 1745 iInt2 mpos = metaPos;
1739 iStringConstIterator iter; 1746 iStringConstIterator iter;
1740 init_StringConstIterator(&iter, &d->meta); 1747 init_StringConstIterator(&iter, &d->meta);