diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bookmarks.c | 141 | ||||
-rw-r--r-- | src/bookmarks.h | 3 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 45 |
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 | ||
35 | void init_Bookmark(iBookmark *d) { | 36 | void 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 | ||
43 | void deinit_Bookmark(iBookmark *d) { | 45 | void deinit_Bookmark(iBookmark *d) { |
@@ -83,7 +85,8 @@ static int cmpTitleAscending_Bookmark_(const iBookmark **a, const iBookmark **b) | |||
83 | 85 | ||
84 | /*----------------------------------------------------------------------------------------------*/ | 86 | /*----------------------------------------------------------------------------------------------*/ |
85 | 87 | ||
86 | static const char *fileName_Bookmarks_ = "bookmarks.txt"; | 88 | static const char *oldFileName_Bookmarks_ = "bookmarks.txt"; |
89 | static const char *fileName_Bookmarks_ = "bookmarks.ini"; /* since v1.7 (TOML subset) */ | ||
87 | 90 | ||
88 | struct Impl_Bookmarks { | 91 | struct 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 | ||
129 | static void insertId_Bookmarks_(iBookmarks *d, iBookmark *bookmark, int id) { | ||
130 | bookmark->node.key = id; | ||
131 | insert_Hash(&d->bookmarks, &bookmark->node); | ||
132 | } | ||
133 | |||
126 | static void insert_Bookmarks_(iBookmarks *d, iBookmark *bookmark) { | 134 | static 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 | ||
133 | void load_Bookmarks(iBookmarks *d, const char *dirPath) { | 140 | static 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 | |||
181 | iDeclareType(BookmarkLoader) | ||
182 | |||
183 | struct Impl_BookmarkLoader { | ||
184 | iTomlParser *toml; | ||
185 | iBookmarks * bookmarks; | ||
186 | iBookmark * bm; | ||
187 | }; | ||
188 | |||
189 | static 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 | |||
203 | static 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 | |||
233 | static 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 | |||
240 | static void deinit_BookmarkLoader(iBookmarkLoader *d) { | ||
241 | delete_TomlParser(d->toml); | ||
242 | } | ||
243 | |||
244 | static 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 | |||
250 | iDefineTypeConstructionArgs(BookmarkLoader, (iBookmarks *b), b) | ||
251 | |||
252 | /*----------------------------------------------------------------------------------------------*/ | ||
253 | |||
254 | void 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 | |||
173 | void save_Bookmarks(const iBookmarks *d, const char *dirPath) { | 274 | void 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 | ||
53 | iLocalDef uint32_t id_Bookmark (const iBookmark *d) { return d->node.key; } | 54 | iLocalDef 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 | ||
119 | static int cmpTitle_Bookmark_(const iBookmark **a, const iBookmark **b) { | 119 | static 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 | ||
139 | static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label, | 141 | static 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); |