summaryrefslogtreecommitdiff
path: root/src/bookmarks.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-11-28 14:03:33 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-11-28 14:03:33 +0200
commit1efe2aab4fdc811c1dd37c76d1f5d37063745649 (patch)
tree410df70002df7256343a993fc91a9b1dcbb3dbca /src/bookmarks.c
parentab44b19cf747c348099c5aa86fcaf8bf0be628c9 (diff)
Bookmarks: Internal tags have a dot prefix
Internal behavior tags are now written in bookmarks.ini with a dot prefix (like hidden files on Unix), and at runtime they are removed from the tags string. This makes things more efficient as it isn't necessary to compile regular expressions all the time. TODO: Add "Edit Feed..." into the Bookmarks context menu, and a new menu item for listing all subscriptions. IssueID #331
Diffstat (limited to 'src/bookmarks.c')
-rw-r--r--src/bookmarks.c135
1 files changed, 113 insertions, 22 deletions
diff --git a/src/bookmarks.c b/src/bookmarks.c
index d4b20162..2606d975 100644
--- a/src/bookmarks.c
+++ b/src/bookmarks.c
@@ -37,6 +37,7 @@ void init_Bookmark(iBookmark *d) {
37 init_String(&d->url); 37 init_String(&d->url);
38 init_String(&d->title); 38 init_String(&d->title);
39 init_String(&d->tags); 39 init_String(&d->tags);
40 iZap(d->flags);
40 iZap(d->when); 41 iZap(d->when);
41 d->parentId = 0; 42 d->parentId = 0;
42 d->order = 0; 43 d->order = 0;
@@ -48,6 +49,7 @@ void deinit_Bookmark(iBookmark *d) {
48 deinit_String(&d->url); 49 deinit_String(&d->url);
49} 50}
50 51
52#if 0
51iBool hasTag_Bookmark(const iBookmark *d, const char *tag) { 53iBool hasTag_Bookmark(const iBookmark *d, const char *tag) {
52 if (!d) return iFalse; 54 if (!d) return iFalse;
53 iRegExp *pattern = new_RegExp(format_CStr("\\b%s\\b", tag), caseSensitive_RegExpOption); 55 iRegExp *pattern = new_RegExp(format_CStr("\\b%s\\b", tag), caseSensitive_RegExpOption);
@@ -60,7 +62,7 @@ iBool hasTag_Bookmark(const iBookmark *d, const char *tag) {
60 62
61void addTag_Bookmark(iBookmark *d, const char *tag) { 63void addTag_Bookmark(iBookmark *d, const char *tag) {
62 if (!isEmpty_String(&d->tags)) { 64 if (!isEmpty_String(&d->tags)) {
63 appendChar_String(&d->tags, ' '); 65 appendCStr_String(&d->tags, " ");
64 } 66 }
65 appendCStr_String(&d->tags, tag); 67 appendCStr_String(&d->tags, tag);
66} 68}
@@ -72,6 +74,93 @@ void removeTag_Bookmark(iBookmark *d, const char *tag) {
72 trim_String(&d->tags); 74 trim_String(&d->tags);
73 } 75 }
74} 76}
77#endif
78
79static struct {
80 uint32_t bit;
81 const char *tag;
82 iRegExp * pattern;
83 iRegExp * oldPattern;
84}
85specialTags_[] = {
86 { homepage_BookmarkFlag, ".homepage" },
87 { remoteSource_BookmarkFlag, ".remotesource" },
88 { linkSplit_BookmarkFlag, ".linksplit" },
89 { userIcon_BookmarkFlag, ".usericon" },
90 { subscribed_BookmarkFlag, ".subscribed" },
91 { headings_BookmarkFlag, ".headings" },
92 { ignoreWeb_BookmarkFlag, ".ignoreweb" },
93 /* `remote_BookmarkFlag` not included because it's runtime only */
94};
95
96static void updatePatterns_(size_t index) {
97 if (!specialTags_[index].pattern) {
98 specialTags_[index].pattern = new_RegExp(format_CStr("(?<!\\w)\\%s\\b(?!\\w)",
99 specialTags_[index].tag),
100 caseSensitive_RegExpOption); /* never released */
101 }
102 if (!specialTags_[index].oldPattern) {
103 /* TODO: Get rid of these when compatibility with v1.9 or older is not important. */
104 specialTags_[index].oldPattern =
105 new_RegExp(format_CStr("\\b%s\\b", specialTags_[index].tag + 1), /* dotless */
106 caseSensitive_RegExpOption); /* never released */
107 }
108}
109
110static void normalizeSpacesInTags_(iString *tags) {
111 iBool wasSpace = iFalse;
112 iString out;
113 init_String(&out);
114 for (const char *ch = constBegin_String(tags); ch != constEnd_String(tags); ch++) {
115 if (*ch == ' ') {
116 if (!wasSpace) {
117 wasSpace = iTrue;
118 }
119 else {
120 continue;
121 }
122 }
123 else {
124 wasSpace = iFalse;
125 }
126 appendData_Block(&out.chars, ch, 1);
127 }
128 trim_String(&out);
129 set_String(tags, &out);
130 deinit_String(&out);
131}
132
133static void unpackDotTags_Bookmark_(iBookmark *d) {
134 iZap(d->flags);
135 iForIndices(i, specialTags_) {
136 updatePatterns_(i);
137 iRegExpMatch m;
138 init_RegExpMatch(&m);
139 iBool isSet = matchString_RegExp(specialTags_[i].pattern, &d->tags, &m);
140 if (!isSet) {
141 init_RegExpMatch(&m);
142 isSet = matchString_RegExp(specialTags_[i].oldPattern, &d->tags, &m);
143 }
144 iChangeFlags(d->flags, specialTags_[i].bit, isSet);
145 if (isSet) {
146 remove_Block(&d->tags.chars, m.range.start, size_Range(&m.range));
147 }
148 }
149 normalizeSpacesInTags_(&d->tags);
150}
151
152static iString *packedDotTags_Bookmark_(const iBookmark *d) {
153 iString *withDot = copy_String(&d->tags);
154 iForIndices(i, specialTags_) {
155 if (d->flags & specialTags_[i].bit) {
156 if (!isEmpty_String(withDot)) {
157 appendCStr_String(withDot, " ");
158 }
159 appendCStr_String(withDot, specialTags_[i].tag);
160 }
161 }
162 return withDot;
163}
75 164
76iDefineTypeConstruction(Bookmark) 165iDefineTypeConstruction(Bookmark)
77 166
@@ -176,6 +265,7 @@ static void loadOldFormat_Bookmarks(iBookmarks *d, const char *dirPath) {
176 setRange_String(&bm->title, line); 265 setRange_String(&bm->title, line);
177 nextSplit_Rangecc(src, "\n", &line); 266 nextSplit_Rangecc(src, "\n", &line);
178 setRange_String(&bm->tags, line); 267 setRange_String(&bm->tags, line);
268 unpackDotTags_Bookmark_(bm);
179 insert_Bookmarks_(d, bm); 269 insert_Bookmarks_(d, bm);
180 } 270 }
181 } 271 }
@@ -220,6 +310,10 @@ static void handleKeyValue_BookmarkLoader_(void *context, const iString *table,
220 } 310 }
221 else if (!cmp_String(key, "tags") && tv->type == string_TomlType) { 311 else if (!cmp_String(key, "tags") && tv->type == string_TomlType) {
222 set_String(&bm->tags, tv->value.string); 312 set_String(&bm->tags, tv->value.string);
313 if (strstr(cstr_String(&bm->tags), "subscribed")) {
314 printf("a\n");
315 }
316 unpackDotTags_Bookmark_(bm);
223 } 317 }
224 else if (!cmp_String(key, "icon") && tv->type == int64_TomlType) { 318 else if (!cmp_String(key, "icon") && tv->type == int64_TomlType) {
225 bm->icon = (iChar) tv->value.int64; 319 bm->icon = (iChar) tv->value.int64;
@@ -292,7 +386,6 @@ void load_Bookmarks(iBookmarks *d, const char *dirPath) {
292 386
293void save_Bookmarks(const iBookmarks *d, const char *dirPath) { 387void save_Bookmarks(const iBookmarks *d, const char *dirPath) {
294 lock_Mutex(d->mtx); 388 lock_Mutex(d->mtx);
295 iRegExp *remotePattern = iClob(new_RegExp("\\bremote\\b", caseSensitive_RegExpOption));
296 iFile *f = newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_)); 389 iFile *f = newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_));
297 if (open_File(f, writeOnly_FileMode | text_FileMode)) { 390 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
298 iString *str = collectNew_String(); 391 iString *str = collectNew_String();
@@ -300,13 +393,12 @@ void save_Bookmarks(const iBookmarks *d, const char *dirPath) {
300 writeData_File(f, cstr_String(str), size_String(str)); 393 writeData_File(f, cstr_String(str), size_String(str));
301 iConstForEach(Hash, i, &d->bookmarks) { 394 iConstForEach(Hash, i, &d->bookmarks) {
302 const iBookmark *bm = (const iBookmark *) i.value; 395 const iBookmark *bm = (const iBookmark *) i.value;
303 iRegExpMatch m; 396 if (bm->flags & remote_BookmarkFlag) {
304 init_RegExpMatch(&m);
305 if (matchString_RegExp(remotePattern, &bm->tags, &m)) {
306 /* Remote bookmarks are not saved. */ 397 /* Remote bookmarks are not saved. */
307 continue; 398 continue;
308 } 399 }
309 iBeginCollect(); 400 iBeginCollect();
401 const iString *packedTags = collect_String(packedDotTags_Bookmark_(bm));
310 format_String(str, 402 format_String(str,
311 "[%d]\n" 403 "[%d]\n"
312 "url = \"%s\"\n" 404 "url = \"%s\"\n"
@@ -317,7 +409,7 @@ void save_Bookmarks(const iBookmarks *d, const char *dirPath) {
317 id_Bookmark(bm), 409 id_Bookmark(bm),
318 cstrCollect_String(quote_String(&bm->url, iFalse)), 410 cstrCollect_String(quote_String(&bm->url, iFalse)),
319 cstrCollect_String(quote_String(&bm->title, iFalse)), 411 cstrCollect_String(quote_String(&bm->title, iFalse)),
320 cstrCollect_String(quote_String(&bm->tags, iFalse)), 412 cstrCollect_String(quote_String(packedTags, iFalse)),
321 bm->icon, 413 bm->icon,
322 seconds_Time(&bm->when), 414 seconds_Time(&bm->when),
323 cstrCollect_String(format_Time(&bm->when, "%Y-%m-%d"))); 415 cstrCollect_String(format_Time(&bm->when, "%Y-%m-%d")));
@@ -397,7 +489,7 @@ iBool updateBookmarkIcon_Bookmarks(iBookmarks *d, const iString *url, iChar icon
397 const uint32_t id = findUrl_Bookmarks(d, url); 489 const uint32_t id = findUrl_Bookmarks(d, url);
398 if (id) { 490 if (id) {
399 iBookmark *bm = get_Bookmarks(d, id); 491 iBookmark *bm = get_Bookmarks(d, id);
400 if (!hasTag_Bookmark(bm, remote_BookmarkTag) && !hasTag_Bookmark(bm, userIcon_BookmarkTag)) { 492 if (~bm->flags & remote_BookmarkFlag && ~bm->flags & userIcon_BookmarkFlag) {
401 if (icon != bm->icon) { 493 if (icon != bm->icon) {
402 bm->icon = icon; 494 bm->icon = icon;
403 changed = iTrue; 495 changed = iTrue;
@@ -422,19 +514,13 @@ iChar siteIcon_Bookmarks(const iBookmarks *d, const iString *url) {
422 if (isEmpty_String(url)) { 514 if (isEmpty_String(url)) {
423 return 0; 515 return 0;
424 } 516 }
425 static iRegExp *tagPattern_;
426 if (!tagPattern_) {
427 tagPattern_ = new_RegExp("\\b" userIcon_BookmarkTag "\\b", caseSensitive_RegExpOption);
428 }
429 const iRangecc urlRoot = urlRoot_String(url); 517 const iRangecc urlRoot = urlRoot_String(url);
430 size_t matchingSize = iInvalidSize; /* we'll pick the shortest matching */ 518 size_t matchingSize = iInvalidSize; /* we'll pick the shortest matching */
431 iChar icon = 0; 519 iChar icon = 0;
432 lock_Mutex(d->mtx); 520 lock_Mutex(d->mtx);
433 iConstForEach(Hash, i, &d->bookmarks) { 521 iConstForEach(Hash, i, &d->bookmarks) {
434 const iBookmark *bm = (const iBookmark *) i.value; 522 const iBookmark *bm = (const iBookmark *) i.value;
435 iRegExpMatch m; 523 if (bm->icon && bm->flags & userIcon_BookmarkFlag) {
436 init_RegExpMatch(&m);
437 if (bm->icon && matchString_RegExp(tagPattern_, &bm->tags, &m)) {
438 const iRangecc bmRoot = urlRoot_String(&bm->url); 524 const iRangecc bmRoot = urlRoot_String(&bm->url);
439 if (equalRangeCase_Rangecc(urlRoot, bmRoot)) { 525 if (equalRangeCase_Rangecc(urlRoot, bmRoot)) {
440 const size_t n = size_String(&bm->url); 526 const size_t n = size_String(&bm->url);
@@ -467,10 +553,15 @@ void reorder_Bookmarks(iBookmarks *d, uint32_t id, int newOrder) {
467 unlock_Mutex(d->mtx); 553 unlock_Mutex(d->mtx);
468} 554}
469 555
470iBool filterTagsRegExp_Bookmarks(void *regExp, const iBookmark *bm) { 556//iBool filterTagsRegExp_Bookmarks(void *regExp, const iBookmark *bm) {
471 iRegExpMatch m; 557// iRegExpMatch m;
472 init_RegExpMatch(&m); 558// init_RegExpMatch(&m);
473 return matchString_RegExp(regExp, &bm->tags, &m); 559// return matchString_RegExp(regExp, &bm->tags, &m);
560//}
561
562iBool filterHomepage_Bookmark(void *d, const iBookmark *bm) {
563 iUnused(d);
564 return (bm->flags & homepage_BookmarkFlag) != 0;
474} 565}
475 566
476static iBool matchUrl_(void *url, const iBookmark *bm) { 567static iBool matchUrl_(void *url, const iBookmark *bm) {
@@ -618,7 +709,7 @@ const iString *bookmarkListPage_Bookmarks(const iBookmarks *d, enum iBookmarkLis
618 709
619static iBool isRemoteSource_Bookmark_(void *context, const iBookmark *d) { 710static iBool isRemoteSource_Bookmark_(void *context, const iBookmark *d) {
620 iUnused(context); 711 iUnused(context);
621 return hasTag_Bookmark(d, remoteSource_BookmarkTag); 712 return (d->flags & remoteSource_BookmarkFlag) != 0;
622} 713}
623 714
624void remoteRequestFinished_Bookmarks_(iBookmarks *d, iGmRequest *req) { 715void remoteRequestFinished_Bookmarks_(iBookmarks *d, iGmRequest *req) {
@@ -642,7 +733,6 @@ void requestFinished_Bookmarks(iBookmarks *d, iGmRequest *req) {
642 initCurrent_Time(&now); 733 initCurrent_Time(&now);
643 iRegExp *linkPattern = new_RegExp("^=>\\s*([^\\s]+)(\\s+(.*))?", 0); 734 iRegExp *linkPattern = new_RegExp("^=>\\s*([^\\s]+)(\\s+(.*))?", 0);
644 iString src; 735 iString src;
645 const iString *remoteTag = collectNewCStr_String("remote");
646 initBlock_String(&src, body_GmRequest(req)); 736 initBlock_String(&src, body_GmRequest(req));
647 iRangecc srcLine = iNullRange; 737 iRangecc srcLine = iNullRange;
648 while (nextSplit_Rangecc(range_String(&src), "\n", &srcLine)) { 738 while (nextSplit_Rangecc(range_String(&src), "\n", &srcLine)) {
@@ -660,8 +750,9 @@ void requestFinished_Bookmarks(iBookmarks *d, iGmRequest *req) {
660 if (isEmpty_String(titleStr)) { 750 if (isEmpty_String(titleStr)) {
661 setRange_String(titleStr, urlHost_String(urlStr)); 751 setRange_String(titleStr, urlHost_String(urlStr));
662 } 752 }
663 const uint32_t bmId = add_Bookmarks(d, absUrl, titleStr, remoteTag, 0x2913); 753 const uint32_t bmId = add_Bookmarks(d, absUrl, titleStr, NULL, 0x2913);
664 iBookmark *bm = get_Bookmarks(d, bmId); 754 iBookmark *bm = get_Bookmarks(d, bmId);
755 bm->flags |= remote_BookmarkFlag;
665 bm->parentId = *(uint32_t *) userData_Object(req); 756 bm->parentId = *(uint32_t *) userData_Object(req);
666 delete_String(titleStr); 757 delete_String(titleStr);
667 } 758 }
@@ -690,7 +781,7 @@ void fetchRemote_Bookmarks(iBookmarks *d) {
690 size_t numRemoved = 0; 781 size_t numRemoved = 0;
691 iForEach(Hash, i, &d->bookmarks) { 782 iForEach(Hash, i, &d->bookmarks) {
692 iBookmark *bm = (iBookmark *) i.value; 783 iBookmark *bm = (iBookmark *) i.value;
693 if (hasTag_Bookmark(bm, remote_BookmarkTag)) { 784 if (bm->flags & remote_BookmarkFlag) {
694 remove_HashIterator(&i); 785 remove_HashIterator(&i);
695 delete_Bookmark(bm); 786 delete_Bookmark(bm);
696 numRemoved++; 787 numRemoved++;