diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-19 10:26:23 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-19 10:26:23 +0200 |
commit | 3bde9d383a107760ee56ebc7f780154bfd3f3de7 (patch) | |
tree | 4703b0625c68a30be76251c5349659f3c9d14a21 | |
parent | 6e54b0b5449e607417b2bd5b68ac4292277bad4b (diff) |
Remote bookmark sources
Bookmarks tagged "remotesource" are fetched and all links are treated as remote bookmarks.
Remote bookmarks are not saved locally.
-rw-r--r-- | src/app.c | 8 | ||||
-rw-r--r-- | src/bookmarks.c | 111 | ||||
-rw-r--r-- | src/bookmarks.h | 5 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 23 | ||||
-rw-r--r-- | src/ui/util.c | 8 | ||||
-rw-r--r-- | src/ui/window.c | 1 |
6 files changed, 147 insertions, 9 deletions
@@ -1363,6 +1363,14 @@ iBool handleCommand_App(const char *cmd) { | |||
1363 | makeFeedSettings_Widget(findUrl_Bookmarks(d->bookmarks, url)); | 1363 | makeFeedSettings_Widget(findUrl_Bookmarks(d->bookmarks, url)); |
1364 | return iTrue; | 1364 | return iTrue; |
1365 | } | 1365 | } |
1366 | else if (equal_Command(cmd, "bookmarks.reload.remote")) { | ||
1367 | fetchRemote_Bookmarks(bookmarks_App()); | ||
1368 | return iTrue; | ||
1369 | } | ||
1370 | else if (equal_Command(cmd, "bookmarks.request.finished")) { | ||
1371 | requestFinished_Bookmarks(bookmarks_App(), pointerLabel_Command(cmd, "req")); | ||
1372 | return iTrue; | ||
1373 | } | ||
1366 | else if (equal_Command(cmd, "bookmarks.changed")) { | 1374 | else if (equal_Command(cmd, "bookmarks.changed")) { |
1367 | save_Bookmarks(d->bookmarks, dataDir_App_); | 1375 | save_Bookmarks(d->bookmarks, dataDir_App_); |
1368 | return iFalse; | 1376 | return iFalse; |
diff --git a/src/bookmarks.c b/src/bookmarks.c index 72c5ad04..27909349 100644 --- a/src/bookmarks.c +++ b/src/bookmarks.c | |||
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "bookmarks.h" | 23 | #include "bookmarks.h" |
24 | #include "visited.h" | 24 | #include "visited.h" |
25 | #include "gmrequest.h" | ||
25 | #include "app.h" | 26 | #include "app.h" |
26 | 27 | ||
27 | #include <the_Foundation/file.h> | 28 | #include <the_Foundation/file.h> |
@@ -82,9 +83,10 @@ static int cmpTitleAscending_Bookmark_(const iBookmark **a, const iBookmark **b) | |||
82 | static const char *fileName_Bookmarks_ = "bookmarks.txt"; | 83 | static const char *fileName_Bookmarks_ = "bookmarks.txt"; |
83 | 84 | ||
84 | struct Impl_Bookmarks { | 85 | struct Impl_Bookmarks { |
85 | iMutex *mtx; | 86 | iMutex * mtx; |
86 | int idEnum; | 87 | int idEnum; |
87 | iHash bookmarks; /* bookmark ID is the hash key */ | 88 | iHash bookmarks; /* bookmark ID is the hash key */ |
89 | iPtrArray remoteRequests; | ||
88 | }; | 90 | }; |
89 | 91 | ||
90 | iDefineTypeConstruction(Bookmarks) | 92 | iDefineTypeConstruction(Bookmarks) |
@@ -93,9 +95,15 @@ void init_Bookmarks(iBookmarks *d) { | |||
93 | d->mtx = new_Mutex(); | 95 | d->mtx = new_Mutex(); |
94 | d->idEnum = 0; | 96 | d->idEnum = 0; |
95 | init_Hash(&d->bookmarks); | 97 | init_Hash(&d->bookmarks); |
98 | init_PtrArray(&d->remoteRequests); | ||
96 | } | 99 | } |
97 | 100 | ||
98 | void deinit_Bookmarks(iBookmarks *d) { | 101 | void deinit_Bookmarks(iBookmarks *d) { |
102 | iForEach(PtrArray, i, &d->remoteRequests) { | ||
103 | cancel_GmRequest(i.ptr); | ||
104 | iRelease(i.ptr); | ||
105 | } | ||
106 | deinit_PtrArray(&d->remoteRequests); | ||
99 | clear_Bookmarks(d); | 107 | clear_Bookmarks(d); |
100 | deinit_Hash(&d->bookmarks); | 108 | deinit_Hash(&d->bookmarks); |
101 | delete_Mutex(d->mtx); | 109 | delete_Mutex(d->mtx); |
@@ -154,15 +162,23 @@ void load_Bookmarks(iBookmarks *d, const char *dirPath) { | |||
154 | } | 162 | } |
155 | } | 163 | } |
156 | iRelease(f); | 164 | iRelease(f); |
165 | fetchRemote_Bookmarks(d); | ||
157 | } | 166 | } |
158 | 167 | ||
159 | void save_Bookmarks(const iBookmarks *d, const char *dirPath) { | 168 | void save_Bookmarks(const iBookmarks *d, const char *dirPath) { |
160 | lock_Mutex(d->mtx); | 169 | lock_Mutex(d->mtx); |
170 | iRegExp *remotePattern = iClob(new_RegExp("\\bremote\\b", caseSensitive_RegExpOption)); | ||
161 | iFile *f = newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_)); | 171 | iFile *f = newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_)); |
162 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { | 172 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { |
163 | iString *str = collectNew_String(); | 173 | iString *str = collectNew_String(); |
164 | iConstForEach(Hash, i, &d->bookmarks) { | 174 | iConstForEach(Hash, i, &d->bookmarks) { |
165 | const iBookmark *bm = (const iBookmark *) i.value; | 175 | const iBookmark *bm = (const iBookmark *) i.value; |
176 | iRegExpMatch m; | ||
177 | init_RegExpMatch(&m); | ||
178 | if (matchString_RegExp(remotePattern, &bm->tags, &m)) { | ||
179 | /* Remote bookmarks are not saved. */ | ||
180 | continue; | ||
181 | } | ||
166 | format_String(str, | 182 | format_String(str, |
167 | "%08x %lf %s\n%s\n%s\n", | 183 | "%08x %lf %s\n%s\n%s\n", |
168 | bm->icon, | 184 | bm->icon, |
@@ -327,3 +343,92 @@ const iString *bookmarkListPage_Bookmarks(const iBookmarks *d, enum iBookmarkLis | |||
327 | } | 343 | } |
328 | return str; | 344 | return str; |
329 | } | 345 | } |
346 | |||
347 | static iBool isRemoteSource_Bookmark_(void *context, const iBookmark *d) { | ||
348 | iUnused(context); | ||
349 | return hasTag_Bookmark(d, "remotesource"); | ||
350 | } | ||
351 | |||
352 | void remoteRequestFinished_Bookmarks_(iBookmarks *d, iGmRequest *req) { | ||
353 | iUnused(d); | ||
354 | postCommandf_App("bookmarks.request.finished req:%p", req); | ||
355 | } | ||
356 | |||
357 | void requestFinished_Bookmarks(iBookmarks *d, iGmRequest *req) { | ||
358 | iBool found = iFalse; | ||
359 | iForEach(PtrArray, i, &d->remoteRequests) { | ||
360 | if (i.ptr == req) { | ||
361 | remove_PtrArrayIterator(&i); | ||
362 | found = iTrue; | ||
363 | break; | ||
364 | } | ||
365 | } | ||
366 | iAssert(found); | ||
367 | /* Parse all links in the result. */ | ||
368 | if (isSuccess_GmStatusCode(status_GmRequest(req))) { | ||
369 | iTime now; | ||
370 | initCurrent_Time(&now); | ||
371 | iRegExp *linkPattern = new_RegExp("^=>\\s*([^\\s]+)\\s+(.*)", 0); | ||
372 | iString src; | ||
373 | const iString *remoteTag = collectNewCStr_String("remote"); | ||
374 | initBlock_String(&src, body_GmRequest(req)); | ||
375 | iRangecc srcLine = iNullRange; | ||
376 | while (nextSplit_Rangecc(range_String(&src), "\n", &srcLine)) { | ||
377 | iRangecc line = srcLine; | ||
378 | trimEnd_Rangecc(&line); | ||
379 | iRegExpMatch m; | ||
380 | init_RegExpMatch(&m); | ||
381 | if (matchRange_RegExp(linkPattern, line, &m)) { | ||
382 | const iRangecc url = capturedRange_RegExpMatch(&m, 1); | ||
383 | const iRangecc title = capturedRange_RegExpMatch(&m, 2); | ||
384 | iString * urlStr = newRange_String(url); | ||
385 | const iString *absUrl = absoluteUrl_String(url_GmRequest(req), urlStr); | ||
386 | if (!findUrl_Bookmarks(d, absUrl)) { | ||
387 | iString *titleStr = newRange_String(title); | ||
388 | add_Bookmarks(d, absUrl, titleStr, remoteTag, 0x2601 /* cloud */); | ||
389 | delete_String(titleStr); | ||
390 | } | ||
391 | delete_String(urlStr); | ||
392 | } | ||
393 | } | ||
394 | deinit_String(&src); | ||
395 | iRelease(linkPattern); | ||
396 | } | ||
397 | else { | ||
398 | /* TODO: Show error? */ | ||
399 | } | ||
400 | iRelease(req); | ||
401 | if (isEmpty_PtrArray(&d->remoteRequests)) { | ||
402 | postCommand_App("bookmarks.changed"); | ||
403 | } | ||
404 | } | ||
405 | |||
406 | void fetchRemote_Bookmarks(iBookmarks *d) { | ||
407 | if (!isEmpty_PtrArray(&d->remoteRequests)) { | ||
408 | return; /* Already ongoing. */ | ||
409 | } | ||
410 | lock_Mutex(d->mtx); | ||
411 | /* Remove all current remote bookmarks. */ { | ||
412 | size_t numRemoved = 0; | ||
413 | iForEach(Hash, i, &d->bookmarks) { | ||
414 | iBookmark *bm = (iBookmark *) i.value; | ||
415 | if (hasTag_Bookmark(bm, "remote")) { | ||
416 | delete_Bookmark(bm); | ||
417 | remove_HashIterator(&i); | ||
418 | numRemoved++; | ||
419 | } | ||
420 | } | ||
421 | if (numRemoved) { | ||
422 | postCommand_App("bookmarks.changed"); | ||
423 | } | ||
424 | } | ||
425 | iConstForEach(PtrArray, i, list_Bookmarks(d, NULL, isRemoteSource_Bookmark_, NULL)) { | ||
426 | const iBookmark *bm = i.ptr; | ||
427 | iGmRequest *req = new_GmRequest(certs_App()); | ||
428 | pushBack_PtrArray(&d->remoteRequests, req); | ||
429 | setUrl_GmRequest(req, &bm->url); | ||
430 | iConnect(GmRequest, req, finished, req, remoteRequestFinished_Bookmarks_); | ||
431 | submit_GmRequest(req); | ||
432 | } | ||
433 | unlock_Mutex(d->mtx); | ||
434 | } | ||
diff --git a/src/bookmarks.h b/src/bookmarks.h index 8273d7bc..2975b082 100644 --- a/src/bookmarks.h +++ b/src/bookmarks.h | |||
@@ -27,6 +27,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
27 | #include <the_Foundation/string.h> | 27 | #include <the_Foundation/string.h> |
28 | #include <the_Foundation/time.h> | 28 | #include <the_Foundation/time.h> |
29 | 29 | ||
30 | iDeclareType(GmRequest) | ||
31 | |||
30 | iDeclareType(Bookmark) | 32 | iDeclareType(Bookmark) |
31 | iDeclareTypeConstruction(Bookmark) | 33 | iDeclareTypeConstruction(Bookmark) |
32 | 34 | ||
@@ -54,6 +56,9 @@ void clear_Bookmarks (iBookmarks *); | |||
54 | void load_Bookmarks (iBookmarks *, const char *dirPath); | 56 | void load_Bookmarks (iBookmarks *, const char *dirPath); |
55 | void save_Bookmarks (const iBookmarks *, const char *dirPath); | 57 | void save_Bookmarks (const iBookmarks *, const char *dirPath); |
56 | 58 | ||
59 | void fetchRemote_Bookmarks (iBookmarks *); | ||
60 | void requestFinished_Bookmarks (iBookmarks *, iGmRequest *req); | ||
61 | |||
57 | void add_Bookmarks (iBookmarks *, const iString *url, const iString *title, | 62 | void add_Bookmarks (iBookmarks *, const iString *url, const iString *title, |
58 | const iString *tags, iChar icon); | 63 | const iString *tags, iChar icon); |
59 | iBool remove_Bookmarks (iBookmarks *, uint32_t id); | 64 | iBool remove_Bookmarks (iBookmarks *, uint32_t id); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index c2672646..3b7e7d4b 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -199,6 +199,7 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
199 | case bookmarks_SidebarMode: { | 199 | case bookmarks_SidebarMode: { |
200 | iRegExp *homeTag = iClob(new_RegExp("\\bhomepage\\b", caseSensitive_RegExpOption)); | 200 | iRegExp *homeTag = iClob(new_RegExp("\\bhomepage\\b", caseSensitive_RegExpOption)); |
201 | iRegExp *subTag = iClob(new_RegExp("\\bsubscribed\\b", caseSensitive_RegExpOption)); | 201 | iRegExp *subTag = iClob(new_RegExp("\\bsubscribed\\b", caseSensitive_RegExpOption)); |
202 | iRegExp *remoteSourceTag = iClob(new_RegExp("\\bremotesource\\b", caseSensitive_RegExpOption)); | ||
202 | iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTitle_Bookmark_, NULL, NULL)) { | 203 | iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTitle_Bookmark_, NULL, NULL)) { |
203 | const iBookmark *bm = i.ptr; | 204 | const iBookmark *bm = i.ptr; |
204 | iSidebarItem *item = new_SidebarItem(); | 205 | iSidebarItem *item = new_SidebarItem(); |
@@ -216,6 +217,10 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
216 | if (matchString_RegExp(homeTag, &bm->tags, &m)) { | 217 | if (matchString_RegExp(homeTag, &bm->tags, &m)) { |
217 | appendChar_String(&item->meta, 0x1f3e0); | 218 | appendChar_String(&item->meta, 0x1f3e0); |
218 | } | 219 | } |
220 | init_RegExpMatch(&m); | ||
221 | if (matchString_RegExp(remoteSourceTag, &bm->tags, &m)) { | ||
222 | appendChar_String(&item->meta, 0x2601); | ||
223 | } | ||
219 | } | 224 | } |
220 | addItem_ListWidget(d->list, item); | 225 | addItem_ListWidget(d->list, item); |
221 | iRelease(item); | 226 | iRelease(item); |
@@ -228,11 +233,14 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
228 | { "Edit Bookmark...", 0, 0, "bookmark.edit" }, | 233 | { "Edit Bookmark...", 0, 0, "bookmark.edit" }, |
229 | { "Copy URL", 0, 0, "bookmark.copy" }, | 234 | { "Copy URL", 0, 0, "bookmark.copy" }, |
230 | { "---", 0, 0, NULL }, | 235 | { "---", 0, 0, NULL }, |
231 | { "Subscribe to Feed", 0, 0, "bookmark.tag tag:subscribed" }, | 236 | { "?", 0, 0, "bookmark.tag tag:subscribed" }, |
232 | { "", 0, 0, "bookmark.tag tag:homepage" }, | 237 | { "?", 0, 0, "bookmark.tag tag:homepage" }, |
238 | { "?", 0, 0, "bookmark.tag tag:remotesource" }, | ||
239 | { "---", 0, 0, NULL }, | ||
240 | { uiTextCaution_ColorEscape "Delete Bookmark", 0, 0, "bookmark.delete" }, | ||
233 | { "---", 0, 0, NULL }, | 241 | { "---", 0, 0, NULL }, |
234 | { uiTextCaution_ColorEscape "Delete Bookmark", 0, 0, "bookmark.delete" } }, | 242 | { "Refresh Remote Bookmarks", 0, 0, "bookmarks.reload.remote" } }, |
235 | 10); | 243 | 13); |
236 | break; | 244 | break; |
237 | } | 245 | } |
238 | case history_SidebarMode: { | 246 | case history_SidebarMode: { |
@@ -1006,6 +1014,13 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1006 | ? "Unsubscribe from Feed" | 1014 | ? "Unsubscribe from Feed" |
1007 | : "Subscribe to Feed"); | 1015 | : "Subscribe to Feed"); |
1008 | } | 1016 | } |
1017 | menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:remotesource"); | ||
1018 | if (menuItem) { | ||
1019 | setTextCStr_LabelWidget(menuItem, | ||
1020 | hasTag_Bookmark(bm, "remotesource") | ||
1021 | ? "Remove Bookmark Source" | ||
1022 | : "Use as Bookmark Source"); | ||
1023 | } | ||
1009 | } | 1024 | } |
1010 | } | 1025 | } |
1011 | else if (d->mode == feeds_SidebarMode && d->contextItem) { | 1026 | else if (d->mode == feeds_SidebarMode && d->contextItem) { |
diff --git a/src/ui/util.c b/src/ui/util.c index 9bd40e80..1118758b 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -376,7 +376,9 @@ iWidget *addAction_Widget(iWidget *parent, int key, int kmods, const char *comma | |||
376 | static iBool isCommandIgnoredByMenus_(const char *cmd) { | 376 | static iBool isCommandIgnoredByMenus_(const char *cmd) { |
377 | return equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.player.update") || | 377 | return equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.player.update") || |
378 | startsWith_CStr(cmd, "feeds.update.") || | 378 | startsWith_CStr(cmd, "feeds.update.") || |
379 | equal_Command(cmd, "document.request.updated") || equal_Command(cmd, "window.resized") || | 379 | equal_Command(cmd, "document.request.updated") || |
380 | equal_Command(cmd, "bookmarks.request.finished") || | ||
381 | equal_Command(cmd, "window.resized") || | ||
380 | equal_Command(cmd, "window.reload.update") || | 382 | equal_Command(cmd, "window.reload.update") || |
381 | (equal_Command(cmd, "mouse.clicked") && !arg_Command(cmd)); /* button released */ | 383 | (equal_Command(cmd, "mouse.clicked") && !arg_Command(cmd)); /* button released */ |
382 | } | 384 | } |
@@ -893,7 +895,9 @@ void updateValueInput_Widget(iWidget *d, const char *title, const char *prompt) | |||
893 | 895 | ||
894 | static iBool messageHandler_(iWidget *msg, const char *cmd) { | 896 | static iBool messageHandler_(iWidget *msg, const char *cmd) { |
895 | /* Almost any command dismisses the sheet. */ | 897 | /* Almost any command dismisses the sheet. */ |
896 | if (!(equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.player.update") || | 898 | if (!(equal_Command(cmd, "media.updated") || |
899 | equal_Command(cmd, "media.player.update") || | ||
900 | equal_Command(cmd, "bookmarks.request.finished") || | ||
897 | equal_Command(cmd, "document.request.updated") || startsWith_CStr(cmd, "window."))) { | 901 | equal_Command(cmd, "document.request.updated") || startsWith_CStr(cmd, "window."))) { |
898 | destroy_Widget(msg); | 902 | destroy_Widget(msg); |
899 | } | 903 | } |
diff --git a/src/ui/window.c b/src/ui/window.c index 6ec4a4f1..bc0a622e 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -167,6 +167,7 @@ static iMenuItem bookmarksMenuItems_[] = { | |||
167 | { "List All", 0, 0, "open url:about:bookmarks" }, | 167 | { "List All", 0, 0, "open url:about:bookmarks" }, |
168 | { "List by Tag", 0, 0, "open url:about:bookmarks?tags" }, | 168 | { "List by Tag", 0, 0, "open url:about:bookmarks?tags" }, |
169 | { "List by Creation Time", 0, 0, "open url:about:bookmarks?created" }, | 169 | { "List by Creation Time", 0, 0, "open url:about:bookmarks?created" }, |
170 | { "Refresh Remote Bookmarks", 0, 0, "bookmarks.reload.remote" }, | ||
170 | { "---", 0, 0, NULL }, | 171 | { "---", 0, 0, NULL }, |
171 | { "Subscribe to This Page...", subscribeToPage_KeyModifier, "feeds.subscribe" }, | 172 | { "Subscribe to This Page...", subscribeToPage_KeyModifier, "feeds.subscribe" }, |
172 | { "---", 0, 0, NULL }, | 173 | { "---", 0, 0, NULL }, |